From: Kern Sibbald Date: Thu, 27 Mar 2014 17:58:16 +0000 (+0100) Subject: Add Baculum X-Git-Tag: Release-7.0.0~6 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=579980f1d612f15214019ff8cd6425f3b47c59bb;p=bacula%2Fbacula Add Baculum --- diff --git a/gui/baculum/.gitignore b/gui/baculum/.gitignore new file mode 100644 index 0000000000..76773ce1c6 --- /dev/null +++ b/gui/baculum/.gitignore @@ -0,0 +1,4 @@ +assets/* +protected/Data/baculum.log* +protected/Data/settings.conf +protected/runtime/* diff --git a/gui/baculum/.htaccess b/gui/baculum/.htaccess new file mode 100644 index 0000000000..d4d3d44ba2 --- /dev/null +++ b/gui/baculum/.htaccess @@ -0,0 +1,5 @@ +Options +FollowSymLinks +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ index.php/$1 [L] diff --git a/gui/baculum/AUTHORS b/gui/baculum/AUTHORS new file mode 100644 index 0000000000..69ff988907 --- /dev/null +++ b/gui/baculum/AUTHORS @@ -0,0 +1 @@ +Marcin Haba \ No newline at end of file diff --git a/gui/baculum/INSTALL b/gui/baculum/INSTALL new file mode 100644 index 0000000000..64b84b530d --- /dev/null +++ b/gui/baculum/INSTALL @@ -0,0 +1,186 @@ +1. Baculum overview +2. Baculum operating system environment + 2.1 General requirements + 2.2 Linux Fedora + 2.3 Linux Debian +3. Preparing Baculum files +4. Preparing Apache Web Server +5. Example configuration VirtualHost + +========================================= + +1. Baculum overview + +Baculum is Bacula web based interface. It enables Bacula administration +functions such as: + +- Running backup and restore tasks +- Monitoring Bacula services by getting status these services +- Bacula console available via web interface +- Multiple Directors support +- Volumes managenment including labeling new volumes +- Basic storage daemon operations on volumes (mount, umount, release actions) +- Easy in use configuration and restore wizards +- other features... + +Note! +This Baculum release does not support Verify kind of jobs. Support for Verify +jobs will be implemented in next Baculum release. + + +2. Baculum operating system environment + + +2.1 General requirements + + +Environmnent for Baculum installation should have next components: + +- Web Server - with mod_rewrite module loaded. Baculum has been +tested on Apache HTTP Server. + +- PHP 5.3 or higher - as web server module. PHP CLI interpreter is not needed. +PHP should have installed next modules: + * PDO PHP support - depending on your catalog database: PDO PostgreSQL, +PDO MySQL or PDO SQLite. + * BCMath PHP module. + * cURL PHP module. + * MB String PHP module. +- Bconsole - configured Bacula text based console +- Access to Bacula catalog database - computer with Baculum installation should be +able to connection to Bacula catalog database. There is not need to install Baculum +on server with Bacula DB because Baculum can communicate with Bacula database +located on remote server. + +Above requirements are validated during Baculum start. So if in installation +environment there lacks some from these components then you will be informed +about it. + + +2.2 Linux Fedora + + +Packages required for run Baculum in Fedora environemnt can be installed by command: + +# yum install httpd \ +php \ +php-common \ +php-pdo \ +php-pgsql \ +php-mysqlnd \ +php-mbstring \ +php-bcmath + +In Fedora 20 PHP module for cURL support is compiled in PHP. + +In case of using SELinux functionality in Baculum environment, recommended way +is switching "httpd_t" SELinux security doman to permissive domain like below: + +# semanage permissive -a httpd_t + +Suggested method for Baculum webGUI access to Bacula Console (bconsole) is using +"sudo" functionality for that. In this case there is need to add to /etc/sudoers file two +lines according next template: + +Defaults:apache_user !requiretty +apache_user ALL= NOPASSWD: bconsole_path + +For example for user called "apache" from that HTTP service working with Baculum +there is need to add line like below: + +Defaults:apache !requiretty +apache ALL= NOPASSWD: /usr/sbin/bconsole + + +2.3 Linux Debian + + +Packages required for run Baculum in Debian environemnt can be installed by command: + +apt-get install apache2 \ +libapache2-mod-php5 \ +php5 \ +php5-pgsql \ +php5-mysql \ +php5-curl + +In Debian 7.4.0, PHP modules for BCMath and MultiByte String support are compiled in PHP. + +There is need to create symbolic link as below, for enable mod_rewrite module in Apache. + +# ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load + +Suggested method for Baculum webGUI access to Bacula Console (bconsole) is using +"sudo" functionality for that. In this case there is need to add to /etc/sudoers below +line according next template: + +apache_user ALL= NOPASSWD: bconsole_path + +For example for user called "www-data" from that HTTP service working with Baculum +there is need to add line like below: + +www-data ALL= NOPASSWD: /usr/sbin/bconsole + + +3. Preparing Baculum files + + +After downloading and unpacking Baculum sources archive there is need to set write +permissions for web server for three below directories: + +/[files base location]/assets +/[files base location]/protected/Data +/[files base location]/protected/runtime + +These directories permissions are validated during Baculum start. If web server +will not be able to write data in some from above directries then you will be +informed about this fact. + + +4. Preparing Apache Web Server + + +First there is need to configure authentication to Baculum from web server side. +Baculum supports HTTP Basic authentication from web server. + +In Apache web server this authentication method may be realizing by next directives +declaration in Apache configuration file: + +AuthType Basic +AuthUserFile /etc/apache2/passwords +Require valid-user + +File located in /etc/apache2/passwords is an example file which contains users and +passwords. For generating passwords file you may use htpasswd binary file in next +way: + +# htpasswd -c /etc/apache2/passwords someuser + +You will be asked about inputing password and retype password for user. + +NOTE! +"-c" switcher you should use only for new created passwords file. For existing +passwords file "-c" switcher should not be inputed. + + +5. Example configuration VirtualHost + + + + DocumentRoot /var/www/baculum + ServerName somehost.example.com + + AllowOverride All + AuthType Basic + AuthName MyPrivateFile + AuthUserFile /etc/apache2/passwords + Require valid-user + + + + +Above configuration should be writen in a new Apache configuration file. +for example, location for that can be put to /etc/apache2/sites-enabled/baculum.conf. + +In this way, configuration file baculum.conf will be loaded automaticly during +Apache server start. diff --git a/gui/baculum/LICENSE b/gui/baculum/LICENSE new file mode 100644 index 0000000000..99999c5793 --- /dev/null +++ b/gui/baculum/LICENSE @@ -0,0 +1,705 @@ +Baculum is licensed under the GNU Affero General Public License, version +3.0 as published by the Free Software Foundation, Inc. ("AGPLv3"). + +Additional Terms on the work licensed herein, pursuant to Section 7 of Affero +General Public License are as follows: + + 1. The name Bacula is a registered trademark of Kern Sibbald, and Kern + Sibbald hereby declines to grant a trademark license to "Bacula" + pursuant to AGPLv3, Section 7(e) without a separate agreement with Kern + Sibbald. + + 2. Pursuant to AGPLv3, Section 7(a), in addition to the warranty and + liability disclaimers already found in AGPLv3, the copyright holders + specifically disclaim any liability for accusations of patent + infringement by any third party. + + 3. Pursuant to AGPLv3, Section 7(b), the portions of the file AUTHORS that + are deemed to be specified reasonable legal notices and/or author + attributions shall be preserved in redistribution of source code and/or + modifications thereof. Furthermore, when the following notice appears in + a source code file, it must be preserved when source code is conveyed + and/or propagated: + + Copyright (C) 2013-2014 Marcin Haba + + The main author of Baculum is Marcin Haba, with contributions from many + others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + Bacula® is a registered trademark of Kern Sibbald. + + +###################################################################### +The entire AGPL is below, in the manuals distributed with the Bacula +documentation and can also be found online on the GNU web site at +www.bacula.org. You may also obtain a copy of the AGPL (or LGPL) by writing +to: Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +02110-1301 USA + + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + 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 Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/gui/baculum/assets/.gitignore b/gui/baculum/assets/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gui/baculum/framework/.htaccess b/gui/baculum/framework/.htaccess new file mode 100644 index 0000000000..e01983226f --- /dev/null +++ b/gui/baculum/framework/.htaccess @@ -0,0 +1 @@ +deny from all diff --git a/gui/baculum/framework/3rdParty/FirePHPCore/FirePHP.class.php b/gui/baculum/framework/3rdParty/FirePHPCore/FirePHP.class.php new file mode 100644 index 0000000000..7d3b95f633 --- /dev/null +++ b/gui/baculum/framework/3rdParty/FirePHPCore/FirePHP.class.php @@ -0,0 +1,1528 @@ + + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * For more information see: http://www.firephp.org/ + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ +class FirePHP { + + /** + * FirePHP version + * + * @var string + */ + const VERSION = '0.3'; + + /** + * Firebug LOG level + * + * Logs a message to firebug console. + * + * @var string + */ + const LOG = 'LOG'; + + /** + * Firebug INFO level + * + * Logs a message to firebug console and displays an info icon before the message. + * + * @var string + */ + const INFO = 'INFO'; + + /** + * Firebug WARN level + * + * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise. + * + * @var string + */ + const WARN = 'WARN'; + + /** + * Firebug ERROR level + * + * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. + * + * @var string + */ + const ERROR = 'ERROR'; + + /** + * Dumps a variable to firebug's server panel + * + * @var string + */ + const DUMP = 'DUMP'; + + /** + * Displays a stack trace in firebug console + * + * @var string + */ + const TRACE = 'TRACE'; + + /** + * Displays an exception in firebug console + * + * Increments the firebug error count. + * + * @var string + */ + const EXCEPTION = 'EXCEPTION'; + + /** + * Displays an table in firebug console + * + * @var string + */ + const TABLE = 'TABLE'; + + /** + * Starts a group in firebug console + * + * @var string + */ + const GROUP_START = 'GROUP_START'; + + /** + * Ends a group in firebug console + * + * @var string + */ + const GROUP_END = 'GROUP_END'; + + /** + * Singleton instance of FirePHP + * + * @var FirePHP + */ + protected static $instance = null; + + /** + * Flag whether we are logging from within the exception handler + * + * @var boolean + */ + protected $inExceptionHandler = false; + + /** + * Flag whether to throw PHP errors that have been converted to ErrorExceptions + * + * @var boolean + */ + protected $throwErrorExceptions = true; + + /** + * Flag whether to convert PHP assertion errors to Exceptions + * + * @var boolean + */ + protected $convertAssertionErrorsToExceptions = true; + + /** + * Flag whether to throw PHP assertion errors that have been converted to Exceptions + * + * @var boolean + */ + protected $throwAssertionExceptions = false; + + /** + * Wildfire protocol message index + * + * @var int + */ + protected $messageIndex = 1; + + /** + * Options for the library + * + * @var array + */ + protected $options = array('maxObjectDepth' => 10, + 'maxArrayDepth' => 20, + 'useNativeJsonEncode' => true, + 'includeLineNumbers' => true); + + /** + * Filters used to exclude object members when encoding + * + * @var array + */ + protected $objectFilters = array(); + + /** + * A stack of objects used to detect recursion during object encoding + * + * @var object + */ + protected $objectStack = array(); + + /** + * Flag to enable/disable logging + * + * @var boolean + */ + protected $enabled = true; + + /** + * The object constructor + */ + function __construct() { + } + + /** + * When the object gets serialized only include specific object members. + * + * @return array + */ + public function __sleep() { + return array('options','objectFilters','enabled'); + } + + /** + * Gets singleton instance of FirePHP + * + * @param boolean $AutoCreate + * @return FirePHP + */ + public static function getInstance($AutoCreate=false) { + if($AutoCreate===true && !self::$instance) { + self::init(); + } + return self::$instance; + } + + /** + * Creates FirePHP object and stores it for singleton access + * + * @return FirePHP + */ + public static function init() { + return self::$instance = new self(); + } + + /** + * Enable and disable logging to Firebug + * + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + public function setEnabled($Enabled) { + $this->enabled = $Enabled; + } + + /** + * Check if logging is enabled + * + * @return boolean TRUE if enabled + */ + public function getEnabled() { + return $this->enabled; + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @param string $Class The class name of the object + * @param array $Filter An array of members to exclude + * @return void + */ + public function setObjectFilter($Class, $Filter) { + $this->objectFilters[$Class] = $Filter; + } + + /** + * Set some options for the library + * + * Options: + * - maxObjectDepth: The maximum depth to traverse objects (default: 10) + * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) + * - useNativeJsonEncode: If true will use json_encode() (default: true) + * - includeLineNumbers: If true will include line numbers and filenames (default: true) + * + * @param array $Options The options to be set + * @return void + */ + public function setOptions($Options) { + $this->options = array_merge($this->options,$Options); + } + + /** + * Get options from the library + * + * @return array The currently set options + */ + public function getOptions() { + return $this->options; + } + + /** + * Register FirePHP as your error handler + * + * Will throw exceptions for each php error. + * + * @return mixed Returns a string containing the previously defined error handler (if any) + */ + public function registerErrorHandler($throwErrorExceptions=true) + { + //NOTE: The following errors will not be caught by this error handler: + // E_ERROR, E_PARSE, E_CORE_ERROR, + // E_CORE_WARNING, E_COMPILE_ERROR, + // E_COMPILE_WARNING, E_STRICT + + $this->throwErrorExceptions = $throwErrorExceptions; + + return set_error_handler(array($this,'errorHandler')); + } + + /** + * FirePHP's error handler + * + * Throws exception for each php error that will occur. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + // Don't throw exception if error reporting is switched off + if (error_reporting() == 0) { + return; + } + // Only throw exceptions for errors we are asking for + if (error_reporting() & $errno) { + + $exception = new ErrorException($errstr, 0, $errno, $errfile, $errline); + if($this->throwErrorExceptions) { + throw $exception; + } else { + $this->fb($exception); + } + } + } + + /** + * Register FirePHP as your exception handler + * + * @return mixed Returns the name of the previously defined exception handler, + * or NULL on error. + * If no previous handler was defined, NULL is also returned. + */ + public function registerExceptionHandler() + { + return set_exception_handler(array($this,'exceptionHandler')); + } + + /** + * FirePHP's exception handler + * + * Logs all exceptions to your firebug console and then stops the script. + * + * @param Exception $Exception + * @throws Exception + */ + function exceptionHandler($Exception) { + + $this->inExceptionHandler = true; + + header('HTTP/1.1 500 Internal Server Error'); + + $this->fb($Exception); + + $this->inExceptionHandler = false; + } + + /** + * Register FirePHP driver as your assert callback + * + * @param boolean $convertAssertionErrorsToExceptions + * @param boolean $throwAssertionExceptions + * @return mixed Returns the original setting or FALSE on errors + */ + public function registerAssertionHandler($convertAssertionErrorsToExceptions=true, $throwAssertionExceptions=false) + { + $this->convertAssertionErrorsToExceptions = $convertAssertionErrorsToExceptions; + $this->throwAssertionExceptions = $throwAssertionExceptions; + + if($throwAssertionExceptions && !$convertAssertionErrorsToExceptions) { + throw $this->newException('Cannot throw assertion exceptions as assertion errors are not being converted to exceptions!'); + } + + return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler')); + } + + /** + * FirePHP's assertion handler + * + * Logs all assertions to your firebug console and then stops the script. + * + * @param string $file File source of assertion + * @param int $line Line source of assertion + * @param mixed $code Assertion code + */ + public function assertionHandler($file, $line, $code) + { + + if($this->convertAssertionErrorsToExceptions) { + + $exception = new ErrorException('Assertion Failed - Code[ '.$code.' ]', 0, null, $file, $line); + + if($this->throwAssertionExceptions) { + throw $exception; + } else { + $this->fb($exception); + } + + } else { + + $this->fb($code, 'Assertion Failed', FirePHP::ERROR, array('File'=>$file,'Line'=>$line)); + + } + } + + /** + * Set custom processor url for FirePHP + * + * @param string $URL + */ + public function setProcessorUrl($URL) + { + $this->setHeader('X-FirePHP-ProcessorURL', $URL); + } + + /** + * Set custom renderer url for FirePHP + * + * @param string $URL + */ + public function setRendererUrl($URL) + { + $this->setHeader('X-FirePHP-RendererURL', $URL); + } + + /** + * Start a group for following messages. + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + * @throws Exception + */ + public function group($Name, $Options=null) { + + if(!$Name) { + throw $this->newException('You must specify a label for the group!'); + } + + if($Options) { + if(!is_array($Options)) { + throw $this->newException('Options must be defined as an array!'); + } + if(array_key_exists('Collapsed', $Options)) { + $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false'; + } + } + + return $this->fb(null, $Name, FirePHP::GROUP_START, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + public function groupEnd() { + return $this->fb(null, null, FirePHP::GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function log($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function info($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function warn($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function error($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + public function dump($Key, $Variable) { + return $this->fb($Variable, $Key, FirePHP::DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + public function trace($Label) { + return $this->fb($Label, FirePHP::TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + public function table($Label, $Table) { + return $this->fb($Table, $Label, FirePHP::TABLE); + } + + /** + * Check if FirePHP is installed on client + * + * @return boolean + */ + public function detectClientExtension() { + /* Check if FirePHP is installed on client */ + if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || + !version_compare($m[1][0],'0.0.6','>=')) { + return false; + } + return true; + } + + /** + * Log varible to Firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object The variable to be logged + * @return true Return TRUE if message was added to headers, FALSE otherwise + * @throws Exception + */ + public function fb($Object) { + + if(!$this->enabled) { + return false; + } + + if (headers_sent($filename, $linenum)) { + // If we are logging from within the exception handler we cannot throw another exception + if($this->inExceptionHandler) { + // Simply echo the error out to the page + echo '
FirePHP ERROR: Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.
'; + } else { + throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); + } + } + + $Type = null; + $Label = null; + $Options = array(); + + if(func_num_args()==1) { + } else + if(func_num_args()==2) { + switch(func_get_arg(1)) { + case self::LOG: + case self::INFO: + case self::WARN: + case self::ERROR: + case self::DUMP: + case self::TRACE: + case self::EXCEPTION: + case self::TABLE: + case self::GROUP_START: + case self::GROUP_END: + $Type = func_get_arg(1); + break; + default: + $Label = func_get_arg(1); + break; + } + } else + if(func_num_args()==3) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + } else + if(func_num_args()==4) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + $Options = func_get_arg(3); + } else { + throw $this->newException('Wrong number of arguments to fb() function!'); + } + + + if(!$this->detectClientExtension()) { + return false; + } + + $meta = array(); + $skipFinalObjectEncode = false; + + if($Object instanceof Exception) { + + $meta['file'] = $this->_escapeTraceFile($Object->getFile()); + $meta['line'] = $Object->getLine(); + + $trace = $Object->getTrace(); + if($Object instanceof ErrorException + && isset($trace[0]['function']) + && $trace[0]['function']=='errorHandler' + && isset($trace[0]['class']) + && $trace[0]['class']=='FirePHP') { + + $severity = false; + switch($Object->getSeverity()) { + case E_WARNING: $severity = 'E_WARNING'; break; + case E_NOTICE: $severity = 'E_NOTICE'; break; + case E_USER_ERROR: $severity = 'E_USER_ERROR'; break; + case E_USER_WARNING: $severity = 'E_USER_WARNING'; break; + case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break; + case E_STRICT: $severity = 'E_STRICT'; break; + case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break; + case E_DEPRECATED: $severity = 'E_DEPRECATED'; break; + case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break; + } + + $Object = array('Class'=>get_class($Object), + 'Message'=>$severity.': '.$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'trigger', + 'Trace'=>$this->_escapeTrace(array_splice($trace,2))); + $skipFinalObjectEncode = true; + } else { + $Object = array('Class'=>get_class($Object), + 'Message'=>$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'throw', + 'Trace'=>$this->_escapeTrace($trace)); + $skipFinalObjectEncode = true; + } + $Type = self::EXCEPTION; + + } else + if($Type==self::TRACE) { + + $trace = debug_backtrace(); + if(!$trace) return false; + for( $i=0 ; $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if($trace[$i]['function']=='fb' + || $trace[$i]['function']=='trace' + || $trace[$i]['function']=='send') { + $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', + 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', + 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', + 'Message'=>$trace[$i]['args'][0], + 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', + 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', + 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', + 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); + + $skipFinalObjectEncode = true; + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } else + if($Type==self::TABLE) { + + if(isset($Object[0]) && is_string($Object[0])) { + $Object[1] = $this->encodeTable($Object[1]); + } else { + $Object = $this->encodeTable($Object); + } + + $skipFinalObjectEncode = true; + + } else + if($Type==self::GROUP_START) { + + if(!$Label) { + throw $this->newException('You must specify a label for the group!'); + } + + } else { + if($Type===null) { + $Type = self::LOG; + } + } + + if($this->options['includeLineNumbers']) { + if(!isset($meta['file']) || !isset($meta['line'])) { + + $trace = debug_backtrace(); + for( $i=0 ; $trace && $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if(isset($trace[$i]['file']) + && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip FB::fb() */ + } else { + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } + } else { + unset($meta['file']); + unset($meta['line']); + } + + $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); + $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION); + + $structure_index = 1; + if($Type==self::DUMP) { + $structure_index = 2; + $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); + } else { + $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); + } + + if($Type==self::DUMP) { + $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; + } else { + $msg_meta = $Options; + $msg_meta['Type'] = $Type; + if($Label!==null) { + $msg_meta['Label'] = $Label; + } + if(isset($meta['file']) && !isset($msg_meta['File'])) { + $msg_meta['File'] = $meta['file']; + } + if(isset($meta['line']) && !isset($msg_meta['Line'])) { + $msg_meta['Line'] = $meta['line']; + } + $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; + } + + $parts = explode("\n",chunk_split($msg, 5000, "\n")); + + for( $i=0 ; $i2) { + // Message needs to be split into multiple parts + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + (($i==0)?strlen($msg):'') + . '|' . $part . '|' + . (($isetHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + strlen($part) . '|' . $part . '|'); + } + + $this->messageIndex++; + + if ($this->messageIndex > 99999) { + throw $this->newException('Maximum number (99,999) of messages reached!'); + } + } + } + + $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); + + return true; + } + + /** + * Standardizes path for windows systems. + * + * @param string $Path + * @return string + */ + protected function _standardizePath($Path) { + return preg_replace('/\\\\+/','/',$Path); + } + + /** + * Escape trace path for windows systems + * + * @param array $Trace + * @return array + */ + protected function _escapeTrace($Trace) { + if(!$Trace) return $Trace; + for( $i=0 ; $i_escapeTraceFile($Trace[$i]['file']); + } + if(isset($Trace[$i]['args'])) { + $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); + } + } + return $Trace; + } + + /** + * Escape file information of trace for windows systems + * + * @param string $File + * @return string + */ + protected function _escapeTraceFile($File) { + /* Check if we have a windows filepath */ + if(strpos($File,'\\')) { + /* First strip down to single \ */ + + $file = preg_replace('/\\\\+/','\\',$File); + + return $file; + } + return $File; + } + + /** + * Send header + * + * @param string $Name + * @param string_type $Value + */ + protected function setHeader($Name, $Value) { + return header($Name.': '.$Value); + } + + /** + * Get user agent + * + * @return string|false + */ + protected function getUserAgent() { + if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * Returns a new exception + * + * @param string $Message + * @return Exception + */ + protected function newException($Message) { + return new Exception($Message); + } + + /** + * Encode an object into a JSON string + * + * Uses PHP's jeson_encode() if available + * + * @param object $Object The object to be encoded + * @return string The JSON string + */ + public function jsonEncode($Object, $skipObjectEncode=false) + { + if(!$skipObjectEncode) { + $Object = $this->encodeObject($Object); + } + + if(function_exists('json_encode') + && $this->options['useNativeJsonEncode']!=false) { + + return json_encode($Object); + } else { + return $this->json_encode($Object); + } + } + + /** + * Encodes a table by encoding each row and column with encodeObject() + * + * @param array $Table The table to be encoded + * @return array + */ + protected function encodeTable($Table) { + + if(!$Table) return $Table; + + $new_table = array(); + foreach($Table as $row) { + + if(is_array($row)) { + $new_row = array(); + + foreach($row as $item) { + $new_row[] = $this->encodeObject($item); + } + + $new_table[] = $new_row; + } + } + + return $new_table; + } + + /** + * Encodes an object including members with + * protected and private visibility + * + * @param Object $Object The object to be encoded + * @param int $Depth The current traversal depth + * @return array All members of the object + */ + protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) + { + $return = array(); + + if (is_resource($Object)) { + + return '** '.(string)$Object.' **'; + + } else + if (is_object($Object)) { + + if ($ObjectDepth > $this->options['maxObjectDepth']) { + return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; + } + + foreach ($this->objectStack as $refVal) { + if ($refVal === $Object) { + return '** Recursion ('.get_class($Object).') **'; + } + } + array_push($this->objectStack, $Object); + + $return['__className'] = $class = get_class($Object); + + $reflectionClass = new ReflectionClass($class); + $properties = array(); + foreach( $reflectionClass->getProperties() as $property) { + $properties[$property->getName()] = $property; + } + + $members = (array)$Object; + + foreach( $properties as $raw_name => $property ) { + + $name = $raw_name; + if($property->isStatic()) { + $name = 'static:'.$name; + } + if($property->isPublic()) { + $name = 'public:'.$name; + } else + if($property->isPrivate()) { + $name = 'private:'.$name; + $raw_name = "\0".$class."\0".$raw_name; + } else + if($property->isProtected()) { + $name = 'protected:'.$name; + $raw_name = "\0".'*'."\0".$raw_name; + } + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + if(array_key_exists($raw_name,$members) + && !$property->isStatic()) { + + $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1); + + } else { + if(method_exists($property,'setAccessible')) { + $property->setAccessible(true); + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else + if($property->isPublic()) { + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Need PHP 5.3 to get value **'; + } + } + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + + // Include all members that are not defined in the class + // but exist in the object + foreach( $members as $raw_name => $value ) { + + $name = $raw_name; + + if ($name{0} == "\0") { + $parts = explode("\0", $name); + $name = $parts[2]; + } + + if(!isset($properties[$name])) { + $name = 'undeclared:'.$name; + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + } + + array_pop($this->objectStack); + + } elseif (is_array($Object)) { + + if ($ArrayDepth > $this->options['maxArrayDepth']) { + return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; + } + + foreach ($Object as $key => $val) { + + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($key=='GLOBALS' + && is_array($val) + && array_key_exists('GLOBALS',$val)) { + $val['GLOBALS'] = '** Recursion (GLOBALS) **'; + } + + $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); + } + } else { + if(self::is_utf8($Object)) { + return $Object; + } else { + return utf8_encode($Object); + } + } + return $return; + } + + /** + * Returns true if $string is valid UTF-8 and false otherwise. + * + * @param mixed $str String to be tested + * @return boolean + */ + protected static function is_utf8($str) { + $c=0; $b=0; + $bits=0; + $len=strlen($str); + for($i=0; $i<$len; $i++){ + $c=ord($str[$i]); + if($c > 128){ + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1){ + $i++; + $b=ord($str[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski + * @author Matt Knapp + * @author Brett Stimmerman + * @author Christoph Dorn + * @copyright 2005 Michal Migurski + * @version CVS: $Id: FirePHP.class.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + + + /** + * Keep a list of objects as we descend into the array so we can detect recursion. + */ + private $json_objectStack = array(); + + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + private function json_utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + private function json_encode($var) + { + + if(is_object($var)) { + if(in_array($var,$this->json_objectStack)) { + return '"** Recursion **"'; + } + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($var), + array_values($var)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + $this->json_objectStack[] = $var; + + // treat it like a regular array + $elements = array_map(array($this, 'json_encode'), $var); + + array_pop($this->json_objectStack); + + foreach($elements as $element) { + if($element instanceof Exception) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = self::encodeObject($var); + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($vars), + array_values($vars)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return null; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + private function json_name_value($name, $value) + { + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($name=='GLOBALS' + && is_array($value) + && array_key_exists('GLOBALS',$value)) { + $value['GLOBALS'] = '** Recursion **'; + } + + $encoded_value = $this->json_encode($value); + + if($encoded_value instanceof Exception) { + return $encoded_value; + } + + return $this->json_encode(strval($name)) . ':' . $encoded_value; + } +} diff --git a/gui/baculum/framework/3rdParty/FirePHPCore/FirePHP.class.php4 b/gui/baculum/framework/3rdParty/FirePHPCore/FirePHP.class.php4 new file mode 100644 index 0000000000..3e20120da7 --- /dev/null +++ b/gui/baculum/framework/3rdParty/FirePHPCore/FirePHP.class.php4 @@ -0,0 +1,1291 @@ + + * @author Michael Day + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + +/** + * FirePHP version + * + * @var string + */ +define('FirePHP_VERSION', '0.3'); + +/** + * Firebug LOG level + * + * Logs a message to firebug console + * + * @var string + */ +define('FirePHP_LOG', 'LOG'); + +/** + * Firebug INFO level + * + * Logs a message to firebug console and displays an info icon before the message + * + * @var string + */ +define('FirePHP_INFO', 'INFO'); + +/** + * Firebug WARN level + * + * Logs a message to firebug console, displays a warning icon before the message and colors the line turquoise + * + * @var string + */ +define('FirePHP_WARN', 'WARN'); + +/** + * Firebug ERROR level + * + * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. + * + * @var string + */ +define('FirePHP_ERROR', 'ERROR'); + +/** + * Dumps a variable to firebug's server panel + * + * @var string + */ +define('FirePHP_DUMP', 'DUMP'); + +/** + * Displays a stack trace in firebug console + * + * @var string + */ +define('FirePHP_TRACE', 'TRACE'); + +/** + * Displays a table in firebug console + * + * @var string + */ +define('FirePHP_TABLE', 'TABLE'); + +/** + * Starts a group in firebug console + * + * @var string + */ +define('FirePHP_GROUP_START', 'GROUP_START'); + +/** + * Ends a group in firebug console + * + * @var string + */ +define('FirePHP_GROUP_END', 'GROUP_END'); + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * For more information see: http://www.firephp.org/ + * + * @copyright Copyright (C) 2007-2009 Christoph Dorn + * @author Christoph Dorn + * @author Michael Day + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ +class FirePHP { + /** + * Wildfire protocol message index + * + * @var int + */ + var $messageIndex = 1; + + /** + * Options for the library + * + * @var array + */ + var $options = array('maxObjectDepth' => 10, + 'maxArrayDepth' => 20, + 'useNativeJsonEncode' => true, + 'includeLineNumbers' => true); + + /** + * Filters used to exclude object members when encoding + * + * @var array + */ + var $objectFilters = array(); + + /** + * A stack of objects used to detect recursion during object encoding + * + * @var object + */ + var $objectStack = array(); + + /** + * Flag to enable/disable logging + * + * @var boolean + */ + var $enabled = true; + + /** + * The object constructor + */ + function FirePHP() { + } + + + /** + * When the object gets serialized only include specific object members. + * + * @return array + */ + function __sleep() { + return array('options','objectFilters','enabled'); + } + + /** + * Gets singleton instance of FirePHP + * + * @param boolean $AutoCreate + * @return FirePHP + */ + function &getInstance($AutoCreate=false) { + global $FirePHP_Instance; + + if($AutoCreate===true && !$FirePHP_Instance) { + $FirePHP_Instance = new FirePHP(); + } + + return $FirePHP_Instance; + } + + /** + * Enable and disable logging to Firebug + * + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + function setEnabled($Enabled) { + $this->enabled = $Enabled; + } + + /** + * Check if logging is enabled + * + * @return boolean TRUE if enabled + */ + function getEnabled() { + return $this->enabled; + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @param string $Class The class name of the object + * @param array $Filter An array of members to exclude + * @return void + */ + function setObjectFilter($Class, $Filter) { + $this->objectFilters[$Class] = $Filter; + } + + /** + * Set some options for the library + * + * Options: + * - maxObjectDepth: The maximum depth to traverse objects (default: 10) + * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) + * - useNativeJsonEncode: If true will use json_encode() (default: true) + * - includeLineNumbers: If true will include line numbers and filenames (default: true) + * + * @param array $Options The options to be set + * @return void + */ + function setOptions($Options) { + $this->options = array_merge($this->options,$Options); + } + + /** + * Get options from the library + * + * @return array The currently set options + */ + function getOptions() { + return $this->options; + } + + /** + * Register FirePHP as your error handler + * + * Will use FirePHP to log each php error. + * + * @return mixed Returns a string containing the previously defined error handler (if any) + */ + function registerErrorHandler() + { + //NOTE: The following errors will not be caught by this error handler: + // E_ERROR, E_PARSE, E_CORE_ERROR, + // E_CORE_WARNING, E_COMPILE_ERROR, + // E_COMPILE_WARNING, E_STRICT + + return set_error_handler(array($this,'errorHandler')); + } + + /** + * FirePHP's error handler + * + * Logs each php error that will occur. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + global $FirePHP_Instance; + // Don't log error if error reporting is switched off + if (error_reporting() == 0) { + return; + } + // Only log error for errors we are asking for + if (error_reporting() & $errno) { + $FirePHP_Instance->group($errstr); + $FirePHP_Instance->error("{$errfile}, line $errline"); + $FirePHP_Instance->groupEnd(); + } + } + + /** + * Register FirePHP driver as your assert callback + * + * @return mixed Returns the original setting + */ + function registerAssertionHandler() + { + return assert_options(ASSERT_CALLBACK, array($this, 'assertionHandler')); + } + + /** + * FirePHP's assertion handler + * + * Logs all assertions to your firebug console and then stops the script. + * + * @param string $file File source of assertion + * @param int $line Line source of assertion + * @param mixed $code Assertion code + */ + function assertionHandler($file, $line, $code) + { + $this->fb($code, 'Assertion Failed', FirePHP_ERROR, array('File'=>$file,'Line'=>$line)); + } + + /** + * Set custom processor url for FirePHP + * + * @param string $URL + */ + function setProcessorUrl($URL) + { + $this->setHeader('X-FirePHP-ProcessorURL', $URL); + } + + /** + * Set custom renderer url for FirePHP + * + * @param string $URL + */ + function setRendererUrl($URL) + { + $this->setHeader('X-FirePHP-RendererURL', $URL); + } + + /** + * Start a group for following messages. + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + * @throws Exception + */ + function group($Name, $Options=null) { + + if(!$Name) { + trigger_error('You must specify a label for the group!'); + } + + if($Options) { + if(!is_array($Options)) { + trigger_error('Options must be defined as an array!'); + } + if(array_key_exists('Collapsed', $Options)) { + $Options['Collapsed'] = ($Options['Collapsed'])?'true':'false'; + } + } + + return $this->fb(null, $Name, FirePHP_GROUP_START, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + function groupEnd() { + return $this->fb(null, null, FirePHP_GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function log($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function info($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function warn($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + function error($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP_ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + function dump($Key, $Variable) { + return $this->fb($Variable, $Key, FirePHP_DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + function trace($Label) { + return $this->fb($Label, FirePHP_TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + function table($Label, $Table) { + return $this->fb($Table, $Label, FirePHP_TABLE); + } + + /** + * Check if FirePHP is installed on client + * + * @return boolean + */ + function detectClientExtension() { + /* Check if FirePHP is installed on client */ + if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || + !version_compare($m[1][0],'0.0.6','>=')) { + return false; + } + return true; + } + + /** + * Log varible to Firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object The variable to be logged + * @return true Return TRUE if message was added to headers, FALSE otherwise + * @throws Exception + */ + function fb($Object) { + + if(!$this->enabled) { + return false; + } + + if (headers_sent($filename, $linenum)) { + trigger_error('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); + } + + $Type = null; + $Label = null; + $Options = array(); + + if(func_num_args()==1) { + } else + if(func_num_args()==2) { + switch(func_get_arg(1)) { + case FirePHP_LOG: + case FirePHP_INFO: + case FirePHP_WARN: + case FirePHP_ERROR: + case FirePHP_DUMP: + case FirePHP_TRACE: + case FirePHP_TABLE: + case FirePHP_GROUP_START: + case FirePHP_GROUP_END: + $Type = func_get_arg(1); + break; + default: + $Label = func_get_arg(1); + break; + } + } else + if(func_num_args()==3) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + } else + if(func_num_args()==4) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + $Options = func_get_arg(3); + } else { + trigger_error('Wrong number of arguments to fb() function!'); + } + + + if(!$this->detectClientExtension()) { + return false; + } + + $meta = array(); + $skipFinalObjectEncode = false; + + if($Type==FirePHP_TRACE) { + + $trace = debug_backtrace(); + if(!$trace) return false; + for( $i=0 ; $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if($trace[$i]['function']=='fb' + || $trace[$i]['function']=='trace' + || $trace[$i]['function']=='send') { + $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', + 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', + 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', + 'Message'=>$trace[$i]['args'][0], + 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', + 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', + 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', + 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); + + $skipFinalObjectEncode = true; + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } else + if($Type==FirePHP_TABLE) { + + if(isset($Object[0]) && is_string($Object[0])) { + $Object[1] = $this->encodeTable($Object[1]); + } else { + $Object = $this->encodeTable($Object); + } + + $skipFinalObjectEncode = true; + + } else + if($Type==FirePHP_GROUP_START) { + + if(!$Label) { + trigger_error('You must specify a label for the group!'); + } + } else { + if($Type===null) { + $Type = FirePHP_LOG; + } + } + + if($this->options['includeLineNumbers']) { + if(!isset($meta['file']) || !isset($meta['line'])) { + + $trace = debug_backtrace(); + for( $i=0 ; $trace && $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if(isset($trace[$i]['file']) + && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip FB::fb() */ + } else { + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } + } else { + unset($meta['file']); + unset($meta['line']); + } + + $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); + $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.FirePHP_VERSION); + + $structure_index = 1; + if($Type==FirePHP_DUMP) { + $structure_index = 2; + $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); + } else { + $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); + } + + if($Type==FirePHP_DUMP) { + $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; + } else { + $msg_meta = $Options; + $msg_meta['Type'] = $Type; + if($Label!==null) { + $msg_meta['Label'] = $Label; + } + if(isset($meta['file']) && !isset($msg_meta['File'])) { + $msg_meta['File'] = $meta['file']; + } + if(isset($meta['line']) && !isset($msg_meta['Line'])) { + $msg_meta['Line'] = $meta['line']; + } + $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; + } + + $parts = explode("\n",chunk_split($msg, 5000, "\n")); + + for( $i=0 ; $i2) { + // Message needs to be split into multiple parts + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + (($i==0)?strlen($msg):'') + . '|' . $part . '|' + . (($isetHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + strlen($part) . '|' . $part . '|'); + } + + $this->messageIndex++; + + if ($this->messageIndex > 99999) { + trigger_error('Maximum number (99,999) of messages reached!'); + } + } + } + + $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); + + return true; + } + + + /** + * Standardizes path for windows systems. + * + * @param string $Path + * @return string + */ + function _standardizePath($Path) { + return preg_replace('/\\\\+/','/',$Path); + } + + /** + * Escape trace path for windows systems + * + * @param array $Trace + * @return array + */ + function _escapeTrace($Trace) { + if(!$Trace) return $Trace; + for( $i=0 ; $i_escapeTraceFile($Trace[$i]['file']); + } + if(isset($Trace[$i]['args'])) { + $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); + } + } + return $Trace; + } + + /** + * Escape file information of trace for windows systems + * + * @param string $File + * @return string + */ + function _escapeTraceFile($File) { + /* Check if we have a windows filepath */ + if(strpos($File,'\\')) { + /* First strip down to single \ */ + + $file = preg_replace('/\\\\+/','\\',$File); + + return $file; + } + return $File; + } + + /** + * Send header + * + * @param string $Name + * @param string_type $Value + */ + function setHeader($Name, $Value) { + return header($Name.': '.$Value); + } + + /** + * Get user agent + * + * @return string|false + */ + function getUserAgent() { + if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * Encode an object into a JSON string + * + * Uses PHP's jeson_encode() if available + * + * @param object $Object The object to be encoded + * @return string The JSON string + */ + function jsonEncode($Object, $skipObjectEncode=false) + { + if(!$skipObjectEncode) { + $Object = $this->encodeObject($Object); + } + + if(function_exists('json_encode') + && $this->options['useNativeJsonEncode']!=false) { + + return json_encode($Object); + } else { + return $this->json_encode($Object); + } + } + + /** + * Encodes a table by encoding each row and column with encodeObject() + * + * @param array $Table The table to be encoded + * @return array + */ + function encodeTable($Table) { + + if(!$Table) return $Table; + + $new_table = array(); + foreach($Table as $row) { + + if(is_array($row)) { + $new_row = array(); + + foreach($row as $item) { + $new_row[] = $this->encodeObject($item); + } + + $new_table[] = $new_row; + } + } + + return $new_table; + } + + /** + * Encodes an object + * + * @param Object $Object The object to be encoded + * @param int $Depth The current traversal depth + * @return array All members of the object + */ + function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) + { + $return = array(); + + if (is_resource($Object)) { + + return '** '.(string)$Object.' **'; + + } else + if (is_object($Object)) { + + if ($ObjectDepth > $this->options['maxObjectDepth']) { + return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; + } + + foreach ($this->objectStack as $refVal) { + if ($refVal === $Object) { + return '** Recursion ('.get_class($Object).') **'; + } + } + array_push($this->objectStack, $Object); + + $return['__className'] = $class = get_class($Object); + + $members = (array)$Object; + + // Include all members that are not defined in the class + // but exist in the object + foreach( $members as $raw_name => $value ) { + + $name = $raw_name; + + if ($name{0} == "\0") { + $parts = explode("\0", $name); + $name = $parts[2]; + } + + if(!isset($properties[$name])) { + $name = 'undeclared:'.$name; + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + } + + array_pop($this->objectStack); + + } elseif (is_array($Object)) { + + if ($ArrayDepth > $this->options['maxArrayDepth']) { + return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; + } + + foreach ($Object as $key => $val) { + + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($key=='GLOBALS' + && is_array($val) + && array_key_exists('GLOBALS',$val)) { + $val['GLOBALS'] = '** Recursion (GLOBALS) **'; + } + + $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); + } + } else { + if($this->is_utf8($Object)) { + return $Object; + } else { + return utf8_encode($Object); + } + } + return $return; + + } + + /** + * Returns true if $string is valid UTF-8 and false otherwise. + * + * @param mixed $str String to be tested + * @return boolean + */ + function is_utf8($str) { + $c=0; $b=0; + $bits=0; + $len=strlen($str); + for($i=0; $i<$len; $i++){ + $c=ord($str[$i]); + if($c > 128){ + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1){ + $i++; + $b=ord($str[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski + * @author Matt Knapp + * @author Brett Stimmerman + * @author Christoph Dorn + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + + + /** + * Keep a list of objects as we descend into the array so we can detect recursion. + */ + var $json_objectStack = array(); + + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function json_utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function json_encode($var) + { + + if(is_object($var)) { + if(in_array($var,$this->json_objectStack)) { + return '"** Recursion **"'; + } + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($var), + array_values($var)); + + array_pop($this->json_objectStack); + + return '{' . join(',', $properties) . '}'; + } + + $this->json_objectStack[] = $var; + + // treat it like a regular array + $elements = array_map(array($this, 'json_encode'), $var); + + array_pop($this->json_objectStack); + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = FirePHP::encodeObject($var); + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($vars), + array_values($vars)); + + array_pop($this->json_objectStack); + + return '{' . join(',', $properties) . '}'; + + default: + return null; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function json_name_value($name, $value) + { + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($name=='GLOBALS' + && is_array($value) + && array_key_exists('GLOBALS',$value)) { + $value['GLOBALS'] = '** Recursion **'; + } + + $encoded_value = $this->json_encode($value); + + return $this->json_encode(strval($name)) . ':' . $encoded_value; + } +} + diff --git a/gui/baculum/framework/3rdParty/FirePHPCore/LICENSE b/gui/baculum/framework/3rdParty/FirePHPCore/LICENSE new file mode 100644 index 0000000000..3e390f9d96 --- /dev/null +++ b/gui/baculum/framework/3rdParty/FirePHPCore/LICENSE @@ -0,0 +1,29 @@ +Software License Agreement (New BSD License) + +Copyright (c) 2006-2009, Christoph Dorn +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Christoph Dorn nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gui/baculum/framework/3rdParty/FirePHPCore/fb.php b/gui/baculum/framework/3rdParty/FirePHPCore/fb.php new file mode 100644 index 0000000000..9d1857cbc9 --- /dev/null +++ b/gui/baculum/framework/3rdParty/FirePHPCore/fb.php @@ -0,0 +1,261 @@ + + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + +require_once dirname(__FILE__).'/FirePHP.class.php'; + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + * @throws Exception + */ +function fb() +{ + $instance = FirePHP::getInstance(true); + + $args = func_get_args(); + return call_user_func_array(array($instance,'fb'),$args); +} + + +class FB +{ + /** + * Enable and disable logging to Firebug + * + * @see FirePHP->setEnabled() + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + public static function setEnabled($Enabled) { + $instance = FirePHP::getInstance(true); + $instance->setEnabled($Enabled); + } + + /** + * Check if logging is enabled + * + * @see FirePHP->getEnabled() + * @return boolean TRUE if enabled + */ + public static function getEnabled() { + $instance = FirePHP::getInstance(true); + return $instance->getEnabled(); + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @see FirePHP->setObjectFilter() + * @param string $Class The class name of the object + * @param array $Filter An array or members to exclude + * @return void + */ + public static function setObjectFilter($Class, $Filter) { + $instance = FirePHP::getInstance(true); + $instance->setObjectFilter($Class, $Filter); + } + + /** + * Set some options for the library + * + * @see FirePHP->setOptions() + * @param array $Options The options to be set + * @return void + */ + public static function setOptions($Options) { + $instance = FirePHP::getInstance(true); + $instance->setOptions($Options); + } + + /** + * Get options for the library + * + * @see FirePHP->getOptions() + * @return array The options + */ + public static function getOptions() { + $instance = FirePHP::getInstance(true); + return $instance->getOptions(); + } + + /** + * Log object to firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + * @throws Exception + */ + public static function send() + { + $instance = FirePHP::getInstance(true); + $args = func_get_args(); + return call_user_func_array(array($instance,'fb'),$args); + } + + /** + * Start a group for following messages + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + */ + public static function group($Name, $Options=null) { + $instance = FirePHP::getInstance(true); + return $instance->group($Name, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + public static function groupEnd() { + return self::send(null, null, FirePHP::GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function log($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function info($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function warn($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public static function error($Object, $Label=null) { + return self::send($Object, $Label, FirePHP::ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + public static function dump($Key, $Variable) { + return self::send($Variable, $Key, FirePHP::DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + public static function trace($Label) { + return self::send($Label, FirePHP::TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + public static function table($Label, $Table) { + return self::send($Table, $Label, FirePHP::TABLE); + } + +} + diff --git a/gui/baculum/framework/3rdParty/FirePHPCore/fb.php4 b/gui/baculum/framework/3rdParty/FirePHPCore/fb.php4 new file mode 100644 index 0000000000..5b69e34873 --- /dev/null +++ b/gui/baculum/framework/3rdParty/FirePHPCore/fb.php4 @@ -0,0 +1,251 @@ + + * @author Michael Day + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + +require_once dirname(__FILE__).'/FirePHP.class.php4'; + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + * @throws Exception + */ +function fb() +{ + $instance =& FirePHP::getInstance(true); + + $args = func_get_args(); + return call_user_func_array(array(&$instance,'fb'),$args); +} + + +class FB +{ + /** + * Enable and disable logging to Firebug + * + * @see FirePHP->setEnabled() + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + function setEnabled($Enabled) { + $instance =& FirePHP::getInstance(true); + $instance->setEnabled($Enabled); + } + + /** + * Check if logging is enabled + * + * @see FirePHP->getEnabled() + * @return boolean TRUE if enabled + */ + function getEnabled() { + $instance =& FirePHP::getInstance(true); + return $instance->getEnabled(); + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @see FirePHP->setObjectFilter() + * @param string $Class The class name of the object + * @param array $Filter An array or members to exclude + * @return void + */ + function setObjectFilter($Class, $Filter) { + $instance =& FirePHP::getInstance(true); + $instance->setObjectFilter($Class, $Filter); + } + + /** + * Set some options for the library + * + * @see FirePHP->setOptions() + * @param array $Options The options to be set + * @return void + */ + function setOptions($Options) { + $instance =& FirePHP::getInstance(true); + $instance->setOptions($Options); + } + + /** + * Get options for the library + * + * @see FirePHP->getOptions() + * @return array The options + */ + function getOptions() { + $instance =& FirePHP::getInstance(true); + return $instance->getOptions(); + } + + /** + * Log object to firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object + * @return true + */ + function send() + { + $instance =& FirePHP::getInstance(true); + $args = func_get_args(); + return call_user_func_array(array(&$instance,'fb'),$args); + } + + /** + * Start a group for following messages + * + * Options: + * Collapsed: [true|false] + * Color: [#RRGGBB|ColorName] + * + * @param string $Name + * @param array $Options OPTIONAL Instructions on how to log the group + * @return true + */ + function group($Name, $Options=null) { + $instance =& FirePHP::getInstance(true); + return $instance->group($Name, $Options); + } + + /** + * Ends a group you have started before + * + * @return true + */ + function groupEnd() { + return FB::send(null, null, FirePHP_GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + */ + function log($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + */ + function info($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + */ + function warn($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + */ + function error($Object, $Label=null) { + return FB::send($Object, $Label, FirePHP_ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + */ + function dump($Key, $Variable) { + return FB::send($Variable, $Key, FirePHP_DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + */ + function trace($Label) { + return FB::send($Label, FirePHP_TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + */ + function table($Label, $Table) { + return FB::send($Table, $Label, FirePHP_TABLE); + } +} diff --git a/gui/baculum/framework/3rdParty/Markdown/License.text b/gui/baculum/framework/3rdParty/Markdown/License.text new file mode 100644 index 0000000000..ea6a6a1ab6 --- /dev/null +++ b/gui/baculum/framework/3rdParty/Markdown/License.text @@ -0,0 +1,34 @@ +Copyright (c) 2004-2005, John Gruber + +All rights reserved. + +Copyright (c) 2004-2005, Michel Fortin + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/gui/baculum/framework/3rdParty/Markdown/MarkdownParser.php b/gui/baculum/framework/3rdParty/Markdown/MarkdownParser.php new file mode 100644 index 0000000000..b581dcb52e --- /dev/null +++ b/gui/baculum/framework/3rdParty/Markdown/MarkdownParser.php @@ -0,0 +1,1256 @@ + +# +# Copyright (c) 2004-2005 Michel Fortin - PHP Port +# +# + +/** + * PHP5 version of the markdown parser. + * Usage: + * + * $markdown = new MarkdownParser; + * echo $markdown->parse($text); + * + */ +class MarkdownParser +{ + private static $md_nested_brackets; + private static $md_escape_table = array(); + private static $md_backslash_escape_table = array(); + private static $md_nested_brackets_depth = 6; + + protected $md_empty_element_suffix = " />"; # Change to ">" for HTML output + protected $md_tab_width = 4; + + private $md_list_level = 0; + private $md_urls = array(); + private $md_titles = array(); + private $md_html_blocks = array(); + + public function __construct() + { + if(is_null(self::$md_nested_brackets)) + $this->initialize(); + } + + private function initialize() + { + self::$md_nested_brackets = + str_repeat('(?>[^\[\]]+|\[', self::$md_nested_brackets_depth). + str_repeat('\])*', self::$md_nested_brackets_depth); + + self::$md_escape_table = array( + "\\" => md5("\\"), + "`" => md5("`"), + "*" => md5("*"), + "_" => md5("_"), + "{" => md5("{"), + "}" => md5("}"), + "[" => md5("["), + "]" => md5("]"), + "(" => md5("("), + ")" => md5(")"), + ">" => md5(">"), + "#" => md5("#"), + "+" => md5("+"), + "-" => md5("-"), + "." => md5("."), + "!" => md5("!") + ); + + # Table of hash values for escaped characters: + # Create an identical table but for escaped characters. + foreach (self::$md_escape_table as $key => $char) + self::$md_backslash_escape_table["\\$key"] = $char; + } + + public function parse($text) + { + # + # Main function. The order in which other subs are called here is + # essential. Link and image substitutions need to happen before + # _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the + # and tags get encoded. + # + # Clear the hashes. If we don't clear these, you get conflicts + # from other articles when generating a page which contains more than + # one article (e.g. an index page that shows the N most recent + # articles): + $this->md_urls = array(); + $this->md_titles = array(); + $this->md_html_blocks = array(); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->_Detab($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ \t]*\n+/ . + $text = preg_replace('/^[ \t]+$/m', '', $text); + + # Turn block-level HTML blocks into hash entries + $text = $this->_HashHTMLBlocks($text); + + # Strip link definitions, store in hashes. + $text = $this->_StripLinkDefinitions($text); + + $text = $this->_RunBlockGamut($text); + + $text = $this->_UnescapeSpecialChars($text); + + return $text . "\n"; + } + + + private function _StripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->md_tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\]: # id = $1 + [ \t]* + \n? # maybe *one* newline + [ \t]* + ? # url = $2 + [ \t]* + \n? # maybe one newline + [ \t]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.+?) # title = $3 + [")] + [ \t]* + )? # title is optional + (?:\n+|\Z) + }xm', + array($this,'_StripLinkDefinitions_callback'), + $text); + return $text; + } + + private function _StripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $this->md_urls[$link_id] = $this->_EncodeAmpsAndAngles($matches[2]); + if (isset($matches[3])) + $this->md_titles[$link_id] = str_replace('"', '"', $matches[3]); + return ''; # String that will replace the block + } + + + private function _HashHTMLBlocks($text) { + $less_than_tab = $this->md_tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + $block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'. + 'script|noscript|form|fieldset|iframe|math|ins|del'; + $block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'. + 'script|noscript|form|fieldset|iframe|math'; + + # First, look for nested blocks, e.g.: + #

+ #
+ # tags for inner block must be indented. + #
+ #
+ # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
` and stop at the first `
`. + $text = preg_replace_callback("{ + ( # save in $1 + ^ # start of line (with /m) + <($block_tags_a) # start tag = $2 + \\b # word break + (.*\\n)*? # any number of lines, minimally matching + # the matching end tag + [ \\t]* # trailing spaces/tabs + (?=\\n+|\\Z) # followed by a newline or end of document + ) + }xm", + array($this,'_HashHTMLBlocks_callback'), + $text); + + # + # Now match more liberally, simply from `\n` to `\n` + # + $text = preg_replace_callback("{ + ( # save in $1 + ^ # start of line (with /m) + <($block_tags_b) # start tag = $2 + \\b # word break + (.*\\n)*? # any number of lines, minimally matching + .* # the matching end tag + [ \\t]* # trailing spaces/tabs + (?=\\n+|\\Z) # followed by a newline or end of document + ) + }xm", + array($this,'_HashHTMLBlocks_callback'), + $text); + + # Special case just for
. It was easier to make a special case than + # to make the other regex more complicated. + $text = preg_replace_callback('{ + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + \b # word break + ([^<>])*? # + /?> # the matching end tag + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + }x', + array($this,'_HashHTMLBlocks_callback'), + $text); + + # Special case for standalone HTML comments: + $text = preg_replace_callback('{ + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,'.$less_than_tab.'} + (?s: + + ) + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + }x', + array($this,'_HashHTMLBlocks_callback'), + $text); + + return $text; + } + private function _HashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = md5($text); + $this->md_html_blocks[$key] = $text; + return "\n\n$key\n\n"; # String that will replace the block + } + + + private function _RunBlockGamut($text) { + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + $text = $this->_DoHeaders($text); + + # Do Horizontal Rules: + $text = preg_replace( + array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}mx', + '{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}mx', + '{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}mx'), + "\nmd_empty_element_suffix}\n", + $text); + + $text = $this->_DoLists($text); + $text = $this->_DoCodeBlocks($text); + $text = $this->_DoBlockQuotes($text); + + # We already ran _HashHTMLBlocks() before, in Markdown(), but that + # was to escape raw HTML in the original Markdown source. This time, + # we're escaping the markup we've just created, so that we don't wrap + #

tags around block-level tags. + $text = $this->_HashHTMLBlocks($text); + $text = $this->_FormParagraphs($text); + + return $text; + } + + + private function _RunSpanGamut($text) { + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + + $text = $this->_DoCodeSpans($text); + + $text = $this->_EscapeSpecialChars($text); + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + $text = $this->_DoImages($text); + $text = $this->_DoAnchors($text); + + # Make links out of things like `` + # Must come after _DoAnchors(), because you can use < and > + # delimiters in inline links like [this](). + $text = $this->_DoAutoLinks($text); + $text = $this->_EncodeAmpsAndAngles($text); + $text = $this->_DoItalicsAndBold($text); + + # Do hard breaks: + $text = preg_replace('/ {2,}\n/', "md_empty_element_suffix}\n", $text); + + return $text; + } + + + private function _EscapeSpecialChars($text) { + $tokens = $this->_TokenizeHTML($text); + + $text = ''; # rebuild $text from the tokens + # $in_pre = 0; # Keep track of when we're inside

 or  tags.
+	#	$tags_to_skip = "!<(/?)(?:pre|code|kbd|script|math)[\s>]!";
+
+		foreach ($tokens as $cur_token) {
+			if ($cur_token[0] == 'tag') {
+				# Within tags, encode * and _ so they don't conflict
+				# with their use in Markdown for italics and strong.
+				# We're replacing each such character with its
+				# corresponding MD5 checksum value; this is likely
+				# overkill, but it should prevent us from colliding
+				# with the escape values by accident.
+				$cur_token[1] = str_replace(array('*', '_'),
+					array(self::$md_escape_table['*'], self::$md_escape_table['_']),
+					$cur_token[1]);
+				$text .= $cur_token[1];
+			} else {
+				$t = $cur_token[1];
+				$t = $this->_EncodeBackslashEscapes($t);
+				$text .= $t;
+			}
+		}
+		return $text;
+	}
+
+
+	private function _DoAnchors($text) {
+	#
+	# Turn Markdown link shortcuts into XHTML  tags.
+	#
+		#
+		# First, handle reference-style links: [link text] [id]
+		#
+		$bracket = self::$md_nested_brackets;
+		$text = preg_replace_callback("{
+			(					# wrap whole match in $1
+			  \\[
+				({$bracket})	# link text = $2
+			  \\]
+
+			  [ ]?				# one optional space
+			  (?:\\n[ ]*)?		# one optional newline followed by spaces
+
+			  \\[
+				(.*?)		# id = $3
+			  \\]
+			)
+			}xs",
+			array($this,'_DoAnchors_reference_callback'), $text);
+
+		#
+		# Next, inline-style links: [link text](url "optional title")
+		#
+		$text = preg_replace_callback("{
+			(				# wrap whole match in $1
+			  \\[
+				({$bracket})	# link text = $2
+			  \\]
+			  \\(			# literal paren
+				[ \\t]*
+				?	# href = $3
+				[ \\t]*
+				(			# $4
+				  (['\"])	# quote char = $5
+				  (.*?)		# Title = $6
+				  \\5		# matching quote
+				)?			# title is optional
+			  \\)
+			)
+			}xs",
+			array($this,'_DoAnchors_inline_callback'), $text);
+
+		return $text;
+	}
+	private function _DoAnchors_reference_callback($matches) {
+		$whole_match = $matches[1];
+		$link_text   = $matches[2];
+		$link_id     = strtolower($matches[3]);
+
+		if ($link_id == "") {
+			$link_id = strtolower($link_text); # for shortcut links like [this][].
+		}
+
+		if (isset($this->md_urls[$link_id])) {
+			$url = $this->md_urls[$link_id];
+			# We've got to encode these to avoid conflicting with italics/bold.
+			$url = str_replace(array('*', '_'),
+							   array(self::$md_escape_table['*'], self::$md_escape_table['_']),
+							   $url);
+			$result = "md_titles[$link_id] ) ) {
+				$title = $this->md_titles[$link_id];
+				$title = str_replace(array('*',     '_'),
+									 array(self::$md_escape_table['*'],
+										   self::$md_escape_table['_']), $title);
+				$result .=  " title=\"$title\"";
+			}
+			$result .= ">$link_text";
+		}
+		else {
+			$result = $whole_match;
+		}
+		return $result;
+	}
+	private function _DoAnchors_inline_callback($matches) {
+		$whole_match	= $matches[1];
+		$link_text		= $matches[2];
+		$url			= $matches[3];
+		$title			=& $matches[6];
+
+		# We've got to encode these to avoid conflicting with italics/bold.
+		$url = str_replace(array('*', '_'),
+						   array(self::$md_escape_table['*'], self::$md_escape_table['_']),
+						   $url);
+		$result = " tags.
+	#
+		#
+		# First, handle reference-style labeled images: ![alt text][id]
+		#
+		$text = preg_replace_callback('{
+			(				# wrap whole match in $1
+			  !\[
+				('.self::$md_nested_brackets.')		# alt text = $2
+			  \]
+
+			  [ ]?				# one optional space
+			  (?:\n[ ]*)?		# one optional newline followed by spaces
+
+			  \[
+				(.*?)		# id = $3
+			  \]
+
+			)
+			}xs',
+			array($this,'_DoImages_reference_callback'), $text);
+
+		#
+		# Next, handle inline images:  ![alt text](url "optional title")
+		# Don't forget: encode * and _
+
+		$text = preg_replace_callback('{
+			(				# wrap whole match in $1
+			  !\[
+				('.self::$md_nested_brackets.')		# alt text = $2
+			  \]
+			  \(			# literal paren
+				[ \t]*
+				?	# src url = $3
+				[ \t]*
+				(			# $4
+				  ([\'"])	# quote char = $5
+				  (.*?)		# title = $6
+				  \5		# matching quote
+				  [ \t]*
+				)?			# title is optional
+			  \)
+			)
+			}xs',
+			array($this,'_DoImages_inline_callback'), $text);
+
+		return $text;
+	}
+	private function _DoImages_reference_callback($matches) {
+		$whole_match = $matches[1];
+		$alt_text    = $matches[2];
+		$link_id     = strtolower($matches[3]);
+
+		if ($link_id == "") {
+			$link_id = strtolower($alt_text); # for shortcut links like ![this][].
+		}
+
+		$alt_text = str_replace('"', '"', $alt_text);
+		if (isset($this->md_urls[$link_id])) {
+			$url = $this->md_urls[$link_id];
+			# We've got to encode these to avoid conflicting with italics/bold.
+			$url = str_replace(array('*', '_'),
+							   array(self::$md_escape_table['*'], self::$md_escape_table['_']),
+							   $url);
+			$result = "\"$alt_text\"";md_titles[$link_id])) {
+				$title = $this->md_titles[$link_id];
+				$title = str_replace(array('*', '_'),
+									 array(self::$md_escape_table['*'],
+										   self::$md_escape_table['_']), $title);
+				$result .=  " title=\"$title\"";
+			}
+			$result .= $this->md_empty_element_suffix;
+		}
+		else {
+			# If there's no such link ID, leave intact:
+			$result = $whole_match;
+		}
+
+		return $result;
+	}
+	private function _DoImages_inline_callback($matches) {
+		$whole_match	= $matches[1];
+		$alt_text		= $matches[2];
+		$url			= $matches[3];
+		$title			= '';
+		if (isset($matches[6])) {
+			$title		= $matches[6];
+		}
+
+		$alt_text = str_replace('"', '"', $alt_text);
+		$title    = str_replace('"', '"', $title);
+		# We've got to encode these to avoid conflicting with italics/bold.
+		$url = str_replace(array('*', '_'),
+						   array(self::$md_escape_table['*'], self::$md_escape_table['_']),
+						   $url);
+		$result = "\"$alt_text\"";md_empty_element_suffix;
+
+		return $result;
+	}
+
+
+	private function _DoHeaders($text) {
+		# Setext-style headers:
+		#	  Header 1
+		#	  ========
+		#
+		#	  Header 2
+		#	  --------
+		#
+		$text = preg_replace(
+			array('{ ^(.+)[ \t]*\n=+[ \t]*\n+ }emx',
+				  '{ ^(.+)[ \t]*\n-+[ \t]*\n+ }emx'),
+			array("'

'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\1')).'

\n\n'", + "'

'.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\1')).'

\n\n'"), + $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace("{ + ^(\\#{1,6}) # $1 = string of #'s + [ \\t]* + (.+?) # $2 = Header text + [ \\t]* + \\#* # optional closing #'s (not counted) + \\n+ + }xme", + "''.\$this->_RunSpanGamut(\$this->_UnslashQuotes('\\2')).'\n\n'", + $text); + + return $text; + } + + + private function _DoLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->md_tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $markers = array($marker_ul, $marker_ol); + + foreach ($markers as $marker) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list = ' + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ('.$marker.') # $3 = first list item marker + [ \t]+ + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ \t]* + '.$marker.'[ \t]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->md_list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list.' + }mx', + array($this,'_DoLists_callback_top'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n\n)|\A\n?) + '.$whole_list.' + }mx', + array($this,'_DoLists_callback_nested'), $text); + } + } + + return $text; + } + private function _DoLists_callback_top($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol"; + + $marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol ); + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $list = preg_replace("/\n{2,}/", "\n\n\n", $list); + $result = $this->_ProcessListItems($list, $marker_any); + + # Trim any trailing whitespace, to put the closing `` + # up on the preceding line, to get it past the current stupid + # HTML block parser. This is a hack to work around the terrible + # hack that is the HTML block parser. + $result = rtrim($result); + $result = "<$list_type>" . $result . "\n"; + return $result; + } + private function _DoLists_callback_nested($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul = '[*+-]'; + $marker_ol = '\d+[.]'; + $marker_any = "(?:$marker_ul|$marker_ol)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol"; + + $marker_any = ( $list_type == "ul" ? $marker_ul : $marker_ol ); + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $list = preg_replace("/\n{2,}/", "\n\n\n", $list); + $result = $this->_ProcessListItems($list, $marker_any); + $result = "<$list_type>\n" . $result . "\n"; + return $result; + } + + + private function _ProcessListItems($list_str, $marker_any) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + + # The $md_list_level keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->md_list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ \t]*) # leading whitespace = $2 + ('.$marker_any.') [ \t]+ # list marker = $3 + ((?s:.+?) # list item text = $4 + (\n{1,2})) + (?= \n* (\z | \2 ('.$marker_any.') [ \t]+)) + }xm', + array($this,'_ProcessListItems_callback'), $list_str); + + $this->md_list_level--; + return $list_str; + } + private function _ProcessListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + + if ($leading_line || preg_match('/\n{2,}/', $item)) { + $item = $this->_RunBlockGamut($this->_Outdent($item)); + } + else { + # Recursion for sub-lists: + $item = $this->_DoLists($this->_Outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->_RunSpanGamut($item); + } + + return "
  • " . $item . "
  • \n"; + } + + + private function _DoCodeBlocks($text) { + # + # Process Markdown `
    ` blocks.
    +	#
    +		$text = preg_replace_callback('{
    +				(?:\n\n|\A)
    +				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    +				  (?:
    +					(?:[ ]{'.$this->md_tab_width.'} | \t)  # Lines must start with a tab or a tab-width of spaces
    +					.*\n+
    +				  )+
    +				)
    +				((?=^[ ]{0,'.$this->md_tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    +			}xm',
    +			array($this,'_DoCodeBlocks_callback'), $text);
    +
    +		return $text;
    +	}
    +	private function _DoCodeBlocks_callback($matches) {
    +		$codeblock = $matches[1];
    +
    +		$codeblock = $this->_EncodeCode($this->_Outdent($codeblock));
    +	//	$codeblock = _Detab($codeblock);
    +		# trim leading newlines and trailing whitespace
    +		$codeblock = preg_replace(array('/\A\n+/', '/\s+\z/'), '', $codeblock);
    +
    +		$result = "\n\n
    " . $codeblock . "\n
    \n\n"; + + return $result; + } + + + private function _DoCodeSpans($text) { + # + # * Backtick quotes are used for spans. + # + # * You can use multiple backticks as the delimiters if you want to + # include literal backticks in the code span. So, this input: + # + # Just type ``foo `bar` baz`` at the prompt. + # + # Will translate to: + # + #

    Just type foo `bar` baz at the prompt.

    + # + # There's no arbitrary limit to the number of backticks you + # can use as delimters. If you need three consecutive backticks + # in your code, use four for delimiters, etc. + # + # * You can use spaces to get literal backticks at the edges: + # + # ... type `` `bar` `` ... + # + # Turns to: + # + # ... type `bar` ... + # + $text = preg_replace_callback('@ + (?_EncodeCode($c); + return "$c"; + } + + + private function _EncodeCode($_) { + # + # Encode/escape certain characters inside Markdown code runs. + # The point is that in code, these characters are literals, + # and lose their special Markdown meanings. + # + # Encode all ampersands; HTML entities are not + # entities within a Markdown code span. + $_ = str_replace('&', '&', $_); + + # Do the angle bracket song and dance: + $_ = str_replace(array('<', '>'), + array('<', '>'), $_); + + # Now, escape characters that are magic in Markdown: + $_ = str_replace(array_keys(self::$md_escape_table), + array_values(self::$md_escape_table), $_); + + return $_; + } + + + private function _DoItalicsAndBold($text) { + # must go first: + $text = preg_replace('{ + ( # $1: Marker + (?\2', $text); + # Then : + $text = preg_replace( + '{ ( (?\2', $text); + + return $text; + } + + + private function _DoBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + ( + ^[ \t]*>[ \t]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array($this,'_DoBlockQuotes_callback'), $text); + + return $text; + } + private function _DoBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace(array('/^[ \t]*>[ \t]?/m', '/^[ \t]+$/m'), '', $bq); + $bq = $this->_RunBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces screw with
     content, so we need to fix that:
    +		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', + array($this,'_DoBlockQuotes_callback2'), $bq); + + return "
    \n$bq\n
    \n\n"; + } + private function _DoBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + private function _FormParagraphs($text) { + # + # Params: + # $text - string to process with html

    tags + # + # Strip leading and trailing lines: + $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags. + # + foreach ($grafs as $key => $value) { + if (!isset( $this->md_html_blocks[$value] )) { + $value = $this->_RunSpanGamut($value); + $value = preg_replace('/^([ \t]*)/', '

    ', $value); + $value .= "

    "; + $grafs[$key] = $value; + } + } + + # + # Unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (isset( $this->md_html_blocks[$value] )) { + $grafs[$key] = $this->md_html_blocks[$value]; + } + } + + return implode("\n\n", $grafs); + } + + + private function _EncodeAmpsAndAngles($text) { + # Smart processing for ampersands and angle brackets that need to be encoded. + + # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + # http://bumppo.net/projects/amputator/ + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text);; + + # Encode naked <'s + $text = preg_replace('{<(?![a-z/?\$!])}i', '<', $text); + + return $text; + } + + + private function _EncodeBackslashEscapes($text) { + # + # Parameter: String. + # Returns: The string, with after processing the following backslash + # escape sequences. + # + # Must process escaped backslashes first. + return str_replace(array_keys(self::$md_backslash_escape_table), + array_values(self::$md_backslash_escape_table), $text); + } + + + private function _DoAutoLinks($text) { + $text = preg_replace("!<((https?|ftp):[^'\">\\s]+)>!", + '
    \1', $text); + + # Email addresses: + $text = preg_replace('{ + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ + ) + > + }exi', + "\$this->_EncodeEmailAddress(\$this->_UnescapeSpecialChars(\$this->_UnslashQuotes('\\1')))", + $text); + + return $text; + } + + + private function _EncodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + # foo + # @example.com + # + # Based by a filter by Matthew Wickline, posted to the BBEdit-Talk + # mailing list: + # + $addr = "mailto:" . $addr; + $length = strlen($addr); + + # leave ':' alone (to spot mailto: later) + $addr = preg_replace_callback('/([^\:])/', + array($this,'_EncodeEmailAddress_callback'), $addr); + + $addr = "$addr"; + # strip the mailto: from the visible part + $addr = preg_replace('/">.+?:/', '">', $addr); + + return $addr; + } + private function _EncodeEmailAddress_callback($matches) { + $char = $matches[1]; + $r = rand(0, 100); + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') return $char; + if ($r < 45) return '&#x'.dechex(ord($char)).';'; + return '&#'.ord($char).';'; + } + + + private function _UnescapeSpecialChars($text) { + # + # Swap back in all the special characters we've hidden. + # + return str_replace(array_values(self::$md_escape_table), + array_keys(self::$md_escape_table), $text); + } + + + # _TokenizeHTML is shared between PHP Markdown and PHP SmartyPants. + # We only define it if it is not already defined. + + private function _TokenizeHTML($str) { + # + # Parameter: String containing HTML markup. + # Returns: An array of the tokens comprising the input + # string. Each token is either a tag (possibly with nested, + # tags contained therein, such as , or a + # run of text between tags. Each element of the array is a + # two-element array; the first is either 'tag' or 'text'; + # the second is the actual value. + # + # + # Regular expression derived from the _tokenize() subroutine in + # Brad Choate's MTRegex plugin. + # + # + $index = 0; + $tokens = array(); + + $match = '(?s:)|'. # comment + '(?s:<\?.*?\?>)|'. # processing instruction + # regular tags + '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)'; + + $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE); + + foreach ($parts as $part) { + if (++$index % 2 && $part != '') + $tokens[] = array('text', $part); + else + $tokens[] = array('tag', $part); + } + + return $tokens; + } + + private function _Outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace("/^(\\t|[ ]{1,".$this->md_tab_width."})/m", "", $text); + } + + + private function _Detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $lines = explode("\n", $text); + $text = ""; + + foreach ($lines as $line) { + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->md_tab_width - strlen($line) % $this->md_tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + $text .= "$line\n"; + } + return $text; + } + + + private function _UnslashQuotes($text) { + # + # This function is useful to remove automaticaly slashed double quotes + # when using preg_replace and evaluating an expression. + # Parameter: String. + # Returns: The string with any slash-double-quote (\") sequence replaced + # by a single double quote. + # + return str_replace('\"', '"', $text); + } +} + +/* + +PHP Markdown +============ + +Description +----------- + +This is a PHP translation of the original Markdown formatter written in +Perl by John Gruber. + +Markdown is a text-to-HTML filter; it translates an easy-to-read / +easy-to-write structured text format into HTML. Markdown's text format +is most similar to that of plain text email, and supports features such +as headers, *emphasis*, code blocks, blockquotes, and links. + +Markdown's syntax is designed not as a generic markup language, but +specifically to serve as a front-end to (X)HTML. You can use span-level +HTML tags anywhere in a Markdown document, and you can use block level +HTML tags (like
    and as well). + +For more information about Markdown's syntax, see: + + + + +Bugs +---- + +To file bug reports please send email to: + + + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output Markdown actually produced. + + +Version History +--------------- + +See the readme file for detailed release notes for this version. + +1.0.1c - 9 Dec 2005 + +1.0.1b - 6 Jun 2005 + +1.0.1a - 15 Apr 2005 + +1.0.1 - 16 Dec 2004 + +1.0 - 21 Aug 2004 + + +Author & Contributors +--------------------- + +Original Perl version by John Gruber + + +PHP port and other contributions by Michel Fortin + + + +Copyright and License +--------------------- + +Copyright (c) 2004-2005 Michel Fortin + +All rights reserved. + +Copyright (c) 2003-2004 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. + +*/ diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell.php new file mode 100644 index 0000000000..8012475e6f --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell.php @@ -0,0 +1,1091 @@ + + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** +* A interactive PHP Shell +* +* The more I work with other languages like python and ruby I like their way how they +* work on problems. While PHP is very forgiving on errors, it is weak on the debugging +* side. It was missing a simple to use interactive shell for years. Python and Ruby have +* their ipython and iruby shell which give you a direct way to interact with the objects. +* No need to write a script and execute it afterwards. +* +* Starting the Shell: +* +* The package contains a shell wrapper for windows and unix: +*
    +* sh>  php-shell.sh
    +* win> php-shell
    +* 
    +* +* Both are calling the wrapper script php -q php-shell-cmd.php +* +* Inline Help +* +*
    +* PHP-Shell - Version 0.2.0, with readline() support
    +* (c) 2006, Jan Kneschke 
    +*
    +* >> use '?' to open the inline help
    +*
    +* >> ?
    +* "inline help for the PHP-shell
    +*
    +*   >> ?
    +*     print this help
    +*   >> ? 
    +*     get the doccomment for a class, method, property or function
    +*   >> p 
    +*     execute a verbose print (if implemented)
    +*   >> quit
    +*     leave shell
    +* "
    +* >> ? PHP_Shell
    +* 
    +* Alternatives +* +* - http://david.acz.org/phpa/ +* - http://www.hping.org/phpinteractive/ +* - the embedded interactive php-shell: $ php -a +* +* @package PHP +*/ + +/** +* PHP_Shell +* +* a interactive PHP Shell with tab-completion and history +* it can catch FATAL errors before executing the code +* +* Extensions are provided through three side-classes: +* +* - PHP_Shell_Commands +* - PHP_Shell_Options +* - PHP_Shell_Extensions +* +* @package PHP +*/ + +require_once(dirname(__FILE__)."/Shell/Commands.php"); +require_once(dirname(__FILE__)."/Shell/Options.php"); /* for the tab-complete */ + +class PHP_Shell { + /** + * current code-buffer + * @var string + */ + protected $code; + + /** + * set if readline support is enabled + * @var bool + */ + protected $have_readline; + + /** + * current version of the class + * @var string + */ + protected $version = '0.3.1'; + + /** + * + */ + protected $stdin; + + protected $code_buffer; + + public $has_semicolon=false; + + /** + * init the shell and change if readline support is available + */ + public function __construct() { + $this->code = ''; + + $this->stdin = null; + + $this->have_readline = function_exists('readline'); + + if ($this->have_readline) { + readline_completion_function('__shell_readline_complete'); + } + + $this->use_readline = true; + + $cmd = PHP_Shell_Commands::getInstance(); + + $cmd->registerCommand('#^quit$#', $this, 'cmdQuit', 'quit', 'leaves the shell'); + $cmd->registerCommand('#^\?$#', $this, 'cmdHelp', '?', 'show this help'); + $cmd->registerCommand('#^\?\s+license$#', $this, 'cmdLicense', '? license', 'show license of the shell'); + } + + + /** + * parse the PHP code + * + * we parse before we eval() the code to + * - fetch fatal errors before they come up + * - know about where we have to wait for closing braces + * + * @return int 0 if a executable statement is in the code-buffer, non-zero otherwise + */ + public function parse() { + ## remove empty lines + if (trim($this->code) == '') return 1; + + $t = token_get_all('code.' ?>'); + + $need_semicolon = 1; /* do we need a semicolon to complete the statement ? */ + $need_return = 1; /* can we prepend a return to the eval-string ? */ + $open_comment = 0; /* a open multi-line comment */ + $eval = ''; /* code to be eval()'ed later */ + $braces = array(); /* to track if we need more closing braces */ + + $methods = array(); /* to track duplicate methods in a class declaration */ + $ts = array(); /* tokens without whitespaces */ + + foreach ($t as $ndx => $token) { + if (is_array($token)) { + $ignore = 0; + + switch($token[0]) { + case T_WHITESPACE: + case T_OPEN_TAG: + case T_CLOSE_TAG: + $ignore = 1; + break; + case T_FOREACH: + case T_DO: + case T_WHILE: + case T_FOR: + + case T_IF: + case T_RETURN: + + case T_CLASS: + case T_FUNCTION: + case T_INTERFACE: + + case T_PRINT: + case T_ECHO: + + case T_COMMENT: + case T_UNSET: + + case T_INCLUDE: + case T_REQUIRE: + case T_INCLUDE_ONCE: + case T_REQUIRE_ONCE: + case T_TRY: + case T_SWITCH: + case T_DEFAULT: + case T_CASE: + case T_BREAK: + case T_DOC_COMMENT: + $need_return = 0; + break; + case T_EMPTY: + case T_ISSET: + case T_EVAL: + case T_EXIT: + + case T_VARIABLE: + case T_STRING: + case T_NEW: + case T_EXTENDS: + case T_IMPLEMENTS: + case T_OBJECT_OPERATOR: + case T_DOUBLE_COLON: + case T_INSTANCEOF: + + case T_CATCH: + case T_THROW: + + case T_ELSE: + case T_AS: + case T_LNUMBER: + case T_DNUMBER: + case T_CONSTANT_ENCAPSED_STRING: + case T_ENCAPSED_AND_WHITESPACE: + case T_CHARACTER: + case T_ARRAY: + case T_DOUBLE_ARROW: + + case T_CONST: + case T_PUBLIC: + case T_PROTECTED: + case T_PRIVATE: + case T_ABSTRACT: + case T_STATIC: + case T_VAR: + + case T_INC: + case T_DEC: + case T_SL: + case T_SL_EQUAL: + case T_SR: + case T_SR_EQUAL: + + case T_IS_EQUAL: + case T_IS_IDENTICAL: + case T_IS_GREATER_OR_EQUAL: + case T_IS_SMALLER_OR_EQUAL: + + case T_BOOLEAN_OR: + case T_LOGICAL_OR: + case T_BOOLEAN_AND: + case T_LOGICAL_AND: + case T_LOGICAL_XOR: + case T_MINUS_EQUAL: + case T_PLUS_EQUAL: + case T_MUL_EQUAL: + case T_DIV_EQUAL: + case T_MOD_EQUAL: + case T_XOR_EQUAL: + case T_AND_EQUAL: + case T_OR_EQUAL: + + case T_FUNC_C: + case T_CLASS_C: + case T_LINE: + case T_FILE: + + case T_BOOL_CAST: + case T_INT_CAST: + case T_STRING_CAST: + + /* just go on */ + break; + default: + /* debug unknown tags*/ + error_log(sprintf("unknown tag: %d (%s): %s".PHP_EOL, $token[0], token_name($token[0]), $token[1])); + + break; + } + if (!$ignore) { + $eval .= $token[1]." "; + $ts[] = array("token" => $token[0], "value" => $token[1]); + } + } else { + $ts[] = array("token" => $token, "value" => ''); + + $last = count($ts) - 1; + + switch ($token) { + case '(': + /* walk backwards through the tokens */ + + if ($last >= 4 && + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && + $ts[$last - 3]['token'] == ')' ) { + /* func()->method() + * + * we can't know what func() is return, so we can't + * say if the method() exists or not + * + */ + } else if ($last >= 3 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */ + $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && + $ts[$last - 3]['token'] == T_VARIABLE ) { + + /* $object->method( */ + + /* catch (Exception $e) does not set $e in $GLOBALS[] */ + $in_catch = 0; + + foreach ($ts as $v) { + if ($v['token'] == T_CATCH) { + $in_catch = 1; + } + } + + if (!$in_catch) { + /* $object has to exist and has to be a object */ + $objname = $ts[$last - 3]['value']; + + if (!isset($GLOBALS[ltrim($objname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $objname)); + } + $object = $GLOBALS[ltrim($objname, '$')]; + + if (!is_object($object)) { + throw new Exception(sprintf('Variable \'%s\' is not a class', $objname)); + } + + $method = $ts[$last - 1]['value']; + + /* obj */ + + if (!method_exists($object, $method)) { + throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'", + $objname, get_class($object), $method)); + } + } + } else if ($last >= 3 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_VARIABLE && + $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && + $ts[$last - 3]['token'] == T_VARIABLE ) { + + /* $object->$method( */ + + /* $object has to exist and has to be a object */ + $objname = $ts[$last - 3]['value']; + + if (!isset($GLOBALS[ltrim($objname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $objname)); + } + $object = $GLOBALS[ltrim($objname, '$')]; + + if (!is_object($object)) { + throw new Exception(sprintf('Variable \'%s\' is not a class', $objname)); + } + + $methodname = $ts[$last - 1]['value']; + + if (!isset($GLOBALS[ltrim($methodname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $methodname)); + } + $method = $GLOBALS[ltrim($methodname, '$')]; + + /* obj */ + + if (!method_exists($object, $method)) { + throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'", + $objname, get_class($object), $method)); + } + + } else if ($last >= 6 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_OBJECT_OPERATOR && + $ts[$last - 3]['token'] == ']' && + /* might be anything as index */ + $ts[$last - 5]['token'] == '[' && + $ts[$last - 6]['token'] == T_VARIABLE ) { + + /* $object[...]->method( */ + + /* $object has to exist and has to be a object */ + $objname = $ts[$last - 6]['value']; + + if (!isset($GLOBALS[ltrim($objname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $objname)); + } + $array = $GLOBALS[ltrim($objname, '$')]; + + if (!is_array($array)) { + throw new Exception(sprintf('Variable \'%s\' is not a array', $objname)); + } + + $andx = $ts[$last - 4]['value']; + + if (!isset($array[$andx])) { + throw new Exception(sprintf('%s[\'%s\'] is not set', $objname, $andx)); + } + + $object = $array[$andx]; + + if (!is_object($object)) { + throw new Exception(sprintf('Variable \'%s\' is not a class', $objname)); + } + + $method = $ts[$last - 1]['value']; + + /* obj */ + + if (!method_exists($object, $method)) { + throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'", + $objname, get_class($object), $method)); + } + + } else if ($last >= 3 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_DOUBLE_COLON && + $ts[$last - 3]['token'] == T_STRING ) { + + /* Class::method() */ + + /* $object has to exist and has to be a object */ + $classname = $ts[$last - 3]['value']; + + if (!class_exists($classname)) { + throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname)); + } + + $method = $ts[$last - 1]['value']; + + if (!in_array($method, get_class_methods($classname))) { + throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'", + $classname, $method)); + } + } else if ($last >= 3 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_VARIABLE && + $ts[$last - 2]['token'] == T_DOUBLE_COLON && + $ts[$last - 3]['token'] == T_STRING ) { + + /* $var::method() */ + + /* $object has to exist and has to be a object */ + $classname = $ts[$last - 3]['value']; + + if (!class_exists($classname)) { + throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname)); + } + + $methodname = $ts[$last - 1]['value']; + + if (!isset($GLOBALS[ltrim($methodname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $methodname)); + } + $method = $GLOBALS[ltrim($methodname, '$')]; + + if (!in_array($method, get_class_methods($classname))) { + throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'", + $classname, $method)); + } + + } else if ($last >= 2 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_NEW ) { + + /* new Class() */ + + /* don't care about this in a class ... { ... } */ + + $classname = $ts[$last - 1]['value']; + + if (!class_exists($classname)) { + throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname)); + } + + $r = new ReflectionClass($classname); + + if ($r->isAbstract()) { + throw new Exception(sprintf("Can't instantiate abstract Class '%s'", $classname)); + } + + if (!$r->isInstantiable()) { + throw new Exception(sprintf('Class \'%s\' can\'t be instantiated. Is the class abstract ?', $classname)); + } + + } else if ($last >= 2 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_FUNCTION ) { + + /* make sure we are not a in class definition */ + + /* function a() */ + + $func = $ts[$last - 1]['value']; + + if (function_exists($func)) { + throw new Exception(sprintf('Function \'%s\' is already defined', $func)); + } + } else if ($last >= 4 && + $ts[0]['token'] == T_CLASS && + $ts[1]['token'] == T_STRING && + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_FUNCTION ) { + + /* make sure we are not a in class definition */ + + /* class a { .. function a() ... } */ + + $func = $ts[$last - 1]['value']; + $classname = $ts[1]['value']; + + if (isset($methods[$func])) { + throw new Exception(sprintf("Can't redeclare method '%s' in Class '%s'", $func, $classname)); + } + + $methods[$func] = 1; + + } else if ($last >= 1 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */ + $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_STRING ) { + /* func() */ + $funcname = $ts[$last - 1]['value']; + + if (!function_exists($funcname)) { + throw new Exception(sprintf("Function %s() doesn't exist", $funcname)); + } + } else if ($last >= 1 && + $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_VARIABLE ) { + + /* $object has to exist and has to be a object */ + $funcname = $ts[$last - 1]['value']; + + if (!isset($GLOBALS[ltrim($funcname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $funcname)); + } + $func = $GLOBALS[ltrim($funcname, '$')]; + + if (!function_exists($func)) { + throw new Exception(sprintf("Function %s() doesn't exist", $func)); + } + + } + + array_push($braces, $token); + break; + case '{': + $need_return = 0; + + if ($last >= 2 && + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_CLASS ) { + + /* class name { */ + + $classname = $ts[$last - 1]['value']; + + if (class_exists($classname, false)) { + throw new Exception(sprintf("Class '%s' can't be redeclared", $classname)); + } + } else if ($last >= 4 && + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_EXTENDS && + $ts[$last - 3]['token'] == T_STRING && + $ts[$last - 4]['token'] == T_CLASS ) { + + /* class classname extends classname { */ + + $classname = $ts[$last - 3]['value']; + $extendsname = $ts[$last - 1]['value']; + + if (class_exists($classname, false)) { + throw new Exception(sprintf("Class '%s' can't be redeclared", + $classname)); + } + if (!class_exists($extendsname, true)) { + throw new Exception(sprintf("Can't extend '%s' ... from not existing Class '%s'", + $classname, $extendsname)); + } + } else if ($last >= 4 && + $ts[$last - 1]['token'] == T_STRING && + $ts[$last - 2]['token'] == T_IMPLEMENTS && + $ts[$last - 3]['token'] == T_STRING && + $ts[$last - 4]['token'] == T_CLASS ) { + + /* class name implements interface { */ + + $classname = $ts[$last - 3]['value']; + $implements = $ts[$last - 1]['value']; + + if (class_exists($classname, false)) { + throw new Exception(sprintf("Class '%s' can't be redeclared", + $classname)); + } + if (!interface_exists($implements, false)) { + throw new Exception(sprintf("Can't implement not existing Interface '%s' for Class '%s'", + $implements, $classname)); + } + } + + array_push($braces, $token); + break; + case '}': + $need_return = 0; + case ')': + array_pop($braces); + break; + case '[': + if ($ts[0]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */ + $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */ + $ts[$last - 1]['token'] == T_VARIABLE) { + /* $a[] only works on array and string */ + + /* $object has to exist and has to be a object */ + $objname = $ts[$last - 1]['value']; + + if (!isset($GLOBALS[ltrim($objname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $objname)); + } + $obj = $GLOBALS[ltrim($objname, '$')]; + + if (is_object($obj)) { + throw new Exception(sprintf('Objects (%s) don\'t support array access operators', $objname)); + } + } + break; + } + + $eval .= $token; + } + } + + $last = count($ts) - 1; + if ($last >= 2 && + $ts[$last - 0]['token'] == T_STRING && + $ts[$last - 1]['token'] == T_DOUBLE_COLON && + $ts[$last - 2]['token'] == T_STRING ) { + + /* Class::constant */ + + /* $object has to exist and has to be a object */ + $classname = $ts[$last - 2]['value']; + + if (!class_exists($classname)) { + throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname)); + } + + $constname = $ts[$last - 0]['value']; + + $c = new ReflectionClass($classname); + if (!$c->hasConstant($constname)) { + throw new Exception(sprintf("Class '%s' doesn't have a constant named '%s'", + $classname, $constname)); + } + } else if ($last == 0 && + $ts[$last - 0]['token'] == T_VARIABLE ) { + + /* $var */ + + $varname = $ts[$last - 0]['value']; + + if (!isset($GLOBALS[ltrim($varname, '$')])) { + throw new Exception(sprintf('Variable \'%s\' is not set', $varname)); + } + } + + + $need_more = (count($braces) > 0) || $open_comment; + + if ($need_more || ';' === $token) { + $need_semicolon = 0; + } + + if ($need_return) { + $eval = "return ".$eval; + } + + /* add a traling ; if necessary */ + if ($need_semicolon) + { + $this->has_semicolon = preg_match('/;\s*$/', $eval); + $eval .= ';'; + } + + if (!$need_more) { + $this->code = $eval; + } + + return $need_more; + } + + /** + * show the prompt and fetch a single line + * + * uses readline() if avaialbe + * + * @return string a input-line + */ + public function readline() { + if (empty($this->code)) print PHP_EOL; + + $prompt = (empty($this->code)) ? '>> ' : '.. '; + + if (count($this->code_buffer) > 0) { + print $prompt; + + $line = array_shift($this->code_buffer); + + print $line.PHP_EOL; + + return $line.PHP_EOL; + } + + if ($this->have_readline) { + $l = readline($prompt); + + readline_add_history($l); + } else { + print $prompt; + + if (is_null($this->stdin)) { + if (false === ($this->stdin = fopen("php://stdin", "r"))) { + return false; + } + } + $l = fgets($this->stdin); + } + return $l; + } + + /** + * get the inline help + * + * @return string the inline help as string + */ + public function cmdHelp($l) { + $o = 'Inline Help:'.PHP_EOL; + + $cmds = PHP_Shell_Commands::getInstance()->getCommands(); + + $help = array(); + foreach ($cmds as $cmd) { + $help[] = sprintf(' >> %s'.PHP_EOL.' %s'.PHP_EOL, + $cmd['command'], + $cmd['description'] + ); + } + + return var_export(implode("\n", $help), 1); + } + + /** + * get the license string + * + * @return string the inline help as string + */ + public function cmdLicense($l) { + $o = << + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +EOF; + + return var_export($o, 1); + } + + /** + * handle the 'quit' command + * + * @return bool false to leave the input() call + * @see input + */ + protected function cmdQuit($l) { + return false; + } + + /** + * handle the input line + * + * read the input and handle the commands of the shell + * + * @return bool false on 'quit' or EOF, true otherwise + */ + public function input() { + $l = $this->readline(); + + /* got EOF ? */ + if (false === $l) return false; + + $l = trim($l); + + if (empty($this->code)) { + $this->verbose = 0; + + $cmds = PHP_Shell_Commands::getInstance()->getCommands(); + + foreach ($cmds as $cmd) { + if (preg_match($cmd['regex'], $l)) { + $obj = $cmd['obj']; + $func = $cmd['method']; + + if (false === ($l = $obj->$func($l))) { + ## quit + return false; + } + + if (is_array($l)) { + $this->code_buffer = $l; + $l = ''; + } + break; + } + } + } + + $this->appendCode($l); + + return true; + } + + /** + * get the code-buffer + * + * @return string the code-buffer + */ + public function getCode() { + return $this->code; + return $code; + } + + /** + * reset the code-buffer + */ + public function resetCode() { + $this->has_semicolon=false; + $this->code = ''; + } + + /** + * append code to the code-buffer + * + * @param string $code input buffer + */ + public function appendCode($code) { + if (strlen($code)) $code .= PHP_EOL; + + $this->code .= $code; + } + + /** + * check if readline support is enabled + * + * @return bool true if enabled, false otherwise + */ + public function hasReadline() { + return $this->have_readline; + } + + /** + * get version of the class + * + * @return string version-string + */ + public function getVersion() { + return $this->version; + } +} + +/** +* a readline completion callback +* +* @param string $str linebuffer +* @param integer $pos position in linebuffer +* @return array list of possible matches +*/ +function __shell_readline_complete($str, $pos) { + $in = readline_info('line_buffer'); + + /** + * parse the line-buffer backwards to see if we have a + * - constant + * - function + * - variable + */ + + $m = array(); + + if (preg_match('#\$([A-Za-z0-9_]+)->#', $in, $a)) { + /* check for $o->... */ + $name = $a[1]; + + if (isset($GLOBALS[$name]) && is_object($GLOBALS[$name])) { + $c = get_class_methods($GLOBALS[$name]); + + foreach ($c as $v) { + $m[] = $v.'('; + } + $c = get_class_vars(get_class($GLOBALS[$name])); + + foreach ($c as $k => $v) { + $m[] = $k; + } + + return $m; + } + } else if (preg_match('#\$([A-Za-z0-9_]+)\[([^\]]+)\]->#', $in, $a)) { + /* check for $o[...]->... */ + $name = $a[1]; + + if (isset($GLOBALS[$name]) && + is_array($GLOBALS[$name]) && + isset($GLOBALS[$name][$a[2]])) { + + $c = get_class_methods($GLOBALS[$name][$a[2]]); + + foreach ($c as $v) { + $m[] = $v.'('; + } + $c = get_class_vars(get_class($GLOBALS[$name][$a[2]])); + + foreach ($c as $k => $v) { + $m[] = $k; + } + return $m; + } + + } else if (preg_match('#([A-Za-z0-9_]+)::#', $in, $a)) { + /* check for Class:: */ + $name = $a[1]; + + if (class_exists($name, false)) { + $c = get_class_methods($name); + + foreach ($c as $v) { + $m[] = sprintf('%s::%s(', $name, $v); + } + + $cl = new ReflectionClass($name); + $c = $cl->getConstants(); + + foreach ($c as $k => $v) { + $m[] = sprintf('%s::%s', $name, $k); + } + + return $m; + } + } else if (preg_match('#\$([a-zA-Z]?[a-zA-Z0-9_]*)$#', $in)) { + $m = array_keys($GLOBALS); + + return $m; + } else if (preg_match('#new #', $in)) { + $c = get_declared_classes(); + + foreach ($c as $v) { + $m[] = $v.'('; + } + + return $m; + } else if (preg_match('#^:set #', $in)) { + foreach (PHP_Shell_Options::getInstance()->getOptions() as $v) { + $m[] = $v; + } + + return $m; + } + + $f = get_defined_functions(); + + foreach ($f['internal'] as $v) { + $m[] = $v.'('; + } + + foreach ($f['user'] as $v) { + $m[] = $v.'('; + } + + $c = get_declared_classes(); + + foreach ($c as $v) { + $m[] = $v.'::'; + } + + $c = get_defined_constants(); + + foreach ($c as $k => $v) { + $m[] = $k; + } + + /* taken from http://de3.php.net/manual/en/reserved.php */ + $m[] = 'abstract'; + $m[] = 'and'; + $m[] = 'array('; + $m[] = 'as'; + $m[] = 'break'; + $m[] = 'case'; + $m[] = 'catch'; + $m[] = 'class'; + $m[] = 'const'; + $m[] = 'continue'; + # $m[] = 'declare'; + $m[] = 'default'; + $m[] = 'die('; + $m[] = 'do'; + $m[] = 'echo('; + $m[] = 'else'; + $m[] = 'elseif'; + $m[] = 'empty('; + # $m[] = 'enddeclare'; + $m[] = 'eval('; + $m[] = 'exception'; + $m[] = 'extends'; + $m[] = 'exit('; + $m[] = 'extends'; + $m[] = 'final'; + $m[] = 'for ('; + $m[] = 'foreach ('; + $m[] = 'function'; + $m[] = 'global'; + $m[] = 'if'; + $m[] = 'implements'; + $m[] = 'include "'; + $m[] = 'include_once "'; + $m[] = 'interface'; + $m[] = 'isset('; + $m[] = 'list('; + $m[] = 'new'; + $m[] = 'or'; + $m[] = 'print('; + $m[] = 'private'; + $m[] = 'protected'; + $m[] = 'public'; + $m[] = 'require "'; + $m[] = 'require_once "'; + $m[] = 'return'; + $m[] = 'static'; + $m[] = 'switch ('; + $m[] = 'throw'; + $m[] = 'try'; + $m[] = 'unset('; + # $m[] = 'use'; + $m[] = 'var'; + $m[] = 'while'; + $m[] = 'xor'; + $m[] = '__FILE__'; + $m[] = '__FUNCTION__'; + $m[] = '__CLASS__'; + $m[] = '__LINE__'; + $m[] = '__METHOD__'; + + # printf("%s ... %s\n", $str, $pos); + return $m; +} + + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Commands.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Commands.php new file mode 100644 index 0000000000..3d4bf5b2a7 --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Commands.php @@ -0,0 +1,72 @@ +' to catch the cmdline strings. +* +* registerCommand() should be called by the extensions in the register() +* method. Its parameters are +* - the regex which matches the command +* - the object and the method to call if the command is matched +* - the human readable command string and the description for the help +*/ +class PHP_Shell_Commands { + /* + * instance of the current class + * + * @var PHP_Shell_Commands + */ + static protected $instance; + + /** + * registered commands + * + * array('quit' => ... ) + * + * @var array + * @see registerCommand + */ + protected $commands = array(); + + /** + * register your own command for the shell + * + * @param string $regex a regex to match against the input line + * @param string $obj a Object + * @param string $method a method in the object to call of the regex matches + * @param string $cmd the command string for the help + * @param string $help the full help description for this command + */ + public function registerCommand($regex, $obj, $method, $cmd, $help) { + $this->commands[] = array( + 'regex' => $regex, + 'obj' => $obj, + 'method' => $method, + 'command' => $cmd, + 'description' => $help + ); + } + + /** + * return a copy of the commands array + * + * @return all commands + */ + public function getCommands() { + return $this->commands; + } + + static function getInstance() { + if (is_null(self::$instance)) { + $class = __CLASS__; + self::$instance = new $class(); + } + return self::$instance; + } +} + + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions.php new file mode 100644 index 0000000000..9b210c47c3 --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions.php @@ -0,0 +1,86 @@ +name->... + * + * @param string registered name of the extension + * @return PHP_Shell_Extension object handle + */ + public function __get($key) { + if (!isset($this->exts[$key])) { + throw new Exception("Extension $s is not known."); + } + return $this->exts[$key]; + } + + /** + * register set of extensions + * + * @param array set of (name, class-name) pairs + */ + public function registerExtensions($exts) { + foreach ($exts as $k => $v) { + $this->registerExtension($k, $v); + } + } + + /** + * register a single extension + * + * @param string name of the registered extension + * @param PHP_Shell_Extension the extension object + */ + public function registerExtension($k, PHP_Shell_Extension $obj) { + $obj->register(); + + $this->exts[$k] = $obj; + } + + /** + * @return object a singleton of the class + */ + static function getInstance() { + if (is_null(self::$instance)) { + $class = __CLASS__; + self::$instance = new $class(); + } + return self::$instance; + } +} + + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Autoload.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Autoload.php new file mode 100644 index 0000000000..72a5692ba5 --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Autoload.php @@ -0,0 +1,60 @@ +registerOption("autoload", $this, "optSetAutoload"); + $opt->registerOptionAlias("al", "autoload"); + } + + /** + * sets the autoload-flag + * + * - the $value is ignored and doesn't have to be set + * - if __autoload() is defined, the set fails + */ + public function optSetAutoload($key, $value) { + if ($this->autoload) { + print('autload is already enabled'); + return; + } + + if (function_exists('__autoload')) { + print('can\'t enabled autoload as a external __autoload() function is already defined'); + return; + } + + $this->autoload = true; + } + + /** + * is the autoload-flag set ? + * + * @return bool true if __autoload() should be set by the external wrapper + */ + public function isAutoloadEnabled() { + return $this->autoload; + } +} + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/AutoloadDebug.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/AutoloadDebug.php new file mode 100644 index 0000000000..3926ff9e65 --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/AutoloadDebug.php @@ -0,0 +1,84 @@ +registerOption('autoloaddebug', $this, 'optSetAutoloadDebug'); + } + + /** + * handle the autoloaddebug flag + * + * @param string + */ + public function optSetAutoloadDebug($key, $value) { + switch ($value) { + case "enable": + case "1": + case "on": + $this->autoload_debug = true; + break; + case "disable": + case "0": + case "off": + $this->autoload_debug = false; + break; + default: + printf(":set %s failed, unknown value. Use :set %s = (on|off)", $key, $key); + return; + } + + } + + /** + * is the autoload-debug flag set ? + * + * @return bool true if debug is enabled + */ + public function isAutoloadDebug() { + return $this->autoload_debug; + } + + /** + * increment the depth counter + */ + public function incAutoloadDepth() { + return $this->autoload_depth++; + } + + /** + * decrement the depth counter + */ + public function decAutoloadDepth() { + return --$this->autoload_depth; + } +} + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Colour.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Colour.php new file mode 100644 index 0000000000..05d7be1dac --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Colour.php @@ -0,0 +1,120 @@ +registerOption("background", $this, "optSetBackground"); + $opt->registerOptionAlias("bg", "background"); + + $this->registerColourScheme( + "plain", array( + "default" => "", "value" => "", + "exception" => "", "reset" => "")); + + $this->registerColourScheme( + "dark", array( + "default" => self::C_YELLOW, + "value" => self::C_WHITE, + "exception" => self::C_PURPLE)); + + $this->registerColourScheme( + "light", array( + "default" => self::C_BLACK, + "value" => self::C_BLUE, + "exception" => self::C_RED)); + + } + + /** + * background colours + */ + public function optSetBackground($key, $value) { + if (is_null($value)) { + print(':set '.$key.' needs a colour-scheme, e.g. :set '.$key.'=dark'); + return; + } + if (false == $this->applyColourScheme($value)) { + print('setting colourscheme failed: colourscheme '.$value.' is unknown'); + return; + } + } + + /** + * get a colour for the shell + * + * @param string $type one of (value|exception|reset|default) + * @return string a colour string or a empty string + */ + public function getColour($type) { + return isset($this->colour[$type]) ? $this->colour[$type] : ''; + } + + /** + * apply a colour scheme to the current shell + * + * @param string $scheme name of the scheme + * @return false if colourscheme is not known, otherwise true + */ + public function applyColourScheme($scheme) { + if (!isset($this->colour_scheme[$scheme])) return false; + + $this->colour = $this->colour_scheme[$scheme]; + + return true; + } + + /** + * registers a colour scheme + * + * @param string $scheme name of the colour scheme + * @param array a array of colours + */ + public function registerColourScheme($scheme, $colours) { + if (!is_array($colours)) return; + + /* set a reset colour if it is not supplied from the outside */ + if (!isset($colours["reset"])) $colours["reset"] = self::C_RESET; + + $this->colour_scheme[$scheme] = $colours; + } +} + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/ExecutionTime.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/ExecutionTime.php new file mode 100644 index 0000000000..2c2bf9ad38 --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/ExecutionTime.php @@ -0,0 +1,56 @@ +registerOption("exectime", $this, "optSetExecTime"); + } + + public function optSetExecTime($key, $val) { + switch ($val) { + case "enable": + case "1": + case "on": + $this->show_exectime = true; + break; + case "disable": + case "0": + case "off": + $this->show_exectime = false; + break; + default: + printf(":set %s failed, unknown value. Use :set %s = (on|off)", $key, $key); + break; + } + } + + public function startParseTime() { + $this->parse_time = microtime(1); + $this->exec_time = 0.0; + } + public function startExecTime() { + $this->exec_time = microtime(1); + } + public function stopTime() { + $this->end_time = microtime(1); + } + + public function getParseTime() { + return ($this->exec_time == 0.0 ? $this->end_time : $this->exec_time) - $this->parse_time; + } + + public function getExecTime() { + return ($this->exec_time == 0.0 ? 0.0 : $this->end_time - $this->exec_time); + } + + public function isShow() { + return $this->show_exectime; + } +} diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/InlineHelp.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/InlineHelp.php new file mode 100644 index 0000000000..4449d1f42b --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/InlineHelp.php @@ -0,0 +1,140 @@ +registerCommand('#^\? #', $this, 'cmdHelp', '? ', + 'show the DocComment a Class, Method or Function'.PHP_EOL. + ' e.g.: ? fopen(), ? PHP_Shell, ? $__shell'); + } + + /** + * handle the '?' commands + * + * With the help of the Reflection Class we extract the DocComments and display them + * For internal Functions we extract the prototype from the php source. + * + * ? Class::method() + * ? $obj->method() + * ? Class::property + * ? $obj::property + * ? Class + * ? $obj + * ? function() + * + * The license of the PHP_Shell class + * ? license + * + * @return string the help text + */ + public function cmdHelp($l) { + if ("? " == substr($l, 0, strlen("? "))) { + $str = substr($l, 2); + + $cmd = ''; + + if (preg_match('#^([A-Za-z0-9_]+)::([a-zA-Z0-9_]+)\(\s*\)\s*#', $str, $a)) { + /* ? Class::method() */ + + $class = $a[1]; + $method = $a[2]; + + if (false !== ($proto = PHP_ShellPrototypes::getInstance()->get($class.'::'.$method))) { + + $cmd = sprintf("/**\n* %s\n\n* @params %s\n* @return %s\n*/\n", + $proto['description'], + $proto['params'], + $proto['return'] + ); + } else if (class_exists($class, false)) { + $c = new ReflectionClass($class); + + if ($c->hasMethod($method)) { + $cmd = $c->getMethod($method)->getDocComment(); + } + } + } else if (preg_match('#^\$([A-Za-z0-9_]+)->([a-zA-Z0-9_]+)\(\s*\)\s*#', $str, $a)) { + /* ? $obj->method() */ + if (isset($GLOBALS[$a[1]]) && is_object($GLOBALS[$a[1]])) { + $class = get_class($GLOBALS[$a[1]]); + $method = $a[2]; + + $c = new ReflectionClass($class); + + if ($c->hasMethod($method)) { + $cmd = $c->getMethod($method)->getDocComment(); + } + } + } else if (preg_match('#^([A-Za-z0-9_]+)::([a-zA-Z0-9_]+)\s*$#', $str, $a)) { + /* ? Class::property */ + $class = $a[1]; + $property = $a[2]; + if (class_exists($class, false)) { + $c = new ReflectionClass($class); + + if ($c->hasProperty($property)) { + $cmd = $c->getProperty($property)->getDocComment(); + } + } + } else if (preg_match('#^\$([A-Za-z0-9_]+)->([a-zA-Z0-9_]+)\s*$#', $str, $a)) { + /* ? $obj->property */ + if (isset($GLOBALS[$a[1]]) && is_object($GLOBALS[$a[1]])) { + $class = get_class($GLOBALS[$a[1]]); + $method = $a[2]; + + $c = new ReflectionClass($class); + + if ($c->hasProperty($property)) { + $cmd = $c->getProperty($property)->getDocComment(); + } + + } + } else if (preg_match('#^([A-Za-z0-9_]+)$#', $str, $a)) { + /* ? Class */ + if (class_exists($a[1], false)) { + $c = new ReflectionClass($a[1]); + $cmd = $c->getDocComment(); + } + } else if (preg_match('#^\$([A-Za-z0-9_]+)$#', $str, $a)) { + /* ? $object */ + $obj = $a[1]; + if (isset($GLOBALS[$obj]) && is_object($GLOBALS[$obj])) { + $class = get_class($GLOBALS[$obj]); + + $c = new ReflectionClass($class); + $cmd = $c->getDocComment(); + } + + } else if (preg_match('#^([A-Za-z0-9_]+)\(\s*\)$#', $str, $a)) { + /* ? function() */ + $func = $a[1]; + + if (false !== ($proto = PHP_ShellPrototypes::getInstance()->get($func))) { + $cmd = sprintf("/**\n* %s\n*\n* @params %s\n* @return %s\n*/\n", + $proto['description'], + $proto['params'], + $proto['return'] + ); + } else if (function_exists($func)) { + $c = new ReflectionFunction($func); + $cmd = $c->getDocComment(); + } + } + + if ($cmd == '') { + $cmd = var_export(sprintf('no help found for \'%s\'', $str), 1); + } else { + $cmd = var_export($cmd, 1); + } + } else if ("?" == $l) { + $cmd = $this->getHelp(); + $cmd = var_export($cmd, 1); + } + + return $cmd; + } +} diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/LoadScript.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/LoadScript.php new file mode 100644 index 0000000000..a8c4697d8b --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/LoadScript.php @@ -0,0 +1,32 @@ +registerCommand('#^r #', $this, 'cmdLoadScript', 'r ', + 'load a php-script and execute each line'); + + } + + public function cmdLoadScript($l) { + $l = substr($l, 2); + + if (file_exists($l)) { + $content = file($l); + + $source = array(); + + foreach ($content as $line) { + $line = chop($line); + + if (preg_match('#^<\?php#', $line)) continue; + + $source[] = $line; + } + + return $source; + } + return ""; + } +} diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Prototypes.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Prototypes.php new file mode 100644 index 0000000000..618074e53a --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/Prototypes.php @@ -0,0 +1,16235 @@ + + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Closes xmlreader - current frees resources until xmlTextReaderClose is fixed in libxml', + ), + 'XMLReader::getAttribute' => + array ( + 'return' => 'string', + 'params' => 'string name', + 'description' => 'Get value of an attribute from current element', + ), + 'XMLReader::getAttributeNo' => + array ( + 'return' => 'string', + 'params' => 'int index', + 'description' => 'Get value of an attribute at index from current element', + ), + 'XMLReader::getAttributeNs' => + array ( + 'return' => 'string', + 'params' => 'string name, string namespaceURI', + 'description' => 'Get value of a attribute via name and namespace from current element', + ), + 'XMLReader::getParserProperty' => + array ( + 'return' => 'boolean', + 'params' => 'int property', + 'description' => 'Indicates whether given property (one of the parser option constants) is set or not on parser', + ), + 'XMLReader::isValid' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns boolean indicating if parsed document is valid or not.Must set XMLREADER_LOADDTD or XMLREADER_VALIDATE parser option prior to the first call to reador this method will always return FALSE', + ), + 'XMLReader::lookupNamespace' => + array ( + 'return' => 'string', + 'params' => 'string prefix', + 'description' => 'Return namespaceURI for associated prefix on current node', + ), + 'XMLReader::moveToAttribute' => + array ( + 'return' => 'boolean', + 'params' => 'string name', + 'description' => 'Positions reader at specified attribute - Returns TRUE on success and FALSE on failure', + ), + 'XMLReader::moveToAttributeNo' => + array ( + 'return' => 'boolean', + 'params' => 'int index', + 'description' => 'Positions reader at attribute at spcecified index.Returns TRUE on success and FALSE on failure', + ), + 'XMLReader::moveToAttributeNs' => + array ( + 'return' => 'boolean', + 'params' => 'string name, string namespaceURI', + 'description' => 'Positions reader at attribute spcified by name and namespaceURI.Returns TRUE on success and FALSE on failure', + ), + 'XMLReader::moveToElement' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Moves the position of the current instance to the node that contains the current Attribute node.', + ), + 'XMLReader::moveToFirstAttribute' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Moves the position of the current instance to the first attribute associated with the current node.', + ), + 'XMLReader::moveToNextAttribute' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Moves the position of the current instance to the next attribute associated with the current node.', + ), + 'XMLReader::read' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Moves the position of the current instance to the next node in the stream.', + ), + 'XMLReader::next' => + array ( + 'return' => 'boolean', + 'params' => '[string localname]', + 'description' => 'Moves the position of the current instance to the next node in the stream.', + ), + 'XMLReader::open' => + array ( + 'return' => 'boolean', + 'params' => 'string URI', + 'description' => 'Sets the URI that the the XMLReader will parse.', + ), + 'XMLReader::setParserProperty' => + array ( + 'return' => 'boolean', + 'params' => 'int property, boolean value', + 'description' => 'Sets parser property (one of the parser option constants).Properties must be set after open() or XML() and before the first read() is called', + ), + 'XMLReader::setRelaxNGSchemaSource' => + array ( + 'return' => 'boolean', + 'params' => 'string source', + 'description' => 'Sets the string that the the XMLReader will parse.', + ), + 'XMLReader::XML' => + array ( + 'return' => 'boolean', + 'params' => 'string source', + 'description' => 'Sets the string that the the XMLReader will parse.', + ), + 'XMLReader::expand' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Moves the position of the current instance to the next node in the stream.', + ), + 'SimpleXMLElement::asXML' => + array ( + 'return' => 'string', + 'params' => '[string filename]', + 'description' => 'Return a well-formed XML string based on SimpleXML element', + ), + 'SimpleXMLElement::getNamespaces' => + array ( + 'return' => 'string', + 'params' => '[bool recursve]', + 'description' => 'Return all namespaces in use', + ), + 'SimpleXMLElement::getDocNamespaces' => + array ( + 'return' => 'string', + 'params' => '[bool recursive]', + 'description' => 'Return all namespaces registered with document', + ), + 'SimpleXMLElement::children' => + array ( + 'return' => 'object', + 'params' => '[string ns]', + 'description' => 'Finds children of given node', + ), + 'SimpleXMLElement::getName' => + array ( + 'return' => 'object', + 'params' => '', + 'description' => 'Finds children of given node', + ), + 'SimpleXMLElement::attributes' => + array ( + 'return' => 'array', + 'params' => '[string ns]', + 'description' => 'Identifies an element\'s attributes', + ), + 'SimpleXMLElement::addChild' => + array ( + 'return' => 'void', + 'params' => 'string qName [, string value [,string ns]]', + 'description' => 'Add Element with optional namespace information', + ), + 'SimpleXMLElement::addAttribute' => + array ( + 'return' => 'void', + 'params' => 'string qName, string value [,string ns]', + 'description' => 'Add Attribute with optional namespace information', + ), + 'simplexml_load_file' => + array ( + 'return' => 'simplemxml_element', + 'params' => 'string filename [, string class_name [, int options]]', + 'description' => 'Load a filename and return a simplexml_element object to allow for processing', + ), + 'simplexml_load_string' => + array ( + 'return' => 'simplemxml_element', + 'params' => 'string data [, string class_name [, int options]]', + 'description' => 'Load a string and return a simplexml_element object to allow for processing', + ), + 'simplexml_import_dom' => + array ( + 'return' => 'simplemxml_element', + 'params' => 'domNode node [, string class_name]', + 'description' => 'Get a simplexml_element object from dom to allow for processing', + ), + 'snmpget' => + array ( + 'return' => 'string', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch a SNMP object', + ), + 'snmpgetnext' => + array ( + 'return' => 'string', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch a SNMP object', + ), + 'snmpwalk' => + array ( + 'return' => 'array', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Return all objects under the specified object id', + ), + 'snmprealwalk' => + array ( + 'return' => 'array', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Return all objects including their respective object id withing the specified one', + ), + 'snmp_get_quick_print' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Return the current status of quick_print', + ), + 'snmp_set_quick_print' => + array ( + 'return' => 'void', + 'params' => 'int quick_print', + 'description' => 'Return all objects including their respective object id withing the specified one', + ), + 'snmp_set_enum_print' => + array ( + 'return' => 'void', + 'params' => 'int enum_print', + 'description' => 'Return all values that are enums with their enum value instead of the raw integer', + ), + 'snmp_set_oid_numeric_print' => + array ( + 'return' => 'void', + 'params' => 'int oid_numeric_print', + 'description' => 'Return all objects including their respective object id withing the specified one', + ), + 'snmpset' => + array ( + 'return' => 'int', + 'params' => 'string host, string community, string object_id, string type, mixed value [, int timeout [, int retries]]', + 'description' => 'Set the value of a SNMP object', + ), + 'snmp2_get' => + array ( + 'return' => 'string', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch a SNMP object', + ), + 'snmp2_getnext' => + array ( + 'return' => 'string', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch a SNMP object', + ), + 'snmp2_walk' => + array ( + 'return' => 'array', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Return all objects under the specified object id', + ), + 'snmp2_real_walk' => + array ( + 'return' => 'array', + 'params' => 'string host, string community, string object_id [, int timeout [, int retries]]', + 'description' => 'Return all objects including their respective object id withing the specified one', + ), + 'snmp2_set' => + array ( + 'return' => 'int', + 'params' => 'string host, string community, string object_id, string type, mixed value [, int timeout [, int retries]]', + 'description' => 'Set the value of a SNMP object', + ), + 'php_snmpv3' => + array ( + 'return' => 'void', + 'params' => 'INTERNAL_FUNCTION_PARAMETERS, int st', + 'description' => '** Generic SNMPv3 object fetcher* From here is passed on the the common internal object fetcher.** st=SNMP_CMD_GET snmp3_get() - query an agent and return a single value.* st=SNMP_CMD_GETNEXT snmp3_getnext() - query an agent and return the next single value.* st=SNMP_CMD_WALK snmp3_walk() - walk the mib and return a single dimensional array* containing the values.* st=SNMP_CMD_REALWALK snmp3_real_walk() - walk the mib and return an* array of oid,value pairs.* st=SNMP_CMD_SET snmp3_set() - query an agent and set a single value*', + ), + 'snmp3_get' => + array ( + 'return' => 'int', + 'params' => 'string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch the value of a SNMP object', + ), + 'snmp3_getnext' => + array ( + 'return' => 'int', + 'params' => 'string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch the value of a SNMP object', + ), + 'snmp3_walk' => + array ( + 'return' => 'int', + 'params' => 'string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch the value of a SNMP object', + ), + 'snmp3_real_walk' => + array ( + 'return' => 'int', + 'params' => 'string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id [, int timeout [, int retries]]', + 'description' => 'Fetch the value of a SNMP object', + ), + 'snmp3_set' => + array ( + 'return' => 'int', + 'params' => 'string host, string sec_name, string sec_level, string auth_protocol, string auth_passphrase, string priv_protocol, string priv_passphrase, string object_id, string type, mixed value [, int timeout [, int retries]]', + 'description' => 'Fetch the value of a SNMP object', + ), + 'snmp_set_valueretrieval' => + array ( + 'return' => 'int', + 'params' => 'int method', + 'description' => 'Specify the method how the SNMP values will be returned', + ), + 'snmp_get_valueretrieval' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Return the method how the SNMP values will be returned', + ), + 'snmp_read_mib' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Reads and parses a MIB file into the active MIB tree.', + ), + 'mysqli_embedded_server_start' => + array ( + 'return' => 'bool', + 'params' => 'bool start, array arguments, array groups', + 'description' => 'initialize and start embedded server', + ), + 'mysqli_embedded_server_end' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => '', + ), + 'mysqli_connect' => + array ( + 'return' => 'object', + 'params' => '[string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]]', + 'description' => 'Open a connection to a mysql server', + ), + 'mysqli_connect_errno' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns the numerical value of the error message from last connect command', + ), + 'mysqli_connect_error' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns the text of the error message from previous MySQL operation', + ), + 'mysqli_multi_query' => + array ( + 'return' => 'bool', + 'params' => 'object link, string query', + 'description' => 'Binary-safe version of mysql_query()', + ), + 'mysqli_set_charset' => + array ( + 'return' => 'bool', + 'params' => 'object link, string csname', + 'description' => 'sets client character set', + ), + 'mysqli_get_charset' => + array ( + 'return' => 'object', + 'params' => 'object link', + 'description' => 'returns a character set object', + ), + 'mysqli_affected_rows' => + array ( + 'return' => 'mixed', + 'params' => 'object link', + 'description' => 'Get number of affected rows in previous MySQL operation', + ), + 'mysqli_autocommit' => + array ( + 'return' => 'bool', + 'params' => 'object link, bool mode', + 'description' => 'Turn auto commit on or of', + ), + 'mysqli_stmt_bind_param' => + array ( + 'return' => 'bool', + 'params' => 'object stmt, string types, mixed variable [,mixed,....]', + 'description' => 'Bind variables to a prepared statement as parameters', + ), + 'mysqli_stmt_bind_result' => + array ( + 'return' => 'bool', + 'params' => 'object stmt, mixed var, [,mixed, ...]', + 'description' => 'Bind variables to a prepared statement for result storage', + ), + 'mysqli_change_user' => + array ( + 'return' => 'bool', + 'params' => 'object link, string user, string password, string database', + 'description' => 'Change logged-in user of the active connection', + ), + 'mysqli_character_set_name' => + array ( + 'return' => 'string', + 'params' => 'object link', + 'description' => 'Returns the name of the character set used for this connection', + ), + 'mysqli_close' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => 'Close connection', + ), + 'mysqli_commit' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => 'Commit outstanding actions and close transaction', + ), + 'mysqli_data_seek' => + array ( + 'return' => 'bool', + 'params' => 'object result, int offset', + 'description' => 'Move internal result pointer', + ), + 'mysqli_debug' => + array ( + 'return' => 'void', + 'params' => 'string debug', + 'description' => '', + ), + 'mysqli_dump_debug_info' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_errno' => + array ( + 'return' => 'int', + 'params' => 'object link', + 'description' => 'Returns the numerical value of the error message from previous MySQL operation', + ), + 'mysqli_error' => + array ( + 'return' => 'string', + 'params' => 'object link', + 'description' => 'Returns the text of the error message from previous MySQL operation', + ), + 'mysqli_stmt_execute' => + array ( + 'return' => 'bool', + 'params' => 'object stmt', + 'description' => 'Execute a prepared statement', + ), + 'mysqli_stmt_fetch' => + array ( + 'return' => 'mixed', + 'params' => 'object stmt', + 'description' => 'Fetch results from a prepared statement into the bound variables', + ), + 'mysqli_field_count' => + array ( + 'return' => 'int', + 'params' => 'object link', + 'description' => 'Fetch the number of fields returned by the last query for the given link', + ), + 'mysqli_field_seek' => + array ( + 'return' => 'int', + 'params' => 'object result, int fieldnr', + 'description' => 'Set result pointer to a specified field offset', + ), + 'mysqli_field_tell' => + array ( + 'return' => 'int', + 'params' => 'object result', + 'description' => 'Get current field offset of result pointer', + ), + 'mysqli_free_result' => + array ( + 'return' => 'void', + 'params' => 'object result', + 'description' => 'Free query result memory for the given result handle', + ), + 'mysqli_get_client_info' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Get MySQL client info', + ), + 'mysqli_get_client_version' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get MySQL client info', + ), + 'mysqli_get_proto_info' => + array ( + 'return' => 'int', + 'params' => 'object link', + 'description' => 'Get MySQL protocol information', + ), + 'mysqli_get_server_info' => + array ( + 'return' => 'string', + 'params' => 'object link', + 'description' => 'Get MySQL server info', + ), + 'mysqli_get_server_version' => + array ( + 'return' => 'int', + 'params' => 'object link', + 'description' => 'Return the MySQL version for the server referenced by the given link', + ), + 'mysqli_info' => + array ( + 'return' => 'string', + 'params' => 'object link', + 'description' => 'Get information about the most recent query', + ), + 'mysqli_init' => + array ( + 'return' => 'resource', + 'params' => 'void', + 'description' => 'Initialize mysqli and return a resource for use with mysql_real_connect', + ), + 'mysqli_insert_id' => + array ( + 'return' => 'mixed', + 'params' => 'object link', + 'description' => 'Get the ID generated from the previous INSERT operation', + ), + 'mysqli_kill' => + array ( + 'return' => 'bool', + 'params' => 'object link, int processid', + 'description' => 'Kill a mysql process on the server', + ), + 'mysqli_set_local_infile_handler' => + array ( + 'return' => 'bool', + 'params' => 'object link, callback read_func', + 'description' => 'Set callback functions for LOAD DATA LOCAL INFILE', + ), + 'mysqli_more_results' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => 'check if there any more query results from a multi query', + ), + 'mysqli_next_result' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => 'read next result from multi_query', + ), + 'mysqli_num_fields' => + array ( + 'return' => 'int', + 'params' => 'object result', + 'description' => 'Get number of fields in result', + ), + 'mysqli_num_rows' => + array ( + 'return' => 'mixed', + 'params' => 'object result', + 'description' => 'Get number of rows in result', + ), + 'mysqli_options' => + array ( + 'return' => 'bool', + 'params' => 'object link, int flags, mixed values', + 'description' => 'Set options', + ), + 'mysqli_ping' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => 'Ping a server connection or reconnect if there is no connection', + ), + 'mysqli_prepare' => + array ( + 'return' => 'mixed', + 'params' => 'object link, string query', + 'description' => 'Prepare a SQL statement for execution', + ), + 'mysqli_real_connect' => + array ( + 'return' => 'bool', + 'params' => 'object link [,string hostname [,string username [,string passwd [,string dbname [,int port [,string socket [,int flags]]]]]]]', + 'description' => 'Open a connection to a mysql server', + ), + 'mysqli_real_query' => + array ( + 'return' => 'bool', + 'params' => 'object link, string query', + 'description' => 'Binary-safe version of mysql_query()', + ), + 'mysqli_real_escape_string' => + array ( + 'return' => 'string', + 'params' => 'object link, string escapestr', + 'description' => 'Escapes special characters in a string for use in a SQL statement, taking into account the current charset of the connection', + ), + 'mysqli_rollback' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => 'Undo actions from current transaction', + ), + 'mysqli_send_long_data' => + array ( + 'return' => 'bool', + 'params' => 'object stmt, int param_nr, string data', + 'description' => '', + ), + 'mysqli_stmt_affected_rows' => + array ( + 'return' => 'mixed', + 'params' => 'object stmt', + 'description' => 'Return the number of rows affected in the last query for the given link', + ), + 'mysqli_stmt_close' => + array ( + 'return' => 'bool', + 'params' => 'object stmt', + 'description' => 'Close statement', + ), + 'mysqli_stmt_data_seek' => + array ( + 'return' => 'void', + 'params' => 'object stmt, int offset', + 'description' => 'Move internal result pointer', + ), + 'mysqli_stmt_free_result' => + array ( + 'return' => 'void', + 'params' => 'object stmt', + 'description' => 'Free stored result memory for the given statement handle', + ), + 'mysqli_stmt_insert_id' => + array ( + 'return' => 'mixed', + 'params' => 'object stmt', + 'description' => 'Get the ID generated from the previous INSERT operation', + ), + 'mysqli_stmt_reset' => + array ( + 'return' => 'bool', + 'params' => 'object stmt', + 'description' => 'reset a prepared statement', + ), + 'mysqli_stmt_num_rows' => + array ( + 'return' => 'mixed', + 'params' => 'object stmt', + 'description' => 'Return the number of rows in statements result set', + ), + 'mysqli_select_db' => + array ( + 'return' => 'string', + 'params' => 'object link, string dbname', + 'description' => 'Select a MySQL database', + ), + 'mysqli_sqlstate' => + array ( + 'return' => 'string', + 'params' => 'object link', + 'description' => 'Returns the SQLSTATE error from previous MySQL operation', + ), + 'mysqli_ssl_set' => + array ( + 'return' => 'bool', + 'params' => 'object link ,string key ,string cert ,string ca ,string capath ,string cipher]', + 'description' => '', + ), + 'mysqli_stat' => + array ( + 'return' => 'mixed', + 'params' => 'object link', + 'description' => 'Get current system status', + ), + 'mysqli_stmt_attr_set' => + array ( + 'return' => 'int', + 'params' => 'object stmt, long attr, bool mode', + 'description' => '', + ), + 'mysqli_stmt_attr_get' => + array ( + 'return' => 'int', + 'params' => 'object stmt, long attr', + 'description' => '', + ), + 'mysqli_stmt_errno' => + array ( + 'return' => 'int', + 'params' => 'object stmt', + 'description' => '', + ), + 'mysqli_stmt_error' => + array ( + 'return' => 'string', + 'params' => 'object stmt', + 'description' => '', + ), + 'mysqli_stmt_init' => + array ( + 'return' => 'mixed', + 'params' => 'object link', + 'description' => 'Initialize statement object', + ), + 'mysqli_stmt_prepare' => + array ( + 'return' => 'bool', + 'params' => 'object stmt, string query', + 'description' => 'prepare server side statement with query', + ), + 'mysqli_stmt_result_metadata' => + array ( + 'return' => 'mixed', + 'params' => 'object stmt', + 'description' => 'return result set from statement', + ), + 'mysqli_stmt_store_result' => + array ( + 'return' => 'bool', + 'params' => 'stmt', + 'description' => '', + ), + 'mysqli_stmt_sqlstate' => + array ( + 'return' => 'string', + 'params' => 'object stmt', + 'description' => '', + ), + 'mysqli_store_result' => + array ( + 'return' => 'object', + 'params' => 'object link', + 'description' => 'Buffer result set on client', + ), + 'mysqli_thread_id' => + array ( + 'return' => 'int', + 'params' => 'object link', + 'description' => 'Return the current thread ID', + ), + 'mysqli_thread_safe' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Return whether thread safety is given or not', + ), + 'mysqli_use_result' => + array ( + 'return' => 'mixed', + 'params' => 'object link', + 'description' => 'Directly retrieve query results - do not buffer results on client side', + ), + 'mysqli_disable_reads_from_master' => + array ( + 'return' => 'void', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_disable_rpl_parse' => + array ( + 'return' => 'void', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_enable_reads_from_master' => + array ( + 'return' => 'void', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_enable_rpl_parse' => + array ( + 'return' => 'void', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_master_query' => + array ( + 'return' => 'bool', + 'params' => 'object link, string query', + 'description' => 'Enforce execution of a query on the master in a master/slave setup', + ), + 'mysqli_rpl_parse_enabled' => + array ( + 'return' => 'int', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_rpl_probe' => + array ( + 'return' => 'bool', + 'params' => 'object link', + 'description' => '', + ), + 'mysqli_rpl_query_type' => + array ( + 'return' => 'int', + 'params' => 'string query', + 'description' => '', + ), + 'mysqli_send_query' => + array ( + 'return' => 'bool', + 'params' => 'object link, string query', + 'description' => '', + ), + 'mysqli_slave_query' => + array ( + 'return' => 'bool', + 'params' => 'object link, string query', + 'description' => 'Enforce execution of a query on a slave in a master/slave setup', + ), + 'imap_open' => + array ( + 'return' => 'resource', + 'params' => 'string mailbox, string user, string password [, int options]', + 'description' => 'Open an IMAP stream to a mailbox', + ), + 'imap_reopen' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string mailbox [, int options]', + 'description' => 'Reopen an IMAP stream to a new mailbox', + ), + 'imap_append' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string folder, string message [, string options]', + 'description' => 'Append a new message to a specified mailbox', + ), + 'imap_num_msg' => + array ( + 'return' => 'int', + 'params' => 'resource stream_id', + 'description' => 'Gives the number of messages in the current mailbox', + ), + 'imap_ping' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id', + 'description' => 'Check if the IMAP stream is still active', + ), + 'imap_num_recent' => + array ( + 'return' => 'int', + 'params' => 'resource stream_id', + 'description' => 'Gives the number of recent messages in current mailbox', + ), + 'imap_get_quota' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string qroot', + 'description' => 'Returns the quota set to the mailbox account qroot', + ), + 'imap_get_quotaroot' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string mbox', + 'description' => 'Returns the quota set to the mailbox account mbox', + ), + 'imap_set_quota' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string qroot, int mailbox_size', + 'description' => 'Will set the quota for qroot mailbox', + ), + 'imap_setacl' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string mailbox, string id, string rights', + 'description' => 'Sets the ACL for a given mailbox', + ), + 'imap_getacl' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string mailbox', + 'description' => 'Gets the ACL for a given mailbox', + ), + 'imap_expunge' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id', + 'description' => 'Permanently delete all messages marked for deletion', + ), + 'imap_close' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id [, int options]', + 'description' => 'Close an IMAP stream', + ), + 'imap_headers' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id', + 'description' => 'Returns headers for all messages in a mailbox', + ), + 'imap_body' => + array ( + 'return' => 'string', + 'params' => 'resource stream_id, int msg_no [, int options]', + 'description' => 'Read the message body', + ), + 'imap_mail_copy' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, int msg_no, string mailbox [, int options]', + 'description' => 'Copy specified message to a mailbox', + ), + 'imap_mail_move' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, int msg_no, string mailbox [, int options]', + 'description' => 'Move specified message to a mailbox', + ), + 'imap_createmailbox' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string mailbox', + 'description' => 'Create a new mailbox', + ), + 'imap_renamemailbox' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string old_name, string new_name', + 'description' => 'Rename a mailbox', + ), + 'imap_deletemailbox' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string mailbox', + 'description' => 'Delete a mailbox', + ), + 'imap_list' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string ref, string pattern', + 'description' => 'Read the list of mailboxes', + ), + 'imap_getmailboxes' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string ref, string pattern', + 'description' => 'Reads the list of mailboxes and returns a full array of objects containing name, attributes, and delimiter', + ), + 'imap_scan' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string ref, string pattern, string content', + 'description' => 'Read list of mailboxes containing a certain string', + ), + 'imap_check' => + array ( + 'return' => 'object', + 'params' => 'resource stream_id', + 'description' => 'Get mailbox properties', + ), + 'imap_delete' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, int msg_no [, int options]', + 'description' => 'Mark a message for deletion', + ), + 'imap_undelete' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, int msg_no', + 'description' => 'Remove the delete flag from a message', + ), + 'imap_headerinfo' => + array ( + 'return' => 'object', + 'params' => 'resource stream_id, int msg_no [, int from_length [, int subject_length [, string default_host]]]', + 'description' => 'Read the headers of the message', + ), + 'imap_rfc822_parse_headers' => + array ( + 'return' => 'object', + 'params' => 'string headers [, string default_host]', + 'description' => 'Parse a set of mail headers contained in a string, and return an object similar to imap_headerinfo()', + ), + 'imap_lsub' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string ref, string pattern', + 'description' => 'Return a list of subscribed mailboxes', + ), + 'imap_getsubscribed' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string ref, string pattern', + 'description' => 'Return a list of subscribed mailboxes, in the same format as imap_getmailboxes()', + ), + 'imap_subscribe' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string mailbox', + 'description' => 'Subscribe to a mailbox', + ), + 'imap_unsubscribe' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string mailbox', + 'description' => 'Unsubscribe from a mailbox', + ), + 'imap_fetchstructure' => + array ( + 'return' => 'object', + 'params' => 'resource stream_id, int msg_no [, int options]', + 'description' => 'Read the full structure of a message', + ), + 'imap_fetchbody' => + array ( + 'return' => 'string', + 'params' => 'resource stream_id, int msg_no, string section [, int options]', + 'description' => 'Get a specific body section', + ), + 'imap_savebody' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string|resource file, int msg_no[, string section = ""[, int options = 0]]', + 'description' => 'Save a specific body section to a file', + ), + 'imap_base64' => + array ( + 'return' => 'string', + 'params' => 'string text', + 'description' => 'Decode BASE64 encoded text', + ), + 'imap_qprint' => + array ( + 'return' => 'string', + 'params' => 'string text', + 'description' => 'Convert a quoted-printable string to an 8-bit string', + ), + 'imap_8bit' => + array ( + 'return' => 'string', + 'params' => 'string text', + 'description' => 'Convert an 8-bit string to a quoted-printable string', + ), + 'imap_binary' => + array ( + 'return' => 'string', + 'params' => 'string text', + 'description' => 'Convert an 8bit string to a base64 string', + ), + 'imap_mailboxmsginfo' => + array ( + 'return' => 'object', + 'params' => 'resource stream_id', + 'description' => 'Returns info about the current mailbox', + ), + 'imap_rfc822_write_address' => + array ( + 'return' => 'string', + 'params' => 'string mailbox, string host, string personal', + 'description' => 'Returns a properly formatted email address given the mailbox, host, and personal info', + ), + 'imap_rfc822_parse_adrlist' => + array ( + 'return' => 'array', + 'params' => 'string address_string, string default_host', + 'description' => 'Parses an address string', + ), + 'imap_utf8' => + array ( + 'return' => 'string', + 'params' => 'string mime_encoded_text', + 'description' => 'Convert a mime-encoded text to UTF-8', + ), + 'imap_utf7_decode' => + array ( + 'return' => 'string', + 'params' => 'string buf', + 'description' => 'Decode a modified UTF-7 string', + ), + 'imap_utf7_encode' => + array ( + 'return' => 'string', + 'params' => 'string buf', + 'description' => 'Encode a string in modified UTF-7', + ), + 'imap_setflag_full' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string sequence, string flag [, int options]', + 'description' => 'Sets flags on messages', + ), + 'imap_clearflag_full' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_id, string sequence, string flag [, int options]', + 'description' => 'Clears flags on messages', + ), + 'imap_sort' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, int criteria, int reverse [, int options [, string search_criteria [, string charset]]]', + 'description' => 'Sort an array of message headers, optionally including only messages that meet specified criteria.', + ), + 'imap_fetchheader' => + array ( + 'return' => 'string', + 'params' => 'resource stream_id, int msg_no [, int options]', + 'description' => 'Get the full unfiltered header for a message', + ), + 'imap_uid' => + array ( + 'return' => 'int', + 'params' => 'resource stream_id, int msg_no', + 'description' => 'Get the unique message id associated with a standard sequential message number', + ), + 'imap_msgno' => + array ( + 'return' => 'int', + 'params' => 'resource stream_id, int unique_msg_id', + 'description' => 'Get the sequence number associated with a UID', + ), + 'imap_status' => + array ( + 'return' => 'object', + 'params' => 'resource stream_id, string mailbox, int options', + 'description' => 'Get status info from a mailbox', + ), + 'imap_bodystruct' => + array ( + 'return' => 'object', + 'params' => 'resource stream_id, int msg_no, string section', + 'description' => 'Read the structure of a specified body section of a specific message', + ), + 'imap_fetch_overview' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, int msg_no [, int options]', + 'description' => 'Read an overview of the information in the headers of the given message sequence', + ), + 'imap_mail_compose' => + array ( + 'return' => 'string', + 'params' => 'array envelope, array body', + 'description' => 'Create a MIME message based on given envelope and body sections', + ), + 'imap_mail' => + array ( + 'return' => 'bool', + 'params' => 'string to, string subject, string message [, string additional_headers [, string cc [, string bcc [, string rpath]]]]', + 'description' => 'Send an email message', + ), + 'imap_search' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id, string criteria [, int options [, string charset]]', + 'description' => 'Return a list of messages matching the given criteria', + ), + 'imap_alerts' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns an array of all IMAP alerts that have been generated since the last page load or since the last imap_alerts() call, whichever came last. The alert stack is cleared after imap_alerts() is called.', + ), + 'imap_errors' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns an array of all IMAP errors generated since the last page load, or since the last imap_errors() call, whichever came last. The error stack is cleared after imap_errors() is called.', + ), + 'imap_last_error' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns the last error that was generated by an IMAP function. The error stack is NOT cleared after this call.', + ), + 'imap_mime_header_decode' => + array ( + 'return' => 'array', + 'params' => 'string str', + 'description' => 'Decode mime header element in accordance with RFC 2047 and return array of objects containing \'charset\' encoding and decoded \'text\'', + ), + 'imap_thread' => + array ( + 'return' => 'array', + 'params' => 'resource stream_id [, int options]', + 'description' => 'Return threaded by REFERENCES tree', + ), + 'imap_timeout' => + array ( + 'return' => 'mixed', + 'params' => 'int timeout_type [, int timeout]', + 'description' => 'Set or fetch imap timeout', + ), + 'session_set_cookie_params' => + array ( + 'return' => 'void', + 'params' => 'int lifetime [, string path [, string domain [, bool secure]]]', + 'description' => 'Set session cookie parameters', + ), + 'session_get_cookie_params' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Return the session cookie parameters', + ), + 'session_name' => + array ( + 'return' => 'string', + 'params' => '[string newname]', + 'description' => 'Return the current session name. If newname is given, the session name is replaced with newname', + ), + 'session_module_name' => + array ( + 'return' => 'string', + 'params' => '[string newname]', + 'description' => 'Return the current module name used for accessing session data. If newname is given, the module name is replaced with newname', + ), + 'session_set_save_handler' => + array ( + 'return' => 'void', + 'params' => 'string open, string close, string read, string write, string destroy, string gc', + 'description' => 'Sets user-level functions', + ), + 'session_save_path' => + array ( + 'return' => 'string', + 'params' => '[string newname]', + 'description' => 'Return the current save path passed to module_name. If newname is given, the save path is replaced with newname', + ), + 'session_id' => + array ( + 'return' => 'string', + 'params' => '[string newid]', + 'description' => 'Return the current session id. If newid is given, the session id is replaced with newid', + ), + 'session_regenerate_id' => + array ( + 'return' => 'bool', + 'params' => '[bool delete_old_session]', + 'description' => 'Update the current session id with a newly generated one. If delete_old_session is set to true, remove the old session.', + ), + 'session_cache_limiter' => + array ( + 'return' => 'string', + 'params' => '[string new_cache_limiter]', + 'description' => 'Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter', + ), + 'session_cache_expire' => + array ( + 'return' => 'int', + 'params' => '[int new_cache_expire]', + 'description' => 'Return the current cache expire. If new_cache_expire is given, the current cache_expire is replaced with new_cache_expire', + ), + 'session_register' => + array ( + 'return' => 'bool', + 'params' => 'mixed var_names [, mixed ...]', + 'description' => 'Adds varname(s) to the list of variables which are freezed at the session end', + ), + 'session_unregister' => + array ( + 'return' => 'bool', + 'params' => 'string varname', + 'description' => 'Removes varname from the list of variables which are freezed at the session end', + ), + 'session_is_registered' => + array ( + 'return' => 'bool', + 'params' => 'string varname', + 'description' => 'Checks if a variable is registered in session', + ), + 'session_encode' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Serializes the current setup and returns the serialized representation', + ), + 'session_decode' => + array ( + 'return' => 'bool', + 'params' => 'string data', + 'description' => 'Deserializes data and reinitializes the variables', + ), + 'session_start' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Begin session - reinitializes freezed variables, registers browsers etc', + ), + 'session_destroy' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Destroy the current session and all data associated with it', + ), + 'session_unset' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Unset all registered variables', + ), + 'session_write_close' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Write session data and end session', + ), + 'mysql_connect' => + array ( + 'return' => 'resource', + 'params' => '[string hostname[:port][:/path/to/socket] [, string username [, string password [, bool new [, int flags]]]]]', + 'description' => 'Opens a connection to a MySQL Server', + ), + 'mysql_pconnect' => + array ( + 'return' => 'resource', + 'params' => '[string hostname[:port][:/path/to/socket] [, string username [, string password [, int flags]]]]', + 'description' => 'Opens a persistent connection to a MySQL Server', + ), + 'mysql_close' => + array ( + 'return' => 'bool', + 'params' => '[int link_identifier]', + 'description' => 'Close a MySQL connection', + ), + 'mysql_select_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, int link_identifier]', + 'description' => 'Selects a MySQL database', + ), + 'mysql_get_client_info' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns a string that represents the client library version', + ), + 'mysql_get_host_info' => + array ( + 'return' => 'string', + 'params' => '[int link_identifier]', + 'description' => 'Returns a string describing the type of connection in use, including the server host name', + ), + 'mysql_get_proto_info' => + array ( + 'return' => 'int', + 'params' => '[int link_identifier]', + 'description' => 'Returns the protocol version used by current connection', + ), + 'mysql_get_server_info' => + array ( + 'return' => 'string', + 'params' => '[int link_identifier]', + 'description' => 'Returns a string that represents the server version number', + ), + 'mysql_info' => + array ( + 'return' => 'string', + 'params' => '[int link_identifier]', + 'description' => 'Returns a string containing information about the most recent query', + ), + 'mysql_thread_id' => + array ( + 'return' => 'int', + 'params' => '[int link_identifier]', + 'description' => 'Returns the thread id of current connection', + ), + 'mysql_stat' => + array ( + 'return' => 'string', + 'params' => '[int link_identifier]', + 'description' => 'Returns a string containing status information', + ), + 'mysql_client_encoding' => + array ( + 'return' => 'string', + 'params' => '[int link_identifier]', + 'description' => 'Returns the default character set for the current connection', + ), + 'mysql_create_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, int link_identifier]', + 'description' => 'Create a MySQL database', + ), + 'mysql_drop_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, int link_identifier]', + 'description' => 'Drops (delete) a MySQL database', + ), + 'mysql_query' => + array ( + 'return' => 'resource', + 'params' => 'string query [, int link_identifier]', + 'description' => 'Sends an SQL query to MySQL', + ), + 'mysql_unbuffered_query' => + array ( + 'return' => 'resource', + 'params' => 'string query [, int link_identifier]', + 'description' => 'Sends an SQL query to MySQL, without fetching and buffering the result rows', + ), + 'mysql_db_query' => + array ( + 'return' => 'resource', + 'params' => 'string database_name, string query [, int link_identifier]', + 'description' => 'Sends an SQL query to MySQL', + ), + 'mysql_list_dbs' => + array ( + 'return' => 'resource', + 'params' => '[int link_identifier]', + 'description' => 'List databases available on a MySQL server', + ), + 'mysql_list_tables' => + array ( + 'return' => 'resource', + 'params' => 'string database_name [, int link_identifier]', + 'description' => 'List tables in a MySQL database', + ), + 'mysql_list_fields' => + array ( + 'return' => 'resource', + 'params' => 'string database_name, string table_name [, int link_identifier]', + 'description' => 'List MySQL result fields', + ), + 'mysql_list_processes' => + array ( + 'return' => 'resource', + 'params' => '[int link_identifier]', + 'description' => 'Returns a result set describing the current server threads', + ), + 'mysql_error' => + array ( + 'return' => 'string', + 'params' => '[int link_identifier]', + 'description' => 'Returns the text of the error message from previous MySQL operation', + ), + 'mysql_errno' => + array ( + 'return' => 'int', + 'params' => '[int link_identifier]', + 'description' => 'Returns the number of the error message from previous MySQL operation', + ), + 'mysql_affected_rows' => + array ( + 'return' => 'int', + 'params' => '[int link_identifier]', + 'description' => 'Gets number of affected rows in previous MySQL operation', + ), + 'mysql_escape_string' => + array ( + 'return' => 'string', + 'params' => 'string to_be_escaped', + 'description' => 'Escape string for mysql query', + ), + 'mysql_real_escape_string' => + array ( + 'return' => 'string', + 'params' => 'string to_be_escaped [, int link_identifier]', + 'description' => 'Escape special characters in a string for use in a SQL statement, taking into account the current charset of the connection', + ), + 'mysql_insert_id' => + array ( + 'return' => 'int', + 'params' => '[int link_identifier]', + 'description' => 'Gets the ID generated from the previous INSERT operation', + ), + 'mysql_result' => + array ( + 'return' => 'mixed', + 'params' => 'resource result, int row [, mixed field]', + 'description' => 'Gets result data', + ), + 'mysql_num_rows' => + array ( + 'return' => 'int', + 'params' => 'resource result', + 'description' => 'Gets number of rows in a result', + ), + 'mysql_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource result', + 'description' => 'Gets number of fields in a result', + ), + 'mysql_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'resource result', + 'description' => 'Gets a result row as an enumerated array', + ), + 'mysql_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'resource result [, string class_name [, NULL|array ctor_params]]', + 'description' => 'Fetch a result row as an object', + ), + 'mysql_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int result_type]', + 'description' => 'Fetch a result row as an array (associative, numeric or both)', + ), + 'mysql_fetch_assoc' => + array ( + 'return' => 'array', + 'params' => 'resource result', + 'description' => 'Fetch a result row as an associative array', + ), + 'mysql_data_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource result, int row_number', + 'description' => 'Move internal result pointer', + ), + 'mysql_fetch_lengths' => + array ( + 'return' => 'array', + 'params' => 'resource result', + 'description' => 'Gets max data size of each column in a result', + ), + 'mysql_fetch_field' => + array ( + 'return' => 'object', + 'params' => 'resource result [, int field_offset]', + 'description' => 'Gets column information from a result and return as an object', + ), + 'mysql_field_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource result, int field_offset', + 'description' => 'Sets result pointer to a specific field offset', + ), + 'mysql_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_index', + 'description' => 'Gets the name of the specified field in a result', + ), + 'mysql_field_table' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_offset', + 'description' => 'Gets name of the table the specified field is in', + ), + 'mysql_field_len' => + array ( + 'return' => 'int', + 'params' => 'resource result, int field_offset', + 'description' => 'Returns the length of the specified field', + ), + 'mysql_field_type' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_offset', + 'description' => 'Gets the type of the specified field in a result', + ), + 'mysql_field_flags' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_offset', + 'description' => 'Gets the flags associated with the specified field in a result', + ), + 'mysql_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result', + 'description' => 'Free result memory', + ), + 'mysql_ping' => + array ( + 'return' => 'bool', + 'params' => '[int link_identifier]', + 'description' => 'Ping a server connection. If no connection then reconnect.', + ), + 'dom_domerrorhandler_handle_error' => + array ( + 'return' => 'dom_boolean', + 'params' => 'domerror error', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-ERRORS-DOMErrorHandler-handleErrorSince:', + ), + 'dom_document_create_element' => + array ( + 'return' => 'DOMElement', + 'params' => 'string tagName [, string value]', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-2141741547Since:', + ), + 'dom_document_create_document_fragment' => + array ( + 'return' => 'DOMDocumentFragment', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-35CB04B5Since:', + ), + 'dom_document_create_text_node' => + array ( + 'return' => 'DOMText', + 'params' => 'string data', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1975348127Since:', + ), + 'dom_document_create_comment' => + array ( + 'return' => 'DOMComment', + 'params' => 'string data', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1334481328Since:', + ), + 'dom_document_create_cdatasection' => + array ( + 'return' => 'DOMCdataSection', + 'params' => 'string data', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D26C0AF8Since:', + ), + 'dom_document_create_processing_instruction' => + array ( + 'return' => 'DOMProcessingInstruction', + 'params' => 'string target, string data', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-135944439Since:', + ), + 'dom_document_create_attribute' => + array ( + 'return' => 'DOMAttr', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1084891198Since:', + ), + 'dom_document_create_entity_reference' => + array ( + 'return' => 'DOMEntityReference', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-392B75AESince:', + ), + 'dom_document_get_elements_by_tag_name' => + array ( + 'return' => 'DOMNodeList', + 'params' => 'string tagname', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C9094Since:', + ), + 'dom_document_import_node' => + array ( + 'return' => 'DOMNode', + 'params' => 'DOMNode importedNode, boolean deep', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Core-Document-importNodeSince: DOM Level 2', + ), + 'dom_document_create_element_ns' => + array ( + 'return' => 'DOMElement', + 'params' => 'string namespaceURI, string qualifiedName [,string value]', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrElNSSince: DOM Level 2', + ), + 'dom_document_create_attribute_ns' => + array ( + 'return' => 'DOMAttr', + 'params' => 'string namespaceURI, string qualifiedName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-DocCrAttrNSSince: DOM Level 2', + ), + 'dom_document_get_elements_by_tag_name_ns' => + array ( + 'return' => 'DOMNodeList', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBTNNSSince: DOM Level 2', + ), + 'dom_document_get_element_by_id' => + array ( + 'return' => 'DOMElement', + 'params' => 'string elementId', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBIdSince: DOM Level 2', + ), + 'dom_document_adopt_node' => + array ( + 'return' => 'DOMNode', + 'params' => 'DOMNode source', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNodeSince: DOM Level 3', + ), + 'dom_document_normalize_document' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-normalizeDocumentSince: DOM Level 3', + ), + 'dom_document_rename_node' => + array ( + 'return' => 'DOMNode', + 'params' => 'node n, string namespaceURI, string qualifiedName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-renameNodeSince: DOM Level 3', + ), + 'dom_document_load' => + array ( + 'return' => 'DOMNode', + 'params' => 'string source [, int options]', + 'description' => 'URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadSince: DOM Level 3', + ), + 'dom_document_loadxml' => + array ( + 'return' => 'DOMNode', + 'params' => 'string source [, int options]', + 'description' => 'URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-loadXMLSince: DOM Level 3', + ), + 'dom_document_save' => + array ( + 'return' => 'int', + 'params' => 'string file', + 'description' => 'Convenience method to save to file', + ), + 'dom_document_savexml' => + array ( + 'return' => 'string', + 'params' => '[node n]', + 'description' => 'URL: http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-DocumentLS-saveXMLSince: DOM Level 3', + ), + 'dom_document_xinclude' => + array ( + 'return' => 'int', + 'params' => '[int options]', + 'description' => 'Substitutues xincludes in a DomDocument', + ), + 'dom_document_validate' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Since: DOM extended', + ), + 'dom_document_load_html_file' => + array ( + 'return' => 'DOMNode', + 'params' => 'string source', + 'description' => 'Since: DOM extended', + ), + 'dom_document_load_html' => + array ( + 'return' => 'DOMNode', + 'params' => 'string source', + 'description' => 'Since: DOM extended', + ), + 'dom_document_save_html_file' => + array ( + 'return' => 'int', + 'params' => 'string file', + 'description' => 'Convenience method to save to file as html', + ), + 'dom_document_save_html' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Convenience method to output as html', + ), + 'dom_domstringlist_item' => + array ( + 'return' => 'domstring', + 'params' => 'int index', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMStringList-itemSince:', + ), + 'dom_string_extend_find_offset16' => + array ( + 'return' => 'int', + 'params' => 'int offset32', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#i18n-methods-StringExtend-findOffset16Since:', + ), + 'dom_string_extend_find_offset32' => + array ( + 'return' => 'int', + 'params' => 'int offset16', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#i18n-methods-StringExtend-findOffset32Since:', + ), + 'dom_import_simplexml' => + array ( + 'return' => 'somNode', + 'params' => 'sxeobject node', + 'description' => 'Get a simplexml_element object from dom to allow for processing', + ), + 'dom_domimplementation_has_feature' => + array ( + 'return' => 'boolean', + 'params' => 'string feature, string version', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-5CED94D7Since:', + ), + 'dom_domimplementation_create_document_type' => + array ( + 'return' => 'DOMDocumentType', + 'params' => 'string qualifiedName, string publicId, string systemId', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocTypeSince: DOM Level 2', + ), + 'dom_domimplementation_create_document' => + array ( + 'return' => 'DOMDocument', + 'params' => 'string namespaceURI, string qualifiedName, DOMDocumentType doctype', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocumentSince: DOM Level 2', + ), + 'dom_domimplementation_get_feature' => + array ( + 'return' => 'DOMNode', + 'params' => 'string feature, string version', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementation3-getFeatureSince: DOM Level 3', + ), + 'dom_namelist_get_name' => + array ( + 'return' => 'string', + 'params' => 'int index', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#NameList-getNameSince:', + ), + 'dom_namelist_get_namespace_uri' => + array ( + 'return' => 'string', + 'params' => 'int index', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#NameList-getNamespaceURISince:', + ), + 'dom_text_split_text' => + array ( + 'return' => 'DOMText', + 'params' => 'int offset', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-38853C1DSince:', + ), + 'dom_text_is_whitespace_in_element_content' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Text3-isWhitespaceInElementContentSince: DOM Level 3', + ), + 'dom_text_replace_whole_text' => + array ( + 'return' => 'DOMText', + 'params' => 'string content', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Text3-replaceWholeTextSince: DOM Level 3', + ), + 'dom_element_get_attribute' => + array ( + 'return' => 'string', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9Since:', + ), + 'dom_element_set_attribute' => + array ( + 'return' => 'void', + 'params' => 'string name, string value', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082Since:', + ), + 'dom_element_remove_attribute' => + array ( + 'return' => 'void', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9Since:', + ), + 'dom_element_get_attribute_node' => + array ( + 'return' => 'DOMAttr', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8Since:', + ), + 'dom_element_set_attribute_node' => + array ( + 'return' => 'DOMAttr', + 'params' => 'DOMAttr newAttr', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154Since:', + ), + 'dom_element_remove_attribute_node' => + array ( + 'return' => 'DOMAttr', + 'params' => 'DOMAttr oldAttr', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198Since:', + ), + 'dom_element_get_elements_by_tag_name' => + array ( + 'return' => 'DOMNodeList', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918DSince:', + ), + 'dom_element_get_attribute_ns' => + array ( + 'return' => 'string', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNSSince: DOM Level 2', + ), + 'dom_element_set_attribute_ns' => + array ( + 'return' => 'void', + 'params' => 'string namespaceURI, string qualifiedName, string value', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNSSince: DOM Level 2', + ), + 'dom_element_remove_attribute_ns' => + array ( + 'return' => 'void', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNSSince: DOM Level 2', + ), + 'dom_element_get_attribute_node_ns' => + array ( + 'return' => 'DOMAttr', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNSSince: DOM Level 2', + ), + 'dom_element_set_attribute_node_ns' => + array ( + 'return' => 'DOMAttr', + 'params' => 'DOMAttr newAttr', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNSSince: DOM Level 2', + ), + 'dom_element_get_elements_by_tag_name_ns' => + array ( + 'return' => 'DOMNodeList', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942Since: DOM Level 2', + ), + 'dom_element_has_attribute' => + array ( + 'return' => 'boolean', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrSince: DOM Level 2', + ), + 'dom_element_has_attribute_ns' => + array ( + 'return' => 'boolean', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNSSince: DOM Level 2', + ), + 'dom_element_set_id_attribute' => + array ( + 'return' => 'void', + 'params' => 'string name, boolean isId', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrSince: DOM Level 3', + ), + 'dom_element_set_id_attribute_ns' => + array ( + 'return' => 'void', + 'params' => 'string namespaceURI, string localName, boolean isId', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNSSince: DOM Level 3', + ), + 'dom_element_set_id_attribute_node' => + array ( + 'return' => 'void', + 'params' => 'attr idAttr, boolean isId', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNodeSince: DOM Level 3', + ), + 'dom_userdatahandler_handle' => + array ( + 'return' => 'dom_void', + 'params' => 'short operation, string key, domobject data, node src, node dst', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-handleUserDataEventSince:', + ), + 'dom_characterdata_substring_data' => + array ( + 'return' => 'string', + 'params' => 'int offset, int count', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6531BCCFSince:', + ), + 'dom_characterdata_append_data' => + array ( + 'return' => 'void', + 'params' => 'string arg', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-32791A2FSince:', + ), + 'dom_characterdata_insert_data' => + array ( + 'return' => 'void', + 'params' => 'int offset, string arg', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3EDB695FSince:', + ), + 'dom_characterdata_delete_data' => + array ( + 'return' => 'void', + 'params' => 'int offset, int count', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-7C603781Since:', + ), + 'dom_characterdata_replace_data' => + array ( + 'return' => 'void', + 'params' => 'int offset, int count, string arg', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-E5CBA7FBSince:', + ), + 'dom_domimplementationsource_get_domimplementation' => + array ( + 'return' => 'domdomimplementation', + 'params' => 'string features', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-getDOMImplSince:', + ), + 'dom_domimplementationsource_get_domimplementations' => + array ( + 'return' => 'domimplementationlist', + 'params' => 'string features', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-getDOMImplsSince:', + ), + 'dom_node_insert_before' => + array ( + 'return' => 'domnode', + 'params' => 'DomNode newChild, DomNode refChild', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-952280727Since:', + ), + 'dom_node_replace_child' => + array ( + 'return' => 'DomNode', + 'params' => 'DomNode newChild, DomNode oldChild', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-785887307Since:', + ), + 'dom_node_remove_child' => + array ( + 'return' => 'DomNode', + 'params' => 'DomNode oldChild', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1734834066Since:', + ), + 'dom_node_append_child' => + array ( + 'return' => 'DomNode', + 'params' => 'DomNode newChild', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-184E7107Since:', + ), + 'dom_node_has_child_nodes' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-810594187Since:', + ), + 'dom_node_clone_node' => + array ( + 'return' => 'DomNode', + 'params' => 'boolean deep', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3A0ED0A4Since:', + ), + 'dom_node_normalize' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-normalizeSince:', + ), + 'dom_node_is_supported' => + array ( + 'return' => 'boolean', + 'params' => 'string feature, string version', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Level-2-Core-Node-supportsSince: DOM Level 2', + ), + 'dom_node_has_attributes' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-NodeHasAttrsSince: DOM Level 2', + ), + 'dom_node_compare_document_position' => + array ( + 'return' => 'short', + 'params' => 'DomNode other', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-compareDocumentPositionSince: DOM Level 3', + ), + 'dom_node_is_same_node' => + array ( + 'return' => 'boolean', + 'params' => 'DomNode other', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isSameNodeSince: DOM Level 3', + ), + 'dom_node_lookup_prefix' => + array ( + 'return' => 'string', + 'params' => 'string namespaceURI', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-lookupNamespacePrefixSince: DOM Level 3', + ), + 'dom_node_is_default_namespace' => + array ( + 'return' => 'boolean', + 'params' => 'string namespaceURI', + 'description' => 'URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespaceSince: DOM Level 3', + ), + 'dom_node_lookup_namespace_uri' => + array ( + 'return' => 'string', + 'params' => 'string prefix', + 'description' => 'URL: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURISince: DOM Level 3', + ), + 'dom_node_is_equal_node' => + array ( + 'return' => 'boolean', + 'params' => 'DomNode arg', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-isEqualNodeSince: DOM Level 3', + ), + 'dom_node_get_feature' => + array ( + 'return' => 'DomNode', + 'params' => 'string feature, string version', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-getFeatureSince: DOM Level 3', + ), + 'dom_node_set_user_data' => + array ( + 'return' => 'DomUserData', + 'params' => 'string key, DomUserData data, userdatahandler handler', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-setUserDataSince: DOM Level 3', + ), + 'dom_node_get_user_data' => + array ( + 'return' => 'DomUserData', + 'params' => 'string key', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Node3-getUserDataSince: DOM Level 3', + ), + 'dom_domconfiguration_set_parameter' => + array ( + 'return' => 'dom_void', + 'params' => 'string name, domuserdata value', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-propertySince:', + ), + 'dom_domconfiguration_get_parameter' => + array ( + 'return' => 'domdomuserdata', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-getParameterSince:', + ), + 'dom_domconfiguration_can_set_parameter' => + array ( + 'return' => 'boolean', + 'params' => 'string name, domuserdata value', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMConfiguration-canSetParameterSince:', + ), + 'dom_namednodemap_get_named_item' => + array ( + 'return' => 'DOMNode', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1074577549Since:', + ), + 'dom_namednodemap_set_named_item' => + array ( + 'return' => 'DOMNode', + 'params' => 'DOMNode arg', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1025163788Since:', + ), + 'dom_namednodemap_remove_named_item' => + array ( + 'return' => 'DOMNode', + 'params' => 'string name', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D58B193Since:', + ), + 'dom_namednodemap_item' => + array ( + 'return' => 'DOMNode', + 'params' => 'int index', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-349467F9Since:', + ), + 'dom_namednodemap_get_named_item_ns' => + array ( + 'return' => 'DOMNode', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getNamedItemNSSince: DOM Level 2', + ), + 'dom_namednodemap_set_named_item_ns' => + array ( + 'return' => 'DOMNode', + 'params' => 'DOMNode arg', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-setNamedItemNSSince: DOM Level 2', + ), + 'dom_namednodemap_remove_named_item_ns' => + array ( + 'return' => 'DOMNode', + 'params' => 'string namespaceURI, string localName', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-removeNamedItemNSSince: DOM Level 2', + ), + 'dom_attr_is_id' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Attr-isIdSince: DOM Level 3', + ), + 'dom_domimplementationlist_item' => + array ( + 'return' => 'domdomimplementation', + 'params' => 'int index', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementationList-itemSince:', + ), + 'dom_nodelist_item' => + array ( + 'return' => 'DOMNode', + 'params' => 'int index', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-844377136Since:', + ), + 'PDO::pgsqlLOBCreate' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Creates a new large object, returning its identifier. Must be called inside a transaction.', + ), + 'PDO::pgsqlLOBOpen' => + array ( + 'return' => 'resource', + 'params' => 'string oid [, string mode = \'rb\']', + 'description' => 'Opens an existing large object stream. Must be called inside a transaction.', + ), + 'PDO::pgsqlLOBUnlink' => + array ( + 'return' => 'bool', + 'params' => 'string oid', + 'description' => 'Deletes the large object identified by oid. Must be called inside a transaction.', + ), + 'xmlrpc_encode_request' => + array ( + 'return' => 'string', + 'params' => 'string method, mixed params', + 'description' => 'Generates XML for a method request', + ), + 'xmlrpc_encode' => + array ( + 'return' => 'string', + 'params' => 'mixed value', + 'description' => 'Generates XML for a PHP value', + ), + 'xmlrpc_decode_request' => + array ( + 'return' => 'array', + 'params' => 'string xml, string& method [, string encoding]', + 'description' => 'Decodes XML into native PHP types', + ), + 'xmlrpc_decode' => + array ( + 'return' => 'array', + 'params' => 'string xml [, string encoding]', + 'description' => 'Decodes XML into native PHP types', + ), + 'xmlrpc_server_create' => + array ( + 'return' => 'resource', + 'params' => 'void', + 'description' => 'Creates an xmlrpc server', + ), + 'xmlrpc_server_destroy' => + array ( + 'return' => 'int', + 'params' => 'resource server', + 'description' => 'Destroys server resources', + ), + 'xmlrpc_server_register_method' => + array ( + 'return' => 'bool', + 'params' => 'resource server, string method_name, string function', + 'description' => 'Register a PHP function to handle method matching method_name', + ), + 'xmlrpc_server_register_introspection_callback' => + array ( + 'return' => 'bool', + 'params' => 'resource server, string function', + 'description' => 'Register a PHP function to generate documentation', + ), + 'xmlrpc_server_call_method' => + array ( + 'return' => 'mixed', + 'params' => 'resource server, string xml, mixed user_data [, array output_options]', + 'description' => 'Parses XML requests and call methods', + ), + 'xmlrpc_server_add_introspection_data' => + array ( + 'return' => 'int', + 'params' => 'resource server, array desc', + 'description' => 'Adds introspection documentation', + ), + 'xmlrpc_parse_method_descriptions' => + array ( + 'return' => 'array', + 'params' => 'string xml', + 'description' => 'Decodes XML into a list of method descriptions', + ), + 'xmlrpc_set_type' => + array ( + 'return' => 'bool', + 'params' => 'string value, string type', + 'description' => 'Sets xmlrpc type, base64 or datetime, for a PHP string value', + ), + 'xmlrpc_get_type' => + array ( + 'return' => 'string', + 'params' => 'mixed value', + 'description' => 'Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings', + ), + 'xmlrpc_is_fault' => + array ( + 'return' => 'bool', + 'params' => 'array', + 'description' => 'Determines if an array value represents an XMLRPC fault.', + ), + 'textdomain' => + array ( + 'return' => 'string', + 'params' => 'string domain', + 'description' => 'Set the textdomain to "domain". Returns the current domain', + ), + 'gettext' => + array ( + 'return' => 'string', + 'params' => 'string msgid', + 'description' => 'Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist', + ), + 'dgettext' => + array ( + 'return' => 'string', + 'params' => 'string domain_name, string msgid', + 'description' => 'Return the translation of msgid for domain_name, or msgid unaltered if a translation does not exist', + ), + 'dcgettext' => + array ( + 'return' => 'string', + 'params' => 'string domain_name, string msgid, long category', + 'description' => 'Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist', + ), + 'bindtextdomain' => + array ( + 'return' => 'string', + 'params' => 'string domain_name, string dir', + 'description' => 'Bind to the text domain domain_name, looking for translations in dir. Returns the current domain', + ), + 'ngettext' => + array ( + 'return' => 'string', + 'params' => 'string MSGID1, string MSGID2, int N', + 'description' => 'Plural version of gettext()', + ), + 'msg_set_queue' => + array ( + 'return' => 'bool', + 'params' => 'resource queue, array data', + 'description' => 'Set information for a message queue', + ), + 'msg_stat_queue' => + array ( + 'return' => 'array', + 'params' => 'resource queue', + 'description' => 'Returns information about a message queue', + ), + 'msg_get_queue' => + array ( + 'return' => 'resource', + 'params' => 'int key [, int perms]', + 'description' => 'Attach to a message queue', + ), + 'msg_remove_queue' => + array ( + 'return' => 'bool', + 'params' => 'resource queue', + 'description' => 'Destroy the queue', + ), + 'msg_receive' => + array ( + 'return' => 'mixed', + 'params' => 'resource queue, int desiredmsgtype, int &msgtype, int maxsize, mixed message [, bool unserialize=true [, int flags=0 [, int errorcode]]]', + 'description' => 'Send a message of type msgtype (must be > 0) to a message queue', + ), + 'msg_send' => + array ( + 'return' => 'bool', + 'params' => 'resource queue, int msgtype, mixed message [, bool serialize=true [, bool blocking=true [, int errorcode]]]', + 'description' => 'Send a message of type msgtype (must be > 0) to a message queue', + ), + 'xml_parser_create' => + array ( + 'return' => 'resource', + 'params' => '[string encoding]', + 'description' => 'Create an XML parser', + ), + 'xml_parser_create_ns' => + array ( + 'return' => 'resource', + 'params' => '[string encoding [, string sep]]', + 'description' => 'Create an XML parser', + ), + 'xml_set_object' => + array ( + 'return' => 'int', + 'params' => 'resource parser, object &obj', + 'description' => 'Set up object which should be used for callbacks', + ), + 'xml_set_element_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string shdl, string ehdl', + 'description' => 'Set up start and end element handlers', + ), + 'xml_set_character_data_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up character data handler', + ), + 'xml_set_processing_instruction_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up processing instruction (PI) handler', + ), + 'xml_set_default_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up default handler', + ), + 'xml_set_unparsed_entity_decl_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up unparsed entity declaration handler', + ), + 'xml_set_notation_decl_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up notation declaration handler', + ), + 'xml_set_external_entity_ref_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up external entity reference handler', + ), + 'xml_set_start_namespace_decl_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up character data handler', + ), + 'xml_set_end_namespace_decl_handler' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string hdl', + 'description' => 'Set up character data handler', + ), + 'xml_parse' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string data [, int isFinal]', + 'description' => 'Start parsing an XML document', + ), + 'xml_parse_into_struct' => + array ( + 'return' => 'int', + 'params' => 'resource parser, string data, array &struct, array &index', + 'description' => 'Parsing a XML document', + ), + 'xml_get_error_code' => + array ( + 'return' => 'int', + 'params' => 'resource parser', + 'description' => 'Get XML parser error code', + ), + 'xml_error_string' => + array ( + 'return' => 'string', + 'params' => 'int code', + 'description' => 'Get XML parser error string', + ), + 'xml_get_current_line_number' => + array ( + 'return' => 'int', + 'params' => 'resource parser', + 'description' => 'Get current line number for an XML parser', + ), + 'xml_get_current_column_number' => + array ( + 'return' => 'int', + 'params' => 'resource parser', + 'description' => 'Get current column number for an XML parser', + ), + 'xml_get_current_byte_index' => + array ( + 'return' => 'int', + 'params' => 'resource parser', + 'description' => 'Get current byte index for an XML parser', + ), + 'xml_parser_free' => + array ( + 'return' => 'int', + 'params' => 'resource parser', + 'description' => 'Free an XML parser', + ), + 'xml_parser_set_option' => + array ( + 'return' => 'int', + 'params' => 'resource parser, int option, mixed value', + 'description' => 'Set options in an XML parser', + ), + 'xml_parser_get_option' => + array ( + 'return' => 'int', + 'params' => 'resource parser, int option', + 'description' => 'Get options from an XML parser', + ), + 'utf8_encode' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'Encodes an ISO-8859-1 string to UTF-8', + ), + 'utf8_decode' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'Converts a UTF-8 encoded string to ISO-8859-1', + ), + 'shm_attach' => + array ( + 'return' => 'int', + 'params' => 'int key [, int memsize [, int perm]]', + 'description' => 'Creates or open a shared memory segment', + ), + 'shm_detach' => + array ( + 'return' => 'bool', + 'params' => 'int shm_identifier', + 'description' => 'Disconnects from shared memory segment', + ), + 'shm_remove' => + array ( + 'return' => 'bool', + 'params' => 'int shm_identifier', + 'description' => 'Removes shared memory from Unix systems', + ), + 'shm_put_var' => + array ( + 'return' => 'bool', + 'params' => 'int shm_identifier, int variable_key, mixed variable', + 'description' => 'Inserts or updates a variable in shared memory', + ), + 'shm_get_var' => + array ( + 'return' => 'mixed', + 'params' => 'int id, int variable_key', + 'description' => 'Returns a variable from shared memory', + ), + 'shm_remove_var' => + array ( + 'return' => 'bool', + 'params' => 'int id, int variable_key', + 'description' => 'Removes variable from shared memory', + ), + 'socket_select' => + array ( + 'return' => 'int', + 'params' => 'array &read_fds, array &write_fds, &array except_fds, int tv_sec[, int tv_usec]', + 'description' => 'Runs the select() system call on the sets mentioned with a timeout specified by tv_sec and tv_usec', + ), + 'socket_create_listen' => + array ( + 'return' => 'resource', + 'params' => 'int port[, int backlog]', + 'description' => 'Opens a socket on port to accept connections', + ), + 'socket_accept' => + array ( + 'return' => 'resource', + 'params' => 'resource socket', + 'description' => 'Accepts a connection on the listening socket fd', + ), + 'socket_set_nonblock' => + array ( + 'return' => 'bool', + 'params' => 'resource socket', + 'description' => 'Sets nonblocking mode on a socket resource', + ), + 'socket_set_block' => + array ( + 'return' => 'bool', + 'params' => 'resource socket', + 'description' => 'Sets blocking mode on a socket resource', + ), + 'socket_listen' => + array ( + 'return' => 'bool', + 'params' => 'resource socket[, int backlog]', + 'description' => 'Sets the maximum number of connections allowed to be waited for on the socket specified by fd', + ), + 'socket_close' => + array ( + 'return' => 'void', + 'params' => 'resource socket', + 'description' => 'Closes a file descriptor', + ), + 'socket_write' => + array ( + 'return' => 'int', + 'params' => 'resource socket, string buf[, int length]', + 'description' => 'Writes the buffer to the socket resource, length is optional', + ), + 'socket_read' => + array ( + 'return' => 'string', + 'params' => 'resource socket, int length [, int type]', + 'description' => 'Reads a maximum of length bytes from socket', + ), + 'socket_getsockname' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, string &addr[, int &port]', + 'description' => 'Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type.', + ), + 'socket_getpeername' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, string &addr[, int &port]', + 'description' => 'Queries the remote side of the given socket which may either result in host/port or in a UNIX filesystem path, dependent on its type.', + ), + 'socket_create' => + array ( + 'return' => 'resource', + 'params' => 'int domain, int type, int protocol', + 'description' => 'Creates an endpoint for communication in the domain specified by domain, of type specified by type', + ), + 'socket_connect' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, string addr [, int port]', + 'description' => 'Opens a connection to addr:port on the socket specified by socket', + ), + 'socket_strerror' => + array ( + 'return' => 'string', + 'params' => 'int errno', + 'description' => 'Returns a string describing an error', + ), + 'socket_bind' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, string addr [, int port]', + 'description' => 'Binds an open socket to a listening port, port is only specified in AF_INET family.', + ), + 'socket_recv' => + array ( + 'return' => 'int', + 'params' => 'resource socket, string &buf, int len, int flags', + 'description' => 'Receives data from a connected socket', + ), + 'socket_send' => + array ( + 'return' => 'int', + 'params' => 'resource socket, string buf, int len, int flags', + 'description' => 'Sends data to a connected socket', + ), + 'socket_recvfrom' => + array ( + 'return' => 'int', + 'params' => 'resource socket, string &buf, int len, int flags, string &name [, int &port]', + 'description' => 'Receives data from a socket, connected or not', + ), + 'socket_sendto' => + array ( + 'return' => 'int', + 'params' => 'resource socket, string buf, int len, int flags, string addr [, int port]', + 'description' => 'Sends a message to a socket, whether it is connected or not', + ), + 'socket_get_option' => + array ( + 'return' => 'mixed', + 'params' => 'resource socket, int level, int optname', + 'description' => 'Gets socket options for the socket', + ), + 'socket_set_option' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, int level, int optname, int|array optval', + 'description' => 'Sets socket options for the socket', + ), + 'socket_create_pair' => + array ( + 'return' => 'bool', + 'params' => 'int domain, int type, int protocol, array &fd', + 'description' => 'Creates a pair of indistinguishable sockets and stores them in fds.', + ), + 'socket_shutdown' => + array ( + 'return' => 'bool', + 'params' => 'resource socket[, int how]', + 'description' => 'Shuts down a socket for receiving, sending, or both.', + ), + 'socket_last_error' => + array ( + 'return' => 'int', + 'params' => '[resource socket]', + 'description' => 'Returns the last socket error (either the last used or the provided socket resource)', + ), + 'socket_clear_error' => + array ( + 'return' => 'void', + 'params' => '[resource socket]', + 'description' => 'Clears the error on the socket or the last error code.', + ), + 'sybase_connect' => + array ( + 'return' => 'int', + 'params' => '[string host [, string user [, string password [, string charset [, string appname]]]]]', + 'description' => 'Open Sybase server connection', + ), + 'sybase_pconnect' => + array ( + 'return' => 'int', + 'params' => '[string host [, string user [, string password [, string charset [, string appname]]]]]', + 'description' => 'Open persistent Sybase connection', + ), + 'sybase_close' => + array ( + 'return' => 'bool', + 'params' => '[int link_id]', + 'description' => 'Close Sybase connection', + ), + 'sybase_select_db' => + array ( + 'return' => 'bool', + 'params' => 'string database [, int link_id]', + 'description' => 'Select Sybase database', + ), + 'sybase_query' => + array ( + 'return' => 'int', + 'params' => 'string query [, int link_id]', + 'description' => 'Send Sybase query', + ), + 'sybase_free_result' => + array ( + 'return' => 'bool', + 'params' => 'int result', + 'description' => 'Free result memory', + ), + 'sybase_get_last_message' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns the last message from server (over min_message_severity)', + ), + 'sybase_num_rows' => + array ( + 'return' => 'int', + 'params' => 'int result', + 'description' => 'Get number of rows in result', + ), + 'sybase_num_fields' => + array ( + 'return' => 'int', + 'params' => 'int result', + 'description' => 'Get number of fields in result', + ), + 'sybase_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'int result', + 'description' => 'Get row as enumerated array', + ), + 'sybase_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'int result [, mixed object]', + 'description' => 'Fetch row as object', + ), + 'sybase_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'int result', + 'description' => 'Fetch row as array', + ), + 'sybase_data_seek' => + array ( + 'return' => 'bool', + 'params' => 'int result, int offset', + 'description' => 'Move internal row pointer', + ), + 'sybase_fetch_field' => + array ( + 'return' => 'object', + 'params' => 'int result [, int offset]', + 'description' => 'Get field information', + ), + 'sybase_field_seek' => + array ( + 'return' => 'bool', + 'params' => 'int result, int offset', + 'description' => 'Set field offset', + ), + 'sybase_result' => + array ( + 'return' => 'string', + 'params' => 'int result, int row, mixed field', + 'description' => 'Get result data', + ), + 'sybase_affected_rows' => + array ( + 'return' => 'int', + 'params' => '[int link_id]', + 'description' => 'Get number of affected rows in last query', + ), + 'sybase_min_error_severity' => + array ( + 'return' => 'void', + 'params' => 'int severity', + 'description' => 'Sets the minimum error severity', + ), + 'sybase_min_message_severity' => + array ( + 'return' => 'void', + 'params' => 'int severity', + 'description' => 'Sets the minimum message severity', + ), + 'confirm_extname_compiled' => + array ( + 'return' => 'string', + 'params' => 'string arg', + 'description' => 'Return a string to confirm that the module is compiled in', + ), + 'fdf_open' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Opens a new FDF document', + ), + 'fdf_open_string' => + array ( + 'return' => 'resource', + 'params' => 'string fdf_data', + 'description' => 'Opens a new FDF document from string', + ), + 'fdf_create' => + array ( + 'return' => 'resource', + 'params' => 'void', + 'description' => 'Creates a new FDF document', + ), + 'fdf_close' => + array ( + 'return' => 'void', + 'params' => 'resource fdfdoc', + 'description' => 'Closes the FDF document', + ), + 'fdf_get_value' => + array ( + 'return' => 'string', + 'params' => 'resource fdfdoc, string fieldname [, int which]', + 'description' => 'Gets the value of a field as string', + ), + 'fdf_set_value' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, mixed value [, int isname]', + 'description' => 'Sets the value of a field', + ), + 'fdf_next_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource fdfdoc [, string fieldname]', + 'description' => 'Gets the name of the next field name or the first field name', + ), + 'fdf_set_ap' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int face, string filename, int pagenr', + 'description' => 'Sets the appearence of a field', + ), + 'fdf_get_ap' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int face, string filename', + 'description' => 'Gets the appearance of a field and creates a PDF document out of it.', + ), + 'fdf_get_encoding' => + array ( + 'return' => 'string', + 'params' => 'resource fdf', + 'description' => 'Gets FDF file encoding scheme', + ), + 'fdf_set_status' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string status', + 'description' => 'Sets the value of /Status key', + ), + 'fdf_get_status' => + array ( + 'return' => 'string', + 'params' => 'resource fdfdoc', + 'description' => 'Gets the value of /Status key', + ), + 'fdf_set_file' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string filename [, string target_frame]', + 'description' => 'Sets the value of /F key', + ), + 'fdf_get_file' => + array ( + 'return' => 'string', + 'params' => 'resource fdfdoc', + 'description' => 'Gets the value of /F key', + ), + 'fdf_save' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc [, string filename]', + 'description' => 'Writes out the FDF file', + ), + 'fdf_save_string' => + array ( + 'return' => 'string', + 'params' => 'resource fdfdoc', + 'description' => 'Returns the FDF file as a string', + ), + 'fdf_add_template' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, int newpage, string filename, string template, int rename', + 'description' => 'Adds a template into the FDF document', + ), + 'fdf_set_flags' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int whichflags, int newflags', + 'description' => 'Sets flags for a field in the FDF document', + ), + 'fdf_get_flags' => + array ( + 'return' => 'int', + 'params' => 'resorce fdfdoc, string fieldname, int whichflags', + 'description' => 'Gets the flags of a field', + ), + 'fdf_set_opt' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int element, string value, string name', + 'description' => 'Sets a value in the opt array for a field', + ), + 'fdf_get_opt' => + array ( + 'return' => 'mixed', + 'params' => 'resource fdfdof, string fieldname [, int element]', + 'description' => 'Gets a value from the opt array of a field', + ), + 'fdf_set_submit_form_action' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int whichtrigger, string url, int flags', + 'description' => 'Sets the submit form action for a field', + ), + 'fdf_set_javascript_action' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int whichtrigger, string script', + 'description' => 'Sets the javascript action for a field', + ), + 'fdf_set_encoding' => + array ( + 'return' => 'bool', + 'params' => 'resource fdf_document, string encoding', + 'description' => 'Sets FDF encoding (either "Shift-JIS" or "Unicode")', + ), + 'fdf_errno' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Gets error code for last operation', + ), + 'fdf_error' => + array ( + 'return' => 'string', + 'params' => '[int errno]', + 'description' => 'Gets error description for error code', + ), + 'fdf_get_version' => + array ( + 'return' => 'string', + 'params' => '[resource fdfdoc]', + 'description' => 'Gets version number for FDF api or file', + ), + 'fdf_set_version' => + array ( + 'return' => 'bool', + 'params' => 'resourece fdfdoc, string version', + 'description' => 'Sets FDF version for a file', + ), + 'fdf_add_doc_javascript' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string scriptname, string script', + 'description' => 'Add javascript code to the fdf file', + ), + 'fdf_set_on_import_javascript' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string script [, bool before_data_import]', + 'description' => 'Adds javascript code to be executed when Acrobat opens the FDF', + ), + 'fdf_set_target_frame' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string target', + 'description' => 'Sets target frame for form', + ), + 'fdf_remove_item' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, string fieldname, int item', + 'description' => 'Sets target frame for form', + ), + 'fdf_get_attachment' => + array ( + 'return' => 'array', + 'params' => 'resource fdfdoc, string fieldname, string savepath', + 'description' => 'Get attached uploaded file', + ), + 'fdf_enum_values' => + array ( + 'return' => 'bool', + 'params' => 'resource fdfdoc, callback function [, mixed userdata]', + 'description' => 'Call a user defined function for each document value', + ), + 'fdf_header' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Set FDF specific HTTP headers', + ), + 'variant_set' => + array ( + 'return' => 'void', + 'params' => 'object variant, mixed value', + 'description' => 'Assigns a new value for a variant object', + ), + 'variant_add' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => '"Adds" two variant values together and returns the result', + ), + 'variant_cat' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'concatenates two variant values together and returns the result', + ), + 'variant_sub' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'subtracts the value of the right variant from the left variant value and returns the result', + ), + 'variant_mul' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'multiplies the values of the two variants and returns the result', + ), + 'variant_and' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'performs a bitwise AND operation between two variants and returns the result', + ), + 'variant_div' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Returns the result from dividing two variants', + ), + 'variant_eqv' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Performs a bitwise equivalence on two variants', + ), + 'variant_idiv' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Converts variants to integers and then returns the result from dividing them', + ), + 'variant_imp' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Performs a bitwise implication on two variants', + ), + 'variant_mod' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Divides two variants and returns only the remainder', + ), + 'variant_or' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Performs a logical disjunction on two variants', + ), + 'variant_pow' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Returns the result of performing the power function with two variants', + ), + 'variant_xor' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, mixed right', + 'description' => 'Performs a logical exclusion on two variants', + ), + 'variant_abs' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left', + 'description' => 'Returns the absolute value of a variant', + ), + 'variant_fix' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left', + 'description' => 'Returns the integer part ? of a variant', + ), + 'variant_int' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left', + 'description' => 'Returns the integer portion of a variant', + ), + 'variant_neg' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left', + 'description' => 'Performs logical negation on a variant', + ), + 'variant_not' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left', + 'description' => 'Performs bitwise not negation on a variant', + ), + 'variant_round' => + array ( + 'return' => 'mixed', + 'params' => 'mixed left, int decimals', + 'description' => 'Rounds a variant to the specified number of decimal places', + ), + 'variant_cmp' => + array ( + 'return' => 'int', + 'params' => 'mixed left, mixed right [, int lcid [, int flags]]', + 'description' => 'Compares two variants', + ), + 'variant_date_to_timestamp' => + array ( + 'return' => 'int', + 'params' => 'object variant', + 'description' => 'Converts a variant date/time value to unix timestamp', + ), + 'variant_date_from_timestamp' => + array ( + 'return' => 'object', + 'params' => 'int timestamp', + 'description' => 'Returns a variant date representation of a unix timestamp', + ), + 'variant_get_type' => + array ( + 'return' => 'int', + 'params' => 'object variant', + 'description' => 'Returns the VT_XXX type code for a variant', + ), + 'variant_set_type' => + array ( + 'return' => 'void', + 'params' => 'object variant, int type', + 'description' => 'Convert a variant into another type. Variant is modified "in-place"', + ), + 'variant_cast' => + array ( + 'return' => 'object', + 'params' => 'object variant, int type', + 'description' => 'Convert a variant into a new variant object of another type', + ), + 'com_get_active_object' => + array ( + 'return' => 'object', + 'params' => 'string progid [, int code_page ]', + 'description' => 'Returns a handle to an already running instance of a COM object', + ), + 'com_create_guid' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Generate a globally unique identifier (GUID)', + ), + 'com_event_sink' => + array ( + 'return' => 'bool', + 'params' => 'object comobject, object sinkobject [, mixed sinkinterface]', + 'description' => 'Connect events from a COM object to a PHP object', + ), + 'com_print_typeinfo' => + array ( + 'return' => 'bool', + 'params' => 'object comobject | string typelib, string dispinterface, bool wantsink', + 'description' => 'Print out a PHP class definition for a dispatchable interface', + ), + 'com_message_pump' => + array ( + 'return' => 'bool', + 'params' => '[int timeoutms]', + 'description' => 'Process COM messages, sleeping for up to timeoutms milliseconds', + ), + 'com_load_typelib' => + array ( + 'return' => 'bool', + 'params' => 'string typelib_name [, int case_insensitive]', + 'description' => 'Loads a Typelibrary and registers its constants', + ), + 'COMPersistHelper::GetCurFile' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Determines the filename into which an object will be saved, or false if none is set, via IPersistFile::GetCurFile', + ), + 'COMPersistHelper::SaveToFile' => + array ( + 'return' => 'bool', + 'params' => 'string filename [, bool remember]', + 'description' => 'Persist object data to file, via IPersistFile::Save', + ), + 'COMPersistHelper::LoadFromFile' => + array ( + 'return' => 'bool', + 'params' => 'string filename [, int flags]', + 'description' => 'Load object data from file, via IPersistFile::Load', + ), + 'COMPersistHelper::GetMaxStreamSize' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Gets maximum stream size required to store the object data, via IPersistStream::GetSizeMax (or IPersistStreamInit::GetSizeMax)', + ), + 'COMPersistHelper::InitNew' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Initializes the object to a default state, via IPersistStreamInit::InitNew', + ), + 'COMPersistHelper::LoadFromStream' => + array ( + 'return' => 'mixed', + 'params' => 'resource stream', + 'description' => 'Initializes an object from the stream where it was previously saved, via IPersistStream::Load or OleLoadFromStream', + ), + 'COMPersistHelper::SaveToStream' => + array ( + 'return' => 'int', + 'params' => 'resource stream', + 'description' => 'Saves the object to a stream, via IPersistStream::Save', + ), + 'COMPersistHelper::__construct' => + array ( + 'return' => 'int', + 'params' => '[object com_object]', + 'description' => 'Creates a persistence helper object, usually associated with a com_object', + ), + 'pg_connect' => + array ( + 'return' => 'resource', + 'params' => 'string connection_string[, int connect_type] | [string host, string port [, string options [, string tty,]]] string database', + 'description' => 'Open a PostgreSQL connection', + ), + 'pg_pconnect' => + array ( + 'return' => 'resource', + 'params' => 'string connection_string | [string host, string port [, string options [, string tty,]]] string database', + 'description' => 'Open a persistent PostgreSQL connection', + ), + 'pg_close' => + array ( + 'return' => 'bool', + 'params' => '[resource connection]', + 'description' => 'Close a PostgreSQL connection', + ), + 'pg_dbname' => + array ( + 'return' => 'string', + 'params' => '[resource connection]', + 'description' => 'Get the database name', + ), + 'pg_last_error' => + array ( + 'return' => 'string', + 'params' => '[resource connection]', + 'description' => 'Get the error message string', + ), + 'pg_options' => + array ( + 'return' => 'string', + 'params' => '[resource connection]', + 'description' => 'Get the options associated with the connection', + ), + 'pg_port' => + array ( + 'return' => 'int', + 'params' => '[resource connection]', + 'description' => 'Return the port number associated with the connection', + ), + 'pg_tty' => + array ( + 'return' => 'string', + 'params' => '[resource connection]', + 'description' => 'Return the tty name associated with the connection', + ), + 'pg_host' => + array ( + 'return' => 'string', + 'params' => '[resource connection]', + 'description' => 'Returns the host name associated with the connection', + ), + 'pg_version' => + array ( + 'return' => 'array', + 'params' => '[resource connection]', + 'description' => 'Returns an array with client, protocol and server version (when available)', + ), + 'pg_parameter_status' => + array ( + 'return' => 'string|false', + 'params' => '[resource connection,] string param_name', + 'description' => 'Returns the value of a server parameter', + ), + 'pg_ping' => + array ( + 'return' => 'bool', + 'params' => '[resource connection]', + 'description' => 'Ping database. If connection is bad, try to reconnect.', + ), + 'pg_query' => + array ( + 'return' => 'resource', + 'params' => '[resource connection,] string query', + 'description' => 'Execute a query', + ), + 'pg_query_params' => + array ( + 'return' => 'resource', + 'params' => '[resource connection,] string query, array params', + 'description' => 'Execute a query', + ), + 'pg_prepare' => + array ( + 'return' => 'resource', + 'params' => '[resource connection,] string stmtname, string query', + 'description' => 'Prepare a query for future execution', + ), + 'pg_execute' => + array ( + 'return' => 'resource', + 'params' => '[resource connection,] string stmtname, array params', + 'description' => 'Execute a prepared query', + ), + 'pg_num_rows' => + array ( + 'return' => 'int', + 'params' => 'resource result', + 'description' => 'Return the number of rows in the result', + ), + 'pg_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource result', + 'description' => 'Return the number of fields in the result', + ), + 'pg_affected_rows' => + array ( + 'return' => 'int', + 'params' => 'resource result', + 'description' => 'Returns the number of affected tuples', + ), + 'pg_last_notice' => + array ( + 'return' => 'string', + 'params' => 'resource connection', + 'description' => 'Returns the last notice set by the backend', + ), + 'pg_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_number', + 'description' => 'Returns the name of the field', + ), + 'pg_field_size' => + array ( + 'return' => 'int', + 'params' => 'resource result, int field_number', + 'description' => 'Returns the internal size of the field', + ), + 'pg_field_type' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_number', + 'description' => 'Returns the type name for the given field', + ), + 'pg_field_type_oid' => + array ( + 'return' => 'string', + 'params' => 'resource result, int field_number', + 'description' => 'Returns the type oid for the given field', + ), + 'pg_field_num' => + array ( + 'return' => 'int', + 'params' => 'resource result, string field_name', + 'description' => 'Returns the field number of the named field', + ), + 'pg_fetch_result' => + array ( + 'return' => 'mixed', + 'params' => 'resource result, [int row_number,] mixed field_name', + 'description' => 'Returns values from a result identifier', + ), + 'pg_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int row [, int result_type]]', + 'description' => 'Get a row as an enumerated array', + ), + 'pg_fetch_assoc' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int row]', + 'description' => 'Fetch a row as an assoc array', + ), + 'pg_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int row [, int result_type]]', + 'description' => 'Fetch a row as an array', + ), + 'pg_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'resource result [, int row [, string class_name [, NULL|array ctor_params]]]', + 'description' => 'Fetch a row as an object', + ), + 'pg_fetch_all' => + array ( + 'return' => 'array', + 'params' => 'resource result', + 'description' => 'Fetch all rows into array', + ), + 'pg_fetch_all_columns' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int column_number]', + 'description' => 'Fetch all rows into array', + ), + 'pg_result_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource result, int offset', + 'description' => 'Set internal row offset', + ), + 'pg_field_prtlen' => + array ( + 'return' => 'int', + 'params' => 'resource result, [int row,] mixed field_name_or_number', + 'description' => 'Returns the printed length', + ), + 'pg_field_is_null' => + array ( + 'return' => 'int', + 'params' => 'resource result, [int row,] mixed field_name_or_number', + 'description' => 'Test if a field is NULL', + ), + 'pg_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result', + 'description' => 'Free result memory', + ), + 'pg_last_oid' => + array ( + 'return' => 'string', + 'params' => 'resource result', + 'description' => 'Returns the last object identifier', + ), + 'pg_trace' => + array ( + 'return' => 'bool', + 'params' => 'string filename [, string mode [, resource connection]]', + 'description' => 'Enable tracing a PostgreSQL connection', + ), + 'pg_untrace' => + array ( + 'return' => 'bool', + 'params' => '[resource connection]', + 'description' => 'Disable tracing of a PostgreSQL connection', + ), + 'pg_lo_create' => + array ( + 'return' => 'int', + 'params' => '[resource connection]', + 'description' => 'Create a large object', + ), + 'pg_lo_unlink' => + array ( + 'return' => 'bool', + 'params' => '[resource connection,] string large_object_oid', + 'description' => 'Delete a large object', + ), + 'pg_lo_open' => + array ( + 'return' => 'resource', + 'params' => '[resource connection,] int large_object_oid, string mode', + 'description' => 'Open a large object and return fd', + ), + 'pg_lo_close' => + array ( + 'return' => 'bool', + 'params' => 'resource large_object', + 'description' => 'Close a large object', + ), + 'pg_lo_read' => + array ( + 'return' => 'string', + 'params' => 'resource large_object [, int len]', + 'description' => 'Read a large object', + ), + 'pg_lo_write' => + array ( + 'return' => 'int', + 'params' => 'resource large_object, string buf [, int len]', + 'description' => 'Write a large object', + ), + 'pg_lo_read_all' => + array ( + 'return' => 'int', + 'params' => 'resource large_object', + 'description' => 'Read a large object and send straight to browser', + ), + 'pg_lo_import' => + array ( + 'return' => 'int', + 'params' => '[resource connection, ] string filename', + 'description' => 'Import large object direct from filesystem', + ), + 'pg_lo_export' => + array ( + 'return' => 'bool', + 'params' => '[resource connection, ] int objoid, string filename', + 'description' => 'Export large object direct to filesystem', + ), + 'pg_lo_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource large_object, int offset [, int whence]', + 'description' => 'Seeks position of large object', + ), + 'pg_lo_tell' => + array ( + 'return' => 'int', + 'params' => 'resource large_object', + 'description' => 'Returns current position of large object', + ), + 'pg_set_error_verbosity' => + array ( + 'return' => 'int', + 'params' => '[resource connection,] int verbosity', + 'description' => 'Set error verbosity', + ), + 'pg_set_client_encoding' => + array ( + 'return' => 'int', + 'params' => '[resource connection,] string encoding', + 'description' => 'Set client encoding', + ), + 'pg_client_encoding' => + array ( + 'return' => 'string', + 'params' => '[resource connection]', + 'description' => 'Get the current client encoding', + ), + 'pg_end_copy' => + array ( + 'return' => 'bool', + 'params' => '[resource connection]', + 'description' => 'Sync with backend. Completes the Copy command', + ), + 'pg_put_line' => + array ( + 'return' => 'bool', + 'params' => '[resource connection,] string query', + 'description' => 'Send null-terminated string to backend server', + ), + 'pg_copy_to' => + array ( + 'return' => 'array', + 'params' => 'resource connection, string table_name [, string delimiter [, string null_as]]', + 'description' => 'Copy table to array', + ), + 'pg_copy_from' => + array ( + 'return' => 'bool', + 'params' => 'resource connection, string table_name , array rows [, string delimiter [, string null_as]]', + 'description' => 'Copy table from array', + ), + 'pg_escape_string' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'Escape string for text/char type', + ), + 'pg_escape_bytea' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'Escape binary for bytea type', + ), + 'pg_unescape_bytea' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'Unescape binary for bytea type', + ), + 'pg_result_error' => + array ( + 'return' => 'string', + 'params' => 'resource result', + 'description' => 'Get error message associated with result', + ), + 'pg_result_error_field' => + array ( + 'return' => 'string', + 'params' => 'resource result, int fieldcode', + 'description' => 'Get error message field associated with result', + ), + 'pg_connection_status' => + array ( + 'return' => 'int', + 'params' => 'resource connnection', + 'description' => 'Get connection status', + ), + 'pg_transaction_status' => + array ( + 'return' => 'int', + 'params' => 'resource connnection', + 'description' => 'Get transaction status', + ), + 'pg_connection_reset' => + array ( + 'return' => 'bool', + 'params' => 'resource connection', + 'description' => 'Reset connection (reconnect)', + ), + 'pg_cancel_query' => + array ( + 'return' => 'bool', + 'params' => 'resource connection', + 'description' => 'Cancel request', + ), + 'pg_connection_busy' => + array ( + 'return' => 'bool', + 'params' => 'resource connection', + 'description' => 'Get connection is busy or not', + ), + 'pg_send_query' => + array ( + 'return' => 'bool', + 'params' => 'resource connection, string query', + 'description' => 'Send asynchronous query', + ), + 'pg_send_query_params' => + array ( + 'return' => 'bool', + 'params' => 'resource connection, string query', + 'description' => 'Send asynchronous parameterized query', + ), + 'pg_send_prepare' => + array ( + 'return' => 'bool', + 'params' => 'resource connection, string stmtname, string query', + 'description' => 'Asynchronously prepare a query for future execution', + ), + 'pg_send_execute' => + array ( + 'return' => 'bool', + 'params' => 'resource connection, string stmtname, array params', + 'description' => 'Executes prevriously prepared stmtname asynchronously', + ), + 'pg_get_result' => + array ( + 'return' => 'resource', + 'params' => 'resource connection', + 'description' => 'Get asynchronous query result', + ), + 'pg_result_status' => + array ( + 'return' => 'mixed', + 'params' => 'resource result[, long result_type]', + 'description' => 'Get status of query result', + ), + 'pg_get_notify' => + array ( + 'return' => 'array', + 'params' => '[resource connection[, result_type]]', + 'description' => 'Get asynchronous notification', + ), + 'pg_get_pid' => + array ( + 'return' => 'int', + 'params' => '[resource connection', + 'description' => 'Get backend(server) pid', + ), + 'pg_meta_data' => + array ( + 'return' => 'array', + 'params' => 'resource db, string table', + 'description' => 'Get meta_data', + ), + 'pg_convert' => + array ( + 'return' => 'array', + 'params' => 'resource db, string table, array values[, int options]', + 'description' => 'Check and convert values for PostgreSQL SQL statement', + ), + 'pg_insert' => + array ( + 'return' => 'mixed', + 'params' => 'resource db, string table, array values[, int options]', + 'description' => 'Insert values (filed=>value) to table', + ), + 'pg_update' => + array ( + 'return' => 'mixed', + 'params' => 'resource db, string table, array fields, array ids[, int options]', + 'description' => 'Update table using values (field=>value) and ids (id=>value)', + ), + 'pg_delete' => + array ( + 'return' => 'mixed', + 'params' => 'resource db, string table, array ids[, int options]', + 'description' => 'Delete records has ids (id=>value)', + ), + 'pg_select' => + array ( + 'return' => 'mixed', + 'params' => 'resource db, string table, array ids[, int options]', + 'description' => 'Select records that has ids (id=>value)', + ), + 'filepro' => + array ( + 'return' => 'bool', + 'params' => 'string directory', + 'description' => 'Read and verify the map file', + ), + 'filepro_rowcount' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Find out how many rows are in a filePro database', + ), + 'filepro_fieldname' => + array ( + 'return' => 'string', + 'params' => 'int fieldnumber', + 'description' => 'Gets the name of a field', + ), + 'filepro_fieldtype' => + array ( + 'return' => 'string', + 'params' => 'int field_number', + 'description' => 'Gets the type of a field', + ), + 'filepro_fieldwidth' => + array ( + 'return' => 'int', + 'params' => 'int field_number', + 'description' => 'Gets the width of a field', + ), + 'filepro_fieldcount' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Find out how many fields are in a filePro database', + ), + 'filepro_retrieve' => + array ( + 'return' => 'string', + 'params' => 'int row_number, int field_number', + 'description' => 'Retrieves data from a filePro database', + ), + 'bzread' => + array ( + 'return' => 'string', + 'params' => 'int bz[, int length]', + 'description' => 'Reads up to length bytes from a BZip2 stream, or 1024 bytes if length is not specified', + ), + 'bzopen' => + array ( + 'return' => 'resource', + 'params' => 'string|int file|fp, string mode', + 'description' => 'Opens a new BZip2 stream', + ), + 'bzerrno' => + array ( + 'return' => 'int', + 'params' => 'resource bz', + 'description' => 'Returns the error number', + ), + 'bzerrstr' => + array ( + 'return' => 'string', + 'params' => 'resource bz', + 'description' => 'Returns the error string', + ), + 'bzerror' => + array ( + 'return' => 'array', + 'params' => 'resource bz', + 'description' => 'Returns the error number and error string in an associative array', + ), + 'bzcompress' => + array ( + 'return' => 'string', + 'params' => 'string source [, int blocksize100k [, int workfactor]]', + 'description' => 'Compresses a string into BZip2 encoded data', + ), + 'bzdecompress' => + array ( + 'return' => 'string', + 'params' => 'string source [, int small]', + 'description' => 'Decompresses BZip2 compressed data', + ), + 'dba_popen' => + array ( + 'return' => 'resource', + 'params' => 'string path, string mode [, string handlername, string ...]', + 'description' => 'Opens path using the specified handler in mode persistently', + ), + 'dba_open' => + array ( + 'return' => 'resource', + 'params' => 'string path, string mode [, string handlername, string ...]', + 'description' => 'Opens path using the specified handler in mode', + ), + 'dba_close' => + array ( + 'return' => 'void', + 'params' => 'resource handle', + 'description' => 'Closes database', + ), + 'dba_exists' => + array ( + 'return' => 'bool', + 'params' => 'string key, resource handle', + 'description' => 'Checks, if the specified key exists', + ), + 'dba_fetch' => + array ( + 'return' => 'string', + 'params' => 'string key, [int skip ,] resource handle', + 'description' => 'Fetches the data associated with key', + ), + 'dba_key_split' => + array ( + 'return' => 'array|false', + 'params' => 'string key', + 'description' => 'Splits an inifile key into an array of the form array(0=>group,1=>value_name) but returns false if input is false or null', + ), + 'dba_firstkey' => + array ( + 'return' => 'string', + 'params' => 'resource handle', + 'description' => 'Resets the internal key pointer and returns the first key', + ), + 'dba_nextkey' => + array ( + 'return' => 'string', + 'params' => 'resource handle', + 'description' => 'Returns the next key', + ), + 'dba_delete' => + array ( + 'return' => 'bool', + 'params' => 'string key, resource handle', + 'description' => 'Deletes the entry associated with keyIf inifile: remove all other key lines', + ), + 'dba_insert' => + array ( + 'return' => 'bool', + 'params' => 'string key, string value, resource handle', + 'description' => 'If not inifile: Insert value as key, return false, if key exists alreadyIf inifile: Add vakue as key (next instance of key)', + ), + 'dba_replace' => + array ( + 'return' => 'bool', + 'params' => 'string key, string value, resource handle', + 'description' => 'Inserts value as key, replaces key, if key exists alreadyIf inifile: remove all other key lines', + ), + 'dba_optimize' => + array ( + 'return' => 'bool', + 'params' => 'resource handle', + 'description' => 'Optimizes (e.g. clean up, vacuum) database', + ), + 'dba_sync' => + array ( + 'return' => 'bool', + 'params' => 'resource handle', + 'description' => 'Synchronizes database', + ), + 'dba_handlers' => + array ( + 'return' => 'array', + 'params' => '[bool full_info]', + 'description' => 'List configured database handlers', + ), + 'dba_list' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'List opened databases', + ), + 'iconv_strlen' => + array ( + 'return' => 'int', + 'params' => 'string str [, string charset]', + 'description' => 'Returns the character count of str', + ), + 'iconv_substr' => + array ( + 'return' => 'string', + 'params' => 'string str, int offset, [int length, string charset]', + 'description' => 'Returns specified part of a string', + ), + 'iconv_strpos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle, int offset [, string charset]', + 'description' => 'Finds position of first occurrence of needle within part of haystack beginning with offset', + ), + 'iconv_strrpos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, string charset]', + 'description' => 'Finds position of last occurrence of needle within part of haystack beginning with offset', + ), + 'iconv_mime_encode' => + array ( + 'return' => 'string', + 'params' => 'string field_name, string field_value, [, array preference]', + 'description' => 'Composes a mime header field with field_name and field_value in a specified scheme', + ), + 'iconv_mime_decode' => + array ( + 'return' => 'string', + 'params' => 'string encoded_string [, int mode, string charset]', + 'description' => 'Decodes a mime header field', + ), + 'iconv_mime_decode_headers' => + array ( + 'return' => 'array', + 'params' => 'string headers [, int mode, string charset]', + 'description' => 'Decodes multiple mime header fields', + ), + 'iconv' => + array ( + 'return' => 'string', + 'params' => 'string in_charset, string out_charset, string str', + 'description' => 'Returns str converted to the out_charset character set', + ), + 'ob_iconv_handler' => + array ( + 'return' => 'string', + 'params' => 'string contents, int status', + 'description' => 'Returns str in output buffer converted to the iconv.output_encoding character set', + ), + 'iconv_set_encoding' => + array ( + 'return' => 'bool', + 'params' => 'string type, string charset', + 'description' => 'Sets internal encoding and output encoding for ob_iconv_handler()', + ), + 'iconv_get_encoding' => + array ( + 'return' => 'mixed', + 'params' => '[string type]', + 'description' => 'Get internal encoding and output encoding for ob_iconv_handler()', + ), + 'ctype_alnum' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for alphanumeric character(s)', + ), + 'ctype_alpha' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for alphabetic character(s)', + ), + 'ctype_cntrl' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for control character(s)', + ), + 'ctype_digit' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for numeric character(s)', + ), + 'ctype_lower' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for lowercase character(s)', + ), + 'ctype_graph' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for any printable character(s) except space', + ), + 'ctype_print' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for printable character(s)', + ), + 'ctype_punct' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for any printable character which is not whitespace or an alphanumeric character', + ), + 'ctype_space' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for whitespace character(s)', + ), + 'ctype_upper' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for uppercase character(s)', + ), + 'ctype_xdigit' => + array ( + 'return' => 'bool', + 'params' => 'mixed c', + 'description' => 'Checks for character(s) representing a hexadecimal digit', + ), + 'bcadd' => + array ( + 'return' => 'string', + 'params' => 'string left_operand, string right_operand [, int scale]', + 'description' => 'Returns the sum of two arbitrary precision numbers', + ), + 'bcsub' => + array ( + 'return' => 'string', + 'params' => 'string left_operand, string right_operand [, int scale]', + 'description' => 'Returns the difference between two arbitrary precision numbers', + ), + 'bcmul' => + array ( + 'return' => 'string', + 'params' => 'string left_operand, string right_operand [, int scale]', + 'description' => 'Returns the multiplication of two arbitrary precision numbers', + ), + 'bcdiv' => + array ( + 'return' => 'string', + 'params' => 'string left_operand, string right_operand [, int scale]', + 'description' => 'Returns the quotient of two arbitrary precision numbers (division)', + ), + 'bcmod' => + array ( + 'return' => 'string', + 'params' => 'string left_operand, string right_operand', + 'description' => 'Returns the modulus of the two arbitrary precision operands', + ), + 'bcpowmod' => + array ( + 'return' => 'string', + 'params' => 'string x, string y, string mod [, int scale]', + 'description' => 'Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous', + ), + 'bcpow' => + array ( + 'return' => 'string', + 'params' => 'string x, string y [, int scale]', + 'description' => 'Returns the value of an arbitrary precision number raised to the power of another', + ), + 'bcsqrt' => + array ( + 'return' => 'string', + 'params' => 'string operand [, int scale]', + 'description' => 'Returns the square root of an arbitray precision number', + ), + 'bccomp' => + array ( + 'return' => 'int', + 'params' => 'string left_operand, string right_operand [, int scale]', + 'description' => 'Compares two arbitrary precision numbers', + ), + 'bcscale' => + array ( + 'return' => 'bool', + 'params' => 'int scale', + 'description' => 'Sets default scale parameter for all bc math functions', + ), + 'ldap_connect' => + array ( + 'return' => 'resource', + 'params' => '[string host [, int port]]', + 'description' => 'Connect to an LDAP server', + ), + 'ldap_bind' => + array ( + 'return' => 'bool', + 'params' => 'resource link [, string dn, string password]', + 'description' => 'Bind to LDAP directory', + ), + 'ldap_sasl_bind' => + array ( + 'return' => 'bool', + 'params' => 'resource link [, string binddn, string password, string sasl_mech, string sasl_realm, string sasl_authz_id, string props]', + 'description' => 'Bind to LDAP directory using SASL', + ), + 'ldap_unbind' => + array ( + 'return' => 'bool', + 'params' => 'resource link', + 'description' => 'Unbind from LDAP directory', + ), + 'ldap_read' => + array ( + 'return' => 'resource', + 'params' => 'resource link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]]', + 'description' => 'Read an entry', + ), + 'ldap_list' => + array ( + 'return' => 'resource', + 'params' => 'resource link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]]', + 'description' => 'Single-level search', + ), + 'ldap_search' => + array ( + 'return' => 'resource', + 'params' => 'resource link, string base_dn, string filter [, array attrs [, int attrsonly [, int sizelimit [, int timelimit [, int deref]]]]]', + 'description' => 'Search LDAP tree under base_dn', + ), + 'ldap_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result', + 'description' => 'Free result memory', + ), + 'ldap_count_entries' => + array ( + 'return' => 'int', + 'params' => 'resource link, resource result', + 'description' => 'Count the number of entries in a search result', + ), + 'ldap_first_entry' => + array ( + 'return' => 'resource', + 'params' => 'resource link, resource result', + 'description' => 'Return first result id', + ), + 'ldap_next_entry' => + array ( + 'return' => 'resource', + 'params' => 'resource link, resource result_entry', + 'description' => 'Get next result entry', + ), + 'ldap_get_entries' => + array ( + 'return' => 'array', + 'params' => 'resource link, resource result', + 'description' => 'Get all result entries', + ), + 'ldap_first_attribute' => + array ( + 'return' => 'string', + 'params' => 'resource link, resource result_entry, int ber', + 'description' => 'Return first attribute', + ), + 'ldap_next_attribute' => + array ( + 'return' => 'string', + 'params' => 'resource link, resource result_entry, resource ber', + 'description' => 'Get the next attribute in result', + ), + 'ldap_get_attributes' => + array ( + 'return' => 'array', + 'params' => 'resource link, resource result_entry', + 'description' => 'Get attributes from a search result entry', + ), + 'ldap_get_values' => + array ( + 'return' => 'array', + 'params' => 'resource link, resource result_entry, string attribute', + 'description' => 'Get all values from a result entry', + ), + 'ldap_get_values_len' => + array ( + 'return' => 'array', + 'params' => 'resource link, resource result_entry, string attribute', + 'description' => 'Get all values with lengths from a result entry', + ), + 'ldap_get_dn' => + array ( + 'return' => 'string', + 'params' => 'resource link, resource result_entry', + 'description' => 'Get the DN of a result entry', + ), + 'ldap_explode_dn' => + array ( + 'return' => 'array', + 'params' => 'string dn, int with_attrib', + 'description' => 'Splits DN into its component parts', + ), + 'ldap_dn2ufn' => + array ( + 'return' => 'string', + 'params' => 'string dn', + 'description' => 'Convert DN to User Friendly Naming format', + ), + 'ldap_add' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn, array entry', + 'description' => 'Add entries to LDAP directory', + ), + 'ldap_mod_replace' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn, array entry', + 'description' => 'Replace attribute values with new ones', + ), + 'ldap_mod_add' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn, array entry', + 'description' => 'Add attribute values to current', + ), + 'ldap_mod_del' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn, array entry', + 'description' => 'Delete attribute values', + ), + 'ldap_delete' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn', + 'description' => 'Delete an entry from a directory', + ), + 'ldap_errno' => + array ( + 'return' => 'int', + 'params' => 'resource link', + 'description' => 'Get the current ldap error number', + ), + 'ldap_err2str' => + array ( + 'return' => 'string', + 'params' => 'int errno', + 'description' => 'Convert error number to error string', + ), + 'ldap_error' => + array ( + 'return' => 'string', + 'params' => 'resource link', + 'description' => 'Get the current ldap error string', + ), + 'ldap_compare' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn, string attr, string value', + 'description' => 'Determine if an entry has a specific value for one of its attributes', + ), + 'ldap_sort' => + array ( + 'return' => 'bool', + 'params' => 'resource link, resource result, string sortfilter', + 'description' => 'Sort LDAP result entries', + ), + 'ldap_get_option' => + array ( + 'return' => 'bool', + 'params' => 'resource link, int option, mixed retval', + 'description' => 'Get the current value of various session-wide parameters', + ), + 'ldap_set_option' => + array ( + 'return' => 'bool', + 'params' => 'resource link, int option, mixed newval', + 'description' => 'Set the value of various session-wide parameters', + ), + 'ldap_parse_result' => + array ( + 'return' => 'bool', + 'params' => 'resource link, resource result, int errcode, string matcheddn, string errmsg, array referrals', + 'description' => 'Extract information from result', + ), + 'ldap_first_reference' => + array ( + 'return' => 'resource', + 'params' => 'resource link, resource result', + 'description' => 'Return first reference', + ), + 'ldap_next_reference' => + array ( + 'return' => 'resource', + 'params' => 'resource link, resource reference_entry', + 'description' => 'Get next reference', + ), + 'ldap_parse_reference' => + array ( + 'return' => 'bool', + 'params' => 'resource link, resource reference_entry, array referrals', + 'description' => 'Extract information from reference entry', + ), + 'ldap_rename' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string dn, string newrdn, string newparent, bool deleteoldrdn', + 'description' => 'Modify the name of an entry', + ), + 'ldap_start_tls' => + array ( + 'return' => 'bool', + 'params' => 'resource link', + 'description' => 'Start TLS', + ), + 'ldap_set_rebind_proc' => + array ( + 'return' => 'bool', + 'params' => 'resource link, string callback', + 'description' => 'Set a callback function to do re-binds on referral chasing.', + ), + 'ldap_t61_to_8859' => + array ( + 'return' => 'string', + 'params' => 'string value', + 'description' => 'Translate t61 characters to 8859 characters', + ), + 'ldap_8859_to_t61' => + array ( + 'return' => 'string', + 'params' => 'string value', + 'description' => 'Translate 8859 characters to t61 characters', + ), + 'SoapServer::setClass' => + array ( + 'return' => 'void', + 'params' => 'string class_name [, mixed args]', + 'description' => 'Sets class which will handle SOAP requests', + ), + 'SoapServer::getFunctions' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns list of defined functions', + ), + 'SoapServer::addFunction' => + array ( + 'return' => 'void', + 'params' => 'mixed functions', + 'description' => 'Adds one or several functions those will handle SOAP requests', + ), + 'SoapClient::__getLastRequestHeaders' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns last SOAP request headers', + ), + 'SoapClient::__getLastResponseHeaders' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns last SOAP response headers', + ), + 'SoapClient::__doRequest' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'SoapClient::__doRequest()', + ), + 'SoapClient::__setCookie' => + array ( + 'return' => 'void', + 'params' => 'string name [, strung value]', + 'description' => 'Sets cookie thet will sent with SOAP request.The call to this function will effect all folowing calls of SOAP methods.If value is not specified cookie is removed.', + ), + 'SoapClient::__setSoapHeaders' => + array ( + 'return' => 'void', + 'params' => 'array SoapHeaders', + 'description' => 'Sets SOAP headers for subsequent calls (replaces any previousvalues).If no value is specified, all of the headers are removed.', + ), + 'SoapClient::__setLocation' => + array ( + 'return' => 'string', + 'params' => '[string new_location]', + 'description' => 'Sets the location option (the endpoint URL that will be touched by thefollowing SOAP requests).If new_location is not specified or null then SoapClient will use endpointfrom WSDL file.The function returns old value of location options.', + ), + 'fbsql_connect' => + array ( + 'return' => 'resource', + 'params' => '[string hostname [, string username [, string password]]]', + 'description' => 'Create a connection to a database server', + ), + 'fbsql_pconnect' => + array ( + 'return' => 'resource', + 'params' => '[string hostname [, string username [, string password]]]', + 'description' => 'Create a persistant connection to a database server', + ), + 'fbsql_close' => + array ( + 'return' => 'bool', + 'params' => '[resource link_identifier]', + 'description' => 'Close a connection to a database server', + ), + 'fbsql_set_transaction' => + array ( + 'return' => 'void', + 'params' => 'resource link_identifier, int locking, int isolation', + 'description' => 'Sets the transaction locking and isolation', + ), + 'fbsql_autocommit' => + array ( + 'return' => 'bool', + 'params' => 'resource link_identifier [, bool OnOff]', + 'description' => 'Turns on auto-commit', + ), + 'fbsql_commit' => + array ( + 'return' => 'bool', + 'params' => '[resource link_identifier]', + 'description' => 'Commit the transaction', + ), + 'fbsql_rollback' => + array ( + 'return' => 'bool', + 'params' => '[resource link_identifier]', + 'description' => 'Rollback all statments since last commit', + ), + 'fbsql_create_blob' => + array ( + 'return' => 'string', + 'params' => 'string blob_data [, resource link_identifier]', + 'description' => 'Create a BLOB in the database for use with an insert or update statement', + ), + 'fbsql_create_clob' => + array ( + 'return' => 'string', + 'params' => 'string clob_data [, resource link_identifier]', + 'description' => 'Create a CLOB in the database for use with an insert or update statement', + ), + 'fbsql_set_lob_mode' => + array ( + 'return' => 'bool', + 'params' => 'resource result, int lob_mode', + 'description' => 'Sets the mode for how LOB data re retreived (actual data or a handle)', + ), + 'fbsql_read_blob' => + array ( + 'return' => 'string', + 'params' => 'string blob_handle [, resource link_identifier]', + 'description' => 'Read the BLOB data identified by blob_handle', + ), + 'fbsql_read_clob' => + array ( + 'return' => 'string', + 'params' => 'string clob_handle [, resource link_identifier]', + 'description' => 'Read the CLOB data identified by clob_handle', + ), + 'fbsql_blob_size' => + array ( + 'return' => 'int', + 'params' => 'string blob_handle [, resource link_identifier]', + 'description' => 'Get the size of a BLOB identified by blob_handle', + ), + 'fbsql_clob_size' => + array ( + 'return' => 'int', + 'params' => 'string clob_handle [, resource link_identifier]', + 'description' => 'Get the size of a CLOB identified by clob_handle', + ), + 'fbsql_hostname' => + array ( + 'return' => 'string', + 'params' => 'resource link_identifier [, string host_name]', + 'description' => 'Get or set the host name used with a connection', + ), + 'fbsql_database' => + array ( + 'return' => 'string', + 'params' => 'resource link_identifier [, string database]', + 'description' => 'Get or set the database name used with a connection', + ), + 'fbsql_database_password' => + array ( + 'return' => 'string', + 'params' => 'resource link_identifier [, string database_password]', + 'description' => 'Get or set the databsae password used with a connection', + ), + 'fbsql_username' => + array ( + 'return' => 'string', + 'params' => 'resource link_identifier [, string username]', + 'description' => 'Get or set the host user used with a connection', + ), + 'fbsql_password' => + array ( + 'return' => 'string', + 'params' => 'resource link_identifier [, string password]', + 'description' => 'Get or set the user password used with a connection', + ), + 'fbsql_set_password' => + array ( + 'return' => 'bool', + 'params' => 'resource link_identifier, string user, string password, string old_password', + 'description' => 'Change the password for a given user', + ), + 'fbsql_select_db' => + array ( + 'return' => 'bool', + 'params' => '[string database_name [, resource link_identifier]]', + 'description' => 'Select the database to open', + ), + 'fbsql_set_characterset' => + array ( + 'return' => 'void', + 'params' => 'resource link_identifier, long charcterset [, long in_out_both]]', + 'description' => 'Change input/output character set', + ), + 'fbsql_change_user' => + array ( + 'return' => 'int', + 'params' => 'string user, string password [, string database [, resource link_identifier]]', + 'description' => 'Change the user for a session', + ), + 'fbsql_create_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Create a new database on the server', + ), + 'fbsql_drop_db' => + array ( + 'return' => 'int', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Drop a database on the server', + ), + 'fbsql_start_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource link_identifier [, string database_options]]', + 'description' => 'Start a database on the server', + ), + 'fbsql_stop_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Stop a database on the server', + ), + 'fbsql_db_status' => + array ( + 'return' => 'int', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Gets the status (Stopped, Starting, Running, Stopping) for a given database', + ), + 'fbsql_query' => + array ( + 'return' => 'resource', + 'params' => 'string query [, resource link_identifier [, long batch_size]]', + 'description' => 'Send one or more SQL statements to the server and execute them', + ), + 'fbsql_db_query' => + array ( + 'return' => 'resource', + 'params' => 'string database_name, string query [, resource link_identifier]', + 'description' => 'Send one or more SQL statements to a specified database on the server', + ), + 'fbsql_list_dbs' => + array ( + 'return' => 'resource', + 'params' => '[resource link_identifier]', + 'description' => 'Retreive a list of all databases on the server', + ), + 'fbsql_list_tables' => + array ( + 'return' => 'resource', + 'params' => 'string database [, int link_identifier]', + 'description' => 'Retreive a list of all tables from the specifoied database', + ), + 'fbsql_list_fields' => + array ( + 'return' => 'resource', + 'params' => 'string database_name, string table_name [, resource link_identifier]', + 'description' => 'Retrieve a list of all fields for the specified database.table', + ), + 'fbsql_error' => + array ( + 'return' => 'string', + 'params' => '[resource link_identifier]', + 'description' => 'Returns the last error string', + ), + 'fbsql_errno' => + array ( + 'return' => 'int', + 'params' => '[resource link_identifier]', + 'description' => 'Returns the last error code', + ), + 'fbsql_warnings' => + array ( + 'return' => 'bool', + 'params' => '[int flag]', + 'description' => 'Enable or disable FrontBase warnings', + ), + 'fbsql_affected_rows' => + array ( + 'return' => 'int', + 'params' => '[resource link_identifier]', + 'description' => 'Get the number of rows affected by the last statement', + ), + 'fbsql_insert_id' => + array ( + 'return' => 'int', + 'params' => '[resource link_identifier]', + 'description' => 'Get the internal index for the last insert statement', + ), + 'fbsql_result' => + array ( + 'return' => 'mixed', + 'params' => 'int result [, int row [, mixed field]]', + 'description' => '???', + ), + 'fbsql_next_result' => + array ( + 'return' => 'bool', + 'params' => 'int result', + 'description' => 'Switch to the next result if multiple results are available', + ), + 'fbsql_num_rows' => + array ( + 'return' => 'int', + 'params' => 'int result', + 'description' => 'Get number of rows', + ), + 'fbsql_num_fields' => + array ( + 'return' => 'int', + 'params' => 'int result', + 'description' => 'Get number of fields in the result set', + ), + 'fbsql_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'resource result', + 'description' => 'Fetch a row of data. Returns an indexed array', + ), + 'fbsql_fetch_assoc' => + array ( + 'return' => 'object', + 'params' => 'resource result', + 'description' => 'Detch a row of data. Returns an assoc array', + ), + 'fbsql_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'resource result [, int result_type]', + 'description' => 'Fetch a row of data. Returns an object', + ), + 'fbsql_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int result_type]', + 'description' => 'Fetches a result row as an array (associative, numeric or both)', + ), + 'fbsql_data_seek' => + array ( + 'return' => 'bool', + 'params' => 'int result, int row_number', + 'description' => 'Move the internal row counter to the specified row_number', + ), + 'fbsql_fetch_lengths' => + array ( + 'return' => 'array', + 'params' => 'int result', + 'description' => 'Returns an array of the lengths of each column in the result set', + ), + 'fbsql_fetch_field' => + array ( + 'return' => 'object', + 'params' => 'int result [, int field_index]', + 'description' => 'Get the field properties for a specified field_index', + ), + 'fbsql_field_seek' => + array ( + 'return' => 'bool', + 'params' => 'int result [, int field_index]', + 'description' => '???', + ), + 'fbsql_field_name' => + array ( + 'return' => 'string', + 'params' => 'int result [, int field_index]', + 'description' => 'Get the column name for a specified field_index', + ), + 'fbsql_field_table' => + array ( + 'return' => 'string', + 'params' => 'int result [, int field_index]', + 'description' => 'Get the table name for a specified field_index', + ), + 'fbsql_field_len' => + array ( + 'return' => 'mixed', + 'params' => 'int result [, int field_index]', + 'description' => 'Get the column length for a specified field_index', + ), + 'fbsql_field_type' => + array ( + 'return' => 'string', + 'params' => 'int result [, int field_index]', + 'description' => 'Get the field type for a specified field_index', + ), + 'fbsql_field_flags' => + array ( + 'return' => 'string', + 'params' => 'int result [, int field_index]', + 'description' => '???', + ), + 'fbsql_table_name' => + array ( + 'return' => 'string', + 'params' => 'resource result, int index', + 'description' => 'Retreive the table name for index after a call to fbsql_list_tables()', + ), + 'fbsql_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result', + 'description' => 'free the memory used to store a result', + ), + 'fbsql_get_autostart_info' => + array ( + 'return' => 'array', + 'params' => '[resource link_identifier]', + 'description' => '???', + ), + 'sem_get' => + array ( + 'return' => 'resource', + 'params' => 'int key [, int max_acquire [, int perm [, int auto_release]]', + 'description' => 'Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously', + ), + 'sem_acquire' => + array ( + 'return' => 'bool', + 'params' => 'resource id', + 'description' => 'Acquires the semaphore with the given id, blocking if necessary', + ), + 'sem_release' => + array ( + 'return' => 'bool', + 'params' => 'resource id', + 'description' => 'Releases the semaphore with the given id', + ), + 'sem_remove' => + array ( + 'return' => 'bool', + 'params' => 'resource id', + 'description' => 'Removes semaphore from Unix systems', + ), + 'token_get_all' => + array ( + 'return' => 'array', + 'params' => 'string source', + 'description' => '', + ), + 'token_name' => + array ( + 'return' => 'string', + 'params' => 'int type', + 'description' => '', + ), + 'gzfile' => + array ( + 'return' => 'array', + 'params' => 'string filename [, int use_include_path]', + 'description' => 'Read und uncompress entire .gz-file into an array', + ), + 'gzopen' => + array ( + 'return' => 'resource', + 'params' => 'string filename, string mode [, int use_include_path]', + 'description' => 'Open a .gz-file and return a .gz-file pointer', + ), + 'readgzfile' => + array ( + 'return' => 'int', + 'params' => 'string filename [, int use_include_path]', + 'description' => 'Output a .gz-file', + ), + 'gzcompress' => + array ( + 'return' => 'string', + 'params' => 'string data [, int level]', + 'description' => 'Gzip-compress a string', + ), + 'gzuncompress' => + array ( + 'return' => 'string', + 'params' => 'string data [, int length]', + 'description' => 'Unzip a gzip-compressed string', + ), + 'gzdeflate' => + array ( + 'return' => 'string', + 'params' => 'string data [, int level]', + 'description' => 'Gzip-compress a string', + ), + 'gzinflate' => + array ( + 'return' => 'string', + 'params' => 'string data [, int length]', + 'description' => 'Unzip a gzip-compressed string', + ), + 'zlib_get_coding_type' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns the coding type used for output compression', + ), + 'gzencode' => + array ( + 'return' => 'string', + 'params' => 'string data [, int level [, int encoding_mode]]', + 'description' => 'GZ encode a string', + ), + 'ob_gzhandler' => + array ( + 'return' => 'string', + 'params' => 'string str, int mode', + 'description' => 'Encode str based on accept-encoding setting - designed to be called from ob_start()', + ), + 'msql_connect' => + array ( + 'return' => 'int', + 'params' => '[string hostname[:port]] [, string username] [, string password]', + 'description' => 'Open a connection to an mSQL Server', + ), + 'msql_pconnect' => + array ( + 'return' => 'int', + 'params' => '[string hostname[:port]] [, string username] [, string password]', + 'description' => 'Open a persistent connection to an mSQL Server', + ), + 'msql_close' => + array ( + 'return' => 'bool', + 'params' => '[resource link_identifier]', + 'description' => 'Close an mSQL connection', + ), + 'msql_select_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Select an mSQL database', + ), + 'msql_create_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Create an mSQL database', + ), + 'msql_drop_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'Drop (delete) an mSQL database', + ), + 'msql_query' => + array ( + 'return' => 'resource', + 'params' => 'string query [, resource link_identifier]', + 'description' => 'Send an SQL query to mSQL', + ), + 'msql_db_query' => + array ( + 'return' => 'resource', + 'params' => 'string database_name, string query [, resource link_identifier]', + 'description' => 'Send an SQL query to mSQL', + ), + 'msql_list_dbs' => + array ( + 'return' => 'resource', + 'params' => '[resource link_identifier]', + 'description' => 'List databases available on an mSQL server', + ), + 'msql_list_tables' => + array ( + 'return' => 'resource', + 'params' => 'string database_name [, resource link_identifier]', + 'description' => 'List tables in an mSQL database', + ), + 'msql_list_fields' => + array ( + 'return' => 'resource', + 'params' => 'string database_name, string table_name [, resource link_identifier]', + 'description' => 'List mSQL result fields', + ), + 'msql_error' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns the text of the error message from previous mSQL operation', + ), + 'msql_result' => + array ( + 'return' => 'string', + 'params' => 'int query, int row [, mixed field]', + 'description' => 'Get result data', + ), + 'msql_num_rows' => + array ( + 'return' => 'int', + 'params' => 'resource query', + 'description' => 'Get number of rows in a result', + ), + 'msql_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource query', + 'description' => 'Get number of fields in a result', + ), + 'msql_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'resource query', + 'description' => 'Get a result row as an enumerated array', + ), + 'msql_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'resource query [, resource result_type]', + 'description' => 'Fetch a result row as an object', + ), + 'msql_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'resource query [, int result_type]', + 'description' => 'Fetch a result row as an associative array', + ), + 'msql_data_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource query, int row_number', + 'description' => 'Move internal result pointer', + ), + 'msql_fetch_field' => + array ( + 'return' => 'object', + 'params' => 'resource query [, int field_offset]', + 'description' => 'Get column information from a result and return as an object', + ), + 'msql_field_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource query, int field_offset', + 'description' => 'Set result pointer to a specific field offset', + ), + 'msql_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource query, int field_index', + 'description' => 'Get the name of the specified field in a result', + ), + 'msql_field_table' => + array ( + 'return' => 'string', + 'params' => 'resource query, int field_offset', + 'description' => 'Get name of the table the specified field is in', + ), + 'msql_field_len' => + array ( + 'return' => 'int', + 'params' => 'int query, int field_offet', + 'description' => 'Returns the length of the specified field', + ), + 'msql_field_type' => + array ( + 'return' => 'string', + 'params' => 'resource query, int field_offset', + 'description' => 'Get the type of the specified field in a result', + ), + 'msql_field_flags' => + array ( + 'return' => 'string', + 'params' => 'resource query, int field_offset', + 'description' => 'Get the flags associated with the specified field in a result', + ), + 'msql_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource query', + 'description' => 'Free result memory', + ), + 'msql_affected_rows' => + array ( + 'return' => 'int', + 'params' => 'resource query', + 'description' => 'Return number of affected rows', + ), + 'PDO::__construct' => + array ( + 'return' => 'void', + 'params' => 'string dsn, string username, string passwd [, array options]', + 'description' => '', + ), + 'PDO::prepare' => + array ( + 'return' => 'object', + 'params' => 'string statment [, array options]', + 'description' => 'Prepares a statement for execution and returns a statement object', + ), + 'PDO::beginTransaction' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Initiates a transaction', + ), + 'PDO::commit' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Commit a transaction', + ), + 'PDO::rollBack' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'roll back a transaction', + ), + 'PDO::setAttribute' => + array ( + 'return' => 'bool', + 'params' => 'long attribute, mixed value', + 'description' => 'Set an attribute', + ), + 'PDO::getAttribute' => + array ( + 'return' => 'mixed', + 'params' => 'long attribute', + 'description' => 'Get an attribute', + ), + 'PDO::exec' => + array ( + 'return' => 'long', + 'params' => 'string query', + 'description' => 'Execute a query that does not return a row set, returning the number of affected rows', + ), + 'PDO::lastInsertId' => + array ( + 'return' => 'string', + 'params' => '[string seqname]', + 'description' => 'Returns the id of the last row that we affected on this connection. Some databases require a sequence or table name to be passed in. Not always meaningful.', + ), + 'PDO::errorCode' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Fetch the error code associated with the last operation on the database handle', + ), + 'PDO::errorInfo' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Fetch extended error information associated with the last operation on the database handle', + ), + 'PDO::query' => + array ( + 'return' => 'object', + 'params' => 'string sql [, PDOStatement::setFetchMode() args]', + 'description' => 'Prepare and execute $sql; returns the statement object for iteration', + ), + 'PDO::quote' => + array ( + 'return' => 'string', + 'params' => 'string string [, int paramtype]', + 'description' => 'quotes string for use in a query. The optional paramtype acts as a hint for drivers that have alternate quoting styles. The default value is PDO_PARAM_STR', + ), + 'PDO::__wakeup' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Prevents use of a PDO instance that has been unserialized', + ), + 'PDO::__sleep' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Prevents serialization of a PDO instance', + ), + 'pdo_drivers' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Return array of available PDO drivers', + ), + 'PDOStatement::execute' => + array ( + 'return' => 'bool', + 'params' => '[array $bound_input_params]', + 'description' => 'Execute a prepared statement, optionally binding parameters', + ), + 'PDOStatement::fetch' => + array ( + 'return' => 'mixed', + 'params' => '[int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]]', + 'description' => 'Fetches the next row and returns it, or false if there are no more rows', + ), + 'PDOStatement::fetchObject' => + array ( + 'return' => 'mixed', + 'params' => 'string class_name [, NULL|array ctor_args]', + 'description' => 'Fetches the next row and returns it as an object.', + ), + 'PDOStatement::fetchColumn' => + array ( + 'return' => 'string', + 'params' => '[int column_number]', + 'description' => 'Returns a data of the specified column in the result set.', + ), + 'PDOStatement::fetchAll' => + array ( + 'return' => 'array', + 'params' => '[int $how = PDO_FETCH_BOTH [, string class_name [, NULL|array ctor_args]]]', + 'description' => 'Returns an array of all of the results.', + ), + 'PDOStatement::bindValue' => + array ( + 'return' => 'bool', + 'params' => 'mixed $paramno, mixed $param [, int $type ]', + 'description' => 'bind an input parameter to the value of a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). It should be called prior to execute().', + ), + 'PDOStatement::bindParam' => + array ( + 'return' => 'bool', + 'params' => 'mixed $paramno, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]]', + 'description' => 'bind a parameter to a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). This isn\'t supported by all drivers. It should be called prior to execute().', + ), + 'PDOStatement::bindColumn' => + array ( + 'return' => 'bool', + 'params' => 'mixed $column, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]]', + 'description' => 'bind a column to a PHP variable. On each row fetch $param will contain the value of the corresponding column. $column is the 1-based offset of the column, or the column name. For portability, don\'t call this before execute().', + ), + 'PDOStatement::rowCount' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the number of rows in a result set, or the number of rows affected by the last execute(). It is not always meaningful.', + ), + 'PDOStatement::errorCode' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Fetch the error code associated with the last operation on the statement handle', + ), + 'PDOStatement::errorInfo' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Fetch extended error information associated with the last operation on the statement handle', + ), + 'PDOStatement::setAttribute' => + array ( + 'return' => 'bool', + 'params' => 'long attribute, mixed value', + 'description' => 'Set an attribute', + ), + 'PDOStatement::getAttribute' => + array ( + 'return' => 'mixed', + 'params' => 'long attribute', + 'description' => 'Get an attribute', + ), + 'PDOStatement::columnCount' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the number of columns in the result set', + ), + 'PDOStatement::getColumnMeta' => + array ( + 'return' => 'array', + 'params' => 'int $column', + 'description' => 'Returns meta data for a numbered column', + ), + 'PDOStatement::setFetchMode' => + array ( + 'return' => 'bool', + 'params' => 'int mode [mixed* params]', + 'description' => 'Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes)', + ), + 'PDOStatement::nextRowset' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeded, false otherwise', + ), + 'PDOStatement::closeCursor' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Closes the cursor, leaving the statement ready for re-execution.', + ), + 'PDOStatement::debugDumpParams' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'A utility for internals hackers to debug parameter internals', + ), + 'PDOStatement::__wakeup' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Prevents use of a PDOStatement instance that has been unserialized', + ), + 'PDOStatement::__sleep' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Prevents serialization of a PDOStatement instance', + ), + 'xsl_xsltprocessor_import_stylesheet' => + array ( + 'return' => 'void', + 'params' => 'domdocument doc', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Since:', + ), + 'xsl_xsltprocessor_transform_to_doc' => + array ( + 'return' => 'domdocument', + 'params' => 'domnode doc', + 'description' => 'URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Since:', + ), + 'xsl_xsltprocessor_transform_to_uri' => + array ( + 'return' => 'int', + 'params' => 'domdocument doc, string uri', + 'description' => '', + ), + 'xsl_xsltprocessor_transform_to_xml' => + array ( + 'return' => 'string', + 'params' => 'domdocument doc', + 'description' => '', + ), + 'xsl_xsltprocessor_set_parameter' => + array ( + 'return' => 'bool', + 'params' => 'string namespace, mixed name [, string value]', + 'description' => '', + ), + 'xsl_xsltprocessor_get_parameter' => + array ( + 'return' => 'string', + 'params' => 'string namespace, string name', + 'description' => '', + ), + 'xsl_xsltprocessor_remove_parameter' => + array ( + 'return' => 'bool', + 'params' => 'string namespace, string name', + 'description' => '', + ), + 'xsl_xsltprocessor_register_php_functions' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => '', + ), + 'xsl_xsltprocessor_has_exslt_support' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => '', + ), + 'libxml_set_streams_context' => + array ( + 'return' => 'void', + 'params' => 'resource streams_context', + 'description' => 'Set the streams context for the next libxml document load or write', + ), + 'libxml_use_internal_errors' => + array ( + 'return' => 'void', + 'params' => 'boolean use_errors', + 'description' => 'Disable libxml errors and allow user to fetch error information as needed', + ), + 'libxml_get_last_error' => + array ( + 'return' => 'object', + 'params' => '', + 'description' => 'Retrieve last error from libxml', + ), + 'libxml_get_errors' => + array ( + 'return' => 'object', + 'params' => '', + 'description' => 'Retrieve array of errors', + ), + 'libxml_clear_errors' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Clear last error from libxml', + ), + 'mssql_connect' => + array ( + 'return' => 'int', + 'params' => '[string servername [, string username [, string password [, bool new_link]]]', + 'description' => 'Establishes a connection to a MS-SQL server', + ), + 'mssql_pconnect' => + array ( + 'return' => 'int', + 'params' => '[string servername [, string username [, string password [, bool new_link]]]]', + 'description' => 'Establishes a persistent connection to a MS-SQL server', + ), + 'mssql_close' => + array ( + 'return' => 'bool', + 'params' => '[resource conn_id]', + 'description' => 'Closes a connection to a MS-SQL server', + ), + 'mssql_select_db' => + array ( + 'return' => 'bool', + 'params' => 'string database_name [, resource conn_id]', + 'description' => 'Select a MS-SQL database', + ), + 'mssql_fetch_batch' => + array ( + 'return' => 'int', + 'params' => 'resource result_index', + 'description' => 'Returns the next batch of records', + ), + 'mssql_query' => + array ( + 'return' => 'resource', + 'params' => 'string query [, resource conn_id [, int batch_size]]', + 'description' => 'Perform an SQL query on a MS-SQL server database', + ), + 'mssql_rows_affected' => + array ( + 'return' => 'int', + 'params' => 'resource conn_id', + 'description' => 'Returns the number of records affected by the query', + ), + 'mssql_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result_index', + 'description' => 'Free a MS-SQL result index', + ), + 'mssql_get_last_message' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Gets the last message from the MS-SQL server', + ), + 'mssql_num_rows' => + array ( + 'return' => 'int', + 'params' => 'resource mssql_result_index', + 'description' => 'Returns the number of rows fetched in from the result id specified', + ), + 'mssql_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource mssql_result_index', + 'description' => 'Returns the number of fields fetched in from the result id specified', + ), + 'mssql_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'resource result_id', + 'description' => 'Returns an array of the current row in the result set specified by result_id', + ), + 'mssql_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'resource result_id [, int result_type]', + 'description' => 'Returns a psuedo-object of the current row in the result set specified by result_id', + ), + 'mssql_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'resource result_id [, int result_type]', + 'description' => 'Returns an associative array of the current row in the result set specified by result_id', + ), + 'mssql_fetch_assoc' => + array ( + 'return' => 'array', + 'params' => 'resource result_id', + 'description' => 'Returns an associative array of the current row in the result set specified by result_id', + ), + 'mssql_data_seek' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id, int offset', + 'description' => 'Moves the internal row pointer of the MS-SQL result associated with the specified result identifier to pointer to the specified row number', + ), + 'mssql_fetch_field' => + array ( + 'return' => 'object', + 'params' => 'resource result_id [, int offset]', + 'description' => 'Gets information about certain fields in a query result', + ), + 'mssql_field_length' => + array ( + 'return' => 'int', + 'params' => 'resource result_id [, int offset]', + 'description' => 'Get the length of a MS-SQL field', + ), + 'mssql_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource result_id [, int offset]', + 'description' => 'Returns the name of the field given by offset in the result set given by result_id', + ), + 'mssql_field_type' => + array ( + 'return' => 'string', + 'params' => 'resource result_id [, int offset]', + 'description' => 'Returns the type of a field', + ), + 'mssql_field_seek' => + array ( + 'return' => 'bool', + 'params' => 'int result_id, int offset', + 'description' => 'Seeks to the specified field offset', + ), + 'mssql_result' => + array ( + 'return' => 'string', + 'params' => 'resource result_id, int row, mixed field', + 'description' => 'Returns the contents of one cell from a MS-SQL result set', + ), + 'mssql_next_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id', + 'description' => 'Move the internal result pointer to the next result', + ), + 'mssql_min_error_severity' => + array ( + 'return' => 'void', + 'params' => 'int severity', + 'description' => 'Sets the lower error severity', + ), + 'mssql_min_message_severity' => + array ( + 'return' => 'void', + 'params' => 'int severity', + 'description' => 'Sets the lower message severity', + ), + 'mssql_init' => + array ( + 'return' => 'int', + 'params' => 'string sp_name [, resource conn_id]', + 'description' => 'Initializes a stored procedure or a remote stored procedure', + ), + 'mssql_bind' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt, string param_name, mixed var, int type [, int is_output [, int is_null [, int maxlen]]]', + 'description' => 'Adds a parameter to a stored procedure or a remote stored procedure', + ), + 'mssql_execute' => + array ( + 'return' => 'mixed', + 'params' => 'resource stmt [, bool skip_results = false]', + 'description' => 'Executes a stored procedure on a MS-SQL server database', + ), + 'mssql_free_statement' => + array ( + 'return' => 'bool', + 'params' => 'resource result_index', + 'description' => 'Free a MS-SQL statement index', + ), + 'mssql_guid_string' => + array ( + 'return' => 'string', + 'params' => 'string binary [,int short_format]', + 'description' => 'Converts a 16 byte binary GUID to a string', + ), + 'oci_define_by_name' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt, string name, mixed &var [, int type]', + 'description' => 'Define a PHP variable to an Oracle column by name', + ), + 'oci_bind_by_name' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt, string name, mixed &var, [, int maxlength [, int type]]', + 'description' => 'Bind a PHP variable to an Oracle placeholder by name', + ), + 'oci_bind_array_by_name' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt, string name, array &var, int max_table_length [, int max_item_length [, int type ]]', + 'description' => 'Bind a PHP array to an Oracle PL/SQL type by name', + ), + 'oci_free_descriptor' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Deletes large object description', + ), + 'oci_lob_save' => + array ( + 'return' => 'bool', + 'params' => ' string data [, int offset ]', + 'description' => 'Saves a large object', + ), + 'oci_lob_import' => + array ( + 'return' => 'bool', + 'params' => ' string filename ', + 'description' => 'Loads file into a LOB', + ), + 'oci_lob_load' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Loads a large object', + ), + 'oci_lob_read' => + array ( + 'return' => 'string', + 'params' => ' int length ', + 'description' => 'Reads particular part of a large object', + ), + 'oci_lob_eof' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Checks if EOF is reached', + ), + 'oci_lob_tell' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Tells LOB pointer position', + ), + 'oci_lob_rewind' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Rewind pointer of a LOB', + ), + 'oci_lob_seek' => + array ( + 'return' => 'bool', + 'params' => ' int offset [, int whence ]', + 'description' => 'Moves the pointer of a LOB', + ), + 'oci_lob_size' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns size of a large object', + ), + 'oci_lob_write' => + array ( + 'return' => 'int', + 'params' => ' string string [, int length ]', + 'description' => 'Writes data to current position of a LOB', + ), + 'oci_lob_append' => + array ( + 'return' => 'bool', + 'params' => ' object lob ', + 'description' => 'Appends data from a LOB to another LOB', + ), + 'oci_lob_truncate' => + array ( + 'return' => 'bool', + 'params' => ' [ int length ]', + 'description' => 'Truncates a LOB', + ), + 'oci_lob_erase' => + array ( + 'return' => 'int', + 'params' => ' [ int offset [, int length ] ] ', + 'description' => 'Erases a specified portion of the internal LOB, starting at a specified offset', + ), + 'oci_lob_flush' => + array ( + 'return' => 'bool', + 'params' => ' [ int flag ] ', + 'description' => 'Flushes the LOB buffer', + ), + 'ocisetbufferinglob' => + array ( + 'return' => 'bool', + 'params' => ' boolean flag ', + 'description' => 'Enables/disables buffering for a LOB', + ), + 'ocigetbufferinglob' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns current state of buffering for a LOB', + ), + 'oci_lob_copy' => + array ( + 'return' => 'bool', + 'params' => ' object lob_to, object lob_from [, int length ] ', + 'description' => 'Copies data from a LOB to another LOB', + ), + 'oci_lob_is_equal' => + array ( + 'return' => 'bool', + 'params' => ' object lob1, object lob2 ', + 'description' => 'Tests to see if two LOB/FILE locators are equal', + ), + 'oci_lob_export' => + array ( + 'return' => 'bool', + 'params' => '[string filename [, int start [, int length]]]', + 'description' => 'Writes a large object into a file', + ), + 'oci_lob_write_temporary' => + array ( + 'return' => 'bool', + 'params' => 'string var [, int lob_type]', + 'description' => 'Writes temporary blob', + ), + 'oci_lob_close' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Closes lob descriptor', + ), + 'oci_new_descriptor' => + array ( + 'return' => 'object', + 'params' => 'resource connection [, int type]', + 'description' => 'Initialize a new empty descriptor LOB/FILE (LOB is default)', + ), + 'oci_rollback' => + array ( + 'return' => 'bool', + 'params' => 'resource connection', + 'description' => 'Rollback the current context', + ), + 'oci_commit' => + array ( + 'return' => 'bool', + 'params' => 'resource connection', + 'description' => 'Commit the current context', + ), + 'oci_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource stmt, int col', + 'description' => 'Tell the name of a column', + ), + 'oci_field_size' => + array ( + 'return' => 'int', + 'params' => 'resource stmt, int col', + 'description' => 'Tell the maximum data size of a column', + ), + 'oci_field_scale' => + array ( + 'return' => 'int', + 'params' => 'resource stmt, int col', + 'description' => 'Tell the scale of a column', + ), + 'oci_field_precision' => + array ( + 'return' => 'int', + 'params' => 'resource stmt, int col', + 'description' => 'Tell the precision of a column', + ), + 'oci_field_type' => + array ( + 'return' => 'mixed', + 'params' => 'resource stmt, int col', + 'description' => 'Tell the data type of a column', + ), + 'oci_field_type_raw' => + array ( + 'return' => 'int', + 'params' => 'resource stmt, int col', + 'description' => 'Tell the raw oracle data type of a column', + ), + 'oci_field_is_null' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt, int col', + 'description' => 'Tell whether a column is NULL', + ), + 'oci_internal_debug' => + array ( + 'return' => 'void', + 'params' => 'int onoff', + 'description' => 'Toggle internal debugging output for the OCI extension', + ), + 'oci_execute' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt [, int mode]', + 'description' => 'Execute a parsed statement', + ), + 'oci_cancel' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt', + 'description' => 'Cancel reading from a cursor', + ), + 'oci_fetch' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt', + 'description' => 'Prepare a new row of data for reading', + ), + 'ocifetchinto' => + array ( + 'return' => 'int', + 'params' => 'resource stmt, array &output [, int mode]', + 'description' => 'Fetch a row of result data into an array', + ), + 'oci_fetch_all' => + array ( + 'return' => 'int', + 'params' => 'resource stmt, array &output[, int skip[, int maxrows[, int flags]]]', + 'description' => 'Fetch all rows of result data into an array', + ), + 'oci_fetch_object' => + array ( + 'return' => 'object', + 'params' => ' resource stmt ', + 'description' => 'Fetch a result row as an object', + ), + 'oci_fetch_row' => + array ( + 'return' => 'array', + 'params' => ' resource stmt ', + 'description' => 'Fetch a result row as an enumerated array', + ), + 'oci_fetch_assoc' => + array ( + 'return' => 'array', + 'params' => ' resource stmt ', + 'description' => 'Fetch a result row as an associative array', + ), + 'oci_fetch_array' => + array ( + 'return' => 'array', + 'params' => ' resource stmt [, int mode ]', + 'description' => 'Fetch a result row as an array', + ), + 'oci_free_statement' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt', + 'description' => 'Free all resources associated with a statement', + ), + 'oci_close' => + array ( + 'return' => 'bool', + 'params' => 'resource connection', + 'description' => 'Disconnect from database', + ), + 'oci_new_connect' => + array ( + 'return' => 'resource', + 'params' => 'string user, string pass [, string db]', + 'description' => 'Connect to an Oracle database and log on. Returns a new session.', + ), + 'oci_connect' => + array ( + 'return' => 'resource', + 'params' => 'string user, string pass [, string db [, string charset [, int session_mode ]]', + 'description' => 'Connect to an Oracle database and log on. Returns a new session.', + ), + 'oci_pconnect' => + array ( + 'return' => 'resource', + 'params' => 'string user, string pass [, string db [, string charset ]]', + 'description' => 'Connect to an Oracle database using a persistent connection and log on. Returns a new session.', + ), + 'oci_error' => + array ( + 'return' => 'array', + 'params' => '[resource stmt|connection|global]', + 'description' => 'Return the last error of stmt|connection|global. If no error happened returns false.', + ), + 'oci_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource stmt', + 'description' => 'Return the number of result columns in a statement', + ), + 'oci_parse' => + array ( + 'return' => 'resource', + 'params' => 'resource connection, string query', + 'description' => 'Parse a query and return a statement', + ), + 'oci_set_prefetch' => + array ( + 'return' => 'bool', + 'params' => 'resource stmt, int prefetch_rows', + 'description' => 'Sets the number of rows to be prefetched on execute to prefetch_rows for stmt', + ), + 'oci_password_change' => + array ( + 'return' => 'bool', + 'params' => 'resource connection, string username, string old_password, string new_password', + 'description' => 'Changes the password of an account', + ), + 'oci_new_cursor' => + array ( + 'return' => 'resource', + 'params' => 'resource connection', + 'description' => 'Return a new cursor (Statement-Handle) - use this to bind ref-cursors!', + ), + 'oci_result' => + array ( + 'return' => 'string', + 'params' => 'resource stmt, mixed column', + 'description' => 'Return a single column of result data', + ), + 'oci_server_version' => + array ( + 'return' => 'string', + 'params' => 'resource connection', + 'description' => 'Return a string containing server version information', + ), + 'oci_statement_type' => + array ( + 'return' => 'string', + 'params' => 'resource stmt', + 'description' => 'Return the query type of an OCI statement', + ), + 'oci_num_rows' => + array ( + 'return' => 'int', + 'params' => 'resource stmt', + 'description' => 'Return the row count of an OCI statement', + ), + 'oci_free_collection' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Deletes collection object', + ), + 'oci_collection_append' => + array ( + 'return' => 'bool', + 'params' => 'string value', + 'description' => 'Append an object to the collection', + ), + 'oci_collection_element_get' => + array ( + 'return' => 'string', + 'params' => 'int ndx', + 'description' => 'Retrieve the value at collection index ndx', + ), + 'oci_collection_assign' => + array ( + 'return' => 'bool', + 'params' => 'object from', + 'description' => 'Assign a collection from another existing collection', + ), + 'oci_collection_element_assign' => + array ( + 'return' => 'bool', + 'params' => 'int index, string val', + 'description' => 'Assign element val to collection at index ndx', + ), + 'oci_collection_size' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Return the size of a collection', + ), + 'oci_collection_max' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Return the max value of a collection. For a varray this is the maximum length of the array', + ), + 'oci_collection_trim' => + array ( + 'return' => 'bool', + 'params' => 'int num', + 'description' => 'Trim num elements from the end of a collection', + ), + 'oci_new_collection' => + array ( + 'return' => 'object', + 'params' => 'resource connection, string tdo [, string schema]', + 'description' => 'Initialize a new collection', + ), + 'mb_language' => + array ( + 'return' => 'string', + 'params' => '[string language]', + 'description' => 'Sets the current language or Returns the current language as a string', + ), + 'mb_internal_encoding' => + array ( + 'return' => 'string', + 'params' => '[string encoding]', + 'description' => 'Sets the current internal encoding or Returns the current internal encoding as a string', + ), + 'mb_http_input' => + array ( + 'return' => 'mixed', + 'params' => '[string type]', + 'description' => 'Returns the input encoding', + ), + 'mb_http_output' => + array ( + 'return' => 'string', + 'params' => '[string encoding]', + 'description' => 'Sets the current output_encoding or returns the current output_encoding as a string', + ), + 'mb_detect_order' => + array ( + 'return' => 'bool|array', + 'params' => '[mixed encoding-list]', + 'description' => 'Sets the current detect_order or Return the current detect_order as a array', + ), + 'mb_substitute_character' => + array ( + 'return' => 'mixed', + 'params' => '[mixed substchar]', + 'description' => 'Sets the current substitute_character or returns the current substitute_character', + ), + 'mb_preferred_mime_name' => + array ( + 'return' => 'string', + 'params' => 'string encoding', + 'description' => 'Return the preferred MIME name (charset) as a string', + ), + 'mb_parse_str' => + array ( + 'return' => 'bool', + 'params' => 'string encoded_string [, array result]', + 'description' => 'Parses GET/POST/COOKIE data and sets global variables', + ), + 'mb_output_handler' => + array ( + 'return' => 'string', + 'params' => 'string contents, int status', + 'description' => 'Returns string in output buffer converted to the http_output encoding', + ), + 'mb_strlen' => + array ( + 'return' => 'int', + 'params' => 'string str [, string encoding]', + 'description' => 'Get character numbers of a string', + ), + 'mb_strpos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, int offset [, string encoding]]', + 'description' => 'Find position of first occurrence of a string within another', + ), + 'mb_strrpos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, string encoding]', + 'description' => 'Find the last occurrence of a character in a string within another', + ), + 'mb_substr_count' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, string encoding]', + 'description' => 'Count the number of substring occurrences', + ), + 'mb_substr' => + array ( + 'return' => 'string', + 'params' => 'string str, int start [, int length [, string encoding]]', + 'description' => 'Returns part of a string', + ), + 'mb_strcut' => + array ( + 'return' => 'string', + 'params' => 'string str, int start [, int length [, string encoding]]', + 'description' => 'Returns part of a string', + ), + 'mb_strwidth' => + array ( + 'return' => 'int', + 'params' => 'string str [, string encoding]', + 'description' => 'Gets terminal width of a string', + ), + 'mb_strimwidth' => + array ( + 'return' => 'string', + 'params' => 'string str, int start, int width [, string trimmarker [, string encoding]]', + 'description' => 'Trim the string in terminal width', + ), + 'mb_convert_encoding' => + array ( + 'return' => 'string', + 'params' => 'string str, string to-encoding [, mixed from-encoding]', + 'description' => 'Returns converted string in desired encoding', + ), + 'mb_convert_case' => + array ( + 'return' => 'string', + 'params' => 'string sourcestring, int mode [, string encoding]', + 'description' => 'Returns a case-folded version of sourcestring', + ), + 'mb_strtoupper' => + array ( + 'return' => 'string', + 'params' => 'string sourcestring [, string encoding]', + 'description' => '* Returns a uppercased version of sourcestring', + ), + 'mb_strtolower' => + array ( + 'return' => 'string', + 'params' => 'string sourcestring [, string encoding]', + 'description' => '* Returns a lowercased version of sourcestring', + ), + 'mb_detect_encoding' => + array ( + 'return' => 'string', + 'params' => 'string str [, mixed encoding_list [, bool strict]]', + 'description' => 'Encodings of the given string is returned (as a string)', + ), + 'mb_list_encodings' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Returns an array of all supported encodings', + ), + 'mb_encode_mimeheader' => + array ( + 'return' => 'string', + 'params' => 'string str [, string charset [, string transfer-encoding [, string linefeed [, int indent]]]]', + 'description' => 'Converts the string to MIME "encoded-word" in the format of =?charset?(B|Q)?encoded_string?=', + ), + 'mb_decode_mimeheader' => + array ( + 'return' => 'string', + 'params' => 'string string', + 'description' => 'Decodes the MIME "encoded-word" in the string', + ), + 'mb_convert_kana' => + array ( + 'return' => 'string', + 'params' => 'string str [, string option] [, string encoding]', + 'description' => 'Conversion between full-width character and half-width character (Japanese)', + ), + 'mb_convert_variables' => + array ( + 'return' => 'string', + 'params' => 'string to-encoding, mixed from-encoding [, mixed ...]', + 'description' => 'Converts the string resource in variables to desired encoding', + ), + 'mb_encode_numericentity' => + array ( + 'return' => 'string', + 'params' => 'string string, array convmap [, string encoding]', + 'description' => 'Converts specified characters to HTML numeric entities', + ), + 'mb_decode_numericentity' => + array ( + 'return' => 'string', + 'params' => 'string string, array convmap [, string encoding]', + 'description' => 'Converts HTML numeric entities to character code', + ), + 'mb_send_mail' => + array ( + 'return' => 'int', + 'params' => 'string to, string subject, string message [, string additional_headers [, string additional_parameters]]', + 'description' => '* Sends an email message with MIME scheme', + ), + 'mb_get_info' => + array ( + 'return' => 'mixed', + 'params' => '[string type]', + 'description' => 'Returns the current settings of mbstring', + ), + 'mb_check_encoding' => + array ( + 'return' => 'bool', + 'params' => '[string var[, string encoding]]', + 'description' => 'Check if the string is valid for the specified encoding', + ), + 'mb_regex_encoding' => + array ( + 'return' => 'string', + 'params' => '[string encoding]', + 'description' => 'Returns the current encoding for regex as a string.', + ), + 'mb_ereg' => + array ( + 'return' => 'int', + 'params' => 'string pattern, string string [, array registers]', + 'description' => 'Regular expression match for multibyte string', + ), + 'mb_eregi' => + array ( + 'return' => 'int', + 'params' => 'string pattern, string string [, array registers]', + 'description' => 'Case-insensitive regular expression match for multibyte string', + ), + 'mb_ereg_replace' => + array ( + 'return' => 'string', + 'params' => 'string pattern, string replacement, string string [, string option]', + 'description' => 'Replace regular expression for multibyte string', + ), + 'mb_eregi_replace' => + array ( + 'return' => 'string', + 'params' => 'string pattern, string replacement, string string', + 'description' => 'Case insensitive replace regular expression for multibyte string', + ), + 'mb_split' => + array ( + 'return' => 'array', + 'params' => 'string pattern, string string [, int limit]', + 'description' => 'split multibyte string into array by regular expression', + ), + 'mb_ereg_match' => + array ( + 'return' => 'bool', + 'params' => 'string pattern, string string [,string option]', + 'description' => 'Regular expression match for multibyte string', + ), + 'mb_ereg_search' => + array ( + 'return' => 'bool', + 'params' => '[string pattern[, string option]]', + 'description' => 'Regular expression search for multibyte string', + ), + 'mb_ereg_search_pos' => + array ( + 'return' => 'array', + 'params' => '[string pattern[, string option]]', + 'description' => 'Regular expression search for multibyte string', + ), + 'mb_ereg_search_regs' => + array ( + 'return' => 'array', + 'params' => '[string pattern[, string option]]', + 'description' => 'Regular expression search for multibyte string', + ), + 'mb_ereg_search_init' => + array ( + 'return' => 'bool', + 'params' => 'string string [, string pattern[, string option]]', + 'description' => 'Initialize string and regular expression for search.', + ), + 'mb_ereg_search_getregs' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get matched substring of the last time', + ), + 'mb_ereg_search_getpos' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get search start position', + ), + 'mb_ereg_search_setpos' => + array ( + 'return' => 'bool', + 'params' => 'int position', + 'description' => 'Set search start position', + ), + 'mb_regex_set_options' => + array ( + 'return' => 'string', + 'params' => '[string options]', + 'description' => 'Set or get the default options for mbregex functions', + ), + 'preg_match' => + array ( + 'return' => 'int', + 'params' => 'string pattern, string subject [, array subpatterns [, int flags [, int offset]]]', + 'description' => 'Perform a Perl-style regular expression match', + ), + 'preg_match_all' => + array ( + 'return' => 'int', + 'params' => 'string pattern, string subject, array subpatterns [, int flags [, int offset]]', + 'description' => 'Perform a Perl-style global regular expression match', + ), + 'preg_replace' => + array ( + 'return' => 'string', + 'params' => 'mixed regex, mixed replace, mixed subject [, int limit [, count]]', + 'description' => 'Perform Perl-style regular expression replacement.', + ), + 'preg_replace_callback' => + array ( + 'return' => 'string', + 'params' => 'mixed regex, mixed callback, mixed subject [, int limit [, count]]', + 'description' => 'Perform Perl-style regular expression replacement using replacement callback.', + ), + 'preg_split' => + array ( + 'return' => 'array', + 'params' => 'string pattern, string subject [, int limit [, int flags]]', + 'description' => 'Split string into an array using a perl-style regular expression as a delimiter', + ), + 'preg_quote' => + array ( + 'return' => 'string', + 'params' => 'string str, string delim_char', + 'description' => 'Quote regular expression characters plus an optional character', + ), + 'preg_grep' => + array ( + 'return' => 'array', + 'params' => 'string regex, array input', + 'description' => 'Searches array and returns entries which match regex', + ), + 'pcntl_fork' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Forks the currently running process following the same behavior as the UNIX fork() system call', + ), + 'pcntl_alarm' => + array ( + 'return' => 'int', + 'params' => 'int seconds', + 'description' => 'Set an alarm clock for delivery of a signal', + ), + 'pcntl_waitpid' => + array ( + 'return' => 'int', + 'params' => 'int pid, int &status, int options', + 'description' => 'Waits on or returns the status of a forked child as defined by the waitpid() system call', + ), + 'pcntl_wait' => + array ( + 'return' => 'int', + 'params' => 'int &status', + 'description' => 'Waits on or returns the status of a forked child as defined by the waitpid() system call', + ), + 'pcntl_wifexited' => + array ( + 'return' => 'bool', + 'params' => 'int status', + 'description' => 'Returns true if the child status code represents a successful exit', + ), + 'pcntl_wifstopped' => + array ( + 'return' => 'bool', + 'params' => 'int status', + 'description' => 'Returns true if the child status code represents a stopped process (WUNTRACED must have been used with waitpid)', + ), + 'pcntl_wifsignaled' => + array ( + 'return' => 'bool', + 'params' => 'int status', + 'description' => 'Returns true if the child status code represents a process that was terminated due to a signal', + ), + 'pcntl_wexitstatus' => + array ( + 'return' => 'int', + 'params' => 'int status', + 'description' => 'Returns the status code of a child\'s exit', + ), + 'pcntl_wtermsig' => + array ( + 'return' => 'int', + 'params' => 'int status', + 'description' => 'Returns the number of the signal that terminated the process who\'s status code is passed', + ), + 'pcntl_wstopsig' => + array ( + 'return' => 'int', + 'params' => 'int status', + 'description' => 'Returns the number of the signal that caused the process to stop who\'s status code is passed', + ), + 'pcntl_exec' => + array ( + 'return' => 'bool', + 'params' => 'string path [, array args [, array envs]]', + 'description' => 'Executes specified program in current process space as defined by exec(2)', + ), + 'pcntl_signal' => + array ( + 'return' => 'bool', + 'params' => 'int signo, callback handle [, bool restart_syscalls]', + 'description' => 'Assigns a system signal handler to a PHP function', + ), + 'pcntl_getpriority' => + array ( + 'return' => 'int', + 'params' => '[int pid [, int process_identifier]]', + 'description' => 'Get the priority of any process', + ), + 'pcntl_setpriority' => + array ( + 'return' => 'bool', + 'params' => 'int priority [, int pid [, int process_identifier]]', + 'description' => 'Change the priority of any process', + ), + 'mcrypt_module_open' => + array ( + 'return' => 'resource', + 'params' => 'string cipher, string cipher_directory, string mode, string mode_directory', + 'description' => 'Opens the module of the algorithm and the mode to be used', + ), + 'mcrypt_generic_init' => + array ( + 'return' => 'int', + 'params' => 'resource td, string key, string iv', + 'description' => 'This function initializes all buffers for the specific module', + ), + 'mcrypt_generic' => + array ( + 'return' => 'string', + 'params' => 'resource td, string data', + 'description' => 'This function encrypts the plaintext', + ), + 'mdecrypt_generic' => + array ( + 'return' => 'string', + 'params' => 'resource td, string data', + 'description' => 'This function decrypts the plaintext', + ), + 'mcrypt_enc_get_supported_key_sizes' => + array ( + 'return' => 'array', + 'params' => 'resource td', + 'description' => 'This function decrypts the crypttext', + ), + 'mcrypt_enc_self_test' => + array ( + 'return' => 'int', + 'params' => 'resource td', + 'description' => 'This function runs the self test on the algorithm specified by the descriptor td', + ), + 'mcrypt_module_close' => + array ( + 'return' => 'bool', + 'params' => 'resource td', + 'description' => 'Free the descriptor td', + ), + 'mcrypt_generic_end' => + array ( + 'return' => 'bool', + 'params' => 'resource td', + 'description' => 'This function terminates encrypt specified by the descriptor td', + ), + 'mcrypt_generic_deinit' => + array ( + 'return' => 'bool', + 'params' => 'resource td', + 'description' => 'This function terminates encrypt specified by the descriptor td', + ), + 'mcrypt_enc_is_block_algorithm_mode' => + array ( + 'return' => 'bool', + 'params' => 'resource td', + 'description' => 'Returns TRUE if the mode is for use with block algorithms', + ), + 'mcrypt_enc_is_block_algorithm' => + array ( + 'return' => 'bool', + 'params' => 'resource td', + 'description' => 'Returns TRUE if the alrogithm is a block algorithms', + ), + 'mcrypt_enc_is_block_mode' => + array ( + 'return' => 'bool', + 'params' => 'resource td', + 'description' => 'Returns TRUE if the mode outputs blocks', + ), + 'mcrypt_enc_get_block_size' => + array ( + 'return' => 'int', + 'params' => 'resource td', + 'description' => 'Returns the block size of the cipher specified by the descriptor td', + ), + 'mcrypt_enc_get_key_size' => + array ( + 'return' => 'int', + 'params' => 'resource td', + 'description' => 'Returns the maximum supported key size in bytes of the algorithm specified by the descriptor td', + ), + 'mcrypt_enc_get_iv_size' => + array ( + 'return' => 'int', + 'params' => 'resource td', + 'description' => 'Returns the size of the IV in bytes of the algorithm specified by the descriptor td', + ), + 'mcrypt_enc_get_algorithms_name' => + array ( + 'return' => 'string', + 'params' => 'resource td', + 'description' => 'Returns the name of the algorithm specified by the descriptor td', + ), + 'mcrypt_enc_get_modes_name' => + array ( + 'return' => 'string', + 'params' => 'resource td', + 'description' => 'Returns the name of the mode specified by the descriptor td', + ), + 'mcrypt_module_self_test' => + array ( + 'return' => 'bool', + 'params' => 'string algorithm [, string lib_dir]', + 'description' => 'Does a self test of the module "module"', + ), + 'mcrypt_module_is_block_algorithm_mode' => + array ( + 'return' => 'bool', + 'params' => 'string mode [, string lib_dir]', + 'description' => 'Returns TRUE if the mode is for use with block algorithms', + ), + 'mcrypt_module_is_block_algorithm' => + array ( + 'return' => 'bool', + 'params' => 'string algorithm [, string lib_dir]', + 'description' => 'Returns TRUE if the algorithm is a block algorithm', + ), + 'mcrypt_module_is_block_mode' => + array ( + 'return' => 'bool', + 'params' => 'string mode [, string lib_dir]', + 'description' => 'Returns TRUE if the mode outputs blocks of bytes', + ), + 'mcrypt_module_get_algo_block_size' => + array ( + 'return' => 'int', + 'params' => 'string algorithm [, string lib_dir]', + 'description' => 'Returns the block size of the algorithm', + ), + 'mcrypt_module_get_algo_key_size' => + array ( + 'return' => 'int', + 'params' => 'string algorithm [, string lib_dir]', + 'description' => 'Returns the maximum supported key size of the algorithm', + ), + 'mcrypt_module_get_supported_key_sizes' => + array ( + 'return' => 'array', + 'params' => 'string algorithm [, string lib_dir]', + 'description' => 'This function decrypts the crypttext', + ), + 'mcrypt_list_algorithms' => + array ( + 'return' => 'array', + 'params' => '[string lib_dir]', + 'description' => 'List all algorithms in "module_dir"', + ), + 'mcrypt_list_modes' => + array ( + 'return' => 'array', + 'params' => '[string lib_dir]', + 'description' => 'List all modes "module_dir"', + ), + 'mcrypt_get_key_size' => + array ( + 'return' => 'int', + 'params' => 'string cipher, string module', + 'description' => 'Get the key size of cipher', + ), + 'mcrypt_get_block_size' => + array ( + 'return' => 'int', + 'params' => 'string cipher, string module', + 'description' => 'Get the key size of cipher', + ), + 'mcrypt_get_iv_size' => + array ( + 'return' => 'int', + 'params' => 'string cipher, string module', + 'description' => 'Get the IV size of cipher (Usually the same as the blocksize)', + ), + 'mcrypt_get_cipher_name' => + array ( + 'return' => 'string', + 'params' => 'string cipher', + 'description' => 'Get the key size of cipher', + ), + 'mcrypt_encrypt' => + array ( + 'return' => 'string', + 'params' => 'string cipher, string key, string data, string mode, string iv', + 'description' => 'OFB crypt/decrypt data using key key with cipher cipher starting with iv', + ), + 'mcrypt_decrypt' => + array ( + 'return' => 'string', + 'params' => 'string cipher, string key, string data, string mode, string iv', + 'description' => 'OFB crypt/decrypt data using key key with cipher cipher starting with iv', + ), + 'mcrypt_ecb' => + array ( + 'return' => 'string', + 'params' => 'int cipher, string key, string data, int mode, string iv', + 'description' => 'ECB crypt/decrypt data using key key with cipher cipher starting with iv', + ), + 'mcrypt_cbc' => + array ( + 'return' => 'string', + 'params' => 'int cipher, string key, string data, int mode, string iv', + 'description' => 'CBC crypt/decrypt data using key key with cipher cipher starting with iv', + ), + 'mcrypt_cfb' => + array ( + 'return' => 'string', + 'params' => 'int cipher, string key, string data, int mode, string iv', + 'description' => 'CFB crypt/decrypt data using key key with cipher cipher starting with iv', + ), + 'mcrypt_ofb' => + array ( + 'return' => 'string', + 'params' => 'int cipher, string key, string data, int mode, string iv', + 'description' => 'OFB crypt/decrypt data using key key with cipher cipher starting with iv', + ), + 'mcrypt_create_iv' => + array ( + 'return' => 'string', + 'params' => 'int size, int source', + 'description' => 'Create an initialization vector (IV)', + ), + 'readline' => + array ( + 'return' => 'string', + 'params' => '[string prompt]', + 'description' => 'Reads a line', + ), + 'readline_info' => + array ( + 'return' => 'mixed', + 'params' => '[string varname] [, string newvalue]', + 'description' => 'Gets/sets various internal readline variables.', + ), + 'readline_add_history' => + array ( + 'return' => 'bool', + 'params' => '[string prompt]', + 'description' => 'Adds a line to the history', + ), + 'readline_clear_history' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Clears the history', + ), + 'readline_list_history' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Lists the history', + ), + 'readline_read_history' => + array ( + 'return' => 'bool', + 'params' => '[string filename] [, int from] [,int to]', + 'description' => 'Reads the history', + ), + 'readline_write_history' => + array ( + 'return' => 'bool', + 'params' => '[string filename]', + 'description' => 'Writes the history', + ), + 'readline_completion_function' => + array ( + 'return' => 'bool', + 'params' => 'string funcname', + 'description' => 'Readline completion function?', + ), + 'readline_callback_handler_install' => + array ( + 'return' => 'void', + 'params' => 'string prompt, mixed callback', + 'description' => 'Initializes the readline callback interface and terminal, prints the prompt and returns immediately', + ), + 'readline_callback_read_char' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Informs the readline callback interface that a character is ready for input', + ), + 'readline_callback_handler_remove' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Removes a previously installed callback handler and restores terminal settings', + ), + 'readline_redisplay' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Ask readline to redraw the display', + ), + 'readline_on_new_line' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Inform readline that the cursor has moved to a new line', + ), + 'dbase_open' => + array ( + 'return' => 'int', + 'params' => 'string name, int mode', + 'description' => 'Opens a dBase-format database file', + ), + 'dbase_close' => + array ( + 'return' => 'bool', + 'params' => 'int identifier', + 'description' => 'Closes an open dBase-format database file', + ), + 'dbase_numrecords' => + array ( + 'return' => 'int', + 'params' => 'int identifier', + 'description' => 'Returns the number of records in the database', + ), + 'dbase_numfields' => + array ( + 'return' => 'int', + 'params' => 'int identifier', + 'description' => 'Returns the number of fields (columns) in the database', + ), + 'dbase_pack' => + array ( + 'return' => 'bool', + 'params' => 'int identifier', + 'description' => 'Packs the database (deletes records marked for deletion)', + ), + 'dbase_add_record' => + array ( + 'return' => 'bool', + 'params' => 'int identifier, array data', + 'description' => 'Adds a record to the database', + ), + 'dbase_replace_record' => + array ( + 'return' => 'bool', + 'params' => 'int identifier, array data, int recnum', + 'description' => 'Replaces a record to the database', + ), + 'dbase_delete_record' => + array ( + 'return' => 'bool', + 'params' => 'int identifier, int record', + 'description' => 'Marks a record to be deleted', + ), + 'dbase_get_record' => + array ( + 'return' => 'array', + 'params' => 'int identifier, int record', + 'description' => 'Returns an array representing a record from the database', + ), + 'dbase_get_record_with_names' => + array ( + 'return' => 'array', + 'params' => 'int identifier, int record', + 'description' => 'Returns an associative array representing a record from the database', + ), + 'dbase_create' => + array ( + 'return' => 'bool', + 'params' => 'string filename, array fields', + 'description' => 'Creates a new dBase-format database file', + ), + 'dbase_get_header_info' => + array ( + 'return' => 'array', + 'params' => 'int database_handle', + 'description' => '', + ), + 'ibase_add_user' => + array ( + 'return' => 'bool', + 'params' => 'resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]]', + 'description' => 'Add a user to security database', + ), + 'ibase_modify_user' => + array ( + 'return' => 'bool', + 'params' => 'resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]]', + 'description' => 'Modify a user in security database', + ), + 'ibase_delete_user' => + array ( + 'return' => 'bool', + 'params' => 'resource service_handle, string user_name, string password [, string first_name [, string middle_name [, string last_name]]]', + 'description' => 'Delete a user from security database', + ), + 'ibase_service_attach' => + array ( + 'return' => 'resource', + 'params' => 'string host, string dba_username, string dba_password', + 'description' => 'Connect to the service manager', + ), + 'ibase_service_detach' => + array ( + 'return' => 'bool', + 'params' => 'resource service_handle', + 'description' => 'Disconnect from the service manager', + ), + 'ibase_backup' => + array ( + 'return' => 'mixed', + 'params' => 'resource service_handle, string source_db, string dest_file [, int options [, bool verbose]]', + 'description' => 'Initiates a backup task in the service manager and returns immediately', + ), + 'ibase_restore' => + array ( + 'return' => 'mixed', + 'params' => 'resource service_handle, string source_file, string dest_db [, int options [, bool verbose]]', + 'description' => 'Initiates a restore task in the service manager and returns immediately', + ), + 'ibase_maintain_db' => + array ( + 'return' => 'bool', + 'params' => 'resource service_handle, string db, int action [, int argument]', + 'description' => 'Execute a maintenance command on the database server', + ), + 'ibase_db_info' => + array ( + 'return' => 'string', + 'params' => 'resource service_handle, string db, int action [, int argument]', + 'description' => 'Request statistics about a database', + ), + 'ibase_server_info' => + array ( + 'return' => 'string', + 'params' => 'resource service_handle, int action', + 'description' => 'Request information about a database server', + ), + 'ibase_errmsg' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return error message', + ), + 'ibase_errcode' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Return error code', + ), + 'ibase_connect' => + array ( + 'return' => 'resource', + 'params' => 'string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]]', + 'description' => 'Open a connection to an InterBase database', + ), + 'ibase_pconnect' => + array ( + 'return' => 'resource', + 'params' => 'string database [, string username [, string password [, string charset [, int buffers [, int dialect [, string role]]]]]]', + 'description' => 'Open a persistent connection to an InterBase database', + ), + 'ibase_close' => + array ( + 'return' => 'bool', + 'params' => '[resource link_identifier]', + 'description' => 'Close an InterBase connection', + ), + 'ibase_drop_db' => + array ( + 'return' => 'bool', + 'params' => '[resource link_identifier]', + 'description' => 'Drop an InterBase database', + ), + 'ibase_trans' => + array ( + 'return' => 'resource', + 'params' => '[int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]]', + 'description' => 'Start a transaction over one or several databases', + ), + 'ibase_commit' => + array ( + 'return' => 'bool', + 'params' => ' resource link_identifier ', + 'description' => 'Commit transaction', + ), + 'ibase_rollback' => + array ( + 'return' => 'bool', + 'params' => ' resource link_identifier ', + 'description' => 'Rollback transaction', + ), + 'ibase_commit_ret' => + array ( + 'return' => 'bool', + 'params' => ' resource link_identifier ', + 'description' => 'Commit transaction and retain the transaction context', + ), + 'ibase_rollback_ret' => + array ( + 'return' => 'bool', + 'params' => ' resource link_identifier ', + 'description' => 'Rollback transaction and retain the transaction context', + ), + 'ibase_gen_id' => + array ( + 'return' => 'int', + 'params' => 'string generator [, int increment [, resource link_identifier ]]', + 'description' => 'Increments the named generator and returns its new value', + ), + 'ibase_blob_create' => + array ( + 'return' => 'resource', + 'params' => '[resource link_identifier]', + 'description' => 'Create blob for adding data', + ), + 'ibase_blob_open' => + array ( + 'return' => 'resource', + 'params' => '[ resource link_identifier, ] string blob_id', + 'description' => 'Open blob for retrieving data parts', + ), + 'ibase_blob_add' => + array ( + 'return' => 'bool', + 'params' => 'resource blob_handle, string data', + 'description' => 'Add data into created blob', + ), + 'ibase_blob_get' => + array ( + 'return' => 'string', + 'params' => 'resource blob_handle, int len', + 'description' => 'Get len bytes data from open blob', + ), + 'ibase_blob_close' => + array ( + 'return' => 'string', + 'params' => 'resource blob_handle', + 'description' => 'Close blob', + ), + 'ibase_blob_cancel' => + array ( + 'return' => 'bool', + 'params' => 'resource blob_handle', + 'description' => 'Cancel creating blob', + ), + 'ibase_blob_info' => + array ( + 'return' => 'array', + 'params' => '[ resource link_identifier, ] string blob_id', + 'description' => 'Return blob length and other useful info', + ), + 'ibase_blob_echo' => + array ( + 'return' => 'bool', + 'params' => '[ resource link_identifier, ] string blob_id', + 'description' => 'Output blob contents to browser', + ), + 'ibase_blob_import' => + array ( + 'return' => 'string', + 'params' => '[ resource link_identifier, ] resource file', + 'description' => 'Create blob, copy file in it, and close it', + ), + 'ibase_query' => + array ( + 'return' => 'mixed', + 'params' => '[resource link_identifier, [ resource link_identifier, ]] string query [, mixed bind_arg [, mixed bind_arg [, ...]]]', + 'description' => 'Execute a query', + ), + 'ibase_affected_rows' => + array ( + 'return' => 'int', + 'params' => ' [ resource link_identifier ] ', + 'description' => 'Returns the number of rows affected by the previous INSERT, UPDATE or DELETE statement', + ), + 'ibase_num_rows' => + array ( + 'return' => 'int', + 'params' => ' resource result_identifier ', + 'description' => 'Return the number of rows that are available in a result', + ), + 'ibase_fetch_row' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int fetch_flags]', + 'description' => 'Fetch a row from the results of a query', + ), + 'ibase_fetch_assoc' => + array ( + 'return' => 'array', + 'params' => 'resource result [, int fetch_flags]', + 'description' => 'Fetch a row from the results of a query', + ), + 'ibase_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'resource result [, int fetch_flags]', + 'description' => 'Fetch a object from the results of a query', + ), + 'ibase_name_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result, string name', + 'description' => 'Assign a name to a result for use with ... WHERE CURRENT OF statements', + ), + 'ibase_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result', + 'description' => 'Free the memory used by a result', + ), + 'ibase_prepare' => + array ( + 'return' => 'resource', + 'params' => '[resource link_identifier, ] string query', + 'description' => 'Prepare a query for later execution', + ), + 'ibase_execute' => + array ( + 'return' => 'mixed', + 'params' => 'resource query [, mixed bind_arg [, mixed bind_arg [, ...]]]', + 'description' => 'Execute a previously prepared query', + ), + 'ibase_free_query' => + array ( + 'return' => 'bool', + 'params' => 'resource query', + 'description' => 'Free memory used by a query', + ), + 'ibase_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource query_result', + 'description' => 'Get the number of fields in result', + ), + 'ibase_field_info' => + array ( + 'return' => 'array', + 'params' => 'resource query_result, int field_number', + 'description' => 'Get information about a field', + ), + 'ibase_num_params' => + array ( + 'return' => 'int', + 'params' => 'resource query', + 'description' => 'Get the number of params in a prepared query', + ), + 'ibase_param_info' => + array ( + 'return' => 'array', + 'params' => 'resource query, int field_number', + 'description' => 'Get information about a parameter', + ), + 'ibase_wait_event' => + array ( + 'return' => 'string', + 'params' => '[resource link_identifier,] string event [, string event [, ...]]', + 'description' => 'Waits for any one of the passed Interbase events to be posted by the database, and returns its name', + ), + 'ibase_set_event_handler' => + array ( + 'return' => 'resource', + 'params' => '[resource link_identifier,] callback handler, string event [, string event [, ...]]', + 'description' => 'Register the callback for handling each of the named events', + ), + 'ibase_free_event_handler' => + array ( + 'return' => 'bool', + 'params' => 'resource event', + 'description' => 'Frees the event handler set by ibase_set_event_handler()', + ), + 'openssl_x509_export_to_file' => + array ( + 'return' => 'bool', + 'params' => 'mixed x509, string outfilename [, bool notext = true]', + 'description' => 'Exports a CERT to file or a var', + ), + 'openssl_x509_export' => + array ( + 'return' => 'bool', + 'params' => 'mixed x509, string &out [, bool notext = true]', + 'description' => 'Exports a CERT to file or a var', + ), + 'openssl_x509_check_private_key' => + array ( + 'return' => 'bool', + 'params' => 'mixed cert, mixed key', + 'description' => 'Checks if a private key corresponds to a CERT', + ), + 'openssl_x509_parse' => + array ( + 'return' => 'array', + 'params' => 'mixed x509 [, bool shortnames=true]', + 'description' => 'Returns an array of the fields/values of the CERT', + ), + 'openssl_x509_checkpurpose' => + array ( + 'return' => 'int', + 'params' => 'mixed x509cert, int purpose, array cainfo [, string untrustedfile]', + 'description' => 'Checks the CERT to see if it can be used for the purpose in purpose. cainfo holds information about trusted CAs', + ), + 'openssl_x509_read' => + array ( + 'return' => 'resource', + 'params' => 'mixed cert', + 'description' => 'Reads X.509 certificates', + ), + 'openssl_x509_free' => + array ( + 'return' => 'void', + 'params' => 'resource x509', + 'description' => 'Frees X.509 certificates', + ), + 'openssl_csr_export_to_file' => + array ( + 'return' => 'bool', + 'params' => 'resource csr, string outfilename [, bool notext=true]', + 'description' => 'Exports a CSR to file', + ), + 'openssl_csr_export' => + array ( + 'return' => 'bool', + 'params' => 'resource csr, string &out [, bool notext=true]', + 'description' => 'Exports a CSR to file or a var', + ), + 'openssl_csr_sign' => + array ( + 'return' => 'resource', + 'params' => 'mixed csr, mixed x509, mixed priv_key, long days [, array config_args [, long serial]]', + 'description' => 'Signs a cert with another CERT', + ), + 'openssl_csr_new' => + array ( + 'return' => 'bool', + 'params' => 'array dn, resource &privkey [, array configargs, array extraattribs]', + 'description' => 'Generates a privkey and CSR', + ), + 'openssl_pkey_new' => + array ( + 'return' => 'resource', + 'params' => '[array configargs]', + 'description' => 'Generates a new private key', + ), + 'openssl_pkey_export_to_file' => + array ( + 'return' => 'bool', + 'params' => 'mixed key, string outfilename [, string passphrase, array config_args', + 'description' => 'Gets an exportable representation of a key into a file', + ), + 'openssl_pkey_export' => + array ( + 'return' => 'bool', + 'params' => 'mixed key, &mixed out [, string passphrase [, array config_args]]', + 'description' => 'Gets an exportable representation of a key into a string or file', + ), + 'openssl_pkey_get_public' => + array ( + 'return' => 'int', + 'params' => 'mixed cert', + 'description' => 'Gets public key from X.509 certificate', + ), + 'openssl_pkey_free' => + array ( + 'return' => 'void', + 'params' => 'int key', + 'description' => 'Frees a key', + ), + 'openssl_pkey_get_private' => + array ( + 'return' => 'int', + 'params' => 'string key [, string passphrase]', + 'description' => 'Gets private keys', + ), + 'openssl_pkcs7_verify' => + array ( + 'return' => 'bool', + 'params' => 'string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]]', + 'description' => 'Verifys that the data block is intact, the signer is who they say they are, and returns the CERTs of the signers', + ), + 'openssl_pkcs7_encrypt' => + array ( + 'return' => 'bool', + 'params' => 'string infile, string outfile, mixed recipcerts, array headers [, long flags [, long cipher]]', + 'description' => 'Encrypts the message in the file named infile with the certificates in recipcerts and output the result to the file named outfile', + ), + 'openssl_pkcs7_sign' => + array ( + 'return' => 'bool', + 'params' => 'string infile, string outfile, mixed signcert, mixed signkey, array headers [, long flags [, string extracertsfilename]]', + 'description' => 'Signs the MIME message in the file named infile with signcert/signkey and output the result to file name outfile. headers lists plain text headers to exclude from the signed portion of the message, and should include to, from and subject as a minimum', + ), + 'openssl_pkcs7_decrypt' => + array ( + 'return' => 'bool', + 'params' => 'string infilename, string outfilename, mixed recipcert [, mixed recipkey]', + 'description' => 'Decrypts the S/MIME message in the file name infilename and output the results to the file name outfilename. recipcert is a CERT for one of the recipients. recipkey specifies the private key matching recipcert, if recipcert does not include the key', + ), + 'openssl_private_encrypt' => + array ( + 'return' => 'bool', + 'params' => 'string data, string crypted, mixed key [, int padding]', + 'description' => 'Encrypts data with private key', + ), + 'openssl_private_decrypt' => + array ( + 'return' => 'bool', + 'params' => 'string data, string decrypted, mixed key [, int padding]', + 'description' => 'Decrypts data with private key', + ), + 'openssl_public_encrypt' => + array ( + 'return' => 'bool', + 'params' => 'string data, string crypted, mixed key [, int padding]', + 'description' => 'Encrypts data with public key', + ), + 'openssl_public_decrypt' => + array ( + 'return' => 'bool', + 'params' => 'string data, string crypted, resource key [, int padding]', + 'description' => 'Decrypts data with public key', + ), + 'openssl_error_string' => + array ( + 'return' => 'mixed', + 'params' => 'void', + 'description' => 'Returns a description of the last error, and alters the index of the error messages. Returns false when the are no more messages', + ), + 'openssl_sign' => + array ( + 'return' => 'bool', + 'params' => 'string data, &string signature, mixed key', + 'description' => 'Signs data', + ), + 'openssl_verify' => + array ( + 'return' => 'int', + 'params' => 'string data, string signature, mixed key', + 'description' => 'Verifys data', + ), + 'openssl_seal' => + array ( + 'return' => 'int', + 'params' => 'string data, &string sealdata, &array ekeys, array pubkeys', + 'description' => 'Seals data', + ), + 'openssl_open' => + array ( + 'return' => 'bool', + 'params' => 'string data, &string opendata, string ekey, mixed privkey', + 'description' => 'Opens data', + ), + 'date' => + array ( + 'return' => 'string', + 'params' => 'string format [, long timestamp]', + 'description' => 'Format a local date/time', + ), + 'gmdate' => + array ( + 'return' => 'string', + 'params' => 'string format [, long timestamp]', + 'description' => 'Format a GMT date/time', + ), + 'idate' => + array ( + 'return' => 'int', + 'params' => 'string format [, int timestamp]', + 'description' => 'Format a local time/date as integer', + ), + 'strtotime' => + array ( + 'return' => 'int', + 'params' => 'string time [, int now ]', + 'description' => 'Convert string representation of date and time to a timestamp', + ), + 'mktime' => + array ( + 'return' => 'int', + 'params' => 'int hour, int min, int sec, int mon, int day, int year', + 'description' => 'Get UNIX timestamp for a date', + ), + 'gmmktime' => + array ( + 'return' => 'int', + 'params' => 'int hour, int min, int sec, int mon, int day, int year', + 'description' => 'Get UNIX timestamp for a GMT date', + ), + 'checkdate' => + array ( + 'return' => 'bool', + 'params' => 'int month, int day, int year', + 'description' => 'Returns true(1) if it is a valid date in gregorian calendar', + ), + 'strftime' => + array ( + 'return' => 'string', + 'params' => 'string format [, int timestamp]', + 'description' => 'Format a local time/date according to locale settings', + ), + 'gmstrftime' => + array ( + 'return' => 'string', + 'params' => 'string format [, int timestamp]', + 'description' => 'Format a GMT/UCT time/date according to locale settings', + ), + 'time' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Return current UNIX timestamp', + ), + 'localtime' => + array ( + 'return' => 'array', + 'params' => '[int timestamp [, bool associative_array]]', + 'description' => 'Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array', + ), + 'getdate' => + array ( + 'return' => 'array', + 'params' => '[int timestamp]', + 'description' => 'Get date/time information', + ), + 'date_default_timezone_set' => + array ( + 'return' => 'bool', + 'params' => 'string timezone_identifier', + 'description' => 'Sets the default timezone used by all date/time functions in a script', + ), + 'date_default_timezone_get' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Gets the default timezone used by all date/time functions in a script', + ), + 'date_sunrise' => + array ( + 'return' => 'mixed', + 'params' => 'mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]]', + 'description' => 'Returns time of sunrise for a given day and location', + ), + 'date_sunset' => + array ( + 'return' => 'mixed', + 'params' => 'mixed time [, int format [, float latitude [, float longitude [, float zenith [, float gmt_offset]]]]]', + 'description' => 'Returns time of sunset for a given day and location', + ), + 'date_sun_info' => + array ( + 'return' => 'array', + 'params' => 'long time, float latitude, float longitude', + 'description' => 'Returns an array with information about sun set/rise and twilight begin/end', + ), + 'wddx_serialize_value' => + array ( + 'return' => 'string', + 'params' => 'mixed var [, string comment]', + 'description' => 'Creates a new packet and serializes the given value', + ), + 'wddx_serialize_vars' => + array ( + 'return' => 'string', + 'params' => 'mixed var_name [, mixed ...]', + 'description' => 'Creates a new packet and serializes given variables into a struct', + ), + 'wddx_packet_start' => + array ( + 'return' => 'int', + 'params' => '[string comment]', + 'description' => 'Starts a WDDX packet with optional comment and returns the packet id', + ), + 'wddx_packet_end' => + array ( + 'return' => 'string', + 'params' => 'int packet_id', + 'description' => 'Ends specified WDDX packet and returns the string containing the packet', + ), + 'wddx_add_vars' => + array ( + 'return' => 'int', + 'params' => 'int packet_id, mixed var_names [, mixed ...]', + 'description' => 'Serializes given variables and adds them to packet given by packet_id', + ), + 'wddx_deserialize' => + array ( + 'return' => 'mixed', + 'params' => 'mixed packet', + 'description' => 'Deserializes given packet and returns a PHP value', + ), + 'gmp_init' => + array ( + 'return' => 'resource', + 'params' => 'mixed number [, int base]', + 'description' => 'Initializes GMP number', + ), + 'gmp_intval' => + array ( + 'return' => 'int', + 'params' => 'resource gmpnumber', + 'description' => 'Gets signed long value of GMP number', + ), + 'gmp_strval' => + array ( + 'return' => 'string', + 'params' => 'resource gmpnumber [, int base]', + 'description' => 'Gets string representation of GMP number', + ), + 'gmp_add' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Add a and b', + ), + 'gmp_sub' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Subtract b from a', + ), + 'gmp_mul' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Multiply a and b', + ), + 'gmp_div_qr' => + array ( + 'return' => 'array', + 'params' => 'resource a, resource b [, int round]', + 'description' => 'Divide a by b, returns quotient and reminder', + ), + 'gmp_div_r' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b [, int round]', + 'description' => 'Divide a by b, returns reminder only', + ), + 'gmp_div_q' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b [, int round]', + 'description' => 'Divide a by b, returns quotient only', + ), + 'gmp_mod' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Computes a modulo b', + ), + 'gmp_divexact' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Divide a by b using exact division algorithm', + ), + 'gmp_neg' => + array ( + 'return' => 'resource', + 'params' => 'resource a', + 'description' => 'Negates a number', + ), + 'gmp_abs' => + array ( + 'return' => 'resource', + 'params' => 'resource a', + 'description' => 'Calculates absolute value', + ), + 'gmp_fact' => + array ( + 'return' => 'resource', + 'params' => 'int a', + 'description' => 'Calculates factorial function', + ), + 'gmp_pow' => + array ( + 'return' => 'resource', + 'params' => 'resource base, int exp', + 'description' => 'Raise base to power exp', + ), + 'gmp_powm' => + array ( + 'return' => 'resource', + 'params' => 'resource base, resource exp, resource mod', + 'description' => 'Raise base to power exp and take result modulo mod', + ), + 'gmp_sqrt' => + array ( + 'return' => 'resource', + 'params' => 'resource a', + 'description' => 'Takes integer part of square root of a', + ), + 'gmp_sqrtrem' => + array ( + 'return' => 'array', + 'params' => 'resource a', + 'description' => 'Square root with remainder', + ), + 'gmp_perfect_square' => + array ( + 'return' => 'bool', + 'params' => 'resource a', + 'description' => 'Checks if a is an exact square', + ), + 'gmp_prob_prime' => + array ( + 'return' => 'int', + 'params' => 'resource a[, int reps]', + 'description' => 'Checks if a is "probably prime"', + ), + 'gmp_gcd' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Computes greatest common denominator (gcd) of a and b', + ), + 'gmp_gcdext' => + array ( + 'return' => 'array', + 'params' => 'resource a, resource b', + 'description' => 'Computes G, S, and T, such that AS + BT = G = `gcd\' (A, B)', + ), + 'gmp_invert' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Computes the inverse of a modulo b', + ), + 'gmp_jacobi' => + array ( + 'return' => 'int', + 'params' => 'resource a, resource b', + 'description' => 'Computes Jacobi symbol', + ), + 'gmp_legendre' => + array ( + 'return' => 'int', + 'params' => 'resource a, resource b', + 'description' => 'Computes Legendre symbol', + ), + 'gmp_cmp' => + array ( + 'return' => 'int', + 'params' => 'resource a, resource b', + 'description' => 'Compares two numbers', + ), + 'gmp_sign' => + array ( + 'return' => 'int', + 'params' => 'resource a', + 'description' => 'Gets the sign of the number', + ), + 'gmp_random' => + array ( + 'return' => 'resource', + 'params' => '[int limiter]', + 'description' => 'Gets random number', + ), + 'gmp_and' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Calculates logical AND of a and b', + ), + 'gmp_or' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Calculates logical OR of a and b', + ), + 'gmp_com' => + array ( + 'return' => 'resource', + 'params' => 'resource a', + 'description' => 'Calculates one\'s complement of a', + ), + 'gmp_xor' => + array ( + 'return' => 'resource', + 'params' => 'resource a, resource b', + 'description' => 'Calculates logical exclusive OR of a and b', + ), + 'gmp_setbit' => + array ( + 'return' => 'void', + 'params' => 'resource &a, int index[, bool set_clear]', + 'description' => 'Sets or clear bit in a', + ), + 'gmp_clrbit' => + array ( + 'return' => 'void', + 'params' => 'resource &a, int index', + 'description' => 'Clears bit in a', + ), + 'gmp_popcount' => + array ( + 'return' => 'int', + 'params' => 'resource a', + 'description' => 'Calculates the population count of a', + ), + 'gmp_hamdist' => + array ( + 'return' => 'int', + 'params' => 'resource a, resource b', + 'description' => 'Calculates hamming distance between a and b', + ), + 'gmp_scan0' => + array ( + 'return' => 'int', + 'params' => 'resource a, int start', + 'description' => 'Finds first zero bit', + ), + 'gmp_scan1' => + array ( + 'return' => 'int', + 'params' => 'resource a, int start', + 'description' => 'Finds first non-zero bit', + ), + 'gd_info' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => '', + ), + 'imageloadfont' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Load a new font', + ), + 'imagesetstyle' => + array ( + 'return' => 'bool', + 'params' => 'resource im, array styles', + 'description' => 'Set the line drawing styles for use with imageline and IMG_COLOR_STYLED.', + ), + 'imagecreatetruecolor' => + array ( + 'return' => 'resource', + 'params' => 'int x_size, int y_size', + 'description' => 'Create a new true color image', + ), + 'imageistruecolor' => + array ( + 'return' => 'bool', + 'params' => 'resource im', + 'description' => 'return true if the image uses truecolor', + ), + 'imagetruecolortopalette' => + array ( + 'return' => 'void', + 'params' => 'resource im, bool ditherFlag, int colorsWanted', + 'description' => 'Convert a true colour image to a palette based image with a number of colours, optionally using dithering.', + ), + 'imagecolormatch' => + array ( + 'return' => 'bool', + 'params' => 'resource im1, resource im2', + 'description' => 'Makes the colors of the palette version of an image more closely match the true color version', + ), + 'imagesetthickness' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int thickness', + 'description' => 'Set line thickness for drawing lines, ellipses, rectangles, polygons etc.', + ), + 'imagefilledellipse' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int cx, int cy, int w, int h, int color', + 'description' => 'Draw an ellipse', + ), + 'imagefilledarc' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int cx, int cy, int w, int h, int s, int e, int col, int style', + 'description' => 'Draw a filled partial ellipse', + ), + 'imagealphablending' => + array ( + 'return' => 'bool', + 'params' => 'resource im, bool on', + 'description' => 'Turn alpha blending mode on or off for the given image', + ), + 'imagesavealpha' => + array ( + 'return' => 'bool', + 'params' => 'resource im, bool on', + 'description' => 'Include alpha channel to a saved image', + ), + 'imagelayereffect' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int effect', + 'description' => 'Set the alpha blending flag to use the bundled libgd layering effects', + ), + 'imagecolorallocatealpha' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue, int alpha', + 'description' => 'Allocate a color with an alpha level. Works for true color and palette based images', + ), + 'imagecolorresolvealpha' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue, int alpha', + 'description' => 'Resolve/Allocate a colour with an alpha level. Works for true colour and palette based images', + ), + 'imagecolorclosestalpha' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue, int alpha', + 'description' => 'Find the closest matching colour with alpha transparency', + ), + 'imagecolorexactalpha' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue, int alpha', + 'description' => 'Find exact match for colour with transparency', + ), + 'imagecopyresampled' => + array ( + 'return' => 'bool', + 'params' => 'resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h', + 'description' => 'Copy and resize part of an image using resampling to help ensure clarity', + ), + 'imagerotate' => + array ( + 'return' => 'resource', + 'params' => 'resource src_im, float angle, int bgdcolor', + 'description' => 'Rotate an image using a custom angle', + ), + 'imagesettile' => + array ( + 'return' => 'bool', + 'params' => 'resource image, resource tile', + 'description' => 'Set the tile image to $tile when filling $image with the "IMG_COLOR_TILED" color', + ), + 'imagesetbrush' => + array ( + 'return' => 'bool', + 'params' => 'resource image, resource brush', + 'description' => 'Set the brush image to $brush when filling $image with the "IMG_COLOR_BRUSHED" color', + ), + 'imagecreate' => + array ( + 'return' => 'resource', + 'params' => 'int x_size, int y_size', + 'description' => 'Create a new image', + ), + 'imagetypes' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Return the types of images supported in a bitfield - 1=GIF, 2=JPEG, 4=PNG, 8=WBMP, 16=XPM', + ), + 'imagecreatefromstring' => + array ( + 'return' => 'resource', + 'params' => 'string image', + 'description' => 'Create a new image from the image stream in the string', + ), + 'imagecreatefromgif' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from GIF file or URL', + ), + 'imagecreatefromjpeg' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from JPEG file or URL', + ), + 'imagecreatefrompng' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from PNG file or URL', + ), + 'imagecreatefromxbm' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from XBM file or URL', + ), + 'imagecreatefromxpm' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from XPM file or URL', + ), + 'imagecreatefromwbmp' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from WBMP file or URL', + ), + 'imagecreatefromgd' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from GD file or URL', + ), + 'imagecreatefromgd2' => + array ( + 'return' => 'resource', + 'params' => 'string filename', + 'description' => 'Create a new image from GD2 file or URL', + ), + 'imagecreatefromgd2part' => + array ( + 'return' => 'resource', + 'params' => 'string filename, int srcX, int srcY, int width, int height', + 'description' => 'Create a new image from a given part of GD2 file or URL', + ), + 'imagexbm' => + array ( + 'return' => 'int', + 'params' => 'int im, string filename [, int foreground]', + 'description' => 'Output XBM image to browser or file', + ), + 'imagegif' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename]', + 'description' => 'Output GIF image to browser or file', + ), + 'imagepng' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename]', + 'description' => 'Output PNG image to browser or file', + ), + 'imagejpeg' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename [, int quality]]', + 'description' => 'Output JPEG image to browser or file', + ), + 'imagewbmp' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename, [, int foreground]]', + 'description' => 'Output WBMP image to browser or file', + ), + 'imagegd' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename]', + 'description' => 'Output GD image to browser or file', + ), + 'imagegd2' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename, [, int chunk_size, [, int type]]]', + 'description' => 'Output GD2 image to browser or file', + ), + 'imagedestroy' => + array ( + 'return' => 'bool', + 'params' => 'resource im', + 'description' => 'Destroy an image', + ), + 'imagecolorallocate' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue', + 'description' => 'Allocate a color for an image', + ), + 'imagepalettecopy' => + array ( + 'return' => 'void', + 'params' => 'resource dst, resource src', + 'description' => 'Copy the palette from the src image onto the dst image', + ), + 'imagecolorat' => + array ( + 'return' => 'int', + 'params' => 'resource im, int x, int y', + 'description' => 'Get the index of the color of a pixel', + ), + 'imagecolorclosest' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue', + 'description' => 'Get the index of the closest color to the specified color', + ), + 'imagecolorclosesthwb' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue', + 'description' => 'Get the index of the color which has the hue, white and blackness nearest to the given color', + ), + 'imagecolordeallocate' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int index', + 'description' => 'De-allocate a color for an image', + ), + 'imagecolorresolve' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue', + 'description' => 'Get the index of the specified color or its closest possible alternative', + ), + 'imagecolorexact' => + array ( + 'return' => 'int', + 'params' => 'resource im, int red, int green, int blue', + 'description' => 'Get the index of the specified color', + ), + 'imagecolorset' => + array ( + 'return' => 'void', + 'params' => 'resource im, int col, int red, int green, int blue', + 'description' => 'Set the color for the specified palette index', + ), + 'imagecolorsforindex' => + array ( + 'return' => 'array', + 'params' => 'resource im, int col', + 'description' => 'Get the colors for an index', + ), + 'imagegammacorrect' => + array ( + 'return' => 'bool', + 'params' => 'resource im, float inputgamma, float outputgamma', + 'description' => 'Apply a gamma correction to a GD image', + ), + 'imagesetpixel' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x, int y, int col', + 'description' => 'Set a single pixel', + ), + 'imageline' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x1, int y1, int x2, int y2, int col', + 'description' => 'Draw a line', + ), + 'imagedashedline' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x1, int y1, int x2, int y2, int col', + 'description' => 'Draw a dashed line', + ), + 'imagerectangle' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x1, int y1, int x2, int y2, int col', + 'description' => 'Draw a rectangle', + ), + 'imagefilledrectangle' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x1, int y1, int x2, int y2, int col', + 'description' => 'Draw a filled rectangle', + ), + 'imagearc' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int cx, int cy, int w, int h, int s, int e, int col', + 'description' => 'Draw a partial ellipse', + ), + 'imageellipse' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int cx, int cy, int w, int h, int color', + 'description' => 'Draw an ellipse', + ), + 'imagefilltoborder' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x, int y, int border, int col', + 'description' => 'Flood fill to specific color', + ), + 'imagefill' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int x, int y, int col', + 'description' => 'Flood fill', + ), + 'imagecolorstotal' => + array ( + 'return' => 'int', + 'params' => 'resource im', + 'description' => 'Find out the number of colors in an image\'s palette', + ), + 'imagecolortransparent' => + array ( + 'return' => 'int', + 'params' => 'resource im [, int col]', + 'description' => 'Define a color as transparent', + ), + 'imageinterlace' => + array ( + 'return' => 'int', + 'params' => 'resource im [, int interlace]', + 'description' => 'Enable or disable interlace', + ), + 'imagepolygon' => + array ( + 'return' => 'bool', + 'params' => 'resource im, array point, int num_points, int col', + 'description' => 'Draw a polygon', + ), + 'imagefilledpolygon' => + array ( + 'return' => 'bool', + 'params' => 'resource im, array point, int num_points, int col', + 'description' => 'Draw a filled polygon', + ), + 'imagefontwidth' => + array ( + 'return' => 'int', + 'params' => 'int font', + 'description' => 'Get font width', + ), + 'imagefontheight' => + array ( + 'return' => 'int', + 'params' => 'int font', + 'description' => 'Get font height', + ), + 'imagechar' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int font, int x, int y, string c, int col', + 'description' => 'Draw a character', + ), + 'imagecharup' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int font, int x, int y, string c, int col', + 'description' => 'Draw a character rotated 90 degrees counter-clockwise', + ), + 'imagestring' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int font, int x, int y, string str, int col', + 'description' => 'Draw a string horizontally', + ), + 'imagestringup' => + array ( + 'return' => 'bool', + 'params' => 'resource im, int font, int x, int y, string str, int col', + 'description' => 'Draw a string vertically - rotated 90 degrees counter-clockwise', + ), + 'imagecopy' => + array ( + 'return' => 'bool', + 'params' => 'resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h', + 'description' => 'Copy part of an image', + ), + 'imagecopymerge' => + array ( + 'return' => 'bool', + 'params' => 'resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct', + 'description' => 'Merge one part of an image with another', + ), + 'imagecopymergegray' => + array ( + 'return' => 'bool', + 'params' => 'resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct', + 'description' => 'Merge one part of an image with another', + ), + 'imagecopyresized' => + array ( + 'return' => 'bool', + 'params' => 'resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h', + 'description' => 'Copy and resize part of an image', + ), + 'imagesx' => + array ( + 'return' => 'int', + 'params' => 'resource im', + 'description' => 'Get image width', + ), + 'imagesy' => + array ( + 'return' => 'int', + 'params' => 'resource im', + 'description' => 'Get image height', + ), + 'imageftbbox' => + array ( + 'return' => 'array', + 'params' => 'float size, float angle, string font_file, string text [, array extrainfo]', + 'description' => 'Give the bounding box of a text using fonts via freetype2', + ), + 'imagefttext' => + array ( + 'return' => 'array', + 'params' => 'resource im, float size, float angle, int x, int y, int col, string font_file, string text [, array extrainfo]', + 'description' => 'Write text to the image using fonts via freetype2', + ), + 'imagettfbbox' => + array ( + 'return' => 'array', + 'params' => 'float size, float angle, string font_file, string text', + 'description' => 'Give the bounding box of a text using TrueType fonts', + ), + 'imagettftext' => + array ( + 'return' => 'array', + 'params' => 'resource im, float size, float angle, int x, int y, int col, string font_file, string text', + 'description' => 'Write text to the image using a TrueType font', + ), + 'imagepsloadfont' => + array ( + 'return' => 'resource', + 'params' => 'string pathname', + 'description' => 'Load a new font from specified file', + ), + 'imagepscopyfont' => + array ( + 'return' => 'int', + 'params' => 'int font_index', + 'description' => 'Make a copy of a font for purposes like extending or reenconding', + ), + 'imagepsfreefont' => + array ( + 'return' => 'bool', + 'params' => 'resource font_index', + 'description' => 'Free memory used by a font', + ), + 'imagepsencodefont' => + array ( + 'return' => 'bool', + 'params' => 'resource font_index, string filename', + 'description' => 'To change a fonts character encoding vector', + ), + 'imagepsextendfont' => + array ( + 'return' => 'bool', + 'params' => 'resource font_index, float extend', + 'description' => 'Extend or or condense (if extend < 1) a font', + ), + 'imagepsslantfont' => + array ( + 'return' => 'bool', + 'params' => 'resource font_index, float slant', + 'description' => 'Slant a font', + ), + 'imagepstext' => + array ( + 'return' => 'array', + 'params' => 'resource image, string text, resource font, int size, int xcoord, int ycoord [, int space, int tightness, float angle, int antialias]', + 'description' => 'Rasterize a string over an image', + ), + 'imagepsbbox' => + array ( + 'return' => 'array', + 'params' => 'string text, resource font, int size [, int space, int tightness, int angle]', + 'description' => 'Return the bounding box needed by a string if rasterized', + ), + 'image2wbmp' => + array ( + 'return' => 'bool', + 'params' => 'resource im [, string filename [, int threshold]]', + 'description' => 'Output WBMP image to browser or file', + ), + 'imagefilter' => + array ( + 'return' => 'bool', + 'params' => 'resource src_im, int filtertype, [args] ', + 'description' => 'Applies Filter an image using a custom angle', + ), + 'imageconvolution' => + array ( + 'return' => 'resource', + 'params' => 'resource src_im, array matrix3x3, double div, double offset', + 'description' => 'Apply a 3x3 convolution matrix, using coefficient div and offset', + ), + 'imageantialias' => + array ( + 'return' => 'bool', + 'params' => 'resource im, bool on', + 'description' => 'Should antialiased functions used or not', + ), + 'recode_string' => + array ( + 'return' => 'string', + 'params' => 'string request, string str', + 'description' => 'Recode string str according to request string', + ), + 'recode_file' => + array ( + 'return' => 'bool', + 'params' => 'string request, resource input, resource output', + 'description' => 'Recode file input into file output according to request', + ), + 'posix_kill' => + array ( + 'return' => 'bool', + 'params' => 'int pid, int sig', + 'description' => 'Send a signal to a process (POSIX.1, 3.3.2)', + ), + 'posix_getpid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current process id (POSIX.1, 4.1.1)', + ), + 'posix_getppid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the parent process id (POSIX.1, 4.1.1)', + ), + 'posix_getuid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current user id (POSIX.1, 4.2.1)', + ), + 'posix_getgid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current group id (POSIX.1, 4.2.1)', + ), + 'posix_geteuid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current effective user id (POSIX.1, 4.2.1)', + ), + 'posix_getegid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current effective group id (POSIX.1, 4.2.1)', + ), + 'posix_setuid' => + array ( + 'return' => 'bool', + 'params' => 'long uid', + 'description' => 'Set user id (POSIX.1, 4.2.2)', + ), + 'posix_setgid' => + array ( + 'return' => 'bool', + 'params' => 'int uid', + 'description' => 'Set group id (POSIX.1, 4.2.2)', + ), + 'posix_seteuid' => + array ( + 'return' => 'bool', + 'params' => 'long uid', + 'description' => 'Set effective user id', + ), + 'posix_setegid' => + array ( + 'return' => 'bool', + 'params' => 'long uid', + 'description' => 'Set effective group id', + ), + 'posix_getgroups' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get supplementary group id\'s (POSIX.1, 4.2.3)', + ), + 'posix_getlogin' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Get user name (POSIX.1, 4.2.4)', + ), + 'posix_getpgrp' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get current process group id (POSIX.1, 4.3.1)', + ), + 'posix_setsid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Create session and set process group id (POSIX.1, 4.3.2)', + ), + 'posix_setpgid' => + array ( + 'return' => 'bool', + 'params' => 'int pid, int pgid', + 'description' => 'Set process group id for job control (POSIX.1, 4.3.3)', + ), + 'posix_getpgid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the process group id of the specified process (This is not a POSIX function, but a SVR4ism, so we compile conditionally)', + ), + 'posix_getsid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get process group id of session leader (This is not a POSIX function, but a SVR4ism, so be compile conditionally)', + ), + 'posix_uname' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get system name (POSIX.1, 4.4.1)', + ), + 'posix_times' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get process times (POSIX.1, 4.5.2)', + ), + 'posix_ctermid' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Generate terminal path name (POSIX.1, 4.7.1)', + ), + 'posix_ttyname' => + array ( + 'return' => 'string', + 'params' => 'int fd', + 'description' => 'Determine terminal device name (POSIX.1, 4.7.2)', + ), + 'posix_isatty' => + array ( + 'return' => 'bool', + 'params' => 'int fd', + 'description' => 'Determine if filedesc is a tty (POSIX.1, 4.7.1)', + ), + 'posix_getcwd' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Get working directory pathname (POSIX.1, 5.2.2)', + ), + 'posix_mkfifo' => + array ( + 'return' => 'bool', + 'params' => 'string pathname, int mode', + 'description' => 'Make a FIFO special file (POSIX.1, 5.4.2)', + ), + 'posix_mknod' => + array ( + 'return' => 'bool', + 'params' => 'string pathname, int mode [, int major [, int minor]]', + 'description' => 'Make a special or ordinary file (POSIX.1)', + ), + 'posix_access' => + array ( + 'return' => 'bool', + 'params' => 'string file [, int mode]', + 'description' => 'Determine accessibility of a file (POSIX.1 5.6.3)', + ), + 'posix_getgrnam' => + array ( + 'return' => 'array', + 'params' => 'string groupname', + 'description' => 'Group database access (POSIX.1, 9.2.1)', + ), + 'posix_getgrgid' => + array ( + 'return' => 'array', + 'params' => 'long gid', + 'description' => 'Group database access (POSIX.1, 9.2.1)', + ), + 'posix_getpwnam' => + array ( + 'return' => 'array', + 'params' => 'string groupname', + 'description' => 'User database access (POSIX.1, 9.2.2)', + ), + 'posix_getpwuid' => + array ( + 'return' => 'array', + 'params' => 'long uid', + 'description' => 'User database access (POSIX.1, 9.2.2)', + ), + 'posix_getrlimit' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get system resource consumption limits (This is not a POSIX function, but a BSDism and a SVR4ism. We compile conditionally)', + ), + 'posix_get_last_error' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Retrieve the error number set by the last posix function which failed.', + ), + 'posix_strerror' => + array ( + 'return' => 'string', + 'params' => 'int errno', + 'description' => 'Retrieve the system error message associated with the given errno.', + ), + 'curl_multi_init' => + array ( + 'return' => 'resource', + 'params' => 'void', + 'description' => 'Returns a new cURL multi handle', + ), + 'curl_multi_add_handle' => + array ( + 'return' => 'int', + 'params' => 'resource multi, resource ch', + 'description' => 'Add a normal cURL handle to a cURL multi handle', + ), + 'curl_multi_remove_handle' => + array ( + 'return' => 'int', + 'params' => 'resource mh, resource ch', + 'description' => 'Remove a multi handle from a set of cURL handles', + ), + 'curl_multi_select' => + array ( + 'return' => 'int', + 'params' => 'resource mh[, double timeout]', + 'description' => 'Get all the sockets associated with the cURL extension, which can then be "selected"', + ), + 'curl_multi_exec' => + array ( + 'return' => 'int', + 'params' => 'resource mh, int &still_running', + 'description' => 'Run the sub-connections of the current cURL handle', + ), + 'curl_multi_getcontent' => + array ( + 'return' => 'string', + 'params' => 'resource ch', + 'description' => 'Return the content of a cURL handle if CURLOPT_RETURNTRANSFER is set', + ), + 'curl_multi_info_read' => + array ( + 'return' => 'array', + 'params' => 'resource mh', + 'description' => 'Get information about the current transfers', + ), + 'curl_multi_close' => + array ( + 'return' => 'void', + 'params' => 'resource mh', + 'description' => 'Close a set of cURL handles', + ), + 'curl_version' => + array ( + 'return' => 'array', + 'params' => '[int version]', + 'description' => 'Return cURL version information.', + ), + 'curl_init' => + array ( + 'return' => 'resource', + 'params' => '[string url]', + 'description' => 'Initialize a CURL session', + ), + 'curl_copy_handle' => + array ( + 'return' => 'resource', + 'params' => 'resource ch', + 'description' => 'Copy a cURL handle along with all of it\'s preferences', + ), + 'curl_setopt' => + array ( + 'return' => 'bool', + 'params' => 'resource ch, int option, mixed value', + 'description' => 'Set an option for a CURL transfer', + ), + 'curl_setopt_array' => + array ( + 'return' => 'bool', + 'params' => 'resource ch, array options', + 'description' => 'Set an array of option for a CURL transfer', + ), + 'curl_exec' => + array ( + 'return' => 'bool', + 'params' => 'resource ch', + 'description' => 'Perform a CURL session', + ), + 'curl_getinfo' => + array ( + 'return' => 'mixed', + 'params' => 'resource ch, int opt', + 'description' => 'Get information regarding a specific transfer', + ), + 'curl_error' => + array ( + 'return' => 'string', + 'params' => 'resource ch', + 'description' => 'Return a string contain the last error for the current session', + ), + 'curl_errno' => + array ( + 'return' => 'int', + 'params' => 'resource ch', + 'description' => 'Return an integer containing the last error number', + ), + 'curl_close' => + array ( + 'return' => 'void', + 'params' => 'resource ch', + 'description' => 'Close a CURL session', + ), + 'ncurses_addch' => + array ( + 'return' => 'int', + 'params' => 'int ch', + 'description' => 'Adds character at current position and advance cursor', + ), + 'ncurses_waddch' => + array ( + 'return' => 'int', + 'params' => 'resource window, int ch', + 'description' => 'Adds character at current position in a window and advance cursor', + ), + 'ncurses_color_set' => + array ( + 'return' => 'int', + 'params' => 'int pair', + 'description' => 'Sets fore- and background color', + ), + 'ncurses_delwin' => + array ( + 'return' => 'bool', + 'params' => 'resource window', + 'description' => 'Deletes a ncurses window', + ), + 'ncurses_end' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Stops using ncurses, clean up the screen', + ), + 'ncurses_getch' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Reads a character from keyboard', + ), + 'ncurses_has_colors' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Checks if terminal has colors', + ), + 'ncurses_init' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Initializes ncurses', + ), + 'ncurses_init_pair' => + array ( + 'return' => 'int', + 'params' => 'int pair, int fg, int bg', + 'description' => 'Allocates a color pair', + ), + 'ncurses_move' => + array ( + 'return' => 'int', + 'params' => 'int y, int x', + 'description' => 'Moves output position', + ), + 'ncurses_newpad' => + array ( + 'return' => 'resource', + 'params' => 'int rows, int cols', + 'description' => 'Creates a new pad (window)', + ), + 'ncurses_prefresh' => + array ( + 'return' => 'int', + 'params' => 'resource pad, int pminrow, int pmincol, int sminrow, int smincol, int smaxrow, int smaxcol', + 'description' => 'Copys a region from a pad into the virtual screen', + ), + 'ncurses_pnoutrefresh' => + array ( + 'return' => 'int', + 'params' => 'resource pad, int pminrow, int pmincol, int sminrow, int smincol, int smaxrow, int smaxcol', + 'description' => 'Copys a region from a pad into the virtual screen', + ), + 'ncurses_newwin' => + array ( + 'return' => 'int', + 'params' => 'int rows, int cols, int y, int x', + 'description' => 'Creates a new window', + ), + 'ncurses_refresh' => + array ( + 'return' => 'int', + 'params' => 'int ch', + 'description' => 'Refresh screen', + ), + 'ncurses_start_color' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Starts using colors', + ), + 'ncurses_standout' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Starts using \'standout\' attribute', + ), + 'ncurses_standend' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Stops using \'standout\' attribute', + ), + 'ncurses_baudrate' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns baudrate of terminal', + ), + 'ncurses_beep' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Let the terminal beep', + ), + 'ncurses_can_change_color' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Checks if we can change terminals colors', + ), + 'ncurses_cbreak' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Switches of input buffering', + ), + 'ncurses_clear' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Clears screen', + ), + 'ncurses_clrtobot' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Clears screen from current position to bottom', + ), + 'ncurses_clrtoeol' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Clears screen from current position to end of line', + ), + 'ncurses_reset_prog_mode' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Resets the prog mode saved by def_prog_mode', + ), + 'ncurses_reset_shell_mode' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Resets the shell mode saved by def_shell_mode', + ), + 'ncurses_def_prog_mode' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Saves terminals (program) mode', + ), + 'ncurses_def_shell_mode' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Saves terminal (shell) mode', + ), + 'ncurses_delch' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Deletes character at current position, move rest of line left', + ), + 'ncurses_deleteln' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Deletes line at current position, move rest of screen up', + ), + 'ncurses_doupdate' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Writes all prepared refreshes to terminal', + ), + 'ncurses_echo' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Activates keyboard input echo', + ), + 'ncurses_erase' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Erases terminal screen', + ), + 'ncurses_erasechar' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns current erase character', + ), + 'ncurses_flash' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Flashes terminal screen (visual bell)', + ), + 'ncurses_flushinp' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Flushes keyboard input buffer', + ), + 'ncurses_has_ic' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Checks for insert- and delete-capabilities', + ), + 'ncurses_has_il' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Checks for line insert- and delete-capabilities', + ), + 'ncurses_inch' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Gets character and attribute at current position', + ), + 'ncurses_insertln' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Inserts a line, move rest of screen down', + ), + 'ncurses_isendwin' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Ncurses is in endwin mode, normal screen output may be performed', + ), + 'ncurses_killchar' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns current line kill character', + ), + 'ncurses_nl' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Translates newline and carriage return / line feed', + ), + 'ncurses_nocbreak' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Switches terminal to cooked mode', + ), + 'ncurses_noecho' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Switches off keyboard input echo', + ), + 'ncurses_nonl' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Do not ranslate newline and carriage return / line feed', + ), + 'ncurses_noraw' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Switches terminal out of raw mode', + ), + 'ncurses_raw' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Switches terminal into raw mode', + ), + 'ncurses_meta' => + array ( + 'return' => 'int', + 'params' => 'resource window, bool 8bit', + 'description' => 'Enables/Disable 8-bit meta key information', + ), + 'ncurses_werase' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'Erase window contents', + ), + 'ncurses_resetty' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Restores saved terminal state', + ), + 'ncurses_savetty' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Saves terminal state', + ), + 'ncurses_termattrs' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns a logical OR of all attribute flags supported by terminal', + ), + 'ncurses_use_default_colors' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Assigns terminal default colors to color id -1', + ), + 'ncurses_slk_attr' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns current soft label keys attribute', + ), + 'ncurses_slk_clear' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Clears soft label keys from screen', + ), + 'ncurses_slk_noutrefresh' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Copies soft label keys to virtual screen', + ), + 'ncurses_slk_refresh' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Copies soft label keys to screen', + ), + 'ncurses_slk_restore' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Restores soft label keys', + ), + 'ncurses_slk_touch' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Forces output when ncurses_slk_noutrefresh is performed', + ), + 'ncurses_slk_set' => + array ( + 'return' => 'bool', + 'params' => 'int labelnr, string label, int format', + 'description' => 'Sets function key labels', + ), + 'ncurses_attroff' => + array ( + 'return' => 'int', + 'params' => 'int attributes', + 'description' => 'Turns off the given attributes', + ), + 'ncurses_attron' => + array ( + 'return' => 'int', + 'params' => 'int attributes', + 'description' => 'Turns on the given attributes', + ), + 'ncurses_attrset' => + array ( + 'return' => 'int', + 'params' => 'int attributes', + 'description' => 'Sets given attributes', + ), + 'ncurses_bkgd' => + array ( + 'return' => 'int', + 'params' => 'int attrchar', + 'description' => 'Sets background property for terminal screen', + ), + 'ncurses_curs_set' => + array ( + 'return' => 'int', + 'params' => 'int visibility', + 'description' => 'Sets cursor state', + ), + 'ncurses_delay_output' => + array ( + 'return' => 'int', + 'params' => 'int milliseconds', + 'description' => 'Delays output on terminal using padding characters', + ), + 'ncurses_echochar' => + array ( + 'return' => 'int', + 'params' => 'int character', + 'description' => 'Single character output including refresh', + ), + 'ncurses_halfdelay' => + array ( + 'return' => 'int', + 'params' => 'int tenth', + 'description' => 'Puts terminal into halfdelay mode', + ), + 'ncurses_has_key' => + array ( + 'return' => 'int', + 'params' => 'int keycode', + 'description' => 'Checks for presence of a function key on terminal keyboard', + ), + 'ncurses_insch' => + array ( + 'return' => 'int', + 'params' => 'int character', + 'description' => 'Inserts character moving rest of line including character at current position', + ), + 'ncurses_insdelln' => + array ( + 'return' => 'int', + 'params' => 'int count', + 'description' => 'Inserts lines before current line scrolling down (negative numbers delete and scroll up)', + ), + 'ncurses_mouseinterval' => + array ( + 'return' => 'int', + 'params' => 'int milliseconds', + 'description' => 'Sets timeout for mouse button clicks', + ), + 'ncurses_napms' => + array ( + 'return' => 'int', + 'params' => 'int milliseconds', + 'description' => 'Sleep', + ), + 'ncurses_scrl' => + array ( + 'return' => 'int', + 'params' => 'int count', + 'description' => 'Scrolls window content up or down without changing current position', + ), + 'ncurses_slk_attroff' => + array ( + 'return' => 'int', + 'params' => 'int intarg', + 'description' => '???', + ), + 'ncurses_slk_attron' => + array ( + 'return' => 'int', + 'params' => 'int intarg', + 'description' => '???', + ), + 'ncurses_slk_attrset' => + array ( + 'return' => 'int', + 'params' => 'int intarg', + 'description' => '???', + ), + 'ncurses_slk_color' => + array ( + 'return' => 'int', + 'params' => 'int intarg', + 'description' => 'Sets color for soft label keys', + ), + 'ncurses_slk_init' => + array ( + 'return' => 'int', + 'params' => 'int intarg', + 'description' => 'Inits soft label keys', + ), + 'ncurses_typeahead' => + array ( + 'return' => 'int', + 'params' => 'int fd', + 'description' => 'Specifys different filedescriptor for typeahead checking', + ), + 'ncurses_ungetch' => + array ( + 'return' => 'int', + 'params' => 'int keycode', + 'description' => 'Puts a character back into the input stream', + ), + 'ncurses_vidattr' => + array ( + 'return' => 'int', + 'params' => 'int intarg', + 'description' => '???', + ), + 'ncurses_use_extended_names' => + array ( + 'return' => 'int', + 'params' => 'bool flag', + 'description' => 'Controls use of extended names in terminfo descriptions', + ), + 'ncurses_bkgdset' => + array ( + 'return' => 'void', + 'params' => 'int attrchar', + 'description' => 'Controls screen background', + ), + 'ncurses_filter' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => '', + ), + 'ncurses_noqiflush' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Do not flush on signal characters', + ), + 'ncurses_qiflush' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Flushes on signal characters', + ), + 'ncurses_timeout' => + array ( + 'return' => 'void', + 'params' => 'int millisec', + 'description' => 'Sets timeout for special key sequences', + ), + 'ncurses_use_env' => + array ( + 'return' => 'void', + 'params' => 'int flag', + 'description' => 'Controls use of environment information about terminal size', + ), + 'ncurses_addstr' => + array ( + 'return' => 'int', + 'params' => 'string text', + 'description' => 'Outputs text at current position', + ), + 'ncurses_putp' => + array ( + 'return' => 'int', + 'params' => 'string text', + 'description' => '???', + ), + 'ncurses_scr_dump' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Dumps screen content to file', + ), + 'ncurses_scr_init' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Initializes screen from file dump', + ), + 'ncurses_scr_restore' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Restores screen from file dump', + ), + 'ncurses_scr_set' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Inherits screen from file dump', + ), + 'ncurses_mvaddch' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, int c', + 'description' => 'Moves current position and add character', + ), + 'ncurses_mvaddchnstr' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, string s, int n', + 'description' => 'Moves position and add attrributed string with specified length', + ), + 'ncurses_addchnstr' => + array ( + 'return' => 'int', + 'params' => 'string s, int n', + 'description' => 'Adds attributed string with specified length at current position', + ), + 'ncurses_mvaddchstr' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, string s', + 'description' => 'Moves position and add attributed string', + ), + 'ncurses_addchstr' => + array ( + 'return' => 'int', + 'params' => 'string s', + 'description' => 'Adds attributed string at current position', + ), + 'ncurses_mvaddnstr' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, string s, int n', + 'description' => 'Moves position and add string with specified length', + ), + 'ncurses_addnstr' => + array ( + 'return' => 'int', + 'params' => 'string s, int n', + 'description' => 'Adds string with specified length at current position', + ), + 'ncurses_mvaddstr' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, string s', + 'description' => 'Moves position and add string', + ), + 'ncurses_mvdelch' => + array ( + 'return' => 'int', + 'params' => 'int y, int x', + 'description' => 'Moves position and delete character, shift rest of line left', + ), + 'ncurses_mvgetch' => + array ( + 'return' => 'int', + 'params' => 'int y, int x', + 'description' => 'Moves position and get character at new position', + ), + 'ncurses_mvinch' => + array ( + 'return' => 'int', + 'params' => 'int y, int x', + 'description' => 'Moves position and get attributed character at new position', + ), + 'ncurses_insstr' => + array ( + 'return' => 'int', + 'params' => 'string text', + 'description' => 'Inserts string at current position, moving rest of line right', + ), + 'ncurses_instr' => + array ( + 'return' => 'int', + 'params' => 'string &buffer', + 'description' => 'Reads string from terminal screen', + ), + 'ncurses_mvhline' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, int attrchar, int n', + 'description' => 'Sets new position and draw a horizontal line using an attributed character and max. n characters long', + ), + 'ncurses_mvvline' => + array ( + 'return' => 'int', + 'params' => 'int y, int x, int attrchar, int n', + 'description' => 'Sets new position and draw a vertical line using an attributed character and max. n characters long', + ), + 'ncurses_mvcur' => + array ( + 'return' => 'int', + 'params' => 'int old_y,int old_x, int new_y, int new_x', + 'description' => 'Moves cursor immediately', + ), + 'ncurses_init_color' => + array ( + 'return' => 'int', + 'params' => 'int color, int r, int g, int b', + 'description' => 'Sets new RGB value for color', + ), + 'ncurses_color_content' => + array ( + 'return' => 'int', + 'params' => 'int color, int &r, int &g, int &b', + 'description' => 'Gets the RGB value for color', + ), + 'ncurses_pair_content' => + array ( + 'return' => 'int', + 'params' => 'int pair, int &f, int &b', + 'description' => 'Gets the RGB value for color', + ), + 'ncurses_border' => + array ( + 'return' => 'int', + 'params' => 'int left, int right, int top, int bottom, int tl_corner, int tr_corner, int bl_corner, int br_corner', + 'description' => 'Draws a border around the screen using attributed characters', + ), + 'ncurses_wborder' => + array ( + 'return' => 'int', + 'params' => 'resource window, int left, int right, int top, int bottom, int tl_corner, int tr_corner, int bl_corner, int br_corner', + 'description' => 'Draws a border around the window using attributed characters', + ), + 'ncurses_assume_default_colors' => + array ( + 'return' => 'int', + 'params' => 'int fg, int bg', + 'description' => 'Defines default colors for color 0', + ), + 'ncurses_define_key' => + array ( + 'return' => 'int', + 'params' => 'string definition, int keycode', + 'description' => 'Defines a keycode', + ), + 'ncurses_hline' => + array ( + 'return' => 'int', + 'params' => 'int charattr, int n', + 'description' => 'Draws a horizontal line at current position using an attributed character and max. n characters long', + ), + 'ncurses_vline' => + array ( + 'return' => 'int', + 'params' => 'int charattr, int n', + 'description' => 'Draws a vertical line at current position using an attributed character and max. n characters long', + ), + 'ncurses_whline' => + array ( + 'return' => 'int', + 'params' => 'resource window, int charattr, int n', + 'description' => 'Draws a horizontal line in a window at current position using an attributed character and max. n characters long', + ), + 'ncurses_wvline' => + array ( + 'return' => 'int', + 'params' => 'resource window, int charattr, int n', + 'description' => 'Draws a vertical line in a window at current position using an attributed character and max. n characters long', + ), + 'ncurses_keyok' => + array ( + 'return' => 'int', + 'params' => 'int keycode, int enable', + 'description' => 'Enables or disable a keycode', + ), + 'ncurses_mvwaddstr' => + array ( + 'return' => 'int', + 'params' => 'resource window, int y, int x, string text', + 'description' => 'Adds string at new position in window', + ), + 'ncurses_wrefresh' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'Refreshes window on terminal screen', + ), + 'ncurses_termname' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns terminal name', + ), + 'ncurses_longname' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Returns terminal description', + ), + 'ncurses_mousemask' => + array ( + 'return' => 'int', + 'params' => 'int newmask, int &oldmask', + 'description' => 'Returns and sets mouse options', + ), + 'ncurses_getmouse' => + array ( + 'return' => 'bool', + 'params' => 'array &mevent', + 'description' => 'Reads mouse event from queue. The content of mevent is cleared before new data is added.', + ), + 'ncurses_ungetmouse' => + array ( + 'return' => 'int', + 'params' => 'array mevent', + 'description' => 'Pushes mouse event to queue', + ), + 'ncurses_mouse_trafo' => + array ( + 'return' => 'bool', + 'params' => 'int &y, int &x, bool toscreen', + 'description' => 'Transforms coordinates', + ), + 'ncurses_wmouse_trafo' => + array ( + 'return' => 'bool', + 'params' => 'resource window, int &y, int &x, bool toscreen', + 'description' => 'Transforms window/stdscr coordinates', + ), + 'ncurses_getyx' => + array ( + 'return' => 'void', + 'params' => 'resource window, int &y, int &x', + 'description' => 'Returns the current cursor position for a window', + ), + 'ncurses_getmaxyx' => + array ( + 'return' => 'void', + 'params' => 'resource window, int &y, int &x', + 'description' => 'Returns the size of a window', + ), + 'ncurses_wmove' => + array ( + 'return' => 'int', + 'params' => 'resource window, int y, int x', + 'description' => 'Moves windows output position', + ), + 'ncurses_keypad' => + array ( + 'return' => 'int', + 'params' => 'resource window, bool bf', + 'description' => 'Turns keypad on or off', + ), + 'ncurses_wcolor_set' => + array ( + 'return' => 'int', + 'params' => 'resource window, int color_pair', + 'description' => 'Sets windows color pairings', + ), + 'ncurses_wclear' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'Clears window', + ), + 'ncurses_wnoutrefresh' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'Copies window to virtual screen', + ), + 'ncurses_waddstr' => + array ( + 'return' => 'int', + 'params' => 'resource window, string str [, int n]', + 'description' => 'Outputs text at current postion in window', + ), + 'ncurses_wgetch' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'Reads a character from keyboard (window)', + ), + 'ncurses_wattroff' => + array ( + 'return' => 'int', + 'params' => 'resource window, int attrs', + 'description' => 'Turns off attributes for a window', + ), + 'ncurses_wattron' => + array ( + 'return' => 'int', + 'params' => 'resource window, int attrs', + 'description' => 'Turns on attributes for a window', + ), + 'ncurses_wattrset' => + array ( + 'return' => 'int', + 'params' => 'resource window, int attrs', + 'description' => 'Set the attributes for a window', + ), + 'ncurses_wstandend' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'End standout mode for a window', + ), + 'ncurses_wstandout' => + array ( + 'return' => 'int', + 'params' => 'resource window', + 'description' => 'Enter standout mode for a window', + ), + 'ncurses_new_panel' => + array ( + 'return' => 'resource', + 'params' => 'resource window', + 'description' => 'Create a new panel and associate it with window', + ), + 'ncurses_del_panel' => + array ( + 'return' => 'bool', + 'params' => 'resource panel', + 'description' => 'Remove panel from the stack and delete it (but not the associated window)', + ), + 'ncurses_hide_panel' => + array ( + 'return' => 'int', + 'params' => 'resource panel', + 'description' => 'Remove panel from the stack, making it invisible', + ), + 'ncurses_show_panel' => + array ( + 'return' => 'int', + 'params' => 'resource panel', + 'description' => 'Places an invisible panel on top of the stack, making it visible', + ), + 'ncurses_top_panel' => + array ( + 'return' => 'int', + 'params' => 'resource panel', + 'description' => 'Moves a visible panel to the top of the stack', + ), + 'ncurses_bottom_panel' => + array ( + 'return' => 'int', + 'params' => 'resource panel', + 'description' => 'Moves a visible panel to the bottom of the stack', + ), + 'ncurses_move_panel' => + array ( + 'return' => 'int', + 'params' => 'resource panel, int startx, int starty', + 'description' => 'Moves a panel so that it\'s upper-left corner is at [startx, starty]', + ), + 'ncurses_replace_panel' => + array ( + 'return' => 'int', + 'params' => 'resource panel, resource window', + 'description' => 'Replaces the window associated with panel', + ), + 'ncurses_panel_above' => + array ( + 'return' => 'resource', + 'params' => 'resource panel', + 'description' => 'Returns the panel above panel. If panel is null, returns the bottom panel in the stack', + ), + 'ncurses_panel_below' => + array ( + 'return' => 'resource', + 'params' => 'resource panel', + 'description' => 'Returns the panel below panel. If panel is null, returns the top panel in the stack', + ), + 'ncurses_panel_window' => + array ( + 'return' => 'resource', + 'params' => 'resource panel', + 'description' => 'Returns the window associated with panel', + ), + 'ncurses_update_panels' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Refreshes the virtual screen to reflect the relations between panels in the stack.', + ), + 'ftp_connect' => + array ( + 'return' => 'resource', + 'params' => 'string host [, int port [, int timeout]]', + 'description' => 'Opens a FTP stream', + ), + 'ftp_ssl_connect' => + array ( + 'return' => 'resource', + 'params' => 'string host [, int port [, int timeout]]', + 'description' => 'Opens a FTP-SSL stream', + ), + 'ftp_login' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string username, string password', + 'description' => 'Logs into the FTP server', + ), + 'ftp_pwd' => + array ( + 'return' => 'string', + 'params' => 'resource stream', + 'description' => 'Returns the present working directory', + ), + 'ftp_cdup' => + array ( + 'return' => 'bool', + 'params' => 'resource stream', + 'description' => 'Changes to the parent directory', + ), + 'ftp_chdir' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string directory', + 'description' => 'Changes directories', + ), + 'ftp_exec' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string command', + 'description' => 'Requests execution of a program on the FTP server', + ), + 'ftp_raw' => + array ( + 'return' => 'array', + 'params' => 'resource stream, string command', + 'description' => 'Sends a literal command to the FTP server', + ), + 'ftp_mkdir' => + array ( + 'return' => 'string', + 'params' => 'resource stream, string directory', + 'description' => 'Creates a directory and returns the absolute path for the new directory or false on error', + ), + 'ftp_rmdir' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string directory', + 'description' => 'Removes a directory', + ), + 'ftp_chmod' => + array ( + 'return' => 'int', + 'params' => 'resource stream, int mode, string filename', + 'description' => 'Sets permissions on a file', + ), + 'ftp_alloc' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, int size[, &response]', + 'description' => 'Attempt to allocate space on the remote FTP server', + ), + 'ftp_nlist' => + array ( + 'return' => 'array', + 'params' => 'resource stream, string directory', + 'description' => 'Returns an array of filenames in the given directory', + ), + 'ftp_rawlist' => + array ( + 'return' => 'array', + 'params' => 'resource stream, string directory [, bool recursive]', + 'description' => 'Returns a detailed listing of a directory as an array of output lines', + ), + 'ftp_systype' => + array ( + 'return' => 'string', + 'params' => 'resource stream', + 'description' => 'Returns the system type identifier', + ), + 'ftp_fget' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, resource fp, string remote_file, int mode[, int resumepos]', + 'description' => 'Retrieves a file from the FTP server and writes it to an open file', + ), + 'ftp_nb_fget' => + array ( + 'return' => 'int', + 'params' => 'resource stream, resource fp, string remote_file, int mode[, int resumepos]', + 'description' => 'Retrieves a file from the FTP server asynchronly and writes it to an open file', + ), + 'ftp_pasv' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, bool pasv', + 'description' => 'Turns passive mode on or off', + ), + 'ftp_get' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string local_file, string remote_file, int mode[, int resume_pos]', + 'description' => 'Retrieves a file from the FTP server and writes it to a local file', + ), + 'ftp_nb_get' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string local_file, string remote_file, int mode[, int resume_pos]', + 'description' => 'Retrieves a file from the FTP server nbhronly and writes it to a local file', + ), + 'ftp_nb_continue' => + array ( + 'return' => 'int', + 'params' => 'resource stream', + 'description' => 'Continues retrieving/sending a file nbronously', + ), + 'ftp_fput' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string remote_file, resource fp, int mode[, int startpos]', + 'description' => 'Stores a file from an open file to the FTP server', + ), + 'ftp_nb_fput' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string remote_file, resource fp, int mode[, int startpos]', + 'description' => 'Stores a file from an open file to the FTP server nbronly', + ), + 'ftp_put' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string remote_file, string local_file, int mode[, int startpos]', + 'description' => 'Stores a file on the FTP server', + ), + 'ftp_nb_put' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string remote_file, string local_file, int mode[, int startpos]', + 'description' => 'Stores a file on the FTP server', + ), + 'ftp_size' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string filename', + 'description' => 'Returns the size of the file, or -1 on error', + ), + 'ftp_mdtm' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string filename', + 'description' => 'Returns the last modification time of the file, or -1 on error', + ), + 'ftp_rename' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string src, string dest', + 'description' => 'Renames the given file to a new path', + ), + 'ftp_delete' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string file', + 'description' => 'Deletes a file', + ), + 'ftp_site' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, string cmd', + 'description' => 'Sends a SITE command to the server', + ), + 'ftp_close' => + array ( + 'return' => 'bool', + 'params' => 'resource stream', + 'description' => 'Closes the FTP stream', + ), + 'ftp_set_option' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, int option, mixed value', + 'description' => 'Sets an FTP option', + ), + 'ftp_get_option' => + array ( + 'return' => 'mixed', + 'params' => 'resource stream, int option', + 'description' => 'Gets an FTP option', + ), + 'birdstep_connect' => + array ( + 'return' => 'int', + 'params' => 'string server, string user, string pass', + 'description' => '', + ), + 'birdstep_close' => + array ( + 'return' => 'bool', + 'params' => 'int id', + 'description' => '', + ), + 'birdstep_exec' => + array ( + 'return' => 'int', + 'params' => 'int index, string exec_str', + 'description' => '', + ), + 'birdstep_fetch' => + array ( + 'return' => 'bool', + 'params' => 'int index', + 'description' => '', + ), + 'birdstep_result' => + array ( + 'return' => 'mixed', + 'params' => 'int index, int col', + 'description' => '', + ), + 'birdstep_freeresult' => + array ( + 'return' => 'bool', + 'params' => 'int index', + 'description' => '', + ), + 'birdstep_autocommit' => + array ( + 'return' => 'bool', + 'params' => 'int index', + 'description' => '', + ), + 'birdstep_off_autocommit' => + array ( + 'return' => 'bool', + 'params' => 'int index', + 'description' => '', + ), + 'birdstep_commit' => + array ( + 'return' => 'bool', + 'params' => 'int index', + 'description' => '', + ), + 'birdstep_rollback' => + array ( + 'return' => 'bool', + 'params' => 'int index', + 'description' => '', + ), + 'birdstep_fieldname' => + array ( + 'return' => 'string', + 'params' => 'int index, int col', + 'description' => '', + ), + 'birdstep_fieldnum' => + array ( + 'return' => 'int', + 'params' => 'int index', + 'description' => '', + ), + 'odbc_close_all' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Close all ODBC connections', + ), + 'odbc_binmode' => + array ( + 'return' => 'bool', + 'params' => 'int result_id, int mode', + 'description' => 'Handle binary column data', + ), + 'odbc_longreadlen' => + array ( + 'return' => 'bool', + 'params' => 'int result_id, int length', + 'description' => 'Handle LONG columns', + ), + 'odbc_prepare' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string query', + 'description' => 'Prepares a statement for execution', + ), + 'odbc_execute' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id [, array parameters_array]', + 'description' => 'Execute a prepared statement', + ), + 'odbc_cursor' => + array ( + 'return' => 'string', + 'params' => 'resource result_id', + 'description' => 'Get cursor name', + ), + 'odbc_data_source' => + array ( + 'return' => 'array', + 'params' => 'resource connection_id, int fetch_type', + 'description' => 'Return information about the currently connected data source', + ), + 'odbc_exec' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string query [, int flags]', + 'description' => 'Prepare and execute an SQL statement', + ), + 'odbc_fetch_object' => + array ( + 'return' => 'object', + 'params' => 'int result [, int rownumber]', + 'description' => 'Fetch a result row as an object', + ), + 'odbc_fetch_array' => + array ( + 'return' => 'array', + 'params' => 'int result [, int rownumber]', + 'description' => 'Fetch a result row as an associative array', + ), + 'odbc_fetch_into' => + array ( + 'return' => 'int', + 'params' => 'resource result_id, array result_array, [, int rownumber]', + 'description' => 'Fetch one result row into an array', + ), + 'solid_fetch_prev' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id', + 'description' => '', + ), + 'odbc_fetch_row' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id [, int row_number]', + 'description' => 'Fetch a row', + ), + 'odbc_result' => + array ( + 'return' => 'mixed', + 'params' => 'resource result_id, mixed field', + 'description' => 'Get result data', + ), + 'odbc_result_all' => + array ( + 'return' => 'int', + 'params' => 'resource result_id [, string format]', + 'description' => 'Print result as HTML table', + ), + 'odbc_free_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id', + 'description' => 'Free resources associated with a result', + ), + 'odbc_connect' => + array ( + 'return' => 'resource', + 'params' => 'string DSN, string user, string password [, int cursor_option]', + 'description' => 'Connect to a datasource', + ), + 'odbc_pconnect' => + array ( + 'return' => 'resource', + 'params' => 'string DSN, string user, string password [, int cursor_option]', + 'description' => 'Establish a persistent connection to a datasource', + ), + 'odbc_close' => + array ( + 'return' => 'void', + 'params' => 'resource connection_id', + 'description' => 'Close an ODBC connection', + ), + 'odbc_num_rows' => + array ( + 'return' => 'int', + 'params' => 'resource result_id', + 'description' => 'Get number of rows in a result', + ), + 'odbc_next_result' => + array ( + 'return' => 'bool', + 'params' => 'resource result_id', + 'description' => 'Checks if multiple results are avaiable', + ), + 'odbc_num_fields' => + array ( + 'return' => 'int', + 'params' => 'resource result_id', + 'description' => 'Get number of columns in a result', + ), + 'odbc_field_name' => + array ( + 'return' => 'string', + 'params' => 'resource result_id, int field_number', + 'description' => 'Get a column name', + ), + 'odbc_field_type' => + array ( + 'return' => 'string', + 'params' => 'resource result_id, int field_number', + 'description' => 'Get the datatype of a column', + ), + 'odbc_field_len' => + array ( + 'return' => 'int', + 'params' => 'resource result_id, int field_number', + 'description' => 'Get the length (precision) of a column', + ), + 'odbc_field_scale' => + array ( + 'return' => 'int', + 'params' => 'resource result_id, int field_number', + 'description' => 'Get the scale of a column', + ), + 'odbc_field_num' => + array ( + 'return' => 'int', + 'params' => 'resource result_id, string field_name', + 'description' => 'Return column number', + ), + 'odbc_autocommit' => + array ( + 'return' => 'mixed', + 'params' => 'resource connection_id [, int OnOff]', + 'description' => 'Toggle autocommit mode or get status', + ), + 'odbc_commit' => + array ( + 'return' => 'bool', + 'params' => 'resource connection_id', + 'description' => 'Commit an ODBC transaction', + ), + 'odbc_rollback' => + array ( + 'return' => 'bool', + 'params' => 'resource connection_id', + 'description' => 'Rollback a transaction', + ), + 'odbc_error' => + array ( + 'return' => 'string', + 'params' => '[resource connection_id]', + 'description' => 'Get the last error code', + ), + 'odbc_errormsg' => + array ( + 'return' => 'string', + 'params' => '[resource connection_id]', + 'description' => 'Get the last error message', + ), + 'odbc_setoption' => + array ( + 'return' => 'bool', + 'params' => 'resource conn_id|result_id, int which, int option, int value', + 'description' => 'Sets connection or statement options', + ), + 'odbc_tables' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id [, string qualifier [, string owner [, string name [, string table_types]]]]', + 'description' => 'Call the SQLTables function', + ), + 'odbc_columns' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id [, string qualifier [, string owner [, string table_name [, string column_name]]]]', + 'description' => 'Returns a result identifier that can be used to fetch a list of column names in specified tables', + ), + 'odbc_columnprivileges' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string catalog, string schema, string table, string column', + 'description' => 'Returns a result identifier that can be used to fetch a list of columns and associated privileges for the specified table', + ), + 'odbc_foreignkeys' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string pk_qualifier, string pk_owner, string pk_table, string fk_qualifier, string fk_owner, string fk_table', + 'description' => 'Returns a result identifier to either a list of foreign keys in the specified table or a list of foreign keys in other tables that refer to the primary key in the specified table', + ), + 'odbc_gettypeinfo' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id [, int data_type]', + 'description' => 'Returns a result identifier containing information about data types supported by the data source', + ), + 'odbc_primarykeys' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string qualifier, string owner, string table', + 'description' => 'Returns a result identifier listing the column names that comprise the primary key for a table', + ), + 'odbc_procedurecolumns' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id [, string qualifier, string owner, string proc, string column]', + 'description' => 'Returns a result identifier containing the list of input and output parameters, as well as the columns that make up the result set for the specified procedures', + ), + 'odbc_procedures' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id [, string qualifier, string owner, string name]', + 'description' => 'Returns a result identifier containg the list of procedure names in a datasource', + ), + 'odbc_specialcolumns' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, int type, string qualifier, string owner, string table, int scope, int nullable', + 'description' => 'Returns a result identifier containing either the optimal set of columns that uniquely identifies a row in the table or columns that are automatically updated when any value in the row is updated by a transaction', + ), + 'odbc_statistics' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string qualifier, string owner, string name, int unique, int accuracy', + 'description' => 'Returns a result identifier that contains statistics about a single table and the indexes associated with the table', + ), + 'odbc_tableprivileges' => + array ( + 'return' => 'resource', + 'params' => 'resource connection_id, string qualifier, string owner, string name', + 'description' => 'Returns a result identifier containing a list of tables and the privileges associated with each table', + ), + 'pspell_new' => + array ( + 'return' => 'int', + 'params' => 'string language [, string spelling [, string jargon [, string encoding [, int mode]]]]', + 'description' => 'Load a dictionary', + ), + 'pspell_new_personal' => + array ( + 'return' => 'int', + 'params' => 'string personal, string language [, string spelling [, string jargon [, string encoding [, int mode]]]]', + 'description' => 'Load a dictionary with a personal wordlist', + ), + 'pspell_new_config' => + array ( + 'return' => 'int', + 'params' => 'int config', + 'description' => 'Load a dictionary based on the given config', + ), + 'pspell_check' => + array ( + 'return' => 'bool', + 'params' => 'int pspell, string word', + 'description' => 'Returns true if word is valid', + ), + 'pspell_suggest' => + array ( + 'return' => 'array', + 'params' => 'int pspell, string word', + 'description' => 'Returns array of suggestions', + ), + 'pspell_store_replacement' => + array ( + 'return' => 'bool', + 'params' => 'int pspell, string misspell, string correct', + 'description' => 'Notify the dictionary of a user-selected replacement', + ), + 'pspell_add_to_personal' => + array ( + 'return' => 'bool', + 'params' => 'int pspell, string word', + 'description' => 'Adds a word to a personal list', + ), + 'pspell_add_to_session' => + array ( + 'return' => 'bool', + 'params' => 'int pspell, string word', + 'description' => 'Adds a word to the current session', + ), + 'pspell_clear_session' => + array ( + 'return' => 'bool', + 'params' => 'int pspell', + 'description' => 'Clears the current session', + ), + 'pspell_save_wordlist' => + array ( + 'return' => 'bool', + 'params' => 'int pspell', + 'description' => 'Saves the current (personal) wordlist', + ), + 'pspell_config_create' => + array ( + 'return' => 'int', + 'params' => 'string language [, string spelling [, string jargon [, string encoding]]]', + 'description' => 'Create a new config to be used later to create a manager', + ), + 'pspell_config_runtogether' => + array ( + 'return' => 'bool', + 'params' => 'int conf, bool runtogether', + 'description' => 'Consider run-together words as valid components', + ), + 'pspell_config_mode' => + array ( + 'return' => 'bool', + 'params' => 'int conf, long mode', + 'description' => 'Select mode for config (PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLERS)', + ), + 'pspell_config_ignore' => + array ( + 'return' => 'bool', + 'params' => 'int conf, int ignore', + 'description' => 'Ignore words <= n chars', + ), + 'pspell_config_personal' => + array ( + 'return' => 'bool', + 'params' => 'int conf, string personal', + 'description' => 'Use a personal dictionary for this config', + ), + 'pspell_config_dict_dir' => + array ( + 'return' => 'bool', + 'params' => 'int conf, string directory', + 'description' => 'location of the main word list', + ), + 'pspell_config_data_dir' => + array ( + 'return' => 'bool', + 'params' => 'int conf, string directory', + 'description' => 'location of language data files', + ), + 'pspell_config_repl' => + array ( + 'return' => 'bool', + 'params' => 'int conf, string repl', + 'description' => 'Use a personal dictionary with replacement pairs for this config', + ), + 'pspell_config_save_repl' => + array ( + 'return' => 'bool', + 'params' => 'int conf, bool save', + 'description' => 'Save replacement pairs when personal list is saved for this config', + ), + 'dl' => + array ( + 'return' => 'int', + 'params' => 'string extension_filename', + 'description' => 'Load a PHP extension at runtime', + ), + 'ftok' => + array ( + 'return' => 'int', + 'params' => 'string pathname, string proj', + 'description' => 'Convert a pathname and a project identifier to a System V IPC key', + ), + 'assert' => + array ( + 'return' => 'int', + 'params' => 'string|bool assertion', + 'description' => 'Checks if assertion is false', + ), + 'assert_options' => + array ( + 'return' => 'mixed', + 'params' => 'int what [, mixed value]', + 'description' => 'Set/get the various assert flags', + ), + 'sprintf' => + array ( + 'return' => 'string', + 'params' => 'string format [, mixed arg1 [, mixed ...]]', + 'description' => 'Return a formatted string', + ), + 'vsprintf' => + array ( + 'return' => 'string', + 'params' => 'string format, array args', + 'description' => 'Return a formatted string', + ), + 'printf' => + array ( + 'return' => 'int', + 'params' => 'string format [, mixed arg1 [, mixed ...]]', + 'description' => 'Output a formatted string', + ), + 'vprintf' => + array ( + 'return' => 'int', + 'params' => 'string format, array args', + 'description' => 'Output a formatted string', + ), + 'fprintf' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string format [, mixed arg1 [, mixed ...]]', + 'description' => 'Output a formatted string into a stream', + ), + 'vfprintf' => + array ( + 'return' => 'int', + 'params' => 'resource stream, string format, array args', + 'description' => 'Output a formatted string into a stream', + ), + 'stream_socket_pair' => + array ( + 'return' => 'array', + 'params' => 'int domain, int type, int protocol', + 'description' => 'Creates a pair of connected, indistinguishable socket streams', + ), + 'stream_socket_client' => + array ( + 'return' => 'resource', + 'params' => 'string remoteaddress [, long &errcode, string &errstring, double timeout, long flags, resource context]', + 'description' => 'Open a client connection to a remote address', + ), + 'stream_socket_server' => + array ( + 'return' => 'resource', + 'params' => 'string localaddress [, long &errcode, string &errstring, long flags, resource context]', + 'description' => 'Create a server socket bound to localaddress', + ), + 'stream_socket_accept' => + array ( + 'return' => 'resource', + 'params' => 'resource serverstream, [ double timeout, string &peername ]', + 'description' => 'Accept a client connection from a server socket', + ), + 'stream_socket_get_name' => + array ( + 'return' => 'string', + 'params' => 'resource stream, bool want_peer', + 'description' => 'Returns either the locally bound or remote name for a socket stream', + ), + 'stream_socket_sendto' => + array ( + 'return' => 'long', + 'params' => 'resouce stream, string data [, long flags [, string target_addr]]', + 'description' => 'Send data to a socket stream. If target_addr is specified it must be in dotted quad (or [ipv6]) format', + ), + 'stream_socket_recvfrom' => + array ( + 'return' => 'string', + 'params' => 'resource stream, long amount [, long flags [, string &remote_addr]]', + 'description' => 'Receives data from a socket stream', + ), + 'stream_get_contents' => + array ( + 'return' => 'long', + 'params' => 'resource source [, long maxlen [, long offset]]', + 'description' => 'Reads all remaining bytes (or up to maxlen bytes) from a stream and returns them as a string.', + ), + 'stream_copy_to_stream' => + array ( + 'return' => 'long', + 'params' => 'resource source, resource dest [, long maxlen [, long pos]]', + 'description' => 'Reads up to maxlen bytes from source stream and writes them to dest stream.', + ), + 'stream_get_meta_data' => + array ( + 'return' => 'resource', + 'params' => 'resource fp', + 'description' => 'Retrieves header/meta data from streams/file pointers', + ), + 'stream_get_transports' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Retrieves list of registered socket transports', + ), + 'stream_get_wrappers' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Retrieves list of registered stream wrappers', + ), + 'stream_select' => + array ( + 'return' => 'int', + 'params' => 'array &read_streams, array &write_streams, array &except_streams, int tv_sec[, int tv_usec]', + 'description' => 'Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec', + ), + 'stream_context_get_options' => + array ( + 'return' => 'array', + 'params' => 'resource context|resource stream', + 'description' => 'Retrieve options for a stream/wrapper/context', + ), + 'stream_context_set_option' => + array ( + 'return' => 'bool', + 'params' => 'resource context|resource stream, string wrappername, string optionname, mixed value', + 'description' => 'Set an option for a wrapper', + ), + 'stream_context_set_params' => + array ( + 'return' => 'bool', + 'params' => 'resource context|resource stream, array options', + 'description' => 'Set parameters for a file context', + ), + 'stream_context_get_default' => + array ( + 'return' => 'resource', + 'params' => '[array options]', + 'description' => 'Get a handle on the default file/stream context and optionally set parameters', + ), + 'stream_context_create' => + array ( + 'return' => 'resource', + 'params' => '[array options]', + 'description' => 'Create a file context and optionally set parameters', + ), + 'stream_filter_prepend' => + array ( + 'return' => 'resource', + 'params' => 'resource stream, string filtername[, int read_write[, string filterparams]]', + 'description' => 'Prepend a filter to a stream', + ), + 'stream_filter_append' => + array ( + 'return' => 'resource', + 'params' => 'resource stream, string filtername[, int read_write[, string filterparams]]', + 'description' => 'Append a filter to a stream', + ), + 'stream_filter_remove' => + array ( + 'return' => 'bool', + 'params' => 'resource stream_filter', + 'description' => 'Flushes any data in the filter\'s internal buffer, removes it from the chain, and frees the resource', + ), + 'stream_get_line' => + array ( + 'return' => 'string', + 'params' => 'resource stream, int maxlen [, string ending]', + 'description' => 'Read up to maxlen bytes from a stream or until the ending string is found', + ), + 'stream_set_blocking' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, int mode', + 'description' => 'Set blocking/non-blocking mode on a socket or stream', + ), + 'set_socket_blocking' => + array ( + 'return' => 'bool', + 'params' => 'resource socket, int mode', + 'description' => 'Set blocking/non-blocking mode on a socket', + ), + 'stream_set_timeout' => + array ( + 'return' => 'bool', + 'params' => 'resource stream, int seconds, int microseconds', + 'description' => 'Set timeout on stream read to seconds + microseonds', + ), + 'stream_set_write_buffer' => + array ( + 'return' => 'int', + 'params' => 'resource fp, int buffer', + 'description' => 'Set file write buffer', + ), + 'stream_socket_enable_crypto' => + array ( + 'return' => 'int', + 'params' => 'resource stream, bool enable [, int cryptokind, resource sessionstream]', + 'description' => 'Enable or disable a specific kind of crypto on the stream', + ), + 'proc_terminate' => + array ( + 'return' => 'int', + 'params' => 'resource process [, long signal]', + 'description' => 'kill a process opened by proc_open', + ), + 'proc_close' => + array ( + 'return' => 'int', + 'params' => 'resource process', + 'description' => 'close a process opened by proc_open', + ), + 'proc_get_status' => + array ( + 'return' => 'array', + 'params' => 'resource process', + 'description' => 'get information about a process opened by proc_open', + ), + 'proc_open' => + array ( + 'return' => 'resource', + 'params' => 'string command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]]', + 'description' => 'Run a process with more control over it\'s file descriptors', + ), + 'opendir' => + array ( + 'return' => 'mixed', + 'params' => 'string path[, resource context]', + 'description' => 'Open a directory and return a dir_handle', + ), + 'dir' => + array ( + 'return' => 'object', + 'params' => 'string directory[, resource context]', + 'description' => 'Directory class with properties, handle and class and methods read, rewind and close', + ), + 'closedir' => + array ( + 'return' => 'void', + 'params' => '[resource dir_handle]', + 'description' => 'Close directory connection identified by the dir_handle', + ), + 'chroot' => + array ( + 'return' => 'bool', + 'params' => 'string directory', + 'description' => 'Change root directory', + ), + 'chdir' => + array ( + 'return' => 'bool', + 'params' => 'string directory', + 'description' => 'Change the current directory', + ), + 'getcwd' => + array ( + 'return' => 'mixed', + 'params' => 'void', + 'description' => 'Gets the current directory', + ), + 'rewinddir' => + array ( + 'return' => 'void', + 'params' => '[resource dir_handle]', + 'description' => 'Rewind dir_handle back to the start', + ), + 'readdir' => + array ( + 'return' => 'string', + 'params' => '[resource dir_handle]', + 'description' => 'Read directory entry from dir_handle', + ), + 'glob' => + array ( + 'return' => 'array', + 'params' => 'string pattern [, int flags]', + 'description' => 'Find pathnames matching a pattern', + ), + 'scandir' => + array ( + 'return' => 'array', + 'params' => 'string dir [, int sorting_order [, resource context]]', + 'description' => 'List files & directories inside the specified path', + ), + 'disk_total_space' => + array ( + 'return' => 'float', + 'params' => 'string path', + 'description' => 'Get total disk space for filesystem that path is on', + ), + 'disk_free_space' => + array ( + 'return' => 'float', + 'params' => 'string path', + 'description' => 'Get free disk space for filesystem that path is on', + ), + 'chgrp' => + array ( + 'return' => 'bool', + 'params' => 'string filename, mixed group', + 'description' => 'Change file group', + ), + 'lchgrp' => + array ( + 'return' => 'bool', + 'params' => 'string filename, mixed group', + 'description' => 'Change symlink group', + ), + 'chmod' => + array ( + 'return' => 'bool', + 'params' => 'string filename, int mode', + 'description' => 'Change file mode', + ), + 'touch' => + array ( + 'return' => 'bool', + 'params' => 'string filename [, int time [, int atime]]', + 'description' => 'Set modification time of file', + ), + 'clearstatcache' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Clear file stat cache', + ), + 'fileperms' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get file permissions', + ), + 'fileinode' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get file inode', + ), + 'filesize' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get file size', + ), + 'fileowner' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get file owner', + ), + 'filegroup' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get file group', + ), + 'fileatime' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get last access time of file', + ), + 'filemtime' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get last modification time of file', + ), + 'filectime' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Get inode modification time of file', + ), + 'filetype' => + array ( + 'return' => 'string', + 'params' => 'string filename', + 'description' => 'Get file type', + ), + 'is_writable' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if file can be written', + ), + 'is_readable' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if file can be read', + ), + 'is_executable' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if file is executable', + ), + 'is_file' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if file is a regular file', + ), + 'is_dir' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if file is directory', + ), + 'is_link' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if file is symbolic link', + ), + 'file_exists' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Returns true if filename exists', + ), + 'lstat' => + array ( + 'return' => 'array', + 'params' => 'string filename', + 'description' => 'Give information about a file or symbolic link', + ), + 'stat' => + array ( + 'return' => 'array', + 'params' => 'string filename', + 'description' => 'Give information about a file', + ), + 'convert_cyr_string' => + array ( + 'return' => 'string', + 'params' => 'string str, string from, string to', + 'description' => 'Convert from one Cyrillic character set to another', + ), + 'krsort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg [, int sort_flags]', + 'description' => 'Sort an array by key value in reverse order', + ), + 'ksort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg [, int sort_flags]', + 'description' => 'Sort an array by key', + ), + 'count' => + array ( + 'return' => 'int', + 'params' => 'mixed var [, int mode]', + 'description' => 'Count the number of elements in a variable (usually an array)', + ), + 'natsort' => + array ( + 'return' => 'void', + 'params' => 'array array_arg', + 'description' => 'Sort an array using natural sort', + ), + 'natcasesort' => + array ( + 'return' => 'void', + 'params' => 'array array_arg', + 'description' => 'Sort an array using case-insensitive natural sort', + ), + 'asort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg [, int sort_flags]', + 'description' => 'Sort an array and maintain index association', + ), + 'arsort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg [, int sort_flags]', + 'description' => 'Sort an array in reverse order and maintain index association', + ), + 'sort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg [, int sort_flags]', + 'description' => 'Sort an array', + ), + 'rsort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg [, int sort_flags]', + 'description' => 'Sort an array in reverse order', + ), + 'usort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg, string cmp_function', + 'description' => 'Sort an array by values using a user-defined comparison function', + ), + 'uasort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg, string cmp_function', + 'description' => 'Sort an array with a user-defined comparison function and maintain index association', + ), + 'uksort' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg, string cmp_function', + 'description' => 'Sort an array by keys using a user-defined comparison function', + ), + 'end' => + array ( + 'return' => 'mixed', + 'params' => 'array array_arg', + 'description' => 'Advances array argument\'s internal pointer to the last element and return it', + ), + 'prev' => + array ( + 'return' => 'mixed', + 'params' => 'array array_arg', + 'description' => 'Move array argument\'s internal pointer to the previous element and return it', + ), + 'next' => + array ( + 'return' => 'mixed', + 'params' => 'array array_arg', + 'description' => 'Move array argument\'s internal pointer to the next element and return it', + ), + 'reset' => + array ( + 'return' => 'mixed', + 'params' => 'array array_arg', + 'description' => 'Set array argument\'s internal pointer to the first element and return it', + ), + 'current' => + array ( + 'return' => 'mixed', + 'params' => 'array array_arg', + 'description' => 'Return the element currently pointed to by the internal array pointer', + ), + 'key' => + array ( + 'return' => 'mixed', + 'params' => 'array array_arg', + 'description' => 'Return the key of the element currently pointed to by the internal array pointer', + ), + 'min' => + array ( + 'return' => 'mixed', + 'params' => 'mixed arg1 [, mixed arg2 [, mixed ...]]', + 'description' => 'Return the lowest value in an array or a series of arguments', + ), + 'max' => + array ( + 'return' => 'mixed', + 'params' => 'mixed arg1 [, mixed arg2 [, mixed ...]]', + 'description' => 'Return the highest value in an array or a series of arguments', + ), + 'array_walk' => + array ( + 'return' => 'bool', + 'params' => 'array input, string funcname [, mixed userdata]', + 'description' => 'Apply a user function to every member of an array', + ), + 'array_walk_recursive' => + array ( + 'return' => 'bool', + 'params' => 'array input, string funcname [, mixed userdata]', + 'description' => 'Apply a user function recursively to every member of an array', + ), + 'in_array' => + array ( + 'return' => 'bool', + 'params' => 'mixed needle, array haystack [, bool strict]', + 'description' => 'Checks if the given value exists in the array', + ), + 'array_search' => + array ( + 'return' => 'mixed', + 'params' => 'mixed needle, array haystack [, bool strict]', + 'description' => 'Searches the array for a given value and returns the corresponding key if successful', + ), + 'extract' => + array ( + 'return' => 'int', + 'params' => 'array var_array [, int extract_type [, string prefix]]', + 'description' => 'Imports variables into symbol table from an array', + ), + 'compact' => + array ( + 'return' => 'array', + 'params' => 'mixed var_names [, mixed ...]', + 'description' => 'Creates a hash containing variables and their values', + ), + 'array_fill' => + array ( + 'return' => 'array', + 'params' => 'int start_key, int num, mixed val', + 'description' => 'Create an array containing num elements starting with index start_key each initialized to val', + ), + 'range' => + array ( + 'return' => 'array', + 'params' => 'mixed low, mixed high[, int step]', + 'description' => 'Create an array containing the range of integers or characters from low to high (inclusive)', + ), + 'shuffle' => + array ( + 'return' => 'bool', + 'params' => 'array array_arg', + 'description' => 'Randomly shuffle the contents of an array', + ), + 'array_push' => + array ( + 'return' => 'int', + 'params' => 'array stack, mixed var [, mixed ...]', + 'description' => 'Pushes elements onto the end of the array', + ), + 'array_pop' => + array ( + 'return' => 'mixed', + 'params' => 'array stack', + 'description' => 'Pops an element off the end of the array', + ), + 'array_shift' => + array ( + 'return' => 'mixed', + 'params' => 'array stack', + 'description' => 'Pops an element off the beginning of the array', + ), + 'array_unshift' => + array ( + 'return' => 'int', + 'params' => 'array stack, mixed var [, mixed ...]', + 'description' => 'Pushes elements onto the beginning of the array', + ), + 'array_splice' => + array ( + 'return' => 'array', + 'params' => 'array input, int offset [, int length [, array replacement]]', + 'description' => 'Removes the elements designated by offset and length and replace them with supplied array', + ), + 'array_slice' => + array ( + 'return' => 'array', + 'params' => 'array input, int offset [, int length]', + 'description' => 'Returns elements specified by offset and length', + ), + 'array_merge' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Merges elements from passed arrays into one array', + ), + 'array_merge_recursive' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Recursively merges elements from passed arrays into one array', + ), + 'array_keys' => + array ( + 'return' => 'array', + 'params' => 'array input [, mixed search_value[, bool strict]]', + 'description' => 'Return just the keys from the input array, optionally only for the specified search_value', + ), + 'array_values' => + array ( + 'return' => 'array', + 'params' => 'array input', + 'description' => 'Return just the values from the input array', + ), + 'array_count_values' => + array ( + 'return' => 'array', + 'params' => 'array input', + 'description' => 'Return the value as key and the frequency of that value in input as value', + ), + 'array_reverse' => + array ( + 'return' => 'array', + 'params' => 'array input [, bool preserve keys]', + 'description' => 'Return input as a new array with the order of the entries reversed', + ), + 'array_pad' => + array ( + 'return' => 'array', + 'params' => 'array input, int pad_size, mixed pad_value', + 'description' => 'Returns a copy of input array padded with pad_value to size pad_size', + ), + 'array_flip' => + array ( + 'return' => 'array', + 'params' => 'array input', + 'description' => 'Return array with key <-> value flipped', + ), + 'array_change_key_case' => + array ( + 'return' => 'array', + 'params' => 'array input [, int case=CASE_LOWER]', + 'description' => 'Retuns an array with all string keys lowercased [or uppercased]', + ), + 'array_unique' => + array ( + 'return' => 'array', + 'params' => 'array input', + 'description' => 'Removes duplicate values from array', + ), + 'array_intersect_key' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data.', + ), + 'array_intersect_ukey' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback key_compare_func', + 'description' => 'Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data.', + ), + 'array_intersect' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Returns the entries of arr1 that have values which are present in all the other arguments', + ), + 'array_uintersect' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback data_compare_func', + 'description' => 'Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using an user-supplied callback.', + ), + 'array_intersect_assoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check', + ), + 'array_uintersect_assoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback data_compare_func', + 'description' => 'Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using an user-supplied callback.', + ), + 'array_intersect_uassoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback key_compare_func', + 'description' => 'Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using an user-supplied callback.', + ), + 'array_uintersect_uassoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func', + 'description' => 'Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks.', + ), + 'array_diff_key' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved.', + ), + 'array_diff_ukey' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback key_comp_func', + 'description' => 'Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved.', + ), + 'array_diff' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Returns the entries of arr1 that have values which are not present in any of the others arguments.', + ), + 'array_udiff' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback data_comp_func', + 'description' => 'Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function.', + ), + 'array_diff_assoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...]', + 'description' => 'Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal', + ), + 'array_diff_uassoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback data_comp_func', + 'description' => 'Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function.', + ), + 'array_udiff_assoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback key_comp_func', + 'description' => 'Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function.', + ), + 'array_udiff_uassoc' => + array ( + 'return' => 'array', + 'params' => 'array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func', + 'description' => 'Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions.', + ), + 'array_multisort' => + array ( + 'return' => 'bool', + 'params' => 'array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING]], ...]', + 'description' => 'Sort multiple arrays at once similar to how ORDER BY clause works in SQL', + ), + 'array_rand' => + array ( + 'return' => 'mixed', + 'params' => 'array input [, int num_req]', + 'description' => 'Return key/keys for random entry/entries in the array', + ), + 'array_sum' => + array ( + 'return' => 'mixed', + 'params' => 'array input', + 'description' => 'Returns the sum of the array entries', + ), + 'array_product' => + array ( + 'return' => 'mixed', + 'params' => 'array input', + 'description' => 'Returns the product of the array entries', + ), + 'array_reduce' => + array ( + 'return' => 'mixed', + 'params' => 'array input, mixed callback [, int initial]', + 'description' => 'Iteratively reduce the array to a single value via the callback.', + ), + 'array_filter' => + array ( + 'return' => 'array', + 'params' => 'array input [, mixed callback]', + 'description' => 'Filters elements from the array via the callback.', + ), + 'array_map' => + array ( + 'return' => 'array', + 'params' => 'mixed callback, array input1 [, array input2 ,...]', + 'description' => 'Applies the callback to the elements in given arrays.', + ), + 'array_key_exists' => + array ( + 'return' => 'bool', + 'params' => 'mixed key, array search', + 'description' => 'Checks if the given key or index exists in the array', + ), + 'array_chunk' => + array ( + 'return' => 'array', + 'params' => 'array input, int size [, bool preserve_keys]', + 'description' => 'Split array into chunks', + ), + 'array_combine' => + array ( + 'return' => 'array', + 'params' => 'array keys, array values', + 'description' => 'Creates an array by using the elements of the first parameter as keys and the elements of the second as correspoding keys', + ), + 'soundex' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Calculate the soundex key of a string', + ), + 'strptime' => + array ( + 'return' => 'string', + 'params' => 'string timestamp, string format', + 'description' => 'Parse a time/date generated with strftime()', + ), + 'md5' => + array ( + 'return' => 'string', + 'params' => 'string str, [ bool raw_output]', + 'description' => 'Calculate the md5 hash of a string', + ), + 'md5_file' => + array ( + 'return' => 'string', + 'params' => 'string filename [, bool raw_output]', + 'description' => 'Calculate the md5 hash of given filename', + ), + 'header' => + array ( + 'return' => 'void', + 'params' => 'string header [, bool replace, [int http_response_code]]', + 'description' => 'Sends a raw HTTP header', + ), + 'setcookie' => + array ( + 'return' => 'bool', + 'params' => 'string name [, string value [, int expires [, string path [, string domain [, bool secure]]]]]', + 'description' => 'Send a cookie', + ), + 'setrawcookie' => + array ( + 'return' => 'bool', + 'params' => 'string name [, string value [, int expires [, string path [, string domain [, bool secure]]]]]', + 'description' => 'Send a cookie with no url encoding of the value', + ), + 'headers_sent' => + array ( + 'return' => 'bool', + 'params' => '[string &$file [, int &$line]]', + 'description' => 'Returns true if headers have already been sent, false otherwise', + ), + 'headers_list' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Return list of headers to be sent / already sent', + ), + 'crc32' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Calculate the crc32 polynomial of a string', + ), + 'abs' => + array ( + 'return' => 'int', + 'params' => 'int number', + 'description' => 'Return the absolute value of the number', + ), + 'ceil' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the next highest integer value of the number', + ), + 'floor' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the next lowest integer value from the number', + ), + 'round' => + array ( + 'return' => 'float', + 'params' => 'float number [, int precision]', + 'description' => 'Returns the number rounded to specified precision', + ), + 'sin' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the sine of the number in radians', + ), + 'cos' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the cosine of the number in radians', + ), + 'tan' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the tangent of the number in radians', + ), + 'asin' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the arc sine of the number in radians', + ), + 'acos' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Return the arc cosine of the number in radians', + ), + 'atan' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the arc tangent of the number in radians', + ), + 'atan2' => + array ( + 'return' => 'float', + 'params' => 'float y, float x', + 'description' => 'Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x', + ), + 'sinh' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2', + ), + 'cosh' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2', + ), + 'tanh' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number)', + ), + 'asinh' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number', + ), + 'acosh' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number', + ), + 'atanh' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number', + ), + 'pi' => + array ( + 'return' => 'float', + 'params' => 'void', + 'description' => 'Returns an approximation of pi', + ), + 'is_finite' => + array ( + 'return' => 'bool', + 'params' => 'float val', + 'description' => 'Returns whether argument is finite', + ), + 'is_infinite' => + array ( + 'return' => 'bool', + 'params' => 'float val', + 'description' => 'Returns whether argument is infinite', + ), + 'is_nan' => + array ( + 'return' => 'bool', + 'params' => 'float val', + 'description' => 'Returns whether argument is not a number', + ), + 'pow' => + array ( + 'return' => 'number', + 'params' => 'number base, number exponent', + 'description' => 'Returns base raised to the power of exponent. Returns integer result when possible', + ), + 'exp' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns e raised to the power of the number', + ), + 'expm1' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero', + ), + 'log1p' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero', + ), + 'log' => + array ( + 'return' => 'float', + 'params' => 'float number, [float base]', + 'description' => 'Returns the natural logarithm of the number, or the base log if base is specified', + ), + 'log10' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the base-10 logarithm of the number', + ), + 'sqrt' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Returns the square root of the number', + ), + 'hypot' => + array ( + 'return' => 'float', + 'params' => 'float num1, float num2', + 'description' => 'Returns sqrt(num1*num1 + num2*num2)', + ), + 'deg2rad' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Converts the number in degrees to the radian equivalent', + ), + 'rad2deg' => + array ( + 'return' => 'float', + 'params' => 'float number', + 'description' => 'Converts the radian number to the equivalent number in degrees', + ), + 'bindec' => + array ( + 'return' => 'int', + 'params' => 'string binary_number', + 'description' => 'Returns the decimal equivalent of the binary number', + ), + 'hexdec' => + array ( + 'return' => 'int', + 'params' => 'string hexadecimal_number', + 'description' => 'Returns the decimal equivalent of the hexadecimal number', + ), + 'octdec' => + array ( + 'return' => 'int', + 'params' => 'string octal_number', + 'description' => 'Returns the decimal equivalent of an octal string', + ), + 'decbin' => + array ( + 'return' => 'string', + 'params' => 'int decimal_number', + 'description' => 'Returns a string containing a binary representation of the number', + ), + 'decoct' => + array ( + 'return' => 'string', + 'params' => 'int decimal_number', + 'description' => 'Returns a string containing an octal representation of the given number', + ), + 'dechex' => + array ( + 'return' => 'string', + 'params' => 'int decimal_number', + 'description' => 'Returns a string containing a hexadecimal representation of the given number', + ), + 'base_convert' => + array ( + 'return' => 'string', + 'params' => 'string number, int frombase, int tobase', + 'description' => 'Converts a number in a string from any base <= 36 to any base <= 36', + ), + 'number_format' => + array ( + 'return' => 'string', + 'params' => 'float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]]', + 'description' => 'Formats a number with grouped thousands', + ), + 'fmod' => + array ( + 'return' => 'float', + 'params' => 'float x, float y', + 'description' => 'Returns the remainder of dividing x by y as a float', + ), + 'gethostbyaddr' => + array ( + 'return' => 'string', + 'params' => 'string ip_address', + 'description' => 'Get the Internet host name corresponding to a given IP address', + ), + 'gethostbyname' => + array ( + 'return' => 'string', + 'params' => 'string hostname', + 'description' => 'Get the IP address corresponding to a given Internet host name', + ), + 'gethostbynamel' => + array ( + 'return' => 'array', + 'params' => 'string hostname', + 'description' => 'Return a list of IP addresses that a given hostname resolves to.', + ), + 'dns_check_record' => + array ( + 'return' => 'int', + 'params' => 'string host [, string type]', + 'description' => 'Check DNS records corresponding to a given Internet host name or IP address', + ), + 'dns_get_record' => + array ( + 'return' => 'array|false', + 'params' => 'string hostname [, int type[, array authns, array addtl]]', + 'description' => 'Get any Resource Record corresponding to a given Internet host name', + ), + 'dns_get_mx' => + array ( + 'return' => 'bool', + 'params' => 'string hostname, array mxhosts [, array weight]', + 'description' => 'Get MX records corresponding to a given Internet host name', + ), + 'bin2hex' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'Converts the binary representation of data to hex', + ), + 'strspn' => + array ( + 'return' => 'int', + 'params' => 'string str, string mask [, start [, len]]', + 'description' => 'Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars)', + ), + 'strcspn' => + array ( + 'return' => 'int', + 'params' => 'string str, string mask [, start [, len]]', + 'description' => 'Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars)', + ), + 'nl_langinfo' => + array ( + 'return' => 'string', + 'params' => 'int item', + 'description' => 'Query language and locale information', + ), + 'strcoll' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2', + 'description' => 'Compares two strings using the current locale', + ), + 'trim' => + array ( + 'return' => 'string', + 'params' => 'string str [, string character_mask]', + 'description' => 'Strips whitespace from the beginning and end of a string', + ), + 'rtrim' => + array ( + 'return' => 'string', + 'params' => 'string str [, string character_mask]', + 'description' => 'Removes trailing whitespace', + ), + 'ltrim' => + array ( + 'return' => 'string', + 'params' => 'string str [, string character_mask]', + 'description' => 'Strips whitespace from the beginning of a string', + ), + 'wordwrap' => + array ( + 'return' => 'string', + 'params' => 'string str [, int width [, string break [, boolean cut]]]', + 'description' => 'Wraps buffer to selected number of characters using string break char', + ), + 'explode' => + array ( + 'return' => 'array', + 'params' => 'string separator, string str [, int limit]', + 'description' => 'Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned.', + ), + 'join' => + array ( + 'return' => 'string', + 'params' => 'array src, string glue', + 'description' => 'An alias for implode', + ), + 'implode' => + array ( + 'return' => 'string', + 'params' => '[string glue,] array pieces', + 'description' => 'Joins array elements placing glue string between items and return one string', + ), + 'strtok' => + array ( + 'return' => 'string', + 'params' => '[string str,] string token', + 'description' => 'Tokenize a string', + ), + 'strtoupper' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Makes a string uppercase', + ), + 'strtolower' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Makes a string lowercase', + ), + 'basename' => + array ( + 'return' => 'string', + 'params' => 'string path [, string suffix]', + 'description' => 'Returns the filename component of the path', + ), + 'dirname' => + array ( + 'return' => 'string', + 'params' => 'string path', + 'description' => 'Returns the directory name component of the path', + ), + 'pathinfo' => + array ( + 'return' => 'array', + 'params' => 'string path', + 'description' => 'Returns information about a certain string', + ), + 'stristr' => + array ( + 'return' => 'string', + 'params' => 'string haystack, string needle', + 'description' => 'Finds first occurrence of a string within another, case insensitive', + ), + 'strstr' => + array ( + 'return' => 'string', + 'params' => 'string haystack, string needle', + 'description' => 'Finds first occurrence of a string within another', + ), + 'strchr' => + array ( + 'return' => 'string', + 'params' => 'string haystack, string needle', + 'description' => 'An alias for strstr', + ), + 'strpos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, int offset]', + 'description' => 'Finds position of first occurrence of a string within another', + ), + 'stripos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, int offset]', + 'description' => 'Finds position of first occurrence of a string within another, case insensitive', + ), + 'strrpos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, int offset]', + 'description' => 'Finds position of last occurrence of a string within another string', + ), + 'strripos' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, int offset]', + 'description' => 'Finds position of last occurrence of a string within another string', + ), + 'strrchr' => + array ( + 'return' => 'string', + 'params' => 'string haystack, string needle', + 'description' => 'Finds the last occurrence of a character in a string within another', + ), + 'chunk_split' => + array ( + 'return' => 'string', + 'params' => 'string str [, int chunklen [, string ending]]', + 'description' => 'Returns split line', + ), + 'substr' => + array ( + 'return' => 'string', + 'params' => 'string str, int start [, int length]', + 'description' => 'Returns part of a string', + ), + 'substr_replace' => + array ( + 'return' => 'mixed', + 'params' => 'mixed str, mixed repl, mixed start [, mixed length]', + 'description' => 'Replaces part of a string with another string', + ), + 'quotemeta' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Quotes meta characters', + ), + 'ord' => + array ( + 'return' => 'int', + 'params' => 'string character', + 'description' => 'Returns ASCII value of character', + ), + 'chr' => + array ( + 'return' => 'string', + 'params' => 'int ascii', + 'description' => 'Converts ASCII code to a character', + ), + 'ucfirst' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Makes a string\'s first character uppercase', + ), + 'ucwords' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Uppercase the first character of every word in a string', + ), + 'strtr' => + array ( + 'return' => 'string', + 'params' => 'string str, string from, string to', + 'description' => 'Translates characters in str using given translation tables', + ), + 'strrev' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Reverse a string', + ), + 'similar_text' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2 [, float percent]', + 'description' => 'Calculates the similarity between two strings', + ), + 'addcslashes' => + array ( + 'return' => 'string', + 'params' => 'string str, string charlist', + 'description' => 'Escapes all chars mentioned in charlist with backslash. It creates octal representations if asked to backslash characters with 8th bit set or with ASCII<32 (except \'\\n\', \'\\r\', \'\\t\' etc...)', + ), + 'addslashes' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Escapes single quote, double quotes and backslash characters in a string with backslashes', + ), + 'stripcslashes' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Strips backslashes from a string. Uses C-style conventions', + ), + 'stripslashes' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Strips backslashes from a string', + ), + 'str_replace' => + array ( + 'return' => 'mixed', + 'params' => 'mixed search, mixed replace, mixed subject [, int &replace_count]', + 'description' => 'Replaces all occurrences of search in haystack with replace', + ), + 'str_ireplace' => + array ( + 'return' => 'mixed', + 'params' => 'mixed search, mixed replace, mixed subject [, int &replace_count]', + 'description' => 'Replaces all occurrences of search in haystack with replace / case-insensitive', + ), + 'hebrev' => + array ( + 'return' => 'string', + 'params' => 'string str [, int max_chars_per_line]', + 'description' => 'Converts logical Hebrew text to visual text', + ), + 'hebrevc' => + array ( + 'return' => 'string', + 'params' => 'string str [, int max_chars_per_line]', + 'description' => 'Converts logical Hebrew text to visual text with newline conversion', + ), + 'nl2br' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Converts newlines to HTML line breaks', + ), + 'strip_tags' => + array ( + 'return' => 'string', + 'params' => 'string str [, string allowable_tags]', + 'description' => 'Strips HTML and PHP tags from a string', + ), + 'setlocale' => + array ( + 'return' => 'string', + 'params' => 'mixed category, string locale [, string ...]', + 'description' => 'Set locale information', + ), + 'parse_str' => + array ( + 'return' => 'void', + 'params' => 'string encoded_string [, array result]', + 'description' => 'Parses GET/POST/COOKIE data and sets global variables', + ), + 'str_repeat' => + array ( + 'return' => 'string', + 'params' => 'string input, int mult', + 'description' => 'Returns the input string repeat mult times', + ), + 'count_chars' => + array ( + 'return' => 'mixed', + 'params' => 'string input [, int mode]', + 'description' => 'Returns info about what characters are used in input', + ), + 'strnatcmp' => + array ( + 'return' => 'int', + 'params' => 'string s1, string s2', + 'description' => 'Returns the result of string comparison using \'natural\' algorithm', + ), + 'localeconv' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns numeric formatting information based on the current locale', + ), + 'strnatcasecmp' => + array ( + 'return' => 'int', + 'params' => 'string s1, string s2', + 'description' => 'Returns the result of case-insensitive string comparison using \'natural\' algorithm', + ), + 'substr_count' => + array ( + 'return' => 'int', + 'params' => 'string haystack, string needle [, int offset [, int length]]', + 'description' => 'Returns the number of times a substring occurs in the string', + ), + 'str_pad' => + array ( + 'return' => 'string', + 'params' => 'string input, int pad_length [, string pad_string [, int pad_type]]', + 'description' => 'Returns input string padded on the left or right to specified length with pad_string', + ), + 'sscanf' => + array ( + 'return' => 'mixed', + 'params' => 'string str, string format [, string ...]', + 'description' => 'Implements an ANSI C compatible sscanf', + ), + 'str_rot13' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Perform the rot13 transform on a string', + ), + 'str_shuffle' => + array ( + 'return' => 'void', + 'params' => 'string str', + 'description' => 'Shuffles string. One permutation of all possible is created', + ), + 'str_word_count' => + array ( + 'return' => 'mixed', + 'params' => 'string str, [int format [, string charlist]]', + 'description' => 'Counts the number of words inside a string. If format of 1 is specified,then the function will return an array containing all the wordsfound inside the string. If format of 2 is specified, then the functionwill return an associated array where the position of the word is the keyand the word itself is the value.For the purpose of this function, \'word\' is defined as a locale dependentstring containing alphabetic characters, which also may contain, but not startwith "\'" and "-" characters.', + ), + 'money_format' => + array ( + 'return' => 'string', + 'params' => 'string format , float value', + 'description' => 'Convert monetary value(s) to string', + ), + 'str_split' => + array ( + 'return' => 'array', + 'params' => 'string str [, int split_length]', + 'description' => 'Convert a string to an array. If split_length is specified, break the string down into chunks each split_length characters long.', + ), + 'strpbrk' => + array ( + 'return' => 'array', + 'params' => 'string haystack, string char_list', + 'description' => 'Search a string for any of a set of characters', + ), + 'substr_compare' => + array ( + 'return' => 'int', + 'params' => 'string main_str, string str, int offset [, int length [, bool case_sensitivity]]', + 'description' => 'Binary safe optionally case insensitive comparison of 2 strings from an offset, up to length characters', + ), + 'fsockopen' => + array ( + 'return' => 'resource', + 'params' => 'string hostname, int port [, int errno [, string errstr [, float timeout]]]', + 'description' => 'Open Internet or Unix domain socket connection', + ), + 'pfsockopen' => + array ( + 'return' => 'resource', + 'params' => 'string hostname, int port [, int errno [, string errstr [, float timeout]]]', + 'description' => 'Open persistent Internet or Unix domain socket connection', + ), + 'readlink' => + array ( + 'return' => 'string', + 'params' => 'string filename', + 'description' => 'Return the target of a symbolic link', + ), + 'linkinfo' => + array ( + 'return' => 'int', + 'params' => 'string filename', + 'description' => 'Returns the st_dev field of the UNIX C stat structure describing the link', + ), + 'symlink' => + array ( + 'return' => 'int', + 'params' => 'string target, string link', + 'description' => 'Create a symbolic link', + ), + 'link' => + array ( + 'return' => 'int', + 'params' => 'string target, string link', + 'description' => 'Create a hard link', + ), + 'getmyuid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get PHP script owner\'s UID', + ), + 'getmygid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get PHP script owner\'s GID', + ), + 'getmypid' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get current process ID', + ), + 'getmyinode' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the inode of the current script being parsed', + ), + 'getlastmod' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get time of last page modification', + ), + 'var_dump' => + array ( + 'return' => 'void', + 'params' => 'mixed var', + 'description' => 'Dumps a string representation of variable to output', + ), + 'debug_zval_dump' => + array ( + 'return' => 'void', + 'params' => 'mixed var', + 'description' => 'Dumps a string representation of an internal zend value to output.', + ), + 'var_export' => + array ( + 'return' => 'mixed', + 'params' => 'mixed var [, bool return]', + 'description' => 'Outputs or returns a string representation of a variable', + ), + 'serialize' => + array ( + 'return' => 'string', + 'params' => 'mixed variable', + 'description' => 'Returns a string representation of variable (which can later be unserialized)', + ), + 'unserialize' => + array ( + 'return' => 'mixed', + 'params' => 'string variable_representation', + 'description' => 'Takes a string representation of variable and recreates it', + ), + 'memory_get_usage' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the allocated by PHP memory', + ), + 'ereg' => + array ( + 'return' => 'int', + 'params' => 'string pattern, string string [, array registers]', + 'description' => 'Regular expression match', + ), + 'eregi' => + array ( + 'return' => 'int', + 'params' => 'string pattern, string string [, array registers]', + 'description' => 'Case-insensitive regular expression match', + ), + 'ereg_replace' => + array ( + 'return' => 'string', + 'params' => 'string pattern, string replacement, string string', + 'description' => 'Replace regular expression', + ), + 'eregi_replace' => + array ( + 'return' => 'string', + 'params' => 'string pattern, string replacement, string string', + 'description' => 'Case insensitive replace regular expression', + ), + 'split' => + array ( + 'return' => 'array', + 'params' => 'string pattern, string string [, int limit]', + 'description' => 'Split string into array by regular expression', + ), + 'spliti' => + array ( + 'return' => 'array', + 'params' => 'string pattern, string string [, int limit]', + 'description' => 'Split string into array by regular expression case-insensitive', + ), + 'sql_regcase' => + array ( + 'return' => 'string', + 'params' => 'string string', + 'description' => 'Make regular expression for case insensitive match', + ), + 'crypt' => + array ( + 'return' => 'string', + 'params' => 'string str [, string salt]', + 'description' => 'Encrypt a string', + ), + 'ezmlm_hash' => + array ( + 'return' => 'int', + 'params' => 'string addr', + 'description' => 'Calculate EZMLM list hash value.', + ), + 'mail' => + array ( + 'return' => 'int', + 'params' => 'string to, string subject, string message [, string additional_headers [, string additional_parameters]]', + 'description' => 'Send an email message', + ), + 'srand' => + array ( + 'return' => 'void', + 'params' => '[int seed]', + 'description' => 'Seeds random number generator', + ), + 'mt_srand' => + array ( + 'return' => 'void', + 'params' => '[int seed]', + 'description' => 'Seeds Mersenne Twister random number generator', + ), + 'rand' => + array ( + 'return' => 'int', + 'params' => '[int min, int max]', + 'description' => 'Returns a random number', + ), + 'mt_rand' => + array ( + 'return' => 'int', + 'params' => '[int min, int max]', + 'description' => 'Returns a random number from Mersenne Twister', + ), + 'getrandmax' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns the maximum value a random number can have', + ), + 'mt_getrandmax' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns the maximum value a random number from Mersenne Twister can have', + ), + 'get_browser' => + array ( + 'return' => 'mixed', + 'params' => '[string browser_name [, bool return_array]]', + 'description' => 'Get information about the capabilities of a browser. If browser_name is omittedor null, HTTP_USER_AGENT is used. Returns an object by default; if return_arrayis true, returns an array.', + ), + 'iptcembed' => + array ( + 'return' => 'array', + 'params' => 'string iptcdata, string jpeg_file_name [, int spool]', + 'description' => 'Embed binary IPTC data into a JPEG image.', + ), + 'iptcparse' => + array ( + 'return' => 'array', + 'params' => 'string iptcdata', + 'description' => 'Parse binary IPTC-data into associative array', + ), + 'quoted_printable_decode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Convert a quoted-printable string to an 8 bit string', + ), + 'pack' => + array ( + 'return' => 'string', + 'params' => 'string format, mixed arg1 [, mixed arg2 [, mixed ...]]', + 'description' => 'Takes one or more arguments and packs them into a binary string according to the format argument', + ), + 'unpack' => + array ( + 'return' => 'array', + 'params' => 'string format, string input', + 'description' => 'Unpack binary string into named array elements according to format argument', + ), + 'base64_encode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Encodes string using MIME base64 algorithm', + ), + 'base64_decode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Decodes string using MIME base64 algorithm', + ), + 'gettype' => + array ( + 'return' => 'string', + 'params' => 'mixed var', + 'description' => 'Returns the type of the variable', + ), + 'settype' => + array ( + 'return' => 'bool', + 'params' => 'mixed var, string type', + 'description' => 'Set the type of the variable', + ), + 'intval' => + array ( + 'return' => 'int', + 'params' => 'mixed var [, int base]', + 'description' => 'Get the integer value of a variable using the optional base for the conversion', + ), + 'floatval' => + array ( + 'return' => 'float', + 'params' => 'mixed var', + 'description' => 'Get the float value of a variable', + ), + 'strval' => + array ( + 'return' => 'string', + 'params' => 'mixed var', + 'description' => 'Get the string value of a variable', + ), + 'is_null' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is null', + ), + 'is_resource' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is a resource', + ), + 'is_bool' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is a boolean', + ), + 'is_long' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is a long (integer)', + ), + 'is_float' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is float point', + ), + 'is_string' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is a string', + ), + 'is_array' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is an array', + ), + 'is_object' => + array ( + 'return' => 'bool', + 'params' => 'mixed var', + 'description' => 'Returns true if variable is an object', + ), + 'is_numeric' => + array ( + 'return' => 'bool', + 'params' => 'mixed value', + 'description' => 'Returns true if value is a number or a numeric string', + ), + 'is_scalar' => + array ( + 'return' => 'bool', + 'params' => 'mixed value', + 'description' => 'Returns true if value is a scalar', + ), + 'is_callable' => + array ( + 'return' => 'bool', + 'params' => 'mixed var [, bool syntax_only [, string callable_name]]', + 'description' => 'Returns true if var is callable.', + ), + 'version_compare' => + array ( + 'return' => 'int', + 'params' => 'string ver1, string ver2 [, string oper]', + 'description' => 'Compares two "PHP-standardized" version number strings', + ), + 'exec' => + array ( + 'return' => 'string', + 'params' => 'string command [, array &output [, int &return_value]]', + 'description' => 'Execute an external program', + ), + 'system' => + array ( + 'return' => 'int', + 'params' => 'string command [, int &return_value]', + 'description' => 'Execute an external program and display output', + ), + 'passthru' => + array ( + 'return' => 'void', + 'params' => 'string command [, int &return_value]', + 'description' => 'Execute an external program and display raw output', + ), + 'escapeshellcmd' => + array ( + 'return' => 'string', + 'params' => 'string command', + 'description' => 'Escape shell metacharacters', + ), + 'escapeshellarg' => + array ( + 'return' => 'string', + 'params' => 'string arg', + 'description' => 'Quote and escape an argument for use in a shell command', + ), + 'shell_exec' => + array ( + 'return' => 'string', + 'params' => 'string cmd', + 'description' => 'Execute command via shell and return complete output as string', + ), + 'proc_nice' => + array ( + 'return' => 'bool', + 'params' => 'int priority', + 'description' => 'Change the priority of the current process', + ), + 'constant' => + array ( + 'return' => 'mixed', + 'params' => 'string const_name', + 'description' => 'Given the name of a constant this function will return the constants associated value', + ), + 'inet_ntop' => + array ( + 'return' => 'string', + 'params' => 'string in_addr', + 'description' => 'Converts a packed inet address to a human readable IP address string', + ), + 'inet_pton' => + array ( + 'return' => 'string', + 'params' => 'string ip_address', + 'description' => 'Converts a human readable IP address to a packed binary string', + ), + 'ip2long' => + array ( + 'return' => 'int', + 'params' => 'string ip_address', + 'description' => 'Converts a string containing an (IPv4) Internet Protocol dotted address into a proper address', + ), + 'long2ip' => + array ( + 'return' => 'string', + 'params' => 'int proper_address', + 'description' => 'Converts an (IPv4) Internet network address into a string in Internet standard dotted format', + ), + 'getenv' => + array ( + 'return' => 'string', + 'params' => 'string varname', + 'description' => 'Get the value of an environment variable', + ), + 'putenv' => + array ( + 'return' => 'bool', + 'params' => 'string setting', + 'description' => 'Set the value of an environment variable', + ), + 'getopt' => + array ( + 'return' => 'array', + 'params' => 'string options [, array longopts]', + 'description' => 'Get options from the command line argument list', + ), + 'flush' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Flush the output buffer', + ), + 'sleep' => + array ( + 'return' => 'void', + 'params' => 'int seconds', + 'description' => 'Delay for a given number of seconds', + ), + 'usleep' => + array ( + 'return' => 'void', + 'params' => 'int micro_seconds', + 'description' => 'Delay for a given number of micro seconds', + ), + 'time_nanosleep' => + array ( + 'return' => 'mixed', + 'params' => 'long seconds, long nanoseconds', + 'description' => 'Delay for a number of seconds and nano seconds', + ), + 'time_sleep_until' => + array ( + 'return' => 'mixed', + 'params' => 'float timestamp', + 'description' => 'Make the script sleep until the specified time', + ), + 'get_current_user' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Get the name of the owner of the current PHP script', + ), + 'get_cfg_var' => + array ( + 'return' => 'string', + 'params' => 'string option_name', + 'description' => 'Get the value of a PHP configuration option', + ), + 'set_magic_quotes_runtime' => + array ( + 'return' => 'bool', + 'params' => 'int new_setting', + 'description' => 'Set the current active configuration setting of magic_quotes_runtime and return previous', + ), + 'get_magic_quotes_runtime' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current active configuration setting of magic_quotes_runtime', + ), + 'get_magic_quotes_gpc' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the current active configuration setting of magic_quotes_gpc', + ), + 'error_log' => + array ( + 'return' => 'bool', + 'params' => 'string message [, int message_type [, string destination [, string extra_headers]]]', + 'description' => 'Send an error message somewhere', + ), + 'call_user_func' => + array ( + 'return' => 'mixed', + 'params' => 'string function_name [, mixed parmeter] [, mixed ...]', + 'description' => 'Call a user function which is the first parameter', + ), + 'call_user_func_array' => + array ( + 'return' => 'mixed', + 'params' => 'string function_name, array parameters', + 'description' => 'Call a user function which is the first parameter with the arguments contained in array', + ), + 'call_user_method' => + array ( + 'return' => 'mixed', + 'params' => 'string method_name, mixed object [, mixed parameter] [, mixed ...]', + 'description' => 'Call a user method on a specific object or class', + ), + 'call_user_method_array' => + array ( + 'return' => 'mixed', + 'params' => 'string method_name, mixed object, array params', + 'description' => 'Call a user method on a specific object or class using a parameter array', + ), + 'register_shutdown_function' => + array ( + 'return' => 'void', + 'params' => 'string function_name', + 'description' => 'Register a user-level function to be called on request termination', + ), + 'highlight_file' => + array ( + 'return' => 'bool', + 'params' => 'string file_name [, bool return] ', + 'description' => 'Syntax highlight a source file', + ), + 'php_strip_whitespace' => + array ( + 'return' => 'string', + 'params' => 'string file_name', + 'description' => 'Return source with stripped comments and whitespace', + ), + 'highlight_string' => + array ( + 'return' => 'bool', + 'params' => 'string string [, bool return] ', + 'description' => 'Syntax highlight a string or optionally return it', + ), + 'ini_get' => + array ( + 'return' => 'string', + 'params' => 'string varname', + 'description' => 'Get a configuration option', + ), + 'ini_get_all' => + array ( + 'return' => 'array', + 'params' => '[string extension]', + 'description' => 'Get all configuration options', + ), + 'ini_set' => + array ( + 'return' => 'string', + 'params' => 'string varname, string newvalue', + 'description' => 'Set a configuration option, returns false on error and the old value of the configuration option on success', + ), + 'ini_restore' => + array ( + 'return' => 'void', + 'params' => 'string varname', + 'description' => 'Restore the value of a configuration option specified by varname', + ), + 'set_include_path' => + array ( + 'return' => 'string', + 'params' => 'string new_include_path', + 'description' => 'Sets the include_path configuration option', + ), + 'get_include_path' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get the current include_path configuration option', + ), + 'restore_include_path' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Restore the value of the include_path configuration option', + ), + 'print_r' => + array ( + 'return' => 'mixed', + 'params' => 'mixed var [, bool return]', + 'description' => 'Prints out or returns information about the specified variable', + ), + 'connection_aborted' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns true if client disconnected', + ), + 'connection_status' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Returns the connection status bitfield', + ), + 'ignore_user_abort' => + array ( + 'return' => 'int', + 'params' => 'bool value', + 'description' => 'Set whether we want to ignore a user abort event or not', + ), + 'getservbyname' => + array ( + 'return' => 'int', + 'params' => 'string service, string protocol', + 'description' => 'Returns port associated with service. Protocol must be "tcp" or "udp"', + ), + 'getservbyport' => + array ( + 'return' => 'string', + 'params' => 'int port, string protocol', + 'description' => 'Returns service name associated with port. Protocol must be "tcp" or "udp"', + ), + 'getprotobyname' => + array ( + 'return' => 'int', + 'params' => 'string name', + 'description' => 'Returns protocol number associated with name as per /etc/protocols', + ), + 'getprotobynumber' => + array ( + 'return' => 'string', + 'params' => 'int proto', + 'description' => 'Returns protocol name associated with protocol number proto', + ), + 'register_tick_function' => + array ( + 'return' => 'bool', + 'params' => 'string function_name [, mixed arg [, mixed ... ]]', + 'description' => 'Registers a tick callback function', + ), + 'unregister_tick_function' => + array ( + 'return' => 'void', + 'params' => 'string function_name', + 'description' => 'Unregisters a tick callback function', + ), + 'is_uploaded_file' => + array ( + 'return' => 'bool', + 'params' => 'string path', + 'description' => 'Check if file was created by rfc1867 upload', + ), + 'move_uploaded_file' => + array ( + 'return' => 'bool', + 'params' => 'string path, string new_path', + 'description' => 'Move a file if and only if it was created by an upload', + ), + 'parse_ini_file' => + array ( + 'return' => 'array', + 'params' => 'string filename [, bool process_sections]', + 'description' => 'Parse configuration file', + ), + 'import_request_variables' => + array ( + 'return' => 'bool', + 'params' => 'string types [, string prefix]', + 'description' => 'Import GET/POST/Cookie variables into the global scope', + ), + 'define_syslog_variables' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Initializes all syslog-related variables', + ), + 'openlog' => + array ( + 'return' => 'bool', + 'params' => 'string ident, int option, int facility', + 'description' => 'Open connection to system logger', + ), + 'closelog' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Close connection to system logger', + ), + 'syslog' => + array ( + 'return' => 'bool', + 'params' => 'int priority, string message', + 'description' => 'Generate a system log message', + ), + 'phpinfo' => + array ( + 'return' => 'void', + 'params' => '[int what]', + 'description' => 'Output a page of useful information about PHP and the current request', + ), + 'phpversion' => + array ( + 'return' => 'string', + 'params' => '[string extension]', + 'description' => 'Return the current PHP version', + ), + 'phpcredits' => + array ( + 'return' => 'void', + 'params' => '[int flag]', + 'description' => 'Prints the list of people who\'ve contributed to the PHP project', + ), + 'php_logo_guid' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return the special ID used to request the PHP logo in phpinfo screens', + ), + 'php_real_logo_guid' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return the special ID used to request the PHP logo in phpinfo screens', + ), + 'php_egg_logo_guid' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return the special ID used to request the PHP logo in phpinfo screens', + ), + 'zend_logo_guid' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return the special ID used to request the Zend logo in phpinfo screens', + ), + 'php_sapi_name' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return the current SAPI module name', + ), + 'php_uname' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return information about the system PHP was built on', + ), + 'php_ini_scanned_files' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return comma-separated string of .ini files parsed from the additional ini dir', + ), + 'levenshtein' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2', + 'description' => 'Calculate Levenshtein distance between two strings', + ), + 'lcg_value' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns a value from the combined linear congruential generator', + ), + 'http_build_query' => + array ( + 'return' => 'string', + 'params' => 'mixed formdata [, string prefix [, string arg_separator]]', + 'description' => 'Generates a form-encoded query string from an associative array or object.', + ), + 'microtime' => + array ( + 'return' => 'mixed', + 'params' => '[bool get_as_float]', + 'description' => 'Returns either a string or a float containing the current time in seconds and microseconds', + ), + 'gettimeofday' => + array ( + 'return' => 'array', + 'params' => '[bool get_as_float]', + 'description' => 'Returns the current time as array', + ), + 'getrusage' => + array ( + 'return' => 'array', + 'params' => '[int who]', + 'description' => 'Returns an array of usage statistics', + ), + 'metaphone' => + array ( + 'return' => 'string', + 'params' => 'string text, int phones', + 'description' => 'Break english phrases down into their phonemes', + ), + 'htmlspecialchars' => + array ( + 'return' => 'string', + 'params' => 'string string [, int quote_style]', + 'description' => 'Convert special HTML entities back to characters', + ), + 'html_entity_decode' => + array ( + 'return' => 'string', + 'params' => 'string string [, int quote_style][, string charset]', + 'description' => 'Convert all HTML entities to their applicable characters', + ), + 'htmlentities' => + array ( + 'return' => 'string', + 'params' => 'string string [, int quote_style][, string charset]', + 'description' => 'Convert all applicable characters to HTML entities', + ), + 'get_html_translation_table' => + array ( + 'return' => 'array', + 'params' => '[int table [, int quote_style]]', + 'description' => 'Returns the internal translation table used by htmlspecialchars and htmlentities', + ), + 'stream_bucket_make_writeable' => + array ( + 'return' => 'object', + 'params' => 'resource brigade', + 'description' => 'Return a bucket object from the brigade for operating on', + ), + 'stream_bucket_prepend' => + array ( + 'return' => 'void', + 'params' => 'resource brigade, resource bucket', + 'description' => 'Prepend bucket to brigade', + ), + 'stream_bucket_append' => + array ( + 'return' => 'void', + 'params' => 'resource brigade, resource bucket', + 'description' => 'Append bucket to brigade', + ), + 'stream_bucket_new' => + array ( + 'return' => 'resource', + 'params' => 'resource stream, string buffer', + 'description' => 'Create a new bucket for use on the current stream', + ), + 'stream_get_filters' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns a list of registered filters', + ), + 'stream_filter_register' => + array ( + 'return' => 'bool', + 'params' => 'string filtername, string classname', + 'description' => 'Registers a custom filter handler class', + ), + 'sha1' => + array ( + 'return' => 'string', + 'params' => 'string str [, bool raw_output]', + 'description' => 'Calculate the sha1 hash of a string', + ), + 'sha1_file' => + array ( + 'return' => 'string', + 'params' => 'string filename [, bool raw_output]', + 'description' => 'Calculate the sha1 hash of given filename', + ), + 'image_type_to_mime_type' => + array ( + 'return' => 'string', + 'params' => 'int imagetype', + 'description' => 'Get Mime-Type for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype', + ), + 'image_type_to_extension' => + array ( + 'return' => 'string', + 'params' => 'int imagetype [, bool include_dot]', + 'description' => 'Get file extension for image-type returned by getimagesize, exif_read_data, exif_thumbnail, exif_imagetype', + ), + 'getimagesize' => + array ( + 'return' => 'array', + 'params' => 'string imagefile [, array info]', + 'description' => 'Get the size of an image as 4-element array', + ), + 'uniqid' => + array ( + 'return' => 'string', + 'params' => '[string prefix , bool more_entropy]', + 'description' => 'Generates a unique ID', + ), + 'parse_url' => + array ( + 'return' => 'mixed', + 'params' => 'string url, [int url_component]', + 'description' => 'Parse a URL and return its components', + ), + 'urlencode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'URL-encodes string', + ), + 'urldecode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Decodes URL-encoded string', + ), + 'rawurlencode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'URL-encodes string', + ), + 'rawurldecode' => + array ( + 'return' => 'string', + 'params' => 'string str', + 'description' => 'Decodes URL-encodes string', + ), + 'get_headers' => + array ( + 'return' => 'array', + 'params' => 'string url', + 'description' => 'fetches all the headers sent by the server in response to a HTTP request', + ), + 'uuencode' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'uuencode a string', + ), + 'uudecode' => + array ( + 'return' => 'string', + 'params' => 'string data', + 'description' => 'decode a uuencoded string', + ), + 'flock' => + array ( + 'return' => 'bool', + 'params' => 'resource fp, int operation [, int &wouldblock]', + 'description' => 'Portable file locking', + ), + 'get_meta_tags' => + array ( + 'return' => 'array', + 'params' => 'string filename [, bool use_include_path]', + 'description' => 'Extracts all meta tag content attributes from a file and returns an array', + ), + 'file_get_contents' => + array ( + 'return' => 'string', + 'params' => 'string filename [, bool use_include_path [, resource context [, long offset [, long maxlen]]]]', + 'description' => 'Read the entire file into a string', + ), + 'file_put_contents' => + array ( + 'return' => 'int', + 'params' => 'string file, mixed data [, int flags [, resource context]]', + 'description' => 'Write/Create a file with contents data and return the number of bytes written', + ), + 'file' => + array ( + 'return' => 'array', + 'params' => 'string filename [, int flags[, resource context]]', + 'description' => 'Read entire file into an array', + ), + 'tempnam' => + array ( + 'return' => 'string', + 'params' => 'string dir, string prefix', + 'description' => 'Create a unique filename in a directory', + ), + 'tmpfile' => + array ( + 'return' => 'resource', + 'params' => 'void', + 'description' => 'Create a temporary file that will be deleted automatically after use', + ), + 'fopen' => + array ( + 'return' => 'resource', + 'params' => 'string filename, string mode [, bool use_include_path [, resource context]]', + 'description' => 'Open a file or a URL and return a file pointer', + ), + 'fclose' => + array ( + 'return' => 'bool', + 'params' => 'resource fp', + 'description' => 'Close an open file pointer', + ), + 'popen' => + array ( + 'return' => 'resource', + 'params' => 'string command, string mode', + 'description' => 'Execute a command and open either a read or a write pipe to it', + ), + 'pclose' => + array ( + 'return' => 'int', + 'params' => 'resource fp', + 'description' => 'Close a file pointer opened by popen()', + ), + 'feof' => + array ( + 'return' => 'bool', + 'params' => 'resource fp', + 'description' => 'Test for end-of-file on a file pointer', + ), + 'fgets' => + array ( + 'return' => 'string', + 'params' => 'resource fp[, int length]', + 'description' => 'Get a line from file pointer', + ), + 'fgetc' => + array ( + 'return' => 'string', + 'params' => 'resource fp', + 'description' => 'Get a character from file pointer', + ), + 'fgetss' => + array ( + 'return' => 'string', + 'params' => 'resource fp [, int length, string allowable_tags]', + 'description' => 'Get a line from file pointer and strip HTML tags', + ), + 'fscanf' => + array ( + 'return' => 'mixed', + 'params' => 'resource stream, string format [, string ...]', + 'description' => 'Implements a mostly ANSI compatible fscanf()', + ), + 'fwrite' => + array ( + 'return' => 'int', + 'params' => 'resource fp, string str [, int length]', + 'description' => 'Binary-safe file write', + ), + 'fflush' => + array ( + 'return' => 'bool', + 'params' => 'resource fp', + 'description' => 'Flushes output', + ), + 'rewind' => + array ( + 'return' => 'bool', + 'params' => 'resource fp', + 'description' => 'Rewind the position of a file pointer', + ), + 'ftell' => + array ( + 'return' => 'int', + 'params' => 'resource fp', + 'description' => 'Get file pointer\'s read/write position', + ), + 'fseek' => + array ( + 'return' => 'int', + 'params' => 'resource fp, int offset [, int whence]', + 'description' => 'Seek on a file pointer', + ), + 'mkdir' => + array ( + 'return' => 'bool', + 'params' => 'string pathname [, int mode [, bool recursive [, resource context]]]', + 'description' => 'Create a directory', + ), + 'rmdir' => + array ( + 'return' => 'bool', + 'params' => 'string dirname[, resource context]', + 'description' => 'Remove a directory', + ), + 'readfile' => + array ( + 'return' => 'int', + 'params' => 'string filename [, bool use_include_path[, resource context]]', + 'description' => 'Output a file or a URL', + ), + 'umask' => + array ( + 'return' => 'int', + 'params' => '[int mask]', + 'description' => 'Return or change the umask', + ), + 'fpassthru' => + array ( + 'return' => 'int', + 'params' => 'resource fp', + 'description' => 'Output all remaining data from a file pointer', + ), + 'rename' => + array ( + 'return' => 'bool', + 'params' => 'string old_name, string new_name[, resource context]', + 'description' => 'Rename a file', + ), + 'unlink' => + array ( + 'return' => 'bool', + 'params' => 'string filename[, context context]', + 'description' => 'Delete a file', + ), + 'ftruncate' => + array ( + 'return' => 'bool', + 'params' => 'resource fp, int size', + 'description' => 'Truncate file to \'size\' length', + ), + 'fstat' => + array ( + 'return' => 'array', + 'params' => 'resource fp', + 'description' => 'Stat() on a filehandle', + ), + 'copy' => + array ( + 'return' => 'bool', + 'params' => 'string source_file, string destination_file', + 'description' => 'Copy a file', + ), + 'fread' => + array ( + 'return' => 'string', + 'params' => 'resource fp, int length', + 'description' => 'Binary-safe file read', + ), + 'fputcsv' => + array ( + 'return' => 'int', + 'params' => 'resource fp, array fields [, string delimiter [, string enclosure]]', + 'description' => 'Format line as CSV and write to file pointer', + ), + 'fgetcsv' => + array ( + 'return' => 'array', + 'params' => 'resource fp [,int length [, string delimiter [, string enclosure]]]', + 'description' => 'Get line from file pointer and parse for CSV fields', + ), + 'realpath' => + array ( + 'return' => 'string', + 'params' => 'string path', + 'description' => 'Return the resolved path', + ), + 'fnmatch' => + array ( + 'return' => 'bool', + 'params' => 'string pattern, string filename [, int flags]', + 'description' => 'Match filename against pattern', + ), + 'xmlwriter_set_indent' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, bool indent', + 'description' => 'Toggle indentation on/off - returns FALSE on error', + ), + 'xmlwriter_set_indent_string' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string indentString', + 'description' => 'Set string used for indenting - returns FALSE on error', + ), + 'xmlwriter_start_attribute' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name', + 'description' => 'Create start attribute - returns FALSE on error', + ), + 'xmlwriter_end_attribute' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End attribute - returns FALSE on error', + ), + 'xmlwriter_start_attribute_ns' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string prefix, string name, string uri', + 'description' => 'Create start namespaced attribute - returns FALSE on error', + ), + 'xmlwriter_write_attribute' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string content', + 'description' => 'Write full attribute - returns FALSE on error', + ), + 'xmlwriter_write_attribute_ns' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string prefix, string name, string uri, string content', + 'description' => 'Write full namespaced attribute - returns FALSE on error', + ), + 'xmlwriter_start_element' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name', + 'description' => 'Create start element tag - returns FALSE on error', + ), + 'xmlwriter_start_element_ns' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string prefix, string name, string uri', + 'description' => 'Create start namespaced element tag - returns FALSE on error', + ), + 'xmlwriter_end_element' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current element - returns FALSE on error', + ), + 'xmlwriter_write_element' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string content', + 'description' => 'Write full element tag - returns FALSE on error', + ), + 'xmlwriter_write_element_ns' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string prefix, string name, string uri, string content', + 'description' => 'Write full namesapced element tag - returns FALSE on error', + ), + 'xmlwriter_start_pi' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string target', + 'description' => 'Create start PI tag - returns FALSE on error', + ), + 'xmlwriter_end_pi' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current PI - returns FALSE on error', + ), + 'xmlwriter_write_pi' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string target, string content', + 'description' => 'Write full PI tag - returns FALSE on error', + ), + 'xmlwriter_start_cdata' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'Create start CDATA tag - returns FALSE on error', + ), + 'xmlwriter_end_cdata' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current CDATA - returns FALSE on error', + ), + 'xmlwriter_write_cdata' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string content', + 'description' => 'Write full CDATA tag - returns FALSE on error', + ), + 'xmlwriter_text' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string content', + 'description' => 'Write text - returns FALSE on error', + ), + 'xmlwriter_start_comment' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'Create start comment - returns FALSE on error', + ), + 'xmlwriter_end_comment' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'Create end comment - returns FALSE on error', + ), + 'xmlwriter_write_comment' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string content', + 'description' => 'Write full comment tag - returns FALSE on error', + ), + 'xmlwriter_start_document' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string version, string encoding, string standalone', + 'description' => 'Create document tag - returns FALSE on error', + ), + 'xmlwriter_end_document' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current document - returns FALSE on error', + ), + 'xmlwriter_start_dtd' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string pubid, string sysid', + 'description' => 'Create start DTD tag - returns FALSE on error', + ), + 'xmlwriter_end_dtd' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current DTD - returns FALSE on error', + ), + 'xmlwriter_write_dtd' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string pubid, string sysid, string subset', + 'description' => 'Write full DTD tag - returns FALSE on error', + ), + 'xmlwriter_start_dtd_element' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name', + 'description' => 'Create start DTD element - returns FALSE on error', + ), + 'xmlwriter_end_dtd_element' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current DTD element - returns FALSE on error', + ), + 'xmlwriter_write_dtd_element' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string content', + 'description' => 'Write full DTD element tag - returns FALSE on error', + ), + 'xmlwriter_start_dtd_attlist' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name', + 'description' => 'Create start DTD AttList - returns FALSE on error', + ), + 'xmlwriter_end_dtd_attlist' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current DTD AttList - returns FALSE on error', + ), + 'xmlwriter_write_dtd_attlist' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string content', + 'description' => 'Write full DTD AttList tag - returns FALSE on error', + ), + 'xmlwriter_start_dtd_entity' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, bool isparam', + 'description' => 'Create start DTD Entity - returns FALSE on error', + ), + 'xmlwriter_end_dtd_entity' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter', + 'description' => 'End current DTD Entity - returns FALSE on error', + ), + 'xmlwriter_write_dtd_entity' => + array ( + 'return' => 'bool', + 'params' => 'resource xmlwriter, string name, string content', + 'description' => 'Write full DTD Entity tag - returns FALSE on error', + ), + 'xmlwriter_open_uri' => + array ( + 'return' => 'resource', + 'params' => 'resource xmlwriter, string source', + 'description' => 'Create new xmlwriter using source uri for output', + ), + 'xmlwriter_open_memory' => + array ( + 'return' => 'resource', + 'params' => '', + 'description' => 'Create new xmlwriter using memory for string output', + ), + 'xmlwriter_output_memory' => + array ( + 'return' => 'string', + 'params' => 'resource xmlwriter [,bool flush]', + 'description' => 'Output current buffer as string', + ), + 'xmlwriter_flush' => + array ( + 'return' => 'mixed', + 'params' => 'resource xmlwriter [,bool empty]', + 'description' => 'Output current buffer', + ), + 'easter_date' => + array ( + 'return' => 'int', + 'params' => '[int year]', + 'description' => 'Return the timestamp of midnight on Easter of a given year (defaults to current year)', + ), + 'easter_days' => + array ( + 'return' => 'int', + 'params' => '[int year, [int method]]', + 'description' => 'Return the number of days after March 21 that Easter falls on for a given year (defaults to current year)', + ), + 'cal_info' => + array ( + 'return' => 'array', + 'params' => 'int calendar', + 'description' => 'Returns information about a particular calendar', + ), + 'cal_days_in_month' => + array ( + 'return' => 'int', + 'params' => 'int calendar, int month, int year', + 'description' => 'Returns the number of days in a month for a given year and calendar', + ), + 'cal_to_jd' => + array ( + 'return' => 'int', + 'params' => 'int calendar, int month, int day, int year', + 'description' => 'Converts from a supported calendar to Julian Day Count', + ), + 'cal_from_jd' => + array ( + 'return' => 'array', + 'params' => 'int jd, int calendar', + 'description' => 'Converts from Julian Day Count to a supported calendar and return extended information', + ), + 'jdtogregorian' => + array ( + 'return' => 'string', + 'params' => 'int juliandaycount', + 'description' => 'Converts a julian day count to a gregorian calendar date', + ), + 'gregoriantojd' => + array ( + 'return' => 'int', + 'params' => 'int month, int day, int year', + 'description' => 'Converts a gregorian calendar date to julian day count', + ), + 'jdtojulian' => + array ( + 'return' => 'string', + 'params' => 'int juliandaycount', + 'description' => 'Convert a julian day count to a julian calendar date', + ), + 'juliantojd' => + array ( + 'return' => 'int', + 'params' => 'int month, int day, int year', + 'description' => 'Converts a julian calendar date to julian day count', + ), + 'jdtojewish' => + array ( + 'return' => 'string', + 'params' => 'int juliandaycount [, bool hebrew [, int fl]]', + 'description' => 'Converts a julian day count to a jewish calendar date', + ), + 'jewishtojd' => + array ( + 'return' => 'int', + 'params' => 'int month, int day, int year', + 'description' => 'Converts a jewish calendar date to a julian day count', + ), + 'jdtofrench' => + array ( + 'return' => 'string', + 'params' => 'int juliandaycount', + 'description' => 'Converts a julian day count to a french republic calendar date', + ), + 'frenchtojd' => + array ( + 'return' => 'int', + 'params' => 'int month, int day, int year', + 'description' => 'Converts a french republic calendar date to julian day count', + ), + 'jddayofweek' => + array ( + 'return' => 'mixed', + 'params' => 'int juliandaycount [, int mode]', + 'description' => 'Returns name or number of day of week from julian day count', + ), + 'jdmonthname' => + array ( + 'return' => 'string', + 'params' => 'int juliandaycount, int mode', + 'description' => 'Returns name of month for julian day count', + ), + 'unixtojd' => + array ( + 'return' => 'int', + 'params' => '[int timestamp]', + 'description' => 'Convert UNIX timestamp to Julian Day', + ), + 'jdtounix' => + array ( + 'return' => 'int', + 'params' => 'int jday', + 'description' => 'Convert Julian Day to UNIX timestamp', + ), + 'mime_content_type' => + array ( + 'return' => 'string', + 'params' => 'string filename|resource stream', + 'description' => 'Return content-type for file', + ), + 'exif_tagname' => + array ( + 'return' => 'string', + 'params' => 'index', + 'description' => 'Get headername for index or false if not defined', + ), + 'exif_read_data' => + array ( + 'return' => 'array', + 'params' => 'string filename [, sections_needed [, sub_arrays[, read_thumbnail]]]', + 'description' => 'Reads header data from the JPEG/TIFF image filename and optionally reads the internal thumbnails', + ), + 'exif_thumbnail' => + array ( + 'return' => 'string', + 'params' => 'string filename [, &width, &height [, &imagetype]]', + 'description' => 'Reads the embedded thumbnail', + ), + 'exif_imagetype' => + array ( + 'return' => 'int', + 'params' => 'string imagefile', + 'description' => 'Get the type of an image', + ), + 'ming_setscale' => + array ( + 'return' => 'void', + 'params' => 'int scale', + 'description' => 'Set scale (?)', + ), + 'ming_useswfversion' => + array ( + 'return' => 'void', + 'params' => 'int version', + 'description' => 'Use SWF version (?)', + ), + 'ming_useconstants' => + array ( + 'return' => 'void', + 'params' => 'int use', + 'description' => 'Use constant pool (?)', + ), + 'swfaction::__construct' => + array ( + 'return' => 'void', + 'params' => 'string', + 'description' => 'Creates a new SWFAction object, compiling the given script', + ), + 'swfbitmap::__construct' => + array ( + 'return' => 'void', + 'params' => 'mixed file [, mixed maskfile]', + 'description' => 'Creates a new SWFBitmap object from jpg (with optional mask) or dbl file', + ), + 'swfbitmap::getWidth' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the width of this bitmap', + ), + 'swfbitmap::getHeight' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the height of this bitmap', + ), + 'swfbutton::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates a new SWFButton object', + ), + 'swfbutton::setHit' => + array ( + 'return' => 'void', + 'params' => 'object SWFCharacter', + 'description' => 'Sets the character for this button\'s hit test state', + ), + 'swfbutton::setOver' => + array ( + 'return' => 'void', + 'params' => 'object SWFCharacter', + 'description' => 'Sets the character for this button\'s over state', + ), + 'swfbutton::setUp' => + array ( + 'return' => 'void', + 'params' => 'object SWFCharacter', + 'description' => 'Sets the character for this button\'s up state', + ), + 'swfbutton::setDown' => + array ( + 'return' => 'void', + 'params' => 'object SWFCharacter', + 'description' => 'Sets the character for this button\'s down state', + ), + 'swfbutton::addShape' => + array ( + 'return' => 'void', + 'params' => 'object SWFCharacter, int flags', + 'description' => 'Sets the character to display for the condition described in flags', + ), + 'swfbutton::setMenu' => + array ( + 'return' => 'void', + 'params' => 'int flag', + 'description' => 'enable track as menu button behaviour', + ), + 'swfbutton::setAction' => + array ( + 'return' => 'void', + 'params' => 'object SWFAction', + 'description' => 'Sets the action to perform when button is pressed', + ), + 'swfbutton::addASound' => + array ( + 'return' => 'SWFSoundInstance', + 'params' => 'SWFSound sound, int flags', + 'description' => 'associates a sound with a button transitionNOTE: the transitions are all wrong _UP, _OVER, _DOWN _HIT', + ), + 'swfbutton::addAction' => + array ( + 'return' => 'void', + 'params' => 'object SWFAction, int flags', + 'description' => 'Sets the action to perform when conditions described in flags is met', + ), + 'ming_keypress' => + array ( + 'return' => 'int', + 'params' => 'string str', + 'description' => 'Returns the action flag for keyPress(char)', + ), + 'swfdisplayitem::moveTo' => + array ( + 'return' => 'void', + 'params' => 'int x, int y', + 'description' => 'Moves this SWFDisplayItem to movie coordinates (x, y)', + ), + 'swfdisplayitem::move' => + array ( + 'return' => 'void', + 'params' => 'float dx, float dy', + 'description' => 'Displaces this SWFDisplayItem by (dx, dy) in movie coordinates', + ), + 'swfdisplayitem::scaleTo' => + array ( + 'return' => 'void', + 'params' => 'float xScale [, float yScale]', + 'description' => 'Scales this SWFDisplayItem by xScale in the x direction, yScale in the y, or both to xScale if only one arg', + ), + 'swfdisplayitem::scale' => + array ( + 'return' => 'void', + 'params' => 'float xScale, float yScale', + 'description' => 'Multiplies this SWFDisplayItem\'s current x scale by xScale, its y scale by yScale', + ), + 'swfdisplayitem::rotateTo' => + array ( + 'return' => 'void', + 'params' => 'float degrees', + 'description' => 'Rotates this SWFDisplayItem the given (clockwise) degrees from its original orientation', + ), + 'swfdisplayitem::rotate' => + array ( + 'return' => 'void', + 'params' => 'float degrees', + 'description' => 'Rotates this SWFDisplayItem the given (clockwise) degrees from its current orientation', + ), + 'swfdisplayitem::skewXTo' => + array ( + 'return' => 'void', + 'params' => 'float xSkew', + 'description' => 'Sets this SWFDisplayItem\'s x skew value to xSkew', + ), + 'swfdisplayitem::skewX' => + array ( + 'return' => 'void', + 'params' => 'float xSkew', + 'description' => 'Adds xSkew to this SWFDisplayItem\'s x skew value', + ), + 'swfdisplayitem::skewYTo' => + array ( + 'return' => 'void', + 'params' => 'float ySkew', + 'description' => 'Sets this SWFDisplayItem\'s y skew value to ySkew', + ), + 'swfdisplayitem::skewY' => + array ( + 'return' => 'void', + 'params' => 'float ySkew', + 'description' => 'Adds ySkew to this SWFDisplayItem\'s y skew value', + ), + 'swfdisplayitem::setMatrix' => + array ( + 'return' => 'void', + 'params' => 'float a, float b, float c, float d, float x, float y', + 'description' => 'Sets the item\'s transform matrix', + ), + 'swfdisplayitem::setDepth' => + array ( + 'return' => 'void', + 'params' => 'int depth', + 'description' => 'Sets this SWFDisplayItem\'s z-depth to depth. Items with higher depth values are drawn on top of those with lower values', + ), + 'swfdisplayitem::setRatio' => + array ( + 'return' => 'void', + 'params' => 'float ratio', + 'description' => 'Sets this SWFDisplayItem\'s ratio to ratio. Obviously only does anything if displayitem was created from an SWFMorph', + ), + 'swfdisplayitem::addColor' => + array ( + 'return' => 'void', + 'params' => 'int r, int g, int b [, int a]', + 'description' => 'Sets the add color part of this SWFDisplayItem\'s CXform to (r, g, b [, a]), a defaults to 0', + ), + 'swfdisplayitem::multColor' => + array ( + 'return' => 'void', + 'params' => 'float r, float g, float b [, float a]', + 'description' => 'Sets the multiply color part of this SWFDisplayItem\'s CXform to (r, g, b [, a]), a defaults to 1.0', + ), + 'swfdisplayitem::setName' => + array ( + 'return' => 'void', + 'params' => 'string name', + 'description' => 'Sets this SWFDisplayItem\'s name to name', + ), + 'swfdisplayitem::addAction' => + array ( + 'return' => 'void', + 'params' => 'object SWFAction, int flags', + 'description' => 'Adds this SWFAction to the given SWFSprite instance', + ), + 'swfdisplayitem::setMaskLevel' => + array ( + 'return' => 'void', + 'params' => 'int level', + 'description' => 'defines a MASK layer at level', + ), + 'swfdisplayitem::endMask' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'another way of defining a MASK layer', + ), + 'swffill::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates a new SWFFill object', + ), + 'swffill::moveTo' => + array ( + 'return' => 'void', + 'params' => 'float x, float y', + 'description' => 'Moves this SWFFill to shape coordinates (x,y)', + ), + 'swffill::scaleTo' => + array ( + 'return' => 'void', + 'params' => 'float xScale [, float yScale]', + 'description' => 'Scales this SWFFill by xScale in the x direction, yScale in the y, or both to xScale if only one arg', + ), + 'swffill::rotateTo' => + array ( + 'return' => 'void', + 'params' => 'float degrees', + 'description' => 'Rotates this SWFFill the given (clockwise) degrees from its original orientation', + ), + 'swffill::skewXTo' => + array ( + 'return' => 'void', + 'params' => 'float xSkew', + 'description' => 'Sets this SWFFill\'s x skew value to xSkew', + ), + 'swffill::skewYTo' => + array ( + 'return' => 'void', + 'params' => 'float ySkew', + 'description' => 'Sets this SWFFill\'s y skew value to ySkew', + ), + 'swffontcha::raddChars' => + array ( + 'return' => 'void', + 'params' => 'string', + 'description' => 'adds characters to a font for exporting font', + ), + 'swffontchar::addChars' => + array ( + 'return' => 'void', + 'params' => 'string', + 'description' => 'adds characters to a font for exporting font', + ), + 'swffont::__construct' => + array ( + 'return' => 'void', + 'params' => 'string filename', + 'description' => 'Creates a new SWFFont object from given file', + ), + 'swffont::getWidth' => + array ( + 'return' => 'float', + 'params' => 'string str', + 'description' => 'Calculates the width of the given string in this font at full height', + ), + 'swffont::getUTF8Width' => + array ( + 'return' => 'int', + 'params' => 'string', + 'description' => 'Calculates the width of the given string in this font at full height', + ), + 'swffont::getWideWidth' => + array ( + 'return' => 'int', + 'params' => 'string', + 'description' => 'Calculates the width of the given string in this font at full height', + ), + 'swffont::getAscent' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the ascent of the font, or 0 if not available', + ), + 'swffont::getDescent' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the descent of the font, or 0 if not available', + ), + 'swffont::getLeading' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the leading of the font, or 0 if not available', + ), + 'swffont::addChars' => + array ( + 'return' => 'void', + 'params' => 'string', + 'description' => 'adds characters to a font required within textfields', + ), + 'swffont::getShape' => + array ( + 'return' => 'string', + 'params' => 'code', + 'description' => 'Returns the glyph shape of a char as a text string', + ), + 'swfgradient::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates a new SWFGradient object', + ), + 'swfgradient::addEntry' => + array ( + 'return' => 'void', + 'params' => 'float ratio, int r, int g, int b [, int a]', + 'description' => 'Adds given entry to the gradient', + ), + 'swfmorph::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates a new SWFMorph object', + ), + 'swfmorph::getShape1' => + array ( + 'return' => 'object', + 'params' => '', + 'description' => 'Return\'s this SWFMorph\'s start shape object', + ), + 'swfmorph::getShape2' => + array ( + 'return' => 'object', + 'params' => '', + 'description' => 'Return\'s this SWFMorph\'s start shape object', + ), + 'swfsound::__construct' => + array ( + 'return' => 'void', + 'params' => 'string filename, int flags', + 'description' => 'Creates a new SWFSound object from given file', + ), + 'swfvideostream_init' => + array ( + 'return' => 'class', + 'params' => '[file]', + 'description' => 'Returns a SWVideoStream object', + ), + 'swfprebuiltclip_init' => + array ( + 'return' => 'class', + 'params' => '[file]', + 'description' => 'Returns a SWFPrebuiltClip object', + ), + 'swfmovie::__construct' => + array ( + 'return' => 'void', + 'params' => 'int version', + 'description' => 'Creates swfmovie object according to the passed version', + ), + 'swfmovie::nextframe' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => '', + ), + 'swfmovie::labelframe' => + array ( + 'return' => 'void', + 'params' => 'object SWFBlock', + 'description' => '', + ), + 'swfmovie::add' => + array ( + 'return' => 'object', + 'params' => 'object SWFBlock', + 'description' => '', + ), + 'swfmovie::output' => + array ( + 'return' => 'int', + 'params' => '[int compression]', + 'description' => '', + ), + 'swfmovie::saveToFile' => + array ( + 'return' => 'int', + 'params' => 'stream x [, int compression]', + 'description' => '', + ), + 'swfmovie::save' => + array ( + 'return' => 'int', + 'params' => 'mixed where [, int compression]', + 'description' => 'Saves the movie. \'where\' can be stream and the movie will be saved there otherwise it is treated as string and written in file with that name', + ), + 'swfmovie::setBackground' => + array ( + 'return' => 'void', + 'params' => 'int r, int g, int b', + 'description' => 'Sets background color (r,g,b)', + ), + 'swfmovie::setRate' => + array ( + 'return' => 'void', + 'params' => 'float rate', + 'description' => 'Sets movie rate', + ), + 'swfmovie::setDimension' => + array ( + 'return' => 'void', + 'params' => 'float x, float y', + 'description' => 'Sets movie dimension', + ), + 'swfmovie::setFrames' => + array ( + 'return' => 'void', + 'params' => 'int frames', + 'description' => 'Sets number of frames', + ), + 'swfmovie::streamMP3' => + array ( + 'return' => 'void', + 'params' => 'mixed file', + 'description' => 'Sets sound stream of the SWF movie. The parameter can be stream or string.', + ), + 'swfshape::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates a new SWFShape object', + ), + 'swfshape::setline' => + array ( + 'return' => 'void', + 'params' => 'int width, int r, int g, int b [, int a]', + 'description' => 'Sets the current line style for this SWFShape', + ), + 'swfshape::addfill' => + array ( + 'return' => 'object', + 'params' => 'mixed arg1, int arg2, [int b [, int a]]', + 'description' => 'Returns a fill object, for use with swfshape_setleftfill and swfshape_setrightfill. If 1 or 2 parameter(s) is (are) passed first should be object (from gradient class) and the second int (flags). Gradient fill is performed. If 3 or 4 parameters are passed : r, g, b [, a]. Solid fill is performed.', + ), + 'swfshape::setleftfill' => + array ( + 'return' => 'void', + 'params' => 'int arg1 [, int g ,int b [,int a]]', + 'description' => 'Sets the right side fill style to fill in case only one parameter is passed. When 3 or 4 parameters are passed they are treated as : int r, int g, int b, int a . Solid fill is performed in this case before setting right side fill type.', + ), + 'swfshape::movepento' => + array ( + 'return' => 'void', + 'params' => 'float x, float y', + 'description' => 'Moves the pen to shape coordinates (x, y)', + ), + 'swfshape::movepen' => + array ( + 'return' => 'void', + 'params' => 'float x, float y', + 'description' => 'Moves the pen from its current location by vector (x, y)', + ), + 'swfshape::drawlineto' => + array ( + 'return' => 'void', + 'params' => 'float x, float y', + 'description' => 'Draws a line from the current pen position to shape coordinates (x, y) in the current line style', + ), + 'swfshape::drawline' => + array ( + 'return' => 'void', + 'params' => 'float dx, float dy', + 'description' => 'Draws a line from the current pen position (x, y) to the point (x+dx, y+dy) in the current line style', + ), + 'swfshape::drawcurveto' => + array ( + 'return' => 'void', + 'params' => 'float ax, float ay, float bx, float by [, float dx, float dy]', + 'description' => 'Draws a curve from the current pen position (x,y) to the point (bx, by) in the current line style, using point (ax, ay) as a control point. Or draws a cubic bezier to point (dx, dy) with control points (ax, ay) and (bx, by)', + ), + 'swfshape::drawcurve' => + array ( + 'return' => 'void', + 'params' => 'float adx, float ady, float bdx, float bdy [, float cdx, float cdy]', + 'description' => 'Draws a curve from the current pen position (x, y) to the point (x+bdx, y+bdy) in the current line style, using point (x+adx, y+ady) as a control point or draws a cubic bezier to point (x+cdx, x+cdy) with control points (x+adx, y+ady) and (x+bdx, y+bdy)', + ), + 'swfshape::drawglyph' => + array ( + 'return' => 'void', + 'params' => 'SWFFont font, string character [, int size]', + 'description' => 'Draws the first character in the given string into the shape using the glyph definition from the given font', + ), + 'swfshape::drawcircle' => + array ( + 'return' => 'void', + 'params' => 'float r', + 'description' => 'Draws a circle of radius r centered at the current location, in a counter-clockwise fashion', + ), + 'swfshape::drawarc' => + array ( + 'return' => 'void', + 'params' => 'float r, float startAngle, float endAngle', + 'description' => 'Draws an arc of radius r centered at the current location, from angle startAngle to angle endAngle measured clockwise from 12 o\'clock', + ), + 'swfshape::drawcubic' => + array ( + 'return' => 'void', + 'params' => 'float bx, float by, float cx, float cy, float dx, float dy', + 'description' => 'Draws a cubic bezier curve using the current position and the three given points as control points', + ), + 'swfsprite::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates a new SWFSprite object', + ), + 'swfsprite::add' => + array ( + 'return' => 'object', + 'params' => 'object SWFCharacter', + 'description' => 'Adds the character to the sprite, returns a displayitem object', + ), + 'swfsprite::remove' => + array ( + 'return' => 'void', + 'params' => 'object SWFDisplayItem', + 'description' => 'Remove the named character from the sprite\'s display list', + ), + 'swfsprite::nextFrame' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Moves the sprite to the next frame', + ), + 'swfsprite::labelFrame' => + array ( + 'return' => 'void', + 'params' => 'string label', + 'description' => 'Labels frame', + ), + 'swfsprite::setFrames' => + array ( + 'return' => 'void', + 'params' => 'int frames', + 'description' => 'Sets the number of frames in this SWFSprite', + ), + 'swftext::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Creates new SWFText object', + ), + 'swftext::setFont' => + array ( + 'return' => 'void', + 'params' => 'object font', + 'description' => 'Sets this SWFText object\'s current font to given font', + ), + 'swftext::setHeight' => + array ( + 'return' => 'void', + 'params' => 'float height', + 'description' => 'Sets this SWFText object\'s current height to given height', + ), + 'swftext::setSpacing' => + array ( + 'return' => 'void', + 'params' => 'float spacing', + 'description' => 'Sets this SWFText object\'s current letterspacing to given spacing', + ), + 'swftext::setColor' => + array ( + 'return' => 'void', + 'params' => 'int r, int g, int b [, int a]', + 'description' => 'Sets this SWFText object\'s current color to the given color', + ), + 'swftext::moveTo' => + array ( + 'return' => 'void', + 'params' => 'float x, float y', + 'description' => 'Moves this SWFText object\'s current pen position to (x, y) in text coordinates', + ), + 'swftext::addString' => + array ( + 'return' => 'void', + 'params' => 'string text', + 'description' => 'Writes the given text into this SWFText object at the current pen position, using the current font, height, spacing, and color', + ), + 'swftext::addUTF8String' => + array ( + 'return' => 'void', + 'params' => 'string text', + 'description' => 'Writes the given text into this SWFText object at the current pen position,using the current font, height, spacing, and color', + ), + 'swftext::addWideString' => + array ( + 'return' => 'void', + 'params' => 'string text', + 'description' => 'Writes the given text into this SWFText object at the current pen position,using the current font, height, spacing, and color', + ), + 'swftext::getWidth' => + array ( + 'return' => 'float', + 'params' => 'string str', + 'description' => 'Calculates the width of the given string in this text objects current font and size', + ), + 'swftext::getUTF8Width' => + array ( + 'return' => 'double', + 'params' => 'string', + 'description' => 'calculates the width of the given string in this text objects current font and size', + ), + 'swftext::getWideWidth' => + array ( + 'return' => 'double', + 'params' => 'string', + 'description' => 'calculates the width of the given string in this text objects current font and size', + ), + 'swftext::getAscent' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the ascent of the current font at its current size, or 0 if not available', + ), + 'swftext::getDescent' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the descent of the current font at its current size, or 0 if not available', + ), + 'swftext::getLeading' => + array ( + 'return' => 'float', + 'params' => '', + 'description' => 'Returns the leading of the current font at its current size, or 0 if not available', + ), + 'swftextfield::__construct' => + array ( + 'return' => 'void', + 'params' => '[int flags]', + 'description' => 'Creates a new SWFTextField object', + ), + 'swftextfield::setFont' => + array ( + 'return' => 'void', + 'params' => 'object font', + 'description' => 'Sets the font for this textfield', + ), + 'swftextfield::setBounds' => + array ( + 'return' => 'void', + 'params' => 'float width, float height', + 'description' => 'Sets the width and height of this textfield', + ), + 'swftextfield::align' => + array ( + 'return' => 'void', + 'params' => 'int alignment', + 'description' => 'Sets the alignment of this textfield', + ), + 'swftextfield::setHeight' => + array ( + 'return' => 'void', + 'params' => 'float height', + 'description' => 'Sets the font height of this textfield', + ), + 'swftextfield::setLeftMargin' => + array ( + 'return' => 'void', + 'params' => 'float margin', + 'description' => 'Sets the left margin of this textfield', + ), + 'swftextfield::setRightMargin' => + array ( + 'return' => 'void', + 'params' => 'float margin', + 'description' => 'Sets the right margin of this textfield', + ), + 'swftextfield::setMargins' => + array ( + 'return' => 'void', + 'params' => 'float left, float right', + 'description' => 'Sets both margins of this textfield', + ), + 'swftextfield::setIndentation' => + array ( + 'return' => 'void', + 'params' => 'float indentation', + 'description' => 'Sets the indentation of the first line of this textfield', + ), + 'swftextfield::setLineSpacing' => + array ( + 'return' => 'void', + 'params' => 'float space', + 'description' => 'Sets the line spacing of this textfield', + ), + 'swftextfield::setColor' => + array ( + 'return' => 'void', + 'params' => 'int r, int g, int b [, int a]', + 'description' => 'Sets the color of this textfield', + ), + 'swftextfield::setName' => + array ( + 'return' => 'void', + 'params' => 'string var_name', + 'description' => 'Sets the variable name of this textfield', + ), + 'swftextfield::addString' => + array ( + 'return' => 'void', + 'params' => 'string str', + 'description' => 'Adds the given string to this textfield', + ), + 'swftextfield::setPadding' => + array ( + 'return' => 'void', + 'params' => 'float padding', + 'description' => 'Sets the padding of this textfield', + ), + 'swftextfield::addChars' => + array ( + 'return' => 'void', + 'params' => 'string', + 'description' => 'adds characters to a font that will be available within a textfield', + ), + 'SplObjectStorage::attach' => + array ( + 'return' => 'void', + 'params' => '$obj', + 'description' => 'Attaches an object to the storage if not yet contained', + ), + 'SplObjectStorage::detach' => + array ( + 'return' => 'void', + 'params' => '$obj', + 'description' => 'Detaches an object from the storage', + ), + 'SplObjectStorage::contains' => + array ( + 'return' => 'bool', + 'params' => '$obj', + 'description' => 'Determine whethe an object is contained in the storage', + ), + 'SplObjectStorage::count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Determine number of objects in storage', + ), + 'SplObjectStorage::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => '', + ), + 'SplObjectStorage::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => '', + ), + 'SplObjectStorage::key' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => '', + ), + 'SplObjectStorage::current' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => '', + ), + 'SplObjectStorage::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => '', + ), + 'RecursiveIteratorIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind the iterator to the first element of the top level inner iterator.', + ), + 'RecursiveIteratorIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the current position is valid', + ), + 'RecursiveIteratorIterator::key' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => 'Access the current key', + ), + 'RecursiveIteratorIterator::current' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => 'Access the current element value', + ), + 'RecursiveIteratorIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move forward to the next element', + ), + 'RecursiveIteratorIterator::getDepth' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get the current depth of the recursive iteration', + ), + 'RecursiveIteratorIterator::getSubIterator' => + array ( + 'return' => 'RecursiveIterator', + 'params' => '[int level]', + 'description' => 'The current active sub iterator or the iterator at specified level', + ), + 'RecursiveIteratorIterator::getInnerIterator' => + array ( + 'return' => 'RecursiveIterator', + 'params' => '', + 'description' => 'The current active sub iterator', + ), + 'RecursiveIteratorIterator::beginIteration' => + array ( + 'return' => 'RecursiveIterator', + 'params' => '', + 'description' => 'Called when iteration begins (after first rewind() call)', + ), + 'RecursiveIteratorIterator::endIteration' => + array ( + 'return' => 'RecursiveIterator', + 'params' => '', + 'description' => 'Called when iteration ends (when valid() first returns false', + ), + 'RecursiveIteratorIterator::callHasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Called for each element to test whether it has children', + ), + 'RecursiveIteratorIterator::callGetChildren' => + array ( + 'return' => 'RecursiveIterator', + 'params' => '', + 'description' => 'Return children of current element', + ), + 'RecursiveIteratorIterator::beginChildren' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Called when recursing one level down', + ), + 'RecursiveIteratorIterator::endChildren' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Called when end recursing one level', + ), + 'RecursiveIteratorIterator::nextElement' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Called when the next element is available', + ), + 'RecursiveIteratorIterator::setMaxDepth' => + array ( + 'return' => 'void', + 'params' => '[$max_depth = -1]', + 'description' => 'Set the maximum allowed depth (or any depth if pmax_depth = -1]', + ), + 'RecursiveIteratorIterator::getMaxDepth' => + array ( + 'return' => 'int|false', + 'params' => '', + 'description' => 'Return the maximum accepted depth or false if any depth is allowed', + ), + 'FilterIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'Iterator it', + 'description' => 'Create an Iterator from another iterator', + ), + 'FilterIterator::getInnerIterator' => + array ( + 'return' => 'Iterator', + 'params' => '', + 'description' => 'proto Iterator CachingIterator::getInnerIterator()proto Iterator LimitIterator::getInnerIterator()proto Iterator ParentIterator::getInnerIterator()Get the inner iterator', + ), + 'ParentIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'proto void IteratorIterator::rewind()Rewind the iterator', + ), + 'FilterIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'proto bool ParentIterator::valid()proto bool IteratorIterator::valid()proto bool NoRewindIterator::valid()Check whether the current element is valid', + ), + 'FilterIterator::key' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => 'proto mixed CachingIterator::key()proto mixed LimitIterator::key()proto mixed ParentIterator::key()proto mixed IteratorIterator::key()proto mixed NoRewindIterator::key()proto mixed AppendIterator::key()Get the current key', + ), + 'FilterIterator::current' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => 'proto mixed CachingIterator::current()proto mixed LimitIterator::current()proto mixed ParentIterator::current()proto mixed IteratorIterator::current()proto mixed NoRewindIterator::current()proto mixed AppendIterator::current()Get the current element value', + ), + 'ParentIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'proto void IteratorIterator::next()proto void NoRewindIterator::next()Move the iterator forward', + ), + 'FilterIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind the iterator', + ), + 'FilterIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move the iterator forward', + ), + 'RecursiveFilterIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'RecursiveIterator it', + 'description' => 'Create a RecursiveFilterIterator from a RecursiveIterator', + ), + 'RecursiveFilterIterator::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the inner iterator\'s current element has children', + ), + 'RecursiveFilterIterator::getChildren' => + array ( + 'return' => 'RecursiveFilterIterator', + 'params' => '', + 'description' => 'Return the inner iterator\'s children contained in a RecursiveFilterIterator', + ), + 'ParentIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'RecursiveIterator it', + 'description' => 'Create a ParentIterator from a RecursiveIterator', + ), + 'ParentIterator::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the inner iterator\'s current element has children', + ), + 'ParentIterator::getChildren' => + array ( + 'return' => 'ParentIterator', + 'params' => '', + 'description' => 'Return the inner iterator\'s children contained in a ParentIterator', + ), + 'LimitIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind the iterator to the specified starting offset', + ), + 'LimitIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the current element is valid', + ), + 'LimitIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move the iterator forward', + ), + 'LimitIterator::seek' => + array ( + 'return' => 'void', + 'params' => 'int position', + 'description' => 'Seek to the given position', + ), + 'LimitIterator::getPosition' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Return the current position', + ), + 'CachingIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'Iterator it [, flags = CIT_CALL_TOSTRING]', + 'description' => 'Construct a CachingIterator from an Iterator', + ), + 'CachingIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind the iterator', + ), + 'CachingIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the current element is valid', + ), + 'CachingIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move the iterator forward', + ), + 'CachingIterator::hasNext' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the inner iterator has a valid next element', + ), + 'CachingIterator::__toString' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return the string representation of the current element', + ), + 'RecursiveCachingIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'RecursiveIterator it [, flags = CIT_CALL_TOSTRING]', + 'description' => 'Create an iterator from a RecursiveIterator', + ), + 'RecursiveCachingIterator::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether the current element of the inner iterator has children', + ), + 'RecursiveCachingIterator::getChildren' => + array ( + 'return' => 'RecursiveCachingIterator', + 'params' => '', + 'description' => 'Return the inner iterator\'s children as a RecursiveCachingIterator', + ), + 'IteratorIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'Traversable it', + 'description' => 'Create an iterator from anything that is traversable', + ), + 'NoRewindIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'Iterator it', + 'description' => 'Create an iterator from another iterator', + ), + 'NoRewindIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Prevent a call to inner iterators rewind()', + ), + 'NoRewindIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Return inner iterators valid()', + ), + 'NoRewindIterator::key' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => 'Return inner iterators key()', + ), + 'NoRewindIterator::current' => + array ( + 'return' => 'mixed', + 'params' => '', + 'description' => 'Return inner iterators current()', + ), + 'NoRewindIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Return inner iterators next()', + ), + 'InfiniteIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'Iterator it', + 'description' => 'Create an iterator from another iterator', + ), + 'InfiniteIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Prevent a call to inner iterators rewind() (internally the current data will be fetched if valid())', + ), + 'EmptyIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Does nothing', + ), + 'EmptyIterator::valid' => + array ( + 'return' => 'false', + 'params' => '', + 'description' => 'Return false', + ), + 'EmptyIterator::key' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Throws exception BadMethodCallException', + ), + 'EmptyIterator::current' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Throws exception BadMethodCallException', + ), + 'EmptyIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Does nothing', + ), + 'AppendIterator::__construct' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Create an AppendIterator', + ), + 'AppendIterator::append' => + array ( + 'return' => 'void', + 'params' => 'Iterator it', + 'description' => 'Append an iterator', + ), + 'AppendIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind to the first iterator and rewind the first iterator, too', + ), + 'AppendIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check if the current state is valid', + ), + 'AppendIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Forward to next element', + ), + 'iterator_to_array' => + array ( + 'return' => 'array', + 'params' => 'Traversable it', + 'description' => 'Copy the iterator into an array', + ), + 'iterator_count' => + array ( + 'return' => 'int', + 'params' => 'Traversable it', + 'description' => 'Count the elements in an iterator', + ), + 'class_parents' => + array ( + 'return' => 'array', + 'params' => 'object instance', + 'description' => 'Return an array containing the names of all parent classes', + ), + 'class_implements' => + array ( + 'return' => 'array', + 'params' => 'mixed what [, bool autoload ]', + 'description' => 'Return all classes and interfaces implemented by SPL', + ), + 'spl_classes' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Return an array containing the names of all clsses and interfaces defined in SPL', + ), + 'spl_autoload' => + array ( + 'return' => 'void', + 'params' => 'string class_name [, string file_extensions]', + 'description' => 'Default implementation for __autoload()', + ), + 'spl_autoload_extensions' => + array ( + 'return' => 'string', + 'params' => '[string file_extensions]', + 'description' => 'Register and return default file extensions for spl_autoload', + ), + 'spl_autoload_call' => + array ( + 'return' => 'void', + 'params' => 'string class_name', + 'description' => 'Try all registerd autoload function to load the requested class', + ), + 'spl_autoload_register' => + array ( + 'return' => 'bool', + 'params' => '[mixed autoload_function = "spl_autoload" [, throw = true]]', + 'description' => 'Register given function as __autoload() implementation', + ), + 'spl_autoload_unregister' => + array ( + 'return' => 'bool', + 'params' => 'mixed autoload_function', + 'description' => 'Unregister given function as __autoload() implementation', + ), + 'spl_autoload_functions' => + array ( + 'return' => 'false|array', + 'params' => '', + 'description' => 'Return all registered __autoload() functionns', + ), + 'SimpleXMLIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind to first element', + ), + 'SimpleXMLIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether iteration is valid', + ), + 'SimpleXMLIterator::current' => + array ( + 'return' => 'SimpleXMLIterator', + 'params' => '', + 'description' => 'Get current element', + ), + 'SimpleXMLIterator::key' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get name of current child element', + ), + 'SimpleXMLIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move to next element', + ), + 'SimpleXMLIterator::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether element has children (elements)', + ), + 'SimpleXMLIterator::getChildren' => + array ( + 'return' => 'SimpleXMLIterator', + 'params' => '', + 'description' => 'Get child element iterator', + ), + 'SimpleXMLIterator::count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get number of child elements', + ), + 'DirectoryIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'string path', + 'description' => 'Cronstructs a new dir iterator from a path.', + ), + 'DirectoryIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind dir back to the start', + ), + 'DirectoryIterator::key' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return current dir entry', + ), + 'DirectoryIterator::current' => + array ( + 'return' => 'DirectoryIterator', + 'params' => '', + 'description' => 'Return this (needed for Iterator interface)', + ), + 'DirectoryIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move to next entry', + ), + 'DirectoryIterator::valid' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Check whether dir contains more entries', + ), + 'SplFileInfo::getPath' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return the path', + ), + 'SplFileInfo::getFilename' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return filename only', + ), + 'DirectoryIterator::getFilename' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return filename of current dir entry', + ), + 'SplFileInfo::getPathname' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return path and filename', + ), + 'RecursiveDirectoryIterator::key' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return getPathname() or getFilename() depending on flags', + ), + 'RecursiveDirectoryIterator::current' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return getFilename(), getFileInfo() or $this depending on flags', + ), + 'DirectoryIterator::isDot' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if current entry is \'.\' or \'..\'', + ), + 'SplFileInfo::__construct' => + array ( + 'return' => 'void', + 'params' => 'string file_name', + 'description' => 'Cronstructs a new SplFileInfo from a path.', + ), + 'SplFileInfo::getPerms' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get file permissions', + ), + 'SplFileInfo::getInode' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get file inode', + ), + 'SplFileInfo::getSize' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get file size', + ), + 'SplFileInfo::getOwner' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get file owner', + ), + 'SplFileInfo::getGroup' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get file group', + ), + 'SplFileInfo::getATime' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get last access time of file', + ), + 'SplFileInfo::getMTime' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get last modification time of file', + ), + 'SplFileInfo::getCTime' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get inode modification time of file', + ), + 'SplFileInfo::getType' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get file type', + ), + 'SplFileInfo::isWritable' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if file can be written', + ), + 'SplFileInfo::isReadable' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if file can be read', + ), + 'SplFileInfo::isExecutable' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if file is executable', + ), + 'SplFileInfo::isFile' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if file is a regular file', + ), + 'SplFileInfo::isDir' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if file is directory', + ), + 'SplFileInfo::isLink' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Returns true if file is symbolic link', + ), + 'SplFileInfo::openFile' => + array ( + 'return' => 'SplFileObject', + 'params' => '[string mode = \'r\' [, bool use_include_path [, resource context]]]', + 'description' => 'Open the current file', + ), + 'SplFileInfo::setFileClass' => + array ( + 'return' => 'void', + 'params' => '[string class_name]', + 'description' => 'Class to use in openFile()', + ), + 'SplFileInfo::setInfoClass' => + array ( + 'return' => 'void', + 'params' => '[string class_name]', + 'description' => 'Class to use in getFileInfo(), getPathInfo()', + ), + 'SplFileInfo::getFileInfo' => + array ( + 'return' => 'SplFileInfo', + 'params' => '[string $class_name]', + 'description' => 'Get/copy file info', + ), + 'SplFileInfo::getPathInfo' => + array ( + 'return' => 'SplFileInfo', + 'params' => '[string $class_name]', + 'description' => 'Get/copy file info', + ), + 'RecursiveDirectoryIterator::__construct' => + array ( + 'return' => 'void', + 'params' => 'string path [, int flags]', + 'description' => 'Cronstructs a new dir iterator from a path.', + ), + 'RecursiveDirectoryIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind dir back to the start', + ), + 'RecursiveDirectoryIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move to next entry', + ), + 'RecursiveDirectoryIterator::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '[bool $allow_links = false]', + 'description' => 'Returns whether current entry is a directory and not \'.\' or \'..\'', + ), + 'DirectoryIterator::getChildren' => + array ( + 'return' => 'RecursiveDirectoryIterator', + 'params' => '', + 'description' => 'Returns an iterator for the current entry if it is a directory', + ), + 'RecursiveDirectoryIterator::getSubPath' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Get sub path', + ), + 'RecursiveDirectoryIterator::getSubPathname' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Get sub path and file name', + ), + 'SplFileObject::__construct' => + array ( + 'return' => 'void', + 'params' => '[int max_memory]', + 'description' => 'Construct a new temp file object', + ), + 'SplFileObject::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind the file and read the first line', + ), + 'SplFileObject::getFilename' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return the filename', + ), + 'SplFileObject::eof' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Return whether end of file is reached', + ), + 'SplFileObject::valid' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Return !eof()', + ), + 'SplFileObject::fgets' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Rturn next line from file', + ), + 'SplFileObject::current' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return current line from file', + ), + 'SplFileObject::key' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Return line number', + ), + 'SplFileObject::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Read next line', + ), + 'SplFileObject::setFlags' => + array ( + 'return' => 'void', + 'params' => 'int flags', + 'description' => 'Set file handling flags', + ), + 'SplFileObject::getFlags' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get file handling flags', + ), + 'SplFileObject::setMaxLineLen' => + array ( + 'return' => 'void', + 'params' => 'int max_len', + 'description' => 'Set maximum line length', + ), + 'SplFileObject::getMaxLineLen' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get maximum line length', + ), + 'SplFileObject::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Return false', + ), + 'SplFileObject::getChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Read NULL', + ), + 'SplFileObject::fgetcsv' => + array ( + 'return' => 'array', + 'params' => '[string delimiter [, string enclosure]]', + 'description' => 'Return current line as csv', + ), + 'SplFileObject::flock' => + array ( + 'return' => 'bool', + 'params' => 'int operation [, int &wouldblock]', + 'description' => 'Portable file locking', + ), + 'SplFileObject::fflush' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Flush the file', + ), + 'SplFileObject::ftell' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Return current file position', + ), + 'SplFileObject::fseek' => + array ( + 'return' => 'int', + 'params' => 'int pos [, int whence = SEEK_SET]', + 'description' => 'Return current file position', + ), + 'SplFileObject::fgetc' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get a character form the file', + ), + 'SplFileObject::fgetss' => + array ( + 'return' => 'string', + 'params' => '[string allowable_tags]', + 'description' => 'Get a line from file pointer and strip HTML tags', + ), + 'SplFileObject::fpassthru' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Output all remaining data from a file pointer', + ), + 'SplFileObject::fscanf' => + array ( + 'return' => 'bool', + 'params' => 'string format [, string ...]', + 'description' => 'Implements a mostly ANSI compatible fscanf()', + ), + 'SplFileObject::fwrite' => + array ( + 'return' => 'mixed', + 'params' => 'string str [, int length]', + 'description' => 'Binary-safe file write', + ), + 'SplFileObject::fstat' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Stat() on a filehandle', + ), + 'SplFileObject::ftruncate' => + array ( + 'return' => 'bool', + 'params' => 'int size', + 'description' => 'Truncate file to \'size\' length', + ), + 'SplFileObject::seek' => + array ( + 'return' => 'void', + 'params' => 'int line_pos', + 'description' => 'Seek to specified line', + ), + 'ArrayObject::offsetExists' => + array ( + 'return' => 'bool', + 'params' => 'mixed $index', + 'description' => 'proto bool ArrayIterator::offsetExists(mixed $index)Returns whether the requested $index exists.', + ), + 'ArrayObject::offsetGet' => + array ( + 'return' => 'bool', + 'params' => 'mixed $index', + 'description' => 'proto bool ArrayIterator::offsetGet(mixed $index)Returns the value at the specified $index.', + ), + 'ArrayObject::offsetSet' => + array ( + 'return' => 'void', + 'params' => 'mixed $index, mixed $newval', + 'description' => 'proto void ArrayIterator::offsetSet(mixed $index, mixed $newval)Sets the value at the specified $index to $newval.', + ), + 'ArrayObject::append' => + array ( + 'return' => 'void', + 'params' => 'mixed $newval', + 'description' => 'proto void ArrayIterator::append(mixed $newval)Appends the value (cannot be called for objects).', + ), + 'ArrayObject::offsetUnset' => + array ( + 'return' => 'void', + 'params' => 'mixed $index', + 'description' => 'proto void ArrayIterator::offsetUnset(mixed $index)Unsets the value at the specified $index.', + ), + 'ArrayObject::__construct' => + array ( + 'return' => 'void', + 'params' => 'array|object ar = array() [, int flags = 0 [, string iterator_class = "ArrayIterator"]]', + 'description' => 'proto void ArrayIterator::__construct(array|object ar = array() [, int flags = 0])Cronstructs a new array iterator from a path.', + ), + 'ArrayObject::setIteratorClass' => + array ( + 'return' => 'void', + 'params' => 'string iterator_class', + 'description' => 'Set the class used in getIterator.', + ), + 'ArrayObject::getIteratorClass' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get the class used in getIterator.', + ), + 'ArrayObject::getFlags' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get flags', + ), + 'ArrayObject::setFlags' => + array ( + 'return' => 'void', + 'params' => 'int flags', + 'description' => 'Set flags', + ), + 'ArrayObject::exchangeArray' => + array ( + 'return' => 'Array|Object', + 'params' => 'Array|Object ar = array()', + 'description' => 'Replace the referenced array or object with a new one and return the old one (right now copy - to be changed)', + ), + 'ArrayObject::getIterator' => + array ( + 'return' => 'ArrayIterator', + 'params' => '', + 'description' => 'Create a new iterator from a ArrayObject instance', + ), + 'ArrayIterator::rewind' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Rewind array back to the start', + ), + 'ArrayIterator::seek' => + array ( + 'return' => 'void', + 'params' => 'int $position', + 'description' => 'Seek to position.', + ), + 'ArrayObject::count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'proto int ArrayIterator::count()Return the number of elements in the Iterator.', + ), + 'ArrayIterator::current' => + array ( + 'return' => 'mixed|NULL', + 'params' => '', + 'description' => 'Return current array entry', + ), + 'ArrayIterator::key' => + array ( + 'return' => 'mixed|NULL', + 'params' => '', + 'description' => 'Return current array key', + ), + 'ArrayIterator::next' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => 'Move to next entry', + ), + 'ArrayIterator::valid' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether array contains more entries', + ), + 'RecursiveArrayIterator::hasChildren' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => 'Check whether current element has children (e.g. is an array)', + ), + 'RecursiveArrayIterator::getChildren' => + array ( + 'return' => 'object', + 'params' => '', + 'description' => 'Create a sub iterator for the current element (same class as $this)', + ), + 'hash' => + array ( + 'return' => 'string', + 'params' => 'string algo, string data[, bool raw_output = false]', + 'description' => 'Generate a hash of a given input stringReturns lowercase hexits by default', + ), + 'hash_file' => + array ( + 'return' => 'string', + 'params' => 'string algo, string filename[, bool raw_output = false]', + 'description' => 'Generate a hash of a given fileReturns lowercase hexits by default', + ), + 'hash_hmac' => + array ( + 'return' => 'string', + 'params' => 'string algo, string data, string key[, bool raw_output = false]', + 'description' => 'Generate a hash of a given input string with a key using HMACReturns lowercase hexits by default', + ), + 'hash_hmac_file' => + array ( + 'return' => 'string', + 'params' => 'string algo, string filename, string key[, bool raw_output = false]', + 'description' => 'Generate a hash of a given file with a key using HMACReturns lowercase hexits by default', + ), + 'hash_init' => + array ( + 'return' => 'resource', + 'params' => 'string algo[, int options, string key]', + 'description' => 'Initialize a hashing context', + ), + 'hash_update' => + array ( + 'return' => 'bool', + 'params' => 'resource context, string data', + 'description' => 'Pump data into the hashing algorithm', + ), + 'hash_update_stream' => + array ( + 'return' => 'int', + 'params' => 'resource context, resource handle[, integer length]', + 'description' => 'Pump data into the hashing algorithm from an open stream', + ), + 'hash_update_file' => + array ( + 'return' => 'bool', + 'params' => 'resource context, string filename[, resource context]', + 'description' => 'Pump data into the hashing algorithm from a file', + ), + 'hash_final' => + array ( + 'return' => 'string', + 'params' => 'resource context[, bool raw_output=false]', + 'description' => 'Output resulting digest', + ), + 'hash_algos' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Return a list of registered hashing algorithms', + ), + 'sybase_unbuffered_query' => + array ( + 'return' => 'int', + 'params' => 'string query [, int link_id]', + 'description' => 'Send Sybase query', + ), + 'sybase_fetch_assoc' => + array ( + 'return' => 'array', + 'params' => 'int result', + 'description' => 'Fetch row as array without numberic indices', + ), + 'sybase_min_client_severity' => + array ( + 'return' => 'void', + 'params' => 'int severity', + 'description' => 'Sets minimum client severity', + ), + 'sybase_min_server_severity' => + array ( + 'return' => 'void', + 'params' => 'int severity', + 'description' => 'Sets minimum server severity', + ), + 'sybase_deadlock_retry_count' => + array ( + 'return' => 'void', + 'params' => 'int retry_count', + 'description' => 'Sets deadlock retry count', + ), + 'sybase_set_message_handler' => + array ( + 'return' => 'bool', + 'params' => 'mixed error_func [, resource connection]', + 'description' => 'Set the error handler, to be called when a server message is raised.If error_func is NULL the handler will be deleted', + ), + 'mhash_count' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Gets the number of available hashes', + ), + 'mhash_get_block_size' => + array ( + 'return' => 'int', + 'params' => 'int hash', + 'description' => 'Gets the block size of hash', + ), + 'mhash_get_hash_name' => + array ( + 'return' => 'string', + 'params' => 'int hash', + 'description' => 'Gets the name of hash', + ), + 'mhash' => + array ( + 'return' => 'string', + 'params' => 'int hash, string data [, string key]', + 'description' => 'Hash data with hash', + ), + 'mhash_keygen_s2k' => + array ( + 'return' => 'string', + 'params' => 'int hash, string input_password, string salt, int bytes', + 'description' => 'Generates a key using hash functions', + ), + 'tidy_parse_string' => + array ( + 'return' => 'bool', + 'params' => 'string input [, mixed config_options [, string encoding]]', + 'description' => 'Parse a document stored in a string', + ), + 'tidy_get_error_buffer' => + array ( + 'return' => 'string', + 'params' => '[boolean detailed]', + 'description' => 'Return warnings and errors which occured parsing the specified document', + ), + 'tidy_get_output' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Return a string representing the parsed tidy markup', + ), + 'tidy_parse_file' => + array ( + 'return' => 'boolean', + 'params' => 'string file [, mixed config_options [, string encoding [, bool use_include_path]]]', + 'description' => 'Parse markup in file or URI', + ), + 'tidy_clean_repair' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Execute configured cleanup and repair operations on parsed markup', + ), + 'tidy_repair_string' => + array ( + 'return' => 'boolean', + 'params' => 'string data [, mixed config_file [, string encoding]]', + 'description' => 'Repair a string using an optionally provided configuration file', + ), + 'tidy_repair_file' => + array ( + 'return' => 'boolean', + 'params' => 'string filename [, mixed config_file [, string encoding [, bool use_include_path]]]', + 'description' => 'Repair a file using an optionally provided configuration file', + ), + 'tidy_diagnose' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Run configured diagnostics on parsed and repaired markup.', + ), + 'tidy_get_release' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get release date (version) for Tidy library', + ), + 'tidy_get_opt_doc' => + array ( + 'return' => 'string', + 'params' => 'tidy resource, string optname', + 'description' => 'Returns the documentation for the given option name', + ), + 'tidy_get_config' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Get current Tidy configuarion', + ), + 'tidy_get_status' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get status of specfied document.', + ), + 'tidy_get_html_ver' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get the Detected HTML version for the specified document.', + ), + 'tidy_is_xhtml' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Indicates if the document is a generic (non HTML/XHTML) XML document.', + ), + 'tidy_error_count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the Number of Tidy errors encountered for specified document.', + ), + 'tidy_warning_count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the Number of Tidy warnings encountered for specified document.', + ), + 'tidy_access_count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the Number of Tidy accessibility warnings encountered for specified document.', + ), + 'tidy_config_count' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Returns the Number of Tidy configuration errors encountered for specified document.', + ), + 'tidy_getopt' => + array ( + 'return' => 'mixed', + 'params' => 'string option', + 'description' => 'Returns the value of the specified configuration option for the tidy document.', + ), + 'tidy_get_root' => + array ( + 'return' => 'TidyNode', + 'params' => '', + 'description' => 'Returns a TidyNode Object representing the root of the tidy parse tree', + ), + 'tidy_get_html' => + array ( + 'return' => 'TidyNode', + 'params' => '', + 'description' => 'Returns a TidyNode Object starting from the tag of the tidy parse tree', + ), + 'tidy_get_head' => + array ( + 'return' => 'TidyNode', + 'params' => '', + 'description' => 'Returns a TidyNode Object starting from the tag of the tidy parse tree', + ), + 'tidy_get_body' => + array ( + 'return' => 'TidyNode', + 'params' => 'resource tidy', + 'description' => 'Returns a TidyNode Object starting from the tag of the tidy parse tree', + ), + 'tidyNode::hasChildren' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node has children', + ), + 'tidyNode::hasSiblings' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node has siblings', + ), + 'tidyNode::isComment' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node represents a comment', + ), + 'tidyNode::isHtml' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node is part of a HTML document', + ), + 'tidyNode::isXhtml' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node is part of a XHTML document', + ), + 'tidyNode::isXml' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node is part of a XML document', + ), + 'tidyNode::isText' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node represents text (no markup)', + ), + 'tidyNode::isJste' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node is JSTE', + ), + 'tidyNode::isAsp' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node is ASP', + ), + 'tidyNode::isPhp' => + array ( + 'return' => 'boolean', + 'params' => '', + 'description' => 'Returns true if this node is PHP', + ), + 'smfi_setflags' => + array ( + 'return' => 'string', + 'params' => 'long flags', + 'description' => 'Sets the flags describing the actions the filter may take.', + ), + 'smfi_settimeout' => + array ( + 'return' => 'string', + 'params' => 'long timeout', + 'description' => 'Sets the number of seconds libmilter will wait for an MTA connection before timing out a socket.', + ), + 'smfi_getsymval' => + array ( + 'return' => 'string', + 'params' => 'string macro', + 'description' => 'Returns the value of the given macro or NULL if the macro is not defined.', + ), + 'smfi_setreply' => + array ( + 'return' => 'string', + 'params' => 'string rcode, string xcode, string message', + 'description' => 'Directly set the SMTP error reply code for this connection.This code will be used on subsequent error replies resulting from actions taken by this filter.', + ), + 'smfi_addheader' => + array ( + 'return' => 'string', + 'params' => 'string headerf, string headerv', + 'description' => 'Adds a header to the current message.', + ), + 'smfi_chgheader' => + array ( + 'return' => 'string', + 'params' => 'string headerf, string headerv', + 'description' => 'Changes a header\'s value for the current message.', + ), + 'smfi_addrcpt' => + array ( + 'return' => 'string', + 'params' => 'string rcpt', + 'description' => 'Add a recipient to the message envelope.', + ), + 'smfi_delrcpt' => + array ( + 'return' => 'string', + 'params' => 'string rcpt', + 'description' => 'Removes the named recipient from the current message\'s envelope.', + ), + 'smfi_replacebody' => + array ( + 'return' => 'string', + 'params' => 'string body', + 'description' => 'Replaces the body of the current message. If called more than once,subsequent calls result in data being appended to the new body.', + ), + 'virtual' => + array ( + 'return' => 'bool', + 'params' => 'string filename', + 'description' => 'Perform an Apache sub-request', + ), + 'getallheaders' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Alias for apache_request_headers()', + ), + 'apache_response_headers' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Fetch all HTTP response headers', + ), + 'apache_note' => + array ( + 'return' => 'string', + 'params' => 'string note_name [, string note_value]', + 'description' => 'Get and set Apache request notes', + ), + 'apache_setenv' => + array ( + 'return' => 'bool', + 'params' => 'string variable, string value [, bool walk_to_top]', + 'description' => 'Set an Apache subprocess_env variable', + ), + 'apache_getenv' => + array ( + 'return' => 'bool', + 'params' => 'string variable [, bool walk_to_top]', + 'description' => 'Get an Apache subprocess_env variable', + ), + 'apache_get_version' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Fetch Apache version', + ), + 'apache_get_modules' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get a list of loaded Apache modules', + ), + 'nsapi_virtual' => + array ( + 'return' => 'bool', + 'params' => 'string uri', + 'description' => 'Perform an NSAPI sub-request', + ), + 'nsapi_request_headers' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get all headers from the request', + ), + 'nsapi_response_headers' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Get all headers from the response', + ), + 'ApacheRequest::filename' => + array ( + 'return' => 'string', + 'params' => '[string new_filename]', + 'description' => '', + ), + 'ApacheRequest::uri' => + array ( + 'return' => 'string', + 'params' => '[string new_uri]', + 'description' => '', + ), + 'ApacheRequest::unparsed_uri' => + array ( + 'return' => 'string', + 'params' => '[string new_unparsed_uri]', + 'description' => '', + ), + 'ApacheRequest::path_info' => + array ( + 'return' => 'string', + 'params' => '[string new_path_info]', + 'description' => '', + ), + 'ApacheRequest::args' => + array ( + 'return' => 'string', + 'params' => '[string new_args]', + 'description' => '', + ), + 'ApacheRequest::boundary' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::content_type' => + array ( + 'return' => 'string', + 'params' => '[string new_type]', + 'description' => '', + ), + 'ApacheRequest::content_encoding' => + array ( + 'return' => 'string', + 'params' => '[string new_encoding]', + 'description' => '', + ), + 'ApacheRequest::handler' => + array ( + 'return' => 'string', + 'params' => '[string new_handler]', + 'description' => '', + ), + 'ApacheRequest::the_request' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::protocol' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::hostname' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::status_line' => + array ( + 'return' => 'string', + 'params' => '[string new_status_line]', + 'description' => '', + ), + 'ApacheRequest::method' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::proto_num' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::assbackwards' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::proxyreq' => + array ( + 'return' => 'int', + 'params' => '[int new_proxyreq]', + 'description' => '', + ), + 'ApacheRequest::chunked' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::header_only' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::request_time' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::status' => + array ( + 'return' => 'int', + 'params' => '[int new_status]', + 'description' => '', + ), + 'ApacheRequest::method_number' => + array ( + 'return' => 'int', + 'params' => '[int method_number]', + 'description' => '', + ), + 'ApacheRequest::allowed' => + array ( + 'return' => 'int', + 'params' => '[int allowed]', + 'description' => '', + ), + 'ApacheRequest::bytes_sent' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::mtime' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::content_length' => + array ( + 'return' => 'int', + 'params' => '[int new_content_length]', + 'description' => '', + ), + 'ApacheRequest::remaining' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::no_cache' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::no_local_copy' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'ApacheRequest::read_body' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'apache_request_headers_in' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => '* fetch all incoming request headers', + ), + 'apache_request_headers_out' => + array ( + 'return' => 'array', + 'params' => '[{string name|array list} [, string value [, bool replace = false]]]', + 'description' => '* fetch all outgoing request headers', + ), + 'apache_request_err_headers_out' => + array ( + 'return' => 'array', + 'params' => '[{string name|array list} [, string value [, bool replace = false]]]', + 'description' => '* fetch all headers that go out in case of an error or a subrequest', + ), + 'apache_request_server_port' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => '', + ), + 'apache_request_remote_host' => + array ( + 'return' => 'int', + 'params' => '[int type]', + 'description' => '', + ), + 'apache_request_update_mtime' => + array ( + 'return' => 'long', + 'params' => '[int dependency_mtime]', + 'description' => '', + ), + 'apache_request_set_etag' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => '', + ), + 'apache_request_set_last_modified' => + array ( + 'return' => 'void', + 'params' => '', + 'description' => '', + ), + 'apache_request_meets_conditions' => + array ( + 'return' => 'long', + 'params' => '', + 'description' => '', + ), + 'apache_request_discard_request_body' => + array ( + 'return' => 'long', + 'params' => '', + 'description' => '', + ), + 'apache_request_satisfies' => + array ( + 'return' => 'long', + 'params' => '', + 'description' => '', + ), + 'apache_request_is_initial_req' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => '', + ), + 'apache_request_some_auth_required' => + array ( + 'return' => 'bool', + 'params' => '', + 'description' => '', + ), + 'apache_request_auth_type' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'apache_request_auth_name' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => '', + ), + 'apache_request_log_error' => + array ( + 'return' => 'boolean', + 'params' => 'string message, [long facility]', + 'description' => '', + ), + 'apache_request_sub_req_lookup_uri' => + array ( + 'return' => 'object', + 'params' => 'string uri', + 'description' => 'Returns sub-request for the specified uri. You wouldneed to run it yourself with run()', + ), + 'apache_request_sub_req_lookup_file' => + array ( + 'return' => 'object', + 'params' => 'string file', + 'description' => 'Returns sub-request for the specified file. You wouldneed to run it yourself with run().', + ), + 'apache_request_sub_req_method_uri' => + array ( + 'return' => 'object', + 'params' => 'string method, string uri', + 'description' => 'Returns sub-request for the specified file. You wouldneed to run it yourself with run().', + ), + 'apache_request_run' => + array ( + 'return' => 'long', + 'params' => '', + 'description' => 'This is a wrapper for ap_sub_run_req and ap_destory_sub_req. It takessub_request, runs it, destroys it, and returns it\'s status.', + ), + 'apache_child_terminate' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Terminate apache process after this request', + ), + 'apache_request_headers' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Fetch all HTTP request headers', + ), + 'apache_lookup_uri' => + array ( + 'return' => 'object', + 'params' => 'string URI', + 'description' => 'Perform a partial request of the given URI to obtain information about it', + ), + 'apache_reset_timeout' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Reset the Apache write timer', + ), + 'stream_wrapper_register' => + array ( + 'return' => 'bool', + 'params' => 'string protocol, string classname', + 'description' => 'Registers a custom URL protocol handler class', + ), + 'stream_wrapper_unregister' => + array ( + 'return' => 'bool', + 'params' => 'string protocol', + 'description' => 'Unregister a wrapper for the life of the current request.', + ), + 'stream_wrapper_restore' => + array ( + 'return' => 'bool', + 'params' => 'string protocol', + 'description' => 'Restore the original protocol handler, overriding if necessary', + ), + 'set_time_limit' => + array ( + 'return' => 'bool', + 'params' => 'int seconds', + 'description' => 'Sets the maximum time a script can run', + ), + 'ob_list_handlers' => + array ( + 'return' => 'false|array', + 'params' => '', + 'description' => '* List all output_buffers in an array', + ), + 'ob_start' => + array ( + 'return' => 'bool', + 'params' => '[ string|array user_function [, int chunk_size [, bool erase]]]', + 'description' => 'Turn on Output Buffering (specifying an optional output handler).', + ), + 'ob_flush' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Flush (send) contents of the output buffer. The last buffer content is sent to next buffer', + ), + 'ob_clean' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Clean (delete) the current output buffer', + ), + 'ob_end_flush' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Flush (send) the output buffer, and delete current output buffer', + ), + 'ob_end_clean' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Clean the output buffer, and delete current output buffer', + ), + 'ob_get_flush' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Get current buffer contents, flush (send) the output buffer, and delete current output buffer', + ), + 'ob_get_clean' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Get current buffer contents and delete current output buffer', + ), + 'ob_get_contents' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Return the contents of the output buffer', + ), + 'ob_get_level' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Return the nesting level of the output buffer', + ), + 'ob_get_length' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Return the length of the output buffer', + ), + 'ob_get_status' => + array ( + 'return' => 'false|array', + 'params' => '[bool full_status]', + 'description' => 'Return the status of the active or all output buffers', + ), + 'ob_implicit_flush' => + array ( + 'return' => 'void', + 'params' => '[int flag]', + 'description' => 'Turn implicit flush on/off and is equivalent to calling flush() after every output call', + ), + 'output_reset_rewrite_vars' => + array ( + 'return' => 'bool', + 'params' => 'void', + 'description' => 'Reset(clear) URL rewriter values', + ), + 'output_add_rewrite_var' => + array ( + 'return' => 'bool', + 'params' => 'string name, string value', + 'description' => 'Add URL rewriter values', + ), + 'zend_version' => + array ( + 'return' => 'string', + 'params' => 'void', + 'description' => 'Get the version of the Zend Engine', + ), + 'func_num_args' => + array ( + 'return' => 'int', + 'params' => 'void', + 'description' => 'Get the number of arguments that were passed to the function', + ), + 'func_get_arg' => + array ( + 'return' => 'mixed', + 'params' => 'int arg_num', + 'description' => 'Get the $arg_num\'th argument that was passed to the function', + ), + 'func_get_args' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Get an array of the arguments that were passed to the function', + ), + 'strlen' => + array ( + 'return' => 'int', + 'params' => 'string str', + 'description' => 'Get string length', + ), + 'strcmp' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2', + 'description' => 'Binary safe string comparison', + ), + 'strncmp' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2, int len', + 'description' => 'Binary safe string comparison', + ), + 'strcasecmp' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2', + 'description' => 'Binary safe case-insensitive string comparison', + ), + 'strncasecmp' => + array ( + 'return' => 'int', + 'params' => 'string str1, string str2, int len', + 'description' => 'Binary safe string comparison', + ), + 'each' => + array ( + 'return' => 'array', + 'params' => 'array arr', + 'description' => 'Return the currently pointed key..value pair in the passed array, and advance the pointer to the next element', + ), + 'error_reporting' => + array ( + 'return' => 'int', + 'params' => 'int new_error_level=null', + 'description' => 'Return the current error_reporting level, and if an argument was passed - change to the new level', + ), + 'define' => + array ( + 'return' => 'bool', + 'params' => 'string constant_name, mixed value, boolean case_sensitive=true', + 'description' => 'Define a new constant', + ), + 'defined' => + array ( + 'return' => 'bool', + 'params' => 'string constant_name', + 'description' => 'Check whether a constant exists', + ), + 'get_class' => + array ( + 'return' => 'string', + 'params' => '[object object]', + 'description' => 'Retrieves the class name', + ), + 'get_parent_class' => + array ( + 'return' => 'string', + 'params' => '[mixed object]', + 'description' => 'Retrieves the parent class name for object or class or current scope.', + ), + 'is_subclass_of' => + array ( + 'return' => 'bool', + 'params' => 'object object, string class_name', + 'description' => 'Returns true if the object has this class as one of its parents', + ), + 'is_a' => + array ( + 'return' => 'bool', + 'params' => 'object object, string class_name', + 'description' => 'Returns true if the object is of this class or has this class as one of its parents', + ), + 'get_class_vars' => + array ( + 'return' => 'array', + 'params' => 'string class_name', + 'description' => 'Returns an array of default properties of the class.', + ), + 'get_object_vars' => + array ( + 'return' => 'array', + 'params' => 'object obj', + 'description' => 'Returns an array of object properties', + ), + 'get_class_methods' => + array ( + 'return' => 'array', + 'params' => 'mixed class', + 'description' => 'Returns an array of method names for class or class instance.', + ), + 'method_exists' => + array ( + 'return' => 'bool', + 'params' => 'object object, string method', + 'description' => 'Checks if the class method exists', + ), + 'property_exists' => + array ( + 'return' => 'bool', + 'params' => 'mixed object_or_class, string property_name', + 'description' => 'Checks if the object or class has a property', + ), + 'class_exists' => + array ( + 'return' => 'bool', + 'params' => 'string classname [, bool autoload]', + 'description' => 'Checks if the class exists', + ), + 'interface_exists' => + array ( + 'return' => 'bool', + 'params' => 'string classname [, bool autoload]', + 'description' => 'Checks if the class exists', + ), + 'function_exists' => + array ( + 'return' => 'bool', + 'params' => 'string function_name', + 'description' => 'Checks if the function exists', + ), + 'leak' => + array ( + 'return' => 'void', + 'params' => 'int num_bytes=3', + 'description' => 'Cause an intentional memory leak, for testing/debugging purposes', + ), + 'get_included_files' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns an array with the file names that were include_once()\'d', + ), + 'trigger_error' => + array ( + 'return' => 'void', + 'params' => 'string messsage [, int error_type]', + 'description' => 'Generates a user-level error/warning/notice message', + ), + 'set_error_handler' => + array ( + 'return' => 'string', + 'params' => 'string error_handler [, int error_types]', + 'description' => 'Sets a user-defined error handler function. Returns the previously defined error handler, or false on error', + ), + 'restore_error_handler' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Restores the previously defined error handler function', + ), + 'set_exception_handler' => + array ( + 'return' => 'string', + 'params' => 'callable exception_handler', + 'description' => 'Sets a user-defined exception handler function. Returns the previously defined exception handler, or false on error', + ), + 'restore_exception_handler' => + array ( + 'return' => 'void', + 'params' => 'void', + 'description' => 'Restores the previously defined exception handler function', + ), + 'get_declared_classes' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Returns an array of all declared classes.', + ), + 'get_declared_interfaces' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Returns an array of all declared interfaces.', + ), + 'get_defined_functions' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns an array of all defined functions', + ), + 'get_defined_vars' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Returns an associative array of names and values of all currently defined variable names (variables in the current scope)', + ), + 'create_function' => + array ( + 'return' => 'string', + 'params' => 'string args, string code', + 'description' => 'Creates an anonymous function, and returns its name (funny, eh?)', + ), + 'get_resource_type' => + array ( + 'return' => 'string', + 'params' => 'resource res', + 'description' => 'Get the resource type name for a given resource', + ), + 'get_loaded_extensions' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Return an array containing names of loaded extensions', + ), + 'get_defined_constants' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Return an array containing the names and values of all defined constants', + ), + 'debug_backtrace' => + array ( + 'return' => 'array', + 'params' => 'void', + 'description' => 'Return backtrace as array', + ), + 'extension_loaded' => + array ( + 'return' => 'bool', + 'params' => 'string extension_name', + 'description' => 'Returns true if the named extension is loaded', + ), + 'get_extension_funcs' => + array ( + 'return' => 'array', + 'params' => 'string extension_name', + 'description' => 'Returns an array with the names of functions belonging to the named extension', + ), + 'Exception::__clone' => + array ( + 'return' => 'Exception', + 'params' => '', + 'description' => 'Clone the exception object', + ), + 'Exception::getFile' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get the file in which the exception occurred', + ), + 'Exception::getLine' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get the line in which the exception occurred', + ), + 'Exception::getMessage' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Get the exception message', + ), + 'Exception::getCode' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get the exception code', + ), + 'Exception::getTrace' => + array ( + 'return' => 'array', + 'params' => '', + 'description' => 'Get the stack trace for the location in which the exception occurred', + ), + 'ErrorException::getSeverity' => + array ( + 'return' => 'int', + 'params' => '', + 'description' => 'Get the exception severity', + ), + 'Exception::getTraceAsString' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Obtain the backtrace for the exception as a string (instead of an array)', + ), + 'Exception::__toString' => + array ( + 'return' => 'string', + 'params' => '', + 'description' => 'Obtain the string representation of the Exception object', + ), +) ; + + public function get($k) { + if (isset($this->prototype[$k])) { + return $this->prototype[$k]; + } else { + return false; + } + } + + static function getInstance() { + if (is_null(self::$instance)) { + $class = __CLASS__; + self::$instance = new $class(); + } + return self::$instance; + } +} \ No newline at end of file diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/VerbosePrint.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/VerbosePrint.php new file mode 100644 index 0000000000..425937c1de --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Extensions/VerbosePrint.php @@ -0,0 +1,56 @@ +registerCommand('#^p #', $this, 'cmdPrint', 'p ', 'print the variable verbosly'); + + $opt = PHP_Shell_Options::getInstance(); + $opt->registerOption('verboseprint', $this, 'optSetVerbose'); +*/ + } + + /** + * handle the 'p ' command + * + * set the verbose flag + * + * @return string the pure command-string without the 'p ' command + */ + public function cmdPrint($l) { + $this->oneshot_verbose = true; + + $cmd = substr($l, 2); + + return $cmd; + } + + public function optSetVerbose($key, $val) { + switch($val) { + case "false": + case "on": + case "1": + $this->opt_verbose = true; + default: + $this->opt_verbose = false; + break; + } + } + + /** + * check if we have a verbose print-out + * + * @return bool 1 if verbose, 0 otherwise + */ + public function isVerbose() { + $v = $this->opt_verbose || $this->oneshot_verbose; + + $this->oneshot_verbose = false; + + return $v; + } +} + + diff --git a/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Options.php b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Options.php new file mode 100644 index 0000000000..8f5e57d1c6 --- /dev/null +++ b/gui/baculum/framework/3rdParty/PhpShell/PHP/Shell/Options.php @@ -0,0 +1,132 @@ +registerCommand('#^:set #', $this, 'cmdSet', ':set ', 'set a shell variable'); + } + + /** + * register a option + * + * @param string name of the option + * @param object a object handle + * @param string method-name of the setor in the object + * @param string (unused) + */ + public function registerOption($option, $obj, $setor, $getor = null) { + if (!method_exists($obj, $setor)) { + throw new Exception(sprintf("setor %s doesn't exist on class %s", $setor, get_class($obj))); + } + + $this->options[trim($option)] = array("obj" => $obj, "setor" => $setor); + } + + /** + * set a shell-var + * + * :set al to enable autoload + * :set bg=dark to enable highlighting with a dark backgroud + */ + public function cmdSet($l) { + if (!preg_match('#:set\s+([a-z]+)\s*(?:=\s*([a-z0-9]+)\s*)?$#i', $l, $a)) { + print(':set failed: either :set
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DirectoryProject NameLicenseRelated PRADO ComponentMemo
    ReCaptchaReCaptcha - Stop spam, read booksBSD-likeSystem.Web.UI.WebControls.TReCaptchaReCaptcha php documentation available at http://recaptcha.net/plugins/php
    Text_HighlighterText_Highlighter - Generic Syntax Highlighter (v0.7.0 beta)The PHP LicenseSystem.Web.UI.WebControls.TTextHighlighterModified many PHP files to make them workable in PHP 5 strict mode and removed their PEAR dependency. Added PRADO syntax file.
    SafeHtmlSafeHTML (v1.3.7)BSD License (3 Clause)System.Web.UI.WebControls.TSafeHtmlTSafeHtml removes malicious javascript code from its rendered output.
    TinyMCETinyMCE Javascript Content Editor (v3.5.6)GNU LESSER GENERAL PUBLIC LICENSESystem.Web.UI.WebControls.THtmlAreaTinyMCE is a powerful WYSIWYG editor control for web browsers such as MSIE or Mozilla that enables the user to edit HTML contents in a more user friendly way.
    N.A.Tar.phpPHP License 3.0System.IO.TTarFileExtractorTTarFileExtractor is based on Tar.php whose author is Vincent Blavet.
    ../I18N/core/dataInternational Components for Unicode (Locale Data)BSD???System.I18N.core.*The ICU data http://dev.icu-project.org/cgi-bin/viewcvs.cgi/icu/source/data/locales/ are PHP serialized.
    ../I18N/core/util.phpPEAR :: Package :: DBPHP License 3.0N.A.The parseDSN() function from DB.php file.
    ../I18N/core/TCache_Lite.phpPEAR :: Package :: Cache_LiteLGPLN.A.Cache_Lite class was renamed as TCache_Lite for packaging purposes.
    ../I18N/core/GettextPEAR :: Package :: File_GettextPHP License 3.0N.A.File_Gettext, File::Gettext::MO, and File::Gettext::PO
    Rico Color is used in the colorpicker.jsRicoApache License v2N.A.Rico Javascript Library (not yet utilized in Prado)
    ../Web/Javascripts/prototypePrototype javascript libraryThe Prototype License (MIT based)N.A.Core of Prado javascript library.
    ../Web/Javascripts/prado/logger/logger.jshttp://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/ http://slayeroffice.comNoneTJavascriptLoggerJavascript logger by Corey Johnson. Object Tree by S.G. Chipman.
    ../../demos/quickstart/protected/index/ZendZend FrameworkBSDQuickstart tutorial indexing and search is provided the Zend Lucene search implementation.This product includes the Zend Framework, freely available at + http://www.zend.com
    adodbADOdb Database Abstraction Library for PHPDual licensed using BSD and LGPL.Provides one database access abstraction layer.Slightly modified to work nicely with E_STRICT in php 5.
    MarkdownPHP MarkdownBSDSystem.Web.UI.WebControls.TMarkdownPHP5 class implementation of the PHP Markdown.
    ../UtilADOdb Date Time LibraryBSDSystem.Util.TDateTimeStampTimeStamp support for dates outside the 1970-2038.
    3rdParty/PhpShellPHP_ShellMITAn interactive PHP ShellAvailable throught the prado-cli.php script.
    3rdParty/FirePHPCoreFirePHPCoreBSDFirePHP enables you to log to your Firebug Console using a simple PHP method call.Available throught TFirePhpLogRoute
    + + + diff --git a/gui/baculum/framework/Caching/TAPCCache.php b/gui/baculum/framework/Caching/TAPCCache.php new file mode 100644 index 0000000000..8826fac63f --- /dev/null +++ b/gui/baculum/framework/Caching/TAPCCache.php @@ -0,0 +1,129 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TAPCCache.php 3281 2013-03-13 21:01:40Z ctrlaltca $ + * @package System.Caching + */ + +/** + * TAPCCache class + * + * TAPCCache implements a cache application module based on {@link http://www.php.net/apc APC}. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * To use this module, the APC PHP extension must be loaded and set in the php.ini file the following: + * + * apc.cache_by_default=0 + * + * + * Some usage examples of TAPCCache are as follows, + * + * $cache=new TAPCCache; // TAPCCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TAPCCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TAPCCache may be configured in application configuration file as follows + * + * + * + * + * @author Alban Hanry + * @author Knut Urdalen + * @version $Id: TAPCCache.php 3281 2013-03-13 21:01:40Z ctrlaltca $ + * @package System.Caching + * @since 3.0b + */ +class TAPCCache extends TCache +{ + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if apc extension is not installed or not started, check your php.ini + */ + public function init($config) + { + if(!extension_loaded('apc')) + throw new TConfigurationException('apccache_extension_required'); + + if(ini_get('apc.enabled') == false) + throw new TConfigurationException('apccache_extension_not_enabled'); + + if(substr(php_sapi_name(), 0, 3) === 'cli' and ini_get('apc.enable_cli') == false) + throw new TConfigurationException('apccache_extension_not_enabled_cli'); + + parent::init($config); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return apc_fetch($key); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return apc_store($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return apc_add($key,$value,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return apc_delete($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return apc_clear_cache('user'); + } +} + diff --git a/gui/baculum/framework/Caching/TCache.php b/gui/baculum/framework/Caching/TCache.php new file mode 100644 index 0000000000..0a0ca2db37 --- /dev/null +++ b/gui/baculum/framework/Caching/TCache.php @@ -0,0 +1,719 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + */ + +Prado::using('System.Collections.TList'); + +/** + * TCache class + * + * TCache is the base class for cache classes with different cache storage implementation. + * + * TCache implements the interface {@link ICache} with the following methods, + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time by + * the number of seconds. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Child classes must implement the following methods: + * - {@link getValue} + * - {@link setValue} + * - {@link addValue} + * - {@link deleteValue} + * and optionally {@link flush} + * + * Since version 3.1.2, TCache implements the ArrayAccess interface such that + * the cache acts as an array. + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.0 + */ +abstract class TCache extends TModule implements ICache, ArrayAccess +{ + private $_prefix=null; + private $_primary=true; + + /** + * Initializes the cache module. + * This method initializes the cache key prefix and registers the cache module + * with the application if the cache is primary. + * @param TXmlElement the module configuration + */ + public function init($config) + { + if($this->_prefix===null) + $this->_prefix=$this->getApplication()->getUniqueID(); + if($this->_primary) + { + if($this->getApplication()->getCache()===null) + $this->getApplication()->setCache($this); + else + throw new TConfigurationException('cache_primary_duplicated',get_class($this)); + } + } + + /** + * @return boolean whether this cache module is used as primary/system cache. + * A primary cache is used by PRADO core framework to cache data such as + * parsed templates, themes, etc. + */ + public function getPrimaryCache() + { + return $this->_primary; + } + + /** + * @param boolean whether this cache module is used as primary/system cache. Defaults to false. + * @see getPrimaryCache + */ + public function setPrimaryCache($value) + { + $this->_primary=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string a unique prefix for the keys of cached values. + * If it is not explicitly set, it will take the value of {@link TApplication::getUniqueID}. + */ + public function getKeyPrefix() + { + return $this->_prefix; + } + + /** + * @param string a unique prefix for the keys of cached values + */ + public function setKeyPrefix($value) + { + $this->_prefix=$value; + } + + /** + * @param string a key identifying a value to be cached + * @return sring a key generated from the provided key which ensures the uniqueness across applications + */ + protected function generateUniqueKey($key) + { + return md5($this->_prefix.$key); + } + + /** + * Retrieves a value from cache with a specified key. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($id) + { + if(($data=$this->getValue($this->generateUniqueKey($id)))!==false) + { + if(!is_array($data)) + return false; + if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()) + return $data[0]; + } + return false; + } + + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. If the value is + * empty, the cache key will be deleted. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null) + { + if(empty($value) && $expire === 0) + $this->delete($id); + else + { + $data=array($value,$dependency); + return $this->setValue($this->generateUniqueKey($id),$data,$expire); + } + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key or if value is empty. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labeled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null) + { + if(empty($value) && $expire === 0) + return false; + $data=array($value,$dependency); + return $this->addValue($this->generateUniqueKey($id),$data,$expire); + } + + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id) + { + return $this->deleteValue($this->generateUniqueKey($id)); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + * Child classes may implement this method to realize the flush operation. + * @throws TNotSupportedException if this method is not overridden by child classes + */ + public function flush() + { + throw new TNotSupportedException('cache_flush_unsupported'); + } + + /** + * Retrieves a value from cache with a specified key. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link get()} already. So only the implementation of data retrieval + * is needed. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + abstract protected function getValue($key); + + /** + * Stores a value identified by a key in cache. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link set()} already. So only the implementation of data storage + * is needed. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + abstract protected function setValue($key,$value,$expire); + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This method should be implemented by child classes to store the data + * in specific cache storage. The uniqueness and dependency are handled + * in {@link add()} already. So only the implementation of data storage + * is needed. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + abstract protected function addValue($key,$value,$expire); + + /** + * Deletes a value with the specified key from cache + * This method should be implemented by child classes to delete the data from actual cache storage. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + abstract protected function deleteValue($key); + + /** + * Returns whether there is a cache entry with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return boolean + */ + public function offsetExists($id) + { + return $this->get($id) !== false; + } + + /** + * Retrieves the value from cache with a specified key. + * This method is required by the interface ArrayAccess. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function offsetGet($id) + { + return $this->get($id); + } + + /** + * Stores the value identified by a key into cache. + * If the cache already contains such a key, the existing value will be + * replaced with the new ones. To add expiration and dependencies, use the set() method. + * This method is required by the interface ArrayAccess. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function offsetSet($id, $value) + { + $this->set($id, $value); + } + + /** + * Deletes the value with the specified key from cache + * This method is required by the interface ArrayAccess. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function offsetUnset($id) + { + $this->delete($id); + } +} + + +/** + * TCacheDependency class. + * + * TCacheDependency is the base class implementing {@link ICacheDependency} interface. + * Descendant classes must implement {@link getHasChanged()} to provide + * actual dependency checking logic. + * + * The property value of {@link getHasChanged HasChanged} tells whether + * the dependency is changed or not. + * + * You may disable the dependency checking by setting {@link setEnabled Enabled} + * to false. + * + * Note, since the dependency objects often need to be serialized so that + * they can persist across requests, you may need to implement __sleep() and + * __wakeup() if the dependency objects contain resource handles which are + * not serializable. + * + * Currently, the following dependency classes are provided in the PRADO release: + * - {@link TFileCacheDependency}: checks whether a file is changed or not + * - {@link TDirectoryCacheDependency}: checks whether a directory is changed or not + * - {@link TGlobalStateCacheDependency}: checks whether a global state is changed or not + * - {@link TChainedCacheDependency}: checks whether any of a list of dependencies is changed or not + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +abstract class TCacheDependency extends TComponent implements ICacheDependency +{ +} + + +/** + * TFileCacheDependency class. + * + * TFileCacheDependency performs dependency checking based on the + * last modification time of the file specified via {@link setFileName FileName}. + * The dependency is reported as unchanged if and only if the file's + * last modification time remains unchanged. + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TFileCacheDependency extends TCacheDependency +{ + private $_fileName; + private $_timestamp; + + /** + * Constructor. + * @param string name of the file whose change is to be checked. + */ + public function __construct($fileName) + { + $this->setFileName($fileName); + } + + /** + * @return string the name of the file whose change is to be checked + */ + public function getFileName() + { + return $this->_fileName; + } + + /** + * @param string the name of the file whose change is to be checked + */ + public function setFileName($value) + { + $this->_fileName=$value; + $this->_timestamp=@filemtime($value); + } + + /** + * @return int the last modification time of the file + */ + public function getTimestamp() + { + return $this->_timestamp; + } + + /** + * Performs the actual dependency checking. + * This method returns true if the last modification time of the file is changed. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return @filemtime($this->_fileName)!==$this->_timestamp; + } +} + +/** + * TDirectoryCacheDependency class. + * + * TDirectoryCacheDependency performs dependency checking based on the + * modification time of the files contained in the specified directory. + * The directory being checked is specified via {@link setDirectory Directory}. + * + * By default, all files under the specified directory and subdirectories + * will be checked. If the last modification time of any of them is changed + * or if different number of files are contained in a directory, the dependency + * is reported as changed. By specifying {@link setRecursiveCheck RecursiveCheck} + * and {@link setRecursiveLevel RecursiveLevel}, one can limit the checking + * to a certain depth of the subdirectories. + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TDirectoryCacheDependency extends TCacheDependency +{ + private $_recursiveCheck=true; + private $_recursiveLevel=-1; + private $_timestamps; + private $_directory; + + /** + * Constructor. + * @param string the directory to be checked + */ + public function __construct($directory) + { + $this->setDirectory($directory); + } + + /** + * @return string the directory to be checked + */ + public function getDirectory() + { + return $this->_directory; + } + + /** + * @param string the directory to be checked + * @throws TInvalidDataValueException if the directory does not exist + */ + public function setDirectory($directory) + { + if(($path=realpath($directory))===false || !is_dir($path)) + throw new TInvalidDataValueException('directorycachedependency_directory_invalid',$directory); + $this->_directory=$path; + $this->_timestamps=$this->generateTimestamps($path); + } + + /** + * @return boolean whether the subdirectories of the directory will also be checked. + * It defaults to true. + */ + public function getRecursiveCheck() + { + return $this->_recursiveCheck; + } + + /** + * @param boolean whether the subdirectories of the directory will also be checked. + */ + public function setRecursiveCheck($value) + { + $this->_recursiveCheck=TPropertyValue::ensureBoolean($value); + } + + /** + * @return int the depth of the subdirectories to be checked. + * It defaults to -1, meaning unlimited depth. + */ + public function getRecursiveLevel() + { + return $this->_recursiveLevel; + } + + /** + * Sets a value indicating the depth of the subdirectories to be checked. + * This is meaningful only when {@link getRecursiveCheck RecursiveCheck} + * is true. + * @param int the depth of the subdirectories to be checked. + * If the value is less than 0, it means unlimited depth. + * If the value is 0, it means checking the files directly under the specified directory. + */ + public function setRecursiveLevel($value) + { + $this->_recursiveLevel=TPropertyValue::ensureInteger($value); + } + + /** + * Performs the actual dependency checking. + * This method returns true if the directory is changed. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return $this->generateTimestamps($this->_directory)!=$this->_timestamps; + } + + /** + * Checks to see if the file should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the file should be checked. + * You may override this method to check only certain files. + * @param string the name of the file that may be checked for dependency. + * @return boolean whether this file should be checked. + */ + protected function validateFile($fileName) + { + return true; + } + + /** + * Checks to see if the specified subdirectory should be checked for dependency. + * This method is invoked when dependency of the whole directory is being checked. + * By default, it always returns true, meaning the subdirectory should be checked. + * You may override this method to check only certain subdirectories. + * @param string the name of the subdirectory that may be checked for dependency. + * @return boolean whether this subdirectory should be checked. + */ + protected function validateDirectory($directory) + { + return true; + } + + /** + * Determines the last modification time for files under the directory. + * This method may go recursively into subdirectories if + * {@link setRecursiveCheck RecursiveCheck} is set true. + * @param string the directory name + * @param int level of the recursion + * @return array list of file modification time indexed by the file path + */ + protected function generateTimestamps($directory,$level=0) + { + if(($dir=opendir($directory))===false) + throw new TIOException('directorycachedependency_directory_invalid',$directory); + $timestamps=array(); + while(($file=readdir($dir))!==false) + { + $path=$directory.DIRECTORY_SEPARATOR.$file; + if($file==='.' || $file==='..') + continue; + else if(is_dir($path)) + { + if(($this->_recursiveLevel<0 || $level<$this->_recursiveLevel) && $this->validateDirectory($path)) + $timestamps=array_merge($this->generateTimestamps($path,$level+1)); + } + else if($this->validateFile($path)) + $timestamps[$path]=filemtime($path); + } + closedir($dir); + return $timestamps; + } +} + + +/** + * TGlobalStateCacheDependency class. + * + * TGlobalStateCacheDependency checks if a global state is changed or not. + * If the global state is changed, the dependency is reported as changed. + * To specify which global state this dependency should check with, + * set {@link setStateName StateName} to the name of the global state. + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TGlobalStateCacheDependency extends TCacheDependency +{ + private $_stateName; + private $_stateValue; + + /** + * Constructor. + * @param string the name of the global state + */ + public function __construct($name) + { + $this->setStateName($name); + } + + /** + * @return string the name of the global state + */ + public function getStateName() + { + return $this->_stateName; + } + + /** + * @param string the name of the global state + * @see TApplication::setGlobalState + */ + public function setStateName($value) + { + $this->_stateName=$value; + $this->_stateValue=Prado::getApplication()->getGlobalState($value); + } + + /** + * Performs the actual dependency checking. + * This method returns true if the specified global state is changed. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return $this->_stateValue!==Prado::getApplication()->getGlobalState($this->_stateName); + } +} + + +/** + * TChainedCacheDependency class. + * + * TChainedCacheDependency represents a list of cache dependency objects + * and performs the dependency checking based on the checking results of + * these objects. If any of them reports a dependency change, TChainedCacheDependency + * will return true for the checking. + * + * To add dependencies to TChainedCacheDependency, use {@link getDependencies Dependencies} + * which gives a {@link TCacheDependencyList} instance and can be used like an array + * (see {@link TList} for more details}). + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TChainedCacheDependency extends TCacheDependency +{ + private $_dependencies=null; + + /** + * @return TCacheDependencyList list of dependency objects + */ + public function getDependencies() + { + if($this->_dependencies===null) + $this->_dependencies=new TCacheDependencyList; + return $this->_dependencies; + } + + /** + * Performs the actual dependency checking. + * This method returns true if any of the dependency objects + * reports a dependency change. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + if($dependency->getHasChanged()) + return true; + } + return false; + } +} + + +/** + * TApplicationStateCacheDependency class. + * + * TApplicationStateCacheDependency performs dependency checking based on + * the mode of the currently running PRADO application. + * The dependency is reportedly as unchanged if and only if the application + * is running in performance mode. + * + * You may chain this dependency together with other dependencies + * so that only when the application is not in performance mode the other dependencies + * will be checked. + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TApplicationStateCacheDependency extends TCacheDependency +{ + /** + * Performs the actual dependency checking. + * This method returns true if the currently running application is not in performance mode. + * @return boolean whether the dependency is changed or not. + */ + public function getHasChanged() + { + return Prado::getApplication()->getMode()!==TApplicationMode::Performance; + } +} + +/** + * TCacheDependencyList class. + * + * TCacheDependencyList represents a list of cache dependency objects. + * Only objects implementing {@link ICacheDependency} can be added into this list. + * + * TCacheDependencyList can be used like an array. See {@link TList} + * for more details. + * + * @author Qiang Xue + * @version $Id: TCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TCacheDependencyList extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional type checking + * for each newly added item. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a dependency instance + */ + public function insertAt($index,$item) + { + if($item instanceof ICacheDependency) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('cachedependencylist_cachedependency_required'); + } +} + diff --git a/gui/baculum/framework/Caching/TDbCache.php b/gui/baculum/framework/Caching/TDbCache.php new file mode 100644 index 0000000000..01962a551a --- /dev/null +++ b/gui/baculum/framework/Caching/TDbCache.php @@ -0,0 +1,578 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + */ + +Prado::using('System.Data.TDbConnection'); + +/** + * TDbCache class + * + * TDbCache implements a cache application module by storing cached data in a database. + * + * TDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to retrieve + * data from databases. In order to use TDbCache, you need to enable the PDO extension + * as well as the corresponding PDO DB driver. For example, to use SQLite database + * to store cached data, you need both php_pdo and php_pdo_sqlite extensions. + * + * By default, TDbCache creates and uses an SQLite database under the application + * runtime directory. You may change this default setting by specifying the following + * properties: + * - {@link setConnectionID ConnectionID} or + * - {@link setConnectionString ConnectionString}, {@link setUsername Username} and {@link setPassword Pasword}. + * + * The cached data is stored in a table in the specified database. + * By default, the name of the table is called 'pradocache'. If the table does not + * exist in the database, it will be automatically created with the following structure: + * + * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT) + * CREATE INDEX IX_itemkey ON pradocache (itemkey) + * CREATE INDEX IX_expire ON pradocache (expire) + * + * + * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable + * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) + * + * Important: Make sure that the indices are non-unique! + * + * If you want to change the cache table name, or if you want to create the table by yourself, + * you may set {@link setCacheTableName CacheTableName} and {@link setAutoCreateCacheTable AutoCreateCacheTableName} properties. + * + * {@link setFlushInterval FlushInterval} control how often expired items will be removed from cache. + * If you prefer to remove expired items manualy e.g. via cronjob you can disable automatic deletion by setting FlushInterval to '0'. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time by + * the number of seconds. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Do not use the same database file for multiple applications using TDbCache. + * Also note, cache is shared by all user sessions of an application. + * + * Some usage examples of TDbCache are as follows, + * + * $cache=new TDbCache; // TDbCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TDbCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TDbCache may be configured in application configuration file as follows + * + * + * + * + * @author Qiang Xue + * @version $Id: TDbCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.1.0 + */ +class TDbCache extends TCache +{ + /** + * @var string the ID of TDataSourceConfig module + */ + private $_connID=''; + /** + * @var TDbConnection the DB connection instance + */ + private $_db; + /** + * @var string name of the DB cache table + */ + private $_cacheTable='pradocache'; + /** + * @var integer Interval expired items will be removed from cache + */ + private $_flushInterval=60; + /** + * @var boolean + */ + private $_cacheInitialized = false; + /** + * @var boolean + */ + private $_createCheck= false; + /** + * @var boolean whether the cache DB table should be created automatically + */ + private $_autoCreate=true; + private $_username=''; + private $_password=''; + private $_connectionString=''; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + if($this->_db!==null) + $this->_db->setActive(false); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. + * attach {@link doInitializeCache} to TApplication.OnLoadStateComplete event + * attach {@link doFlushCacheExpired} to TApplication.OnSaveState event + * + * @param TXmlElement configuration for this module, can be null + */ + public function init($config) + { + $this -> getApplication() -> attachEventHandler('OnLoadStateComplete', array($this, 'doInitializeCache')); + $this -> getApplication() -> attachEventHandler('OnSaveState', array($this, 'doFlushCacheExpired')); + parent::init($config); + } + + /** + * Event listener for TApplication.OnSaveState + * @return void + * @since 3.1.5 + * @see flushCacheExpired + */ + public function doFlushCacheExpired() + { + $this->flushCacheExpired(false); + } + + /** + * Event listener for TApplication.OnLoadStateComplete + * + * @return void + * @since 3.1.5 + * @see initializeCache + */ + public function doInitializeCache() + { + $this->initializeCache(); + } + + /** + * Initialize TDbCache + * + * If {@link setAutoCreateCacheTable AutoCreateCacheTableName} is 'true' check existence of cache table + * and create table if does not exist. + * + * @param boolean Force override global state check + * @return void + * @throws TConfigurationException if any error happens during creating database or cache table. + * @since 3.1.5 + */ + protected function initializeCache($force=false) + { + if($this->_cacheInitialized && !$force) return; + $db=$this->getDbConnection(); + try + { + $key = 'TDbCache:' . $this->_cacheTable . ':created'; + if($force) + $this -> _createCheck = false; + else + $this -> _createCheck = $this -> getApplication() -> getGlobalState($key, 0); + + if($this->_autoCreate && !$this -> _createCheck) { + + Prado::trace(($force ? 'Force initializing: ' : 'Initializing: ') . $this -> id . ', ' . $this->_cacheTable, 'System.Caching.TDbCache'); + + $sql='SELECT 1 FROM '.$this->_cacheTable.' WHERE 0=1'; + $db->createCommand($sql)->queryScalar(); + + $this -> _createCheck = true; + $this -> getApplication() -> setGlobalState($key, time()); + } + } + catch(Exception $e) + { + // DB table not exists + if($this->_autoCreate) + { + Prado::trace('Autocreate: ' . $this->_cacheTable, 'System.Caching.TDbCache'); + + $driver=$db->getDriverName(); + if($driver==='mysql') + $blob='LONGBLOB'; + else if($driver==='pgsql') + $blob='BYTEA'; + else + $blob='BLOB'; + + $sql='CREATE TABLE '.$this->_cacheTable." (itemkey CHAR(128) PRIMARY KEY, value $blob, expire INTEGER)"; + $db->createCommand($sql)->execute(); + + $sql='CREATE INDEX IX_expire ON ' . $this->_cacheTable . ' (expire)'; + $db->createCommand($sql)->execute(); + + $this -> _createCheck = true; + $this -> getApplication() -> setGlobalState($key, time()); + } + else + throw new TConfigurationException('db_cachetable_inexistent',$this->_cacheTable); + } + $this->_cacheInitialized = true; + } + + /** + * Flush expired values from cache depending on {@link setFlushInterval FlushInterval} + * @param boolean override {@link setFlushInterval FlushInterval} and force deletion of expired items + * @return void + * @since 3.1.5 + */ + public function flushCacheExpired($force=false) + { + $interval = $this -> getFlushInterval(); + if(!$force && $interval === 0) return; + + $key = 'TDbCache:' . $this->_cacheTable . ':flushed'; + $now = time(); + $next = $interval + (integer)$this -> getApplication() -> getGlobalState($key, 0); + + if($force || $next <= $now) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + Prado::trace(($force ? 'Force flush of expired items: ' : 'Flush expired items: ') . $this -> id . ', ' . $this->_cacheTable, 'System.Caching.TDbCache'); + $sql='DELETE FROM '.$this->_cacheTable.' WHERE expire<>0 AND expire<'.$now; + $this->getDbConnection()->createCommand($sql)->execute(); + $this -> getApplication() -> setGlobalState($key, $now); + } + } + + /** + * @return integer Interval in sec expired items will be removed from cache. Default to 60 + * @since 3.1.5 + */ + public function getFlushInterval() + { + return $this->_flushInterval; + } + + /** + * Sets interval expired items will be removed from cache + * + * To disable automatic deletion of expired items, + * e.g. for external flushing via cron you can set value to '0' + * + * @param integer Interval in sec + * @since 3.1.5 + */ + public function setFlushInterval($value) + { + $this->_flushInterval = (integer) $value; + } + + /** + * Creates the DB connection. + * @param string the module ID for TDataSourceConfig + * @return TDbConnection the created DB connection + * @throws TConfigurationException if module ID is invalid or empty + */ + protected function createDbConnection() + { + if($this->_connID!=='') + { + $config=$this->getApplication()->getModule($this->_connID); + if($config instanceof TDataSourceConfig) + return $config->getDbConnection(); + else + throw new TConfigurationException('dbcache_connectionid_invalid',$this->_connID); + } + else + { + $db=new TDbConnection; + if($this->_connectionString!=='') + { + $db->setConnectionString($this->_connectionString); + if($this->_username!=='') + $db->setUsername($this->_username); + if($this->_password!=='') + $db->setPassword($this->_password); + } + else + { + // default to SQLite3 database + $dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.cache'; + $db->setConnectionString('sqlite:'.$dbFile); + } + return $db; + } + } + + /** + * @return TDbConnection the DB connection instance + */ + public function getDbConnection() + { + if($this->_db===null) + $this->_db=$this->createDbConnection(); + + $this->_db->setActive(true); + return $this->_db; + } + + /** + * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set. + * @since 3.1.1 + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Sets the ID of a TDataSourceConfig module. + * The datasource module will be used to establish the DB connection for this cache module. + * The database connection can also be specified via {@link setConnectionString ConnectionString}. + * When both ConnectionID and ConnectionString are specified, the former takes precedence. + * @param string ID of the {@link TDataSourceConfig} module + * @since 3.1.1 + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return string The Data Source Name, or DSN, contains the information required to connect to the database. + */ + public function getConnectionString() + { + return $this->_connectionString; + } + + /** + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @see http://www.php.net/manual/en/function.pdo-construct.php + */ + public function setConnectionString($value) + { + $this->_connectionString=$value; + } + + /** + * @return string the username for establishing DB connection. Defaults to empty string. + */ + public function getUsername() + { + return $this->_username; + } + + /** + * @param string the username for establishing DB connection + */ + public function setUsername($value) + { + $this->_username=$value; + } + + /** + * @return string the password for establishing DB connection. Defaults to empty string. + */ + public function getPassword() + { + return $this->_password; + } + + /** + * @param string the password for establishing DB connection + */ + public function setPassword($value) + { + $this->_password=$value; + } + + /** + * @return string the name of the DB table to store cache content. Defaults to 'pradocache'. + * @see setAutoCreateCacheTable + */ + public function getCacheTableName() + { + return $this->_cacheTable; + } + + /** + * Sets the name of the DB table to store cache content. + * Note, if {@link setAutoCreateCacheTable AutoCreateCacheTable} is false + * and you want to create the DB table manually by yourself, + * you need to make sure the DB table is of the following structure: + * + * CREATE TABLE pradocache (itemkey CHAR(128), value BLOB, expire INT) + * CREATE INDEX IX_itemkey ON pradocache (itemkey) + * CREATE INDEX IX_expire ON pradocache (expire) + * + * + * Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable + * binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.) + * + * Important: Make sure that the indices are non-unique! + * + * @param string the name of the DB table to store cache content + * @see setAutoCreateCacheTable + */ + public function setCacheTableName($value) + { + $this->_cacheTable=$value; + } + + /** + * @return boolean whether the cache DB table should be automatically created if not exists. Defaults to true. + * @see setAutoCreateCacheTable + */ + public function getAutoCreateCacheTable() + { + return $this->_autoCreate; + } + + /** + * @param boolean whether the cache DB table should be automatically created if not exists. + * @see setCacheTableName + */ + public function setAutoCreateCacheTable($value) + { + $this->_autoCreate=TPropertyValue::ensureBoolean($value); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + try { + $sql='SELECT value FROM '.$this->_cacheTable.' WHERE itemkey=\''.$key.'\' AND (expire=0 OR expire>'.time().') ORDER BY expire DESC'; + $command=$this->getDbConnection()->createCommand($sql); + return Prado::unserialize($command->queryScalar()); + } + catch(Exception $e) + { + $this->initializeCache(true); + return Prado::unserialize($command->queryScalar()); + } + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + $this->deleteValue($key); + return $this->addValue($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + $expire=($expire<=0)?0:time()+$expire; + $sql="INSERT INTO {$this->_cacheTable} (itemkey,value,expire) VALUES(:key,:value,$expire)"; + try + { + $command=$this->getDbConnection()->createCommand($sql); + $command->bindValue(':key',$key,PDO::PARAM_STR); + $command->bindValue(':value',Prado::serialize($value),PDO::PARAM_LOB); + $command->execute(); + return true; + } + catch(Exception $e) + { + try + { + $this->initializeCache(true); + $command->execute(); + return true; + } + catch(Exception $e) + { + return false; + } + } + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + if(!$this->_cacheInitialized) $this->initializeCache(); + try + { + $command=$this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable} WHERE itemkey=:key"); + $command->bindValue(':key',$key,PDO::PARAM_STR); + $command->execute(); + return true; + } + catch(Exception $e) + { + $this->initializeCache(true); + $command->execute(); + return true; + } + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + if(!$this->_cacheInitialized) $this->initializeCache(); + try + { + $command = $this->getDbConnection()->createCommand("DELETE FROM {$this->_cacheTable}"); + $command->execute(); + } + catch(Exception $e) + { + try + { + $this->initializeCache(true); + $command->execute(); + return true; + } + catch(Exception $e) + { + return false; + } + } + return true; + } +} diff --git a/gui/baculum/framework/Caching/TEACache.php b/gui/baculum/framework/Caching/TEACache.php new file mode 100644 index 0000000000..6a20f63b17 --- /dev/null +++ b/gui/baculum/framework/Caching/TEACache.php @@ -0,0 +1 @@ + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2013 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Id: TEACache.php 3281 2013-03-13 19:31:03Z xue $ * @package System.Caching */ /** * TEACache class * * TEACache implements a cache application module based on {@link http://eaccelerator.net/ eAccelerator}. * * By definition, cache does not ensure the existence of a value * even if it never expires. Cache is not meant to be an persistent storage. * * To use this module, the eAccelerator PHP extension must be loaded and enabled * * Please note that as of v0.9.6, eAccelerator no longer supports data caching. * This means if you still want to use this component, your eAccelerator should be of 0.9.5.x or lower version. * * Some usage examples of TEACache are as follows, * * $cache=new TEACache; // TEACache may also be loaded as a Prado application module * $cache->init(null); * $cache->add('object',$object); * $object2=$cache->get('object'); * * * If loaded, TEACache will register itself with {@link TApplication} as the * cache module. It can be accessed via {@link TApplication::getCache()}. * * TEACache may be configured in application configuration file as follows * * * * * @author Dario Rigolin * @version $Id: TEACache.php 3281 2013-03-13 19:31:03Z xue $ * @package System.Caching * @since 3.2.2 */ class TEACache extends TCache { /** * Initializes this module. * This method is required by the IModule interface. * @param TXmlElement configuration for this module, can be null * @throws TConfigurationException if eaccelerator extension is not installed or not started, check your php.ini */ public function init($config) { if(!function_exists('eaccelerator_get')) throw new TConfigurationException('eacceleratorcache_extension_required'); parent::init($config); } /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string a unique key identifying the cached value * @return string the value stored in cache, false if the value is not in the cache or expired. */ protected function getValue($key) { $value = eaccelerator_get($key); return ($value === null) ? false : $value; } /** * Stores a value identified by a key in cache. * This is the implementation of the method declared in the parent class. * * @param string the key identifying the value to be cached * @param string the value to be cached * @param integer the number of seconds in which the cached value will expire. 0 means never expire. * @return boolean true if the value is successfully stored into cache, false otherwise */ protected function setValue($key,$value,$expire) { return eaccelerator_put($key,$value,$expire); } /** * Stores a value identified by a key into cache if the cache does not contain this key. * This is the implementation of the method declared in the parent class. * * @param string the key identifying the value to be cached * @param string the value to be cached * @param integer the number of seconds in which the cached value will expire. 0 means never expire. * @return boolean true if the value is successfully stored into cache, false otherwise */ protected function addValue($key,$value,$expire) { return (null === eaccelerator_get($key)) ? $this->setValue($key,$value,$expire) : false; } /** * Deletes a value with the specified key from cache * This is the implementation of the method declared in the parent class. * @param string the key of the value to be deleted * @return boolean if no error happens during deletion */ protected function deleteValue($key) { return eaccelerator_rm($key); } /** * Deletes all values from cache. * Be careful of performing this operation if the cache is shared by multiple applications. */ public function flush() { // first, remove expired content from cache eaccelerator_gc(); // now, remove leftover cache-keys $keys = eaccelerator_list_keys(); foreach($keys as $key) $this->deleteValue(substr($key['name'], 1)); return true; } } \ No newline at end of file diff --git a/gui/baculum/framework/Caching/TMemCache.php b/gui/baculum/framework/Caching/TMemCache.php new file mode 100644 index 0000000000..8d2e6a4b3d --- /dev/null +++ b/gui/baculum/framework/Caching/TMemCache.php @@ -0,0 +1,363 @@ + + * @author Carl G. Mathisen + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMemCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + */ + +/** + * TMemCache class + * + * TMemCache implements a cache application module based on {@link http://www.danga.com/memcached/ memcached}. + * + * TMemCache can be configured with the Host and Port properties, which + * specify the host and port of the memcache server to be used. + * By default, they take the value 'localhost' and 11211, respectively. + * These properties must be set before {@link init} is invoked. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time can + * be specified by the number of seconds (maximum 60*60*24*30) + * or a UNIX timestamp. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Also note, there is no security measure to protected data in memcache. + * All data in memcache can be accessed by any process running in the system. + * + * To use this module, the memcache PHP extension must be loaded. + * + * Some usage examples of TMemCache are as follows, + * + * $cache=new TMemCache; // TMemCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * You can configure TMemCache two different ways. If you only need one memcache server + * you may use the method as follows. + * + * + * + * + * If you want a more complex configuration, you may use the method as follows. + * + * + * + * + * + * + * + * If loaded, TMemCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TMemCache may be configured in application configuration file as follows + * + * + * + * where {@link getHost Host} and {@link getPort Port} are configurable properties + * of TMemCache. + * + * Automatic compression of values may be used (using zlib extension) by setting {@link getThreshold Threshold} and {@link getMinSavings MinSavings} properties. + * NB : MemCache server(s) must be restarted to apply settings. Require (PECL memcache >= 2.0.0). + * + * @author Qiang Xue + * @version $Id: TMemCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.0 + */ +class TMemCache extends TCache +{ + /** + * @var boolean if the module is initialized + */ + private $_initialized=false; + /** + * @var Memcache the Memcache instance + */ + private $_cache=null; + /** + * @var string a unique prefix used to identify this cache instance from the others + */ + private $_prefix=null; + /** + * @var string host name of the memcache server + */ + private $_host='localhost'; + /** + * @var integer the port number of the memcache server + */ + private $_port=11211; + /** + * @var boolean controls the use of a persistent connection. Default to true. + */ + private $_persistence = true; + /** + * @var integer number of buckets to create for this server which in turn control its + * probability of it being selected. The probability is relative to the total weight + * of all servers. + */ + private $_weight = 1; + + private $_timeout = 360; + + private $_retryInterval = 15; + /** + * @var integer Controls the minimum value length before attempting to compress automatically. + */ + private $_threshold=0; + + /** + * @var float Specifies the minimum amount of savings to actually store the value compressed. The supplied value must be between 0 and 1. Default value is 0.2 giving a minimum 20% compression savings. + */ + private $_minSavings=0.0; + + private $_status = true; + + private $_failureCallback = null; + + /** + * @var array list of servers available + */ + private $_servers=array(); + + /** + * Destructor. + * Disconnect the memcache server. + */ + public function __destruct() + { + if($this->_cache!==null) + $this->_cache->close(); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. It makes sure that + * UniquePrefix has been set, creates a Memcache instance and connects + * to the memcache server. + * @param TApplication Prado application, can be null + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if memcache extension is not installed or memcache sever connection fails + */ + public function init($config) + { + if(!extension_loaded('memcache')) + throw new TConfigurationException('memcache_extension_required'); + $this->_cache=new Memcache; + $this->loadConfig($config); + if(count($this->_servers)) + { + foreach($this->_servers as $server) + { + Prado::trace('Adding server '.$server['Host'].' from serverlist', 'System.Caching.TMemCache'); + if($this->_cache->addServer($server['Host'],$server['Port'],$server['Persistent'], + $server['Weight'],$server['Timeout'],$server['RetryInterval'])===false) + throw new TConfigurationException('memcache_connection_failed',$server['Host'],$server['Port']); + } + } + else + { + Prado::trace('Adding server '.$this->_host, 'System.Caching.TMemCache'); + if($this->_cache->addServer($this->_host,$this->_port)===false) + throw new TConfigurationException('memcache_connection_failed',$this->_host,$this->_port); + } + if($this->_threshold!==0) + $this->_cache->setCompressThreshold($this->_threshold,$this->_minSavings); + $this->_initialized=true; + parent::init($config); + } + + /** + * Loads configuration from an XML element + * @param TXmlElement configuration node + * @throws TConfigurationException if log route class or type is not specified + */ + private function loadConfig($xml) + { + if($xml instanceof TXmlElement) + { + foreach($xml->getElementsByTagName('server') as $serverConfig) + { + $properties=$serverConfig->getAttributes(); + if(($host=$properties->remove('Host'))===null) + throw new TConfigurationException('memcache_serverhost_required'); + if(($port=$properties->remove('Port'))===null) + throw new TConfigurationException('memcache_serverport_required'); + if(!is_numeric($port)) + throw new TConfigurationException('memcache_serverport_invalid'); + $server = array('Host'=>$host,'Port'=>$port,'Weight'=>1,'Timeout'=>1800,'RetryInterval'=>15,'Persistent'=>true); + $checks = array( + 'Weight'=>'memcache_serverweight_invalid', + 'Timeout'=>'memcache_servertimeout_invalid', + 'RetryInterval'=>'memcach_serverretryinterval_invalid' + ); + foreach($checks as $property=>$exception) + { + $value=$properties->remove($property); + if($value!==null && is_numeric($value)) + $server[$property]=$value; + else if($value!==null) + throw new TConfigurationException($exception); + } + $server['Persistent']= TPropertyValue::ensureBoolean($properties->remove('Persistent')); + $this->_servers[]=$server; + } + } + } + + /** + * @return string host name of the memcache server + */ + public function getHost() + { + return $this->_host; + } + + /** + * @param string host name of the memcache server + * @throws TInvalidOperationException if the module is already initialized + */ + public function setHost($value) + { + if($this->_initialized) + throw new TInvalidOperationException('memcache_host_unchangeable'); + else + $this->_host=$value; + } + + /** + * @return integer port number of the memcache server + */ + public function getPort() + { + return $this->_port; + } + + /** + * @param integer port number of the memcache server + * @throws TInvalidOperationException if the module is already initialized + */ + public function setPort($value) + { + if($this->_initialized) + throw new TInvalidOperationException('memcache_port_unchangeable'); + else + $this->_port=TPropertyValue::ensureInteger($value); + } + + /** + * @return integer minimum value length before attempting to compress + */ + public function getThreshold() + { + return $this->_threshold; + } + + /** + * @param integer minimum value length before attempting to compress + * @throws TInvalidOperationException if the module is already initialized + */ + public function setThreshold($value) + { + if($this->_initialized) + throw new TInvalidOperationException('memcache_threshold_unchangeable'); + else + $this->_threshold=TPropertyValue::ensureInteger($value); + } + + /** + * @return float minimum amount of savings to actually store the value compressed + */ + public function getMinSavings() + { + return $this->_minSavings; + } + + /** + * @param float minimum amount of savings to actually store the value compressed + * @throws TInvalidOperationException if the module is already initialized + */ + public function setMinSavings($value) + { + if($this->_initialized) + throw new TInvalidOperationException('memcache_min_savings_unchangeable'); + else + $this->_minSavings=TPropertyValue::ensureFloat($value); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return $this->_cache->get($key); + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return $this->_cache->set($key,$value,0,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return $this->_cache->add($key,$value,0,$expire); + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return $this->_cache->delete($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->_cache->flush(); + } +} + diff --git a/gui/baculum/framework/Caching/TSqliteCache.php b/gui/baculum/framework/Caching/TSqliteCache.php new file mode 100644 index 0000000000..a00a847256 --- /dev/null +++ b/gui/baculum/framework/Caching/TSqliteCache.php @@ -0,0 +1,224 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + */ + +/** + * TSqliteCache class + * + * TSqliteCache implements a cache application module based on SQLite database. + * + * THIS CLASS IS DEPRECATED since it relies on the sqlite PHP extension, that is + * no longer loaded by default since PHP 5.1. You are discouraged from using it: + * use {@link TDbCache} instead. + * + * Since PRADO v3.1.0, a new DB-based cache module called {@link TDbCache} + * is provided. If you have PDO extension installed, you may consider using + * the new cache module instead as it allows you to use different database + * to store the cached data. + * + * The database file is specified by the {@link setDbFile DbFile} property. + * If not set, the database file will be created under the system state path. + * If the specified database file does not exist, it will be created automatically. + * Make sure the directory containing the specified DB file and the file itself is + * writable by the Web server process. + * + * The following basic cache operations are implemented: + * - {@link get} : retrieve the value with a key (if any) from cache + * - {@link set} : store the value with a key into cache + * - {@link add} : store the value only if cache does not have this key + * - {@link delete} : delete the value with the specified key from cache + * - {@link flush} : delete all values from cache + * + * Each value is associated with an expiration time. The {@link get} operation + * ensures that any expired value will not be returned. The expiration time by + * the number of seconds. A expiration time 0 represents never expire. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * Do not use the same database file for multiple applications using TSqliteCache. + * Also note, cache is shared by all user sessions of an application. + * + * Some usage examples of TSqliteCache are as follows, + * + * $cache=new TSqliteCache; // TSqliteCache may also be loaded as a Prado application module + * $cache->setDbFile($dbFilePath); + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TSqliteCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TSqliteCache may be configured in application configuration file as follows + * + * + * + * where {@link getDbFile DbFile} is a property specifying the location of the + * SQLite DB file (in the namespace format). + * + * @author Qiang Xue + * @version $Id: TSqliteCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Caching + * @since 3.0 + */ +class TSqliteCache extends TCache +{ + /** + * name of the table storing cache data + */ + const CACHE_TABLE='cache'; + /** + * extension of the db file name + */ + const DB_FILE_EXT='.db'; + + /** + * @var boolean if the module has been initialized + */ + private $_initialized=false; + /** + * @var SQLiteDatabase the sqlite database instance + */ + private $_db=null; + /** + * @var string the database file name + */ + private $_file=null; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + $this->_db=null; + } + + /** + * Initializes this module. + * This method is required by the IModule interface. It checks if the DbFile + * property is set, and creates a SQLiteDatabase instance for it. + * The database or the cache table does not exist, they will be created. + * Expired values are also deleted. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if sqlite extension is not installed, + * DbFile is set invalid, or any error happens during creating database or cache table. + */ + public function init($config) + { + if(!function_exists('sqlite_open')) + throw new TConfigurationException('sqlitecache_extension_required'); + if($this->_file===null) + $this->_file=$this->getApplication()->getRuntimePath().'/sqlite.cache'; + $error=''; + if(($this->_db=new SQLiteDatabase($this->_file,0666,$error))===false) + throw new TConfigurationException('sqlitecache_connection_failed',$error); + if(@$this->_db->query('DELETE FROM '.self::CACHE_TABLE.' WHERE expire<>0 AND expire<'.time())===false) + { + if($this->_db->query('CREATE TABLE '.self::CACHE_TABLE.' (key CHAR(128) PRIMARY KEY, value BLOB, expire INT)')===false) + throw new TConfigurationException('sqlitecache_table_creation_failed',sqlite_error_string(sqlite_last_error())); + } + $this->_initialized=true; + parent::init($config); + } + + /** + * @return string database file path (in namespace form) + */ + public function getDbFile() + { + return $this->_file; + } + + /** + * @param string database file path (in namespace form) + * @throws TInvalidOperationException if the module is already initialized + * @throws TConfigurationException if the file is not in proper namespace format + */ + public function setDbFile($value) + { + if($this->_initialized) + throw new TInvalidOperationException('sqlitecache_dbfile_unchangeable'); + else if(($this->_file=Prado::getPathOfNamespace($value,self::DB_FILE_EXT))===null) + throw new TConfigurationException('sqlitecache_dbfile_invalid',$value); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + $sql='SELECT value FROM '.self::CACHE_TABLE.' WHERE key=\''.$key.'\' AND (expire=0 OR expire>'.time().') LIMIT 1'; + if(($ret=$this->_db->query($sql))!=false && ($row=$ret->fetch(SQLITE_ASSOC))!==false) + return Prado::unserialize($row['value']); + else + return false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + $expire=($expire<=0)?0:time()+$expire; + $sql='REPLACE INTO '.self::CACHE_TABLE.' VALUES(\''.$key.'\',\''.sqlite_escape_string(Prado::serialize($value)).'\','.$expire.')'; + return $this->_db->query($sql)!==false; + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + $expire=($expire<=0)?0:time()+$expire; + $sql='INSERT INTO '.self::CACHE_TABLE.' VALUES(\''.$key.'\',\''.sqlite_escape_string(Prado::serialize($value)).'\','.$expire.')'; + return @$this->_db->query($sql)!==false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + $sql='DELETE FROM '.self::CACHE_TABLE.' WHERE key=\''.$key.'\''; + return $this->_db->query($sql)!==false; + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + return $this->_db->query('DELETE FROM '.self::CACHE_TABLE)!==false; + } +} + diff --git a/gui/baculum/framework/Caching/TXCache.php b/gui/baculum/framework/Caching/TXCache.php new file mode 100644 index 0000000000..e86c8fa940 --- /dev/null +++ b/gui/baculum/framework/Caching/TXCache.php @@ -0,0 +1,130 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TXCache.php 1994 2007-06-11 16:02:28Z knut $ + * @package System.Caching + */ + +/** + * TXCache class + * + * TXCache implements a cache application module based on {@link http://xcache.lighttpd.net/ xcache}. + * + * By definition, cache does not ensure the existence of a value + * even if it never expires. Cache is not meant to be an persistent storage. + * + * To use this module, the xcache PHP extension must be loaded and configured in the php.ini. + * + * Some usage examples of TXCache are as follows, + * + * $cache=new TXCache; // TXCache may also be loaded as a Prado application module + * $cache->init(null); + * $cache->add('object',$object); + * $object2=$cache->get('object'); + * + * + * If loaded, TXCache will register itself with {@link TApplication} as the + * cache module. It can be accessed via {@link TApplication::getCache()}. + * + * TXCache may be configured in application configuration file as follows + * + * + * + * + * @author Wei Zhuo + * @version $Id: TXCache.php 1994 2007-06-11 16:02:28Z knut $ + * @package System.Caching + * @since 3.1.1 + */ +class TXCache extends TCache +{ + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if xcache extension is not installed or not started, check your php.ini + */ + public function init($config) + { + if(!function_exists('xcache_isset')) + throw new TConfigurationException('xcache_extension_required'); + + $enabled = (int)ini_get('xcache.cacher') !== 0; + $var_size = (int)ini_get('xcache.var_size'); + + if(!($enabled && $var_size > 0)) + throw new TConfigurationException('xcache_extension_not_enabled'); + + parent::init($config); + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string a unique key identifying the cached value + * @return string the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return xcache_isset($key) ? xcache_get($key) : false; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + return xcache_set($key,$value,$expire); + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string the key identifying the value to be cached + * @param string the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + return !xcache_isset($key) ? $this->setValue($key,$value,$expire) : false; + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return xcache_unset($key); + } + + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush() + { + for($i=0, $max=xcache_count(XC_TYPE_VAR); $i<$max; $i++) + { + if(xcache_clear_cache(XC_TYPE_VAR, $i)===false) + return false; + } + return true; + } +} + diff --git a/gui/baculum/framework/Collections/TAttributeCollection.php b/gui/baculum/framework/Collections/TAttributeCollection.php new file mode 100644 index 0000000000..d72640f23e --- /dev/null +++ b/gui/baculum/framework/Collections/TAttributeCollection.php @@ -0,0 +1,172 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TAttributeCollection.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * Includes TMap class + */ +Prado::using('System.Collections.TMap'); + +/** + * TAttributeCollection class + * + * TAttributeCollection implements a collection for storing attribute names and values. + * + * Besides all functionalities provided by {@link TMap}, TAttributeCollection + * allows you to get and set attribute values like getting and setting + * properties. For example, the following usages are all valid for a + * TAttributeCollection object: + * + * $collection->Text='text'; + * echo $collection->Text; + * + * They are equivalent to the following: + * + * $collection->add('Text','text'); + * echo $collection->itemAt('Text'); + * + * + * Note, attribute names are case-insensitive. They are converted to lower-case + * in the collection storage. + * + * @author Qiang Xue + * @version $Id: TAttributeCollection.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TAttributeCollection extends TMap +{ + private $_caseSensitive=false; + + /** + * Returns a property value or an event handler list by property or event name. + * This method overrides the parent implementation by returning + * a key value if the key exists in the collection. + * @param string the property name or the event name + * @return mixed the property value or the event handler list + * @throws TInvalidOperationException if the property/event is not defined. + */ + public function __get($name) + { + return $this->contains($name)?$this->itemAt($name):parent::__get($name); + } + + /** + * Sets value of a component property. + * This method overrides the parent implementation by adding a new key value + * to the collection. + * @param string the property name or event name + * @param mixed the property value or event handler + * @throws TInvalidOperationException If the property is not defined or read-only. + */ + public function __set($name,$value) + { + $this->add($name,$value); + } + + /** + * @return boolean whether the keys are case-sensitive. Defaults to false. + */ + public function getCaseSensitive() + { + return $this->_caseSensitive; + } + + /** + * @param boolean whether the keys are case-sensitive. + */ + public function setCaseSensitive($value) + { + $this->_caseSensitive=TPropertyValue::ensureBoolean($value); + } + + /** + * Returns the item with the specified key. + * This overrides the parent implementation by converting the key to lower case first if CaseSensitive is false. + * @param mixed the key + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key) + { + return parent::itemAt($this->_caseSensitive?$key:strtolower($key)); + } + + + /** + * Adds an item into the map. + * This overrides the parent implementation by converting the key to lower case first if CaseSensitive is false. + * @param mixed key + * @param mixed value + */ + public function add($key,$value) + { + parent::add($this->_caseSensitive?$key:strtolower($key),$value); + } + + /** + * Removes an item from the map by its key. + * This overrides the parent implementation by converting the key to lower case first if CaseSensitive is false. + * @param mixed the key of the item to be removed + * @return mixed the removed value, null if no such key exists. + */ + public function remove($key) + { + return parent::remove($this->_caseSensitive?$key:strtolower($key)); + } + + /** + * Returns whether the specified is in the map. + * This overrides the parent implementation by converting the key to lower case first if CaseSensitive is false. + * @param mixed the key + * @return boolean whether the map contains an item with the specified key + */ + public function contains($key) + { + return parent::contains($this->_caseSensitive?$key:strtolower($key)); + } + + /** + * Determines whether a property is defined. + * This method overrides parent implementation by returning true + * if the collection contains the named key. + * @param string the property name + * @return boolean whether the property is defined + */ + public function hasProperty($name) + { + return $this->contains($name) || parent::canGetProperty($name) || parent::canSetProperty($name); + } + + /** + * Determines whether a property can be read. + * This method overrides parent implementation by returning true + * if the collection contains the named key. + * @param string the property name + * @return boolean whether the property can be read + */ + public function canGetProperty($name) + { + return $this->contains($name) || parent::canGetProperty($name); + } + + /** + * Determines whether a property can be set. + * This method overrides parent implementation by always returning true + * because you can always add a new value to the collection. + * @param string the property name + * @return boolean true + */ + public function canSetProperty($name) + { + return true; + } +} + diff --git a/gui/baculum/framework/Collections/TDummyDataSource.php b/gui/baculum/framework/Collections/TDummyDataSource.php new file mode 100644 index 0000000000..bf74dbec8f --- /dev/null +++ b/gui/baculum/framework/Collections/TDummyDataSource.php @@ -0,0 +1,146 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDummyDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * TDummyDataSource class + * + * TDummyDataSource implements a dummy data collection with a specified number + * of dummy data items. The number of virtual items can be set via + * {@link setCount Count} property. You can traverse it using foreach + * PHP statement like the following, + * + * foreach($dummyDataSource as $dataItem) + * + * + * @author Qiang Xue + * @version $Id: TDummyDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TDummyDataSource extends TComponent implements IteratorAggregate, Countable +{ + private $_count; + + /** + * Constructor. + * @param integer number of (virtual) items in the data source. + */ + public function __construct($count) + { + $this->_count=$count; + } + + /** + * @return integer number of (virtual) items in the data source. + */ + public function getCount() + { + return $this->_count; + } + + /** + * @return Iterator iterator + */ + public function getIterator() + { + return new TDummyDataSourceIterator($this->_count); + } + + /** + * Returns the number of (virtual) items in the data source. + * This method is required by Countable interface. + * @return integer number of (virtual) items in the data source. + */ + public function count() + { + return $this->getCount(); + } +} + +/** + * TDummyDataSourceIterator class + * + * TDummyDataSourceIterator implements Iterator interface. + * + * TDummyDataSourceIterator is used by {@link TDummyDataSource}. + * It allows TDummyDataSource to return a new iterator + * for traversing its dummy items. + * + * @author Qiang Xue + * @version $Id: TDummyDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TDummyDataSourceIterator implements Iterator +{ + private $_index; + private $_count; + + /** + * Constructor. + * @param integer number of (virtual) items in the data source. + */ + public function __construct($count) + { + $this->_count=$count; + $this->_index=0; + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_index=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_index; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return null; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_index++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_index<$this->_count; + } +} + diff --git a/gui/baculum/framework/Collections/TList.php b/gui/baculum/framework/Collections/TList.php new file mode 100644 index 0000000000..b82f676e51 --- /dev/null +++ b/gui/baculum/framework/Collections/TList.php @@ -0,0 +1,486 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * TList class + * + * TList implements an integer-indexed collection class. + * + * You can access, append, insert, remove an item by using + * {@link itemAt}, {@link add}, {@link insertAt}, {@link remove}, and {@link removeAt}. + * To get the number of the items in the list, use {@link getCount}. + * TList can also be used like a regular array as follows, + * + * $list[]=$item; // append at the end + * $list[$index]=$item; // $index must be between 0 and $list->Count + * unset($list[$index]); // remove the item at $index + * if(isset($list[$index])) // if the list has an item at $index + * foreach($list as $index=>$item) // traverse each item in the list + * $n=count($list); // returns the number of items in the list + * + * + * To extend TList by doing additional operations with each addition or removal + * operation, override {@link insertAt()}, and {@link removeAt()}. + * + * @author Qiang Xue + * @version $Id: TList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TList extends TComponent implements IteratorAggregate,ArrayAccess,Countable +{ + /** + * internal data storage + * @var array + */ + private $_d=array(); + /** + * number of items + * @var integer + */ + private $_c=0; + /** + * @var boolean whether this list is read-only + */ + private $_r=false; + + /** + * Constructor. + * Initializes the list with an array or an iterable object. + * @param array|Iterator the initial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only + * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + } + + /** + * @return boolean whether this list is read-only or not. Defaults to false. + */ + public function getReadOnly() + { + return $this->_r; + } + + /** + * @param boolean whether this list is read-only or not + */ + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + + /** + * Returns an iterator for traversing the items in the list. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + + /** + * Returns the number of items in the list. + * This method is required by Countable interface. + * @return integer number of items in the list. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer the number of items in the list + */ + public function getCount() + { + return $this->_c; + } + + /** + * Returns the item at the specified offset. + * This method is exactly the same as {@link offsetGet}. + * @param integer the index of the item + * @return mixed the item at the index + * @throws TInvalidDataValueException if the index is out of the range + */ + public function itemAt($index) + { + if($index>=0 && $index<$this->_c) + return $this->_d[$index]; + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + + /** + * Appends an item at the end of the list. + * @param mixed new item + * @return integer the zero-based index at which the item is added + * @throws TInvalidOperationException if the list is read-only + */ + public function add($item) + { + $this->insertAt($this->_c,$item); + return $this->_c-1; + } + + /** + * Inserts an item at the specified position. + * Original item at the position and the next items + * will be moved one step towards the end. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataValueException If the index specified exceeds the bound + * @throws TInvalidOperationException if the list is read-only + */ + public function insertAt($index,$item) + { + if(!$this->_r) + { + if($index===$this->_c) + $this->_d[$this->_c++]=$item; + else if($index>=0 && $index<$this->_c) + { + array_splice($this->_d,$index,0,array($item)); + $this->_c++; + } + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + + /** + * Removes an item from the list. + * The list will first search for the item. + * The first item found will be removed from the list. + * @param mixed the item to be removed. + * @return integer the index at which the item is being removed + * @throws TInvalidDataValueException If the item does not exist + * @throws TInvalidOperationException if the list is read-only + */ + public function remove($item) + { + if(!$this->_r) + { + if(($index=$this->indexOf($item))>=0) + { + $this->removeAt($index); + return $index; + } + else + throw new TInvalidDataValueException('list_item_inexistent'); + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + + /** + * Removes an item at the specified position. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + * @throws TInvalidDataValueException If the index specified exceeds the bound + * @throws TInvalidOperationException if the list is read-only + */ + public function removeAt($index) + { + if(!$this->_r) + { + if($index>=0 && $index<$this->_c) + { + $this->_c--; + if($index===$this->_c) + return array_pop($this->_d); + else + { + $item=$this->_d[$index]; + array_splice($this->_d,$index,1); + return $item; + } + } + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + + /** + * Removes all items in the list. + * @throws TInvalidOperationException if the list is read-only + */ + public function clear() + { + for($i=$this->_c-1;$i>=0;--$i) + $this->removeAt($i); + } + + /** + * @param mixed the item + * @return boolean whether the list contains the item + */ + public function contains($item) + { + return $this->indexOf($item)>=0; + } + + /** + * @param mixed the item + * @return integer the index of the item in the list (0 based), -1 if not found. + */ + public function indexOf($item) + { + if(($index=array_search($item,$this->_d,true))===false) + return -1; + else + return $index; + } + + /** + * Finds the base item. If found, the item is inserted before it. + * @param mixed the base item which will be pushed back by the second parameter + * @param mixed the item + * @return int the index where the item is inserted + * @throws TInvalidDataValueException if the base item is not within this list + * @throws TInvalidOperationException if the list is read-only + * @since 3.2a + */ + public function insertBefore($baseitem, $item) + { + if(!$this->_r) + { + if(($index = $this->indexOf($baseitem)) == -1) + throw new TInvalidDataValueException('list_item_inexistent'); + + $this->insertAt($index, $item); + + return $index; + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + + /** + * Finds the base item. If found, the item is inserted after it. + * @param mixed the base item which comes before the second parameter when added to the list + * @param mixed the item + * @return int the index where the item is inserted + * @throws TInvalidDataValueException if the base item is not within this list + * @throws TInvalidOperationException if the list is read-only + * @since 3.2a + */ + public function insertAfter($baseitem, $item) + { + if(!$this->_r) + { + if(($index = $this->indexOf($baseitem)) == -1) + throw new TInvalidDataValueException('list_item_inexistent'); + + $this->insertAt($index + 1, $item); + + return $index + 1; + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + + /** + * @return array the list of items in array + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the list. + * Note, existing data in the list will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor a Traversable. + */ + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + if($this->_c>0) + $this->clear(); + foreach($data as $item) + $this->add($item); + } + else if($data!==null) + throw new TInvalidDataTypeException('list_data_not_iterable'); + } + + /** + * Merges iterable data into the map. + * New data will be appended to the end of the existing data. + * @param mixed the data to be merged with, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function mergeWith($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + foreach($data as $item) + $this->add($item); + } + else if($data!==null) + throw new TInvalidDataTypeException('list_data_not_iterable'); + } + + /** + * Returns whether there is an item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return ($offset>=0 && $offset<$this->_c); + } + + /** + * Returns the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve item. + * @return mixed the item at the offset + * @throws TInvalidDataValueException if the offset is invalid + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set item + * @param mixed the item value + */ + public function offsetSet($offset,$item) + { + if($offset===null || $offset===$this->_c) + $this->insertAt($this->_c,$item); + else + { + $this->removeAt($offset); + $this->insertAt($offset,$item); + } + } + + /** + * Unsets the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to unset item + */ + public function offsetUnset($offset) + { + $this->removeAt($offset); + } +} + + +/** + * TListIterator class + * + * TListIterator implements Iterator interface. + * + * TListIterator is used by TList. It allows TList to return a new iterator + * for traversing the items in the list. + * + * @deprecated Issue 264 : ArrayIterator should be used instead + * @author Qiang Xue + * @version $Id: TList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TListIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var integer index of the current item + */ + private $_i; + /** + * @var integer count of the data items + */ + private $_c; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_i=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_i; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_d[$this->_i]; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_i++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_i<$this->_c; + } +} + diff --git a/gui/baculum/framework/Collections/TListItemCollection.php b/gui/baculum/framework/Collections/TListItemCollection.php new file mode 100644 index 0000000000..23d5ade86d --- /dev/null +++ b/gui/baculum/framework/Collections/TListItemCollection.php @@ -0,0 +1,164 @@ + + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TListControl.php 2624 2009-03-19 21:20:47Z godzilla80@gmx.net $ + * @package System.Collections + */ + +/** + * Includes the supporting classes + */ +Prado::using('System.Collections.TList'); +Prado::using('System.Web.UI.WebControls.TListItem'); + +/** + * TListItemCollection class. + * + * TListItemCollection maintains a list of {@link TListItem} for {@link TListControl}. + * + * @author Qiang Xue + * @version $Id: TListControl.php 2624 2009-03-19 21:20:47Z godzilla80@gmx.net $ + * @package System.Collections + * @since 3.0 + */ +class TListItemCollection extends TList +{ + /** + * Creates a list item object. + * This method may be overriden to provide a customized list item object. + * @param integer index where the newly created item is to be inserted at. + * If -1, the item will be appended to the end. + * @return TListItem list item object + */ + public function createListItem($index=-1) + { + $item=$this->createNewListItem(); + if($index<0) + $this->add($item); + else + $this->insertAt($index,$item); + return $item; + } + + /** + * @return TListItem new item. + */ + protected function createNewListItem($text=null) + { + $item = new TListItem; + if($text!==null) + $item->setText($text); + return $item; + } + + /** + * Inserts an item into the collection. + * @param integer the location where the item will be inserted. + * The current item at the place and the following ones will be moved backward. + * @param TListItem the item to be inserted. + * @throws TInvalidDataTypeException if the item being inserted is neither a string nor TListItem + */ + public function insertAt($index,$item) + { + if(is_string($item)) + $item = $this->createNewListItem($item); + if(!($item instanceof TListItem)) + throw new TInvalidDataTypeException('listitemcollection_item_invalid',get_class($this)); + parent::insertAt($index,$item); + } + + /** + * Finds the lowest cardinal index of the item whose value is the one being looked for. + * @param string the value to be looked for + * @param boolean whether to look for disabled items also + * @return integer the index of the item found, -1 if not found. + */ + public function findIndexByValue($value,$includeDisabled=true) + { + $value=TPropertyValue::ensureString($value); + $index=0; + foreach($this as $item) + { + if($item->getValue()===$value && ($includeDisabled || $item->getEnabled())) + return $index; + $index++; + } + return -1; + } + + /** + * Finds the lowest cardinal index of the item whose text is the one being looked for. + * @param string the text to be looked for + * @param boolean whether to look for disabled items also + * @return integer the index of the item found, -1 if not found. + */ + public function findIndexByText($text,$includeDisabled=true) + { + $text=TPropertyValue::ensureString($text); + $index=0; + foreach($this as $item) + { + if($item->getText()===$text && ($includeDisabled || $item->getEnabled())) + return $index; + $index++; + } + return -1; + } + + /** + * Finds the item whose value is the one being looked for. + * @param string the value to be looked for + * @param boolean whether to look for disabled items also + * @return TListItem the item found, null if not found. + */ + public function findItemByValue($value,$includeDisabled=true) + { + if(($index=$this->findIndexByValue($value,$includeDisabled))>=0) + return $this->itemAt($index); + else + return null; + } + + /** + * Finds the item whose text is the one being looked for. + * @param string the text to be looked for + * @param boolean whether to look for disabled items also + * @return TListItem the item found, null if not found. + */ + public function findItemByText($text,$includeDisabled=true) + { + if(($index=$this->findIndexByText($text,$includeDisabled))>=0) + return $this->itemAt($index); + else + return null; + } + + /** + * Loads state into every item in the collection. + * This method should only be used by framework and control developers. + * @param array|null state to be loaded. + */ + public function loadState($state) + { + $this->clear(); + if($state!==null) + $this->copyFrom($state); + } + + /** + * Saves state of items. + * This method should only be used by framework and control developers. + * @return array|null the saved state + */ + public function saveState() + { + return ($this->getCount()>0) ? $this->toArray() : null; + } +} diff --git a/gui/baculum/framework/Collections/TMap.php b/gui/baculum/framework/Collections/TMap.php new file mode 100644 index 0000000000..d5f2ffce0f --- /dev/null +++ b/gui/baculum/framework/Collections/TMap.php @@ -0,0 +1,353 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * TMap class + * + * TMap implements a collection that takes key-value pairs. + * + * You can access, add or remove an item with a key by using + * {@link itemAt}, {@link add}, and {@link remove}. + * To get the number of the items in the map, use {@link getCount}. + * TMap can also be used like a regular array as follows, + * + * $map[$key]=$value; // add a key-value pair + * unset($map[$key]); // remove the value with the specified key + * if(isset($map[$key])) // if the map contains the key + * foreach($map as $key=>$value) // traverse the items in the map + * $n=count($map); // returns the number of items in the map + * + * + * @author Qiang Xue + * @version $Id: TMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TMap extends TComponent implements IteratorAggregate,ArrayAccess,Countable +{ + /** + * @var array internal data storage + */ + private $_d=array(); + /** + * @var boolean whether this list is read-only + */ + private $_r=false; + + /** + * Constructor. + * Initializes the list with an array or an iterable object. + * @param array|Iterator the intial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only + * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + } + + /** + * @return boolean whether this map is read-only or not. Defaults to false. + */ + public function getReadOnly() + { + return $this->_r; + } + + /** + * @param boolean whether this list is read-only or not + */ + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + + /** + * Returns an iterator for traversing the items in the list. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + + /** + * Returns the number of items in the map. + * This method is required by Countable interface. + * @return integer number of items in the map. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer the number of items in the map + */ + public function getCount() + { + return count($this->_d); + } + + /** + * @return array the key list + */ + public function getKeys() + { + return array_keys($this->_d); + } + + /** + * Returns the item with the specified key. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the key + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key) + { + return isset($this->_d[$key]) ? $this->_d[$key] : null; + } + + /** + * Adds an item into the map. + * Note, if the specified key already exists, the old value will be overwritten. + * @param mixed key + * @param mixed value + * @throws TInvalidOperationException if the map is read-only + */ + public function add($key,$value) + { + if(!$this->_r) + $this->_d[$key]=$value; + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + + /** + * Removes an item from the map by its key. + * @param mixed the key of the item to be removed + * @return mixed the removed value, null if no such key exists. + * @throws TInvalidOperationException if the map is read-only + */ + public function remove($key) + { + if(!$this->_r) + { + if(isset($this->_d[$key]) || array_key_exists($key,$this->_d)) + { + $value=$this->_d[$key]; + unset($this->_d[$key]); + return $value; + } + else + return null; + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + + /** + * Removes all items in the map. + */ + public function clear() + { + foreach(array_keys($this->_d) as $key) + $this->remove($key); + } + + /** + * @param mixed the key + * @return boolean whether the map contains an item with the specified key + */ + public function contains($key) + { + return isset($this->_d[$key]) || array_key_exists($key,$this->_d); + } + + /** + * @return array the list of items in array + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the map. + * Note, existing data in the map will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function copyFrom($data) + { + if(is_array($data) || $data instanceof Traversable) + { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Merges iterable data into the map. + * Existing data in the map will be kept and overwritten if the keys are the same. + * @param mixed the data to be merged with, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function mergeWith($data) + { + if(is_array($data) || $data instanceof Traversable) + { + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return $this->contains($offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } +} + +/** + * TMapIterator class + * + * TMapIterator implements Iterator interface. + * + * TMapIterator is used by TMap. It allows TMap to return a new iterator + * for traversing the items in the map. + * + * @deprecated Issue 264 : ArrayIterator should be used instead + * @author Qiang Xue + * @version $Id: TMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TMapIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var array list of keys in the map + */ + private $_keys; + /** + * @var mixed current key + */ + private $_key; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_keys=array_keys($data); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_key=reset($this->_keys); + } + + /** + * Returns the key of the current array element. + * This method is required by the interface Iterator. + * @return mixed the key of the current array element + */ + public function key() + { + return $this->_key; + } + + /** + * Returns the current array element. + * This method is required by the interface Iterator. + * @return mixed the current array element + */ + public function current() + { + return $this->_d[$this->_key]; + } + + /** + * Moves the internal pointer to the next array element. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_key=next($this->_keys); + } + + /** + * Returns whether there is an element at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_key!==false; + } +} diff --git a/gui/baculum/framework/Collections/TPagedDataSource.php b/gui/baculum/framework/Collections/TPagedDataSource.php new file mode 100644 index 0000000000..60d2736b1d --- /dev/null +++ b/gui/baculum/framework/Collections/TPagedDataSource.php @@ -0,0 +1,446 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPagedDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * TPagedDataSource class + * + * TPagedDataSource implements an integer-indexed collection class with paging functionality. + * + * Data items in TPagedDataSource can be traversed using foreach + * PHP statement like the following, + * + * foreach($pagedDataSource as $dataItem) + * + * The data are fetched from {@link setDataSource DataSource}. Only the items + * within the specified page will be returned and traversed. + * + * @author Qiang Xue + * @version $Id: TPagedDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TPagedDataSource extends TComponent implements IteratorAggregate,Countable +{ + /** + * @var mixed original data source + */ + private $_dataSource=null; + /** + * @var integer number of items in each page + */ + private $_pageSize=10; + /** + * @var integer current page index + */ + private $_currentPageIndex=0; + /** + * @var boolean whether to allow paging + */ + private $_allowPaging=false; + /** + * @var boolean whether to allow custom paging + */ + private $_allowCustomPaging=false; + /** + * @var integer user-assigned number of items in data source + */ + private $_virtualCount=0; + + /** + * @return mixed original data source. Defaults to null. + */ + public function getDataSource() + { + return $this->_dataSource; + } + + /** + * @param mixed original data source + */ + public function setDataSource($value) + { + if(!($value instanceof TMap) && !($value instanceof TList)) + { + if(is_array($value)) + $value=new TMap($value); + else if($value instanceof Traversable) + $value=new TList($value); + else if($value!==null) + throw new TInvalidDataTypeException('pageddatasource_datasource_invalid'); + } + $this->_dataSource=$value; + } + + /** + * @return integer number of items in each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->_pageSize; + } + + /** + * @param integer number of items in each page + */ + public function setPageSize($value) + { + if(($value=TPropertyValue::ensureInteger($value))>0) + $this->_pageSize=$value; + else + throw new TInvalidDataValueException('pageddatasource_pagesize_invalid'); + } + + /** + * @return integer current page index. Defaults to 0. + */ + public function getCurrentPageIndex() + { + return $this->_currentPageIndex; + } + + /** + * @param integer current page index + */ + public function setCurrentPageIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_currentPageIndex=$value; + } + + /** + * @return boolean whether to allow paging. Defaults to false. + */ + public function getAllowPaging() + { + return $this->_allowPaging; + } + + /** + * @param boolean whether to allow paging + */ + public function setAllowPaging($value) + { + $this->_allowPaging=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether to allow custom paging. Defaults to false. + */ + public function getAllowCustomPaging() + { + return $this->_allowCustomPaging; + } + + /** + * @param boolean whether to allow custom paging + */ + public function setAllowCustomPaging($value) + { + $this->_allowCustomPaging=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer user-assigned number of items in data source Defaults to 0. + */ + public function getVirtualItemCount() + { + return $this->_virtualCount; + } + + /** + * @param integer user-assigned number of items in data source + */ + public function setVirtualItemCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))>=0) + $this->_virtualCount=$value; + else + throw new TInvalidDataValueException('pageddatasource_virtualitemcount_invalid'); + } + + /** + * @return integer number of items in current page + */ + public function getCount() + { + if($this->_dataSource===null) + return 0; + if(!$this->_allowPaging) + return $this->getDataSourceCount(); + if(!$this->_allowCustomPaging && $this->getIsLastPage()) + return $this->getDataSourceCount()-$this->getFirstIndexInPage(); + return $this->_pageSize; + } + + /** + * Returns the number of items in the current page. + * This method is required by Countable interface. + * @return integer number of items in the current page. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer number of pages + */ + public function getPageCount() + { + if($this->_dataSource===null) + return 0; + $count=$this->getDataSourceCount(); + if(!$this->_allowPaging || $count<=0) + return 1; + return (int)(($count+$this->_pageSize-1)/$this->_pageSize); + } + + /** + * @return boolean whether the current page is the first page Defaults to false. + */ + public function getIsFirstPage() + { + if($this->_allowPaging) + return $this->_currentPageIndex===0; + else + return true; + } + + /** + * @return boolean whether the current page is the last page + */ + public function getIsLastPage() + { + if($this->_allowPaging) + return $this->_currentPageIndex===$this->getPageCount()-1; + else + return true; + } + + /** + * @return integer the index of the item in data source, where the item is the first in + * current page + */ + public function getFirstIndexInPage() + { + if($this->_dataSource!==null && $this->_allowPaging && !$this->_allowCustomPaging) + return $this->_currentPageIndex*$this->_pageSize; + else + return 0; + } + + /** + * @return integer number of items in data source, if available + */ + public function getDataSourceCount() + { + if($this->_dataSource===null) + return 0; + else if($this->_allowCustomPaging) + return $this->_virtualCount; + else + return $this->_dataSource->getCount(); + } + + /** + * @return Iterator iterator + */ + public function getIterator() + { + if($this->_dataSource instanceof TList) + return new TPagedListIterator($this->_dataSource,$this->getFirstIndexInPage(),$this->getCount()); + else if($this->_dataSource instanceof TMap) + return new TPagedMapIterator($this->_dataSource,$this->getFirstIndexInPage(),$this->getCount()); + else + return null; + } +} + + + +/** + * TPagedListIterator class + * + * TPagedListIterator implements Iterator interface. + * + * TPagedListIterator is used by {@link TPagedDataSource}. It allows TPagedDataSource + * to return a new iterator for traversing the items in a {@link TList} object. + * + * @author Qiang Xue + * @version $Id: TPagedDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TPagedListIterator implements Iterator +{ + private $_list; + private $_startIndex; + private $_count; + private $_index; + + /** + * Constructor. + * @param TList the data to be iterated through + * @param integer start index + * @param integer number of items to be iterated through + */ + public function __construct(TList $list,$startIndex,$count) + { + $this->_list=$list; + $this->_index=0; + $this->_startIndex=$startIndex; + if($startIndex+$count>$list->getCount()) + $this->_count=$list->getCount()-$startIndex; + else + $this->_count=$count; + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_index=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_index; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_list->itemAt($this->_startIndex+$this->_index); + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_index++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_index<$this->_count; + } +} + +/** + * TPagedMapIterator class + * + * TPagedMapIterator implements Iterator interface. + * + * TPagedMapIterator is used by {@link TPagedDataSource}. It allows TPagedDataSource + * to return a new iterator for traversing the items in a {@link TMap} object. + * + * @author Qiang Xue + * @version $Id: TPagedDataSource.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TPagedMapIterator implements Iterator +{ + private $_map; + private $_startIndex; + private $_count; + private $_index; + private $_iterator; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(TMap $map,$startIndex,$count) + { + $this->_map=$map; + $this->_index=0; + $this->_startIndex=$startIndex; + if($startIndex+$count>$map->getCount()) + $this->_count=$map->getCount()-$startIndex; + else + $this->_count=$count; + $this->_iterator=$map->getIterator(); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_iterator->rewind(); + for($i=0;$i<$this->_startIndex;++$i) + $this->_iterator->next(); + $this->_index=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_iterator->key(); + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_iterator->current(); + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_index++; + $this->_iterator->next(); + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_index<$this->_count; + } +} + diff --git a/gui/baculum/framework/Collections/TPagedList.php b/gui/baculum/framework/Collections/TPagedList.php new file mode 100644 index 0000000000..ff03606fc4 --- /dev/null +++ b/gui/baculum/framework/Collections/TPagedList.php @@ -0,0 +1,477 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPagedList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * TPagedList class + * + * TPagedList implements a list with paging functionality. + * + * TPagedList works in one of two modes, managed paging or customized paging, + * specified by {@link setCustomPaging CustomPaging}. + * - Managed paging ({@link setCustomPaging CustomPaging}=false) : + * the list is assumed to contain all data and it will manage which page + * of data are available to user. + * - Customized paging ({@link setCustomPaging CustomPaging}=true) : + * the list is assumed to contain only one page of data. An {@link onFetchData OnFetchData} + * event will be raised if the list changes to a different page. + * Developers can attach a handler to the event and supply the needed data. + * The event handler can be written as follows, + * + * public function fetchData($sender,$param) + * { + * $offset=$param->Offset; // beginning index of the data needed + * $limit=$param->Limit; // maximum number of data items needed + * // get data according to the above two parameters + * $param->Data=$data; + * } + * + * + * Data in TPagedList can be accessed like an integer-indexed array and can + * be traversed using foreach. For example, + * + * $count=$list->Count; + * for($index=0;$index<$count;++$index) + * echo $list[$index]; + * foreach($list as $index=>$item) // traverse each item in the list + * + * + * The {@link setPageSize PageSize} property specifies the number of items in each page. + * To access different page of data in the list, set {@link setCurrentPageIndex CurrentPageIndex} + * or call {@link nextPage()}, {@link previousPage()}, or {@link gotoPage()}. + * The total number of pages can be obtained by {@link getPageCount() PageCount}. + * + * + * @author Qiang Xue + * @version $Id: TPagedList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TPagedList extends TList +{ + /** + * @var boolean whether to allow custom paging + */ + private $_customPaging=false; + /** + * @var integer number of items in each page + */ + private $_pageSize=10; + /** + * @var integer current page index + */ + private $_currentPageIndex=-1; + /** + * @var integer user-assigned number of items in data source + */ + private $_virtualCount=-1; + + /** + * Constructor. + * @param array|Iterator the initial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only. Always true for paged list. + */ + public function __construct($data=null,$readOnly=false) + { + parent::__construct($data,true); + } + + /** + * @return boolean whether to use custom paging. Defaults to false. + */ + public function getCustomPaging() + { + return $this->_customPaging; + } + + /** + * @param boolean whether to allow custom paging + */ + public function setCustomPaging($value) + { + $this->_customPaging=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer number of items in each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->_pageSize; + } + + /** + * @param integer number of items in each page + */ + public function setPageSize($value) + { + if(($value=TPropertyValue::ensureInteger($value))>0) + $this->_pageSize=$value; + else + throw new TInvalidDataValueException('pagedlist_pagesize_invalid'); + } + + /** + * @return integer current page index. Defaults to 0. + */ + public function getCurrentPageIndex() + { + return $this->_currentPageIndex; + } + + /** + * @param integer current page index + * @throws TInvalidDataValueException if the page index is out of range + */ + public function setCurrentPageIndex($value) + { + if($this->gotoPage($value=TPropertyValue::ensureInteger($value))===false) + throw new TInvalidDataValueException('pagedlist_currentpageindex_invalid'); + } + + /** + * Raises OnPageIndexChanged event. + * This event is raised each time when the list changes to a different page. + * @param TPagedListPageChangedEventParameter event parameter + */ + public function onPageIndexChanged($param) + { + $this->raiseEvent('OnPageIndexChanged',$this,$param); + } + + /** + * Raises OnFetchData event. + * This event is raised each time when the list changes to a different page + * and needs the new page of data. This event can only be raised when + * {@link setCustomPaging CustomPaging} is true. + * @param TPagedListFetchDataEventParameter event parameter + */ + public function onFetchData($param) + { + $this->raiseEvent('OnFetchData',$this,$param); + } + + /** + * Changes to a page with the specified page index. + * @param integer page index + * @return integer|boolean the new page index, false if page index is out of range. + */ + public function gotoPage($pageIndex) + { + if($pageIndex===$this->_currentPageIndex) + return $pageIndex; + if($this->_customPaging) + { + if($pageIndex>=0 && ($this->_virtualCount<0 || $pageIndex<$this->getPageCount())) + { + $param=new TPagedListFetchDataEventParameter($pageIndex,$this->_pageSize*$pageIndex,$this->_pageSize); + $this->onFetchData($param); + if(($data=$param->getData())!==null) + { + $this->setReadOnly(false); + $this->copyFrom($data); + $this->setReadOnly(true); + $oldPage=$this->_currentPageIndex; + $this->_currentPageIndex=$pageIndex; + $this->onPageIndexChanged(new TPagedListPageChangedEventParameter($oldPage)); + return $pageIndex; + } + else + return false; + } + else + return false; + } + else + { + if($pageIndex>=0 && $pageIndex<$this->getPageCount()) + { + $this->_currentPageIndex=$pageIndex; + $this->onPageIndexChanged(null); + return $pageIndex; + } + else + return false; + } + } + + /** + * Switches to the next page. + * @return integer|boolean the new page index, false if next page is not available. + */ + public function nextPage() + { + return $this->gotoPage($this->_currentPageIndex+1); + } + + /** + * Switches to the previous page. + * @return integer|boolean the new page index, false if previous page is not available. + */ + public function previousPage() + { + return $this->gotoPage($this->_currentPageIndex-1); + } + + /** + * @return integer user-assigned number of items in data source. Defaults to 0. + */ + public function getVirtualCount() + { + return $this->_virtualCount; + } + + /** + * @param integer user-assigned number of items in data source + */ + public function setVirtualCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->_virtualCount=$value; + } + + /** + * @return integer number of pages, -1 if under custom paging mode and {@link setVirtualCount VirtualCount} is not set. + */ + public function getPageCount() + { + if($this->_customPaging) + { + if($this->_virtualCount>=0) + return (int)(($this->_virtualCount+$this->_pageSize-1)/$this->_pageSize); + else + return -1; + } + else + return (int)((parent::getCount()+$this->_pageSize-1)/$this->_pageSize); + } + + /** + * @return boolean whether the current page is the first page + */ + public function getIsFirstPage() + { + return $this->_currentPageIndex===0; + } + + /** + * @return boolean whether the current page is the last page + */ + public function getIsLastPage() + { + return $this->_currentPageIndex===$this->getPageCount()-1; + } + + /** + * @return integer the number of items in current page + */ + public function getCount() + { + if($this->_customPaging) + return parent::getCount(); + else + { + if($this->_currentPageIndex===$this->getPageCount()-1) + return parent::getCount()-$this->_pageSize*$this->_currentPageIndex; + else + return $this->_pageSize; + } + } + + /** + * @return Iterator iterator + */ + public function getIterator() + { + if($this->_customPaging) + return parent::getIterator(); + else + { + $data=$this->toArray(); + return new TListIterator($data); + } + } + + /** + * Returns the item at the specified offset. + * This method is exactly the same as {@link offsetGet}. + * @param integer the index of the item + * @return mixed the item at the index + * @throws TInvalidDataValueException if the index is out of the range + */ + public function itemAt($index) + { + if($this->_customPaging) + return parent::itemAt($index); + else + return parent::itemAt($this->_pageSize*$this->_currentPageIndex+$index); + } + + /** + * @param mixed the item + * @return integer the index of the item in the list (0 based), -1 if not found. + */ + public function indexOf($item) + { + $c=$this->getCount(); + for($i=0;$i<$c;++$i) + if($this->itemAt($i)===$item) + return $i; + return -1; + } + + /** + * Returns whether there is an item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return ($offset>=0 && $offset<$this->getCount()); + } + + /** + * Returns the item at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve item. + * @return mixed the item at the offset + * @throws TInvalidDataValueException if the offset is invalid + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * @return array the list of items in array + */ + public function toArray() + { + $c=$this->getCount(); + $array=array(); + for($i=0;$i<$c;++$i) + $array[$i]=$this->itemAt($i); + return $array; + } +} + +/** + * TPagedListPageChangedEventParameter class. + * TPagedListPageChangedEventParameter is used as the parameter for + * {@link TPagedList::onPageChanged OnPageChanged} event. + * To obtain the page index before it was changed, use {@link getOldPageIndex OldPageIndex}. + * + * @author Qiang Xue + * @version $Id: TPagedList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TPagedListPageChangedEventParameter extends TEventParameter +{ + private $_oldPage; + + /** + * Constructor. + * @param integer old page index + */ + public function __construct($oldPage) + { + $this->_oldPage=$oldPage; + } + + /** + * @return integer the index of the page before the list changed to the new page + */ + public function getOldPageIndex() + { + return $this->_oldPage; + } +} + +/** + * TPagedListFetchDataEventParameter class. + * + * TPagedListFetchDataEventParameter is used as the parameter for + * {@link TPagedList::onFetchData OnFetchData} event. + * To obtain the new page index, use {@link getNewPageIndex NewPageIndex}. + * The {@link getOffset Offset} property refers to the index + * of the first item in the new page, while {@link getLimit Limit} + * specifies how many items are requested for the page. + * Newly fetched data should be saved in {@link setData Data} property. + * + * @author Qiang Xue + * @version $Id: TPagedList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TPagedListFetchDataEventParameter extends TEventParameter +{ + private $_pageIndex; + private $_offset; + private $_limit; + private $_data=null; + + /** + * Constructor. + * @param integer new page index + * @param integer offset of the first item in the new page + * @param integer number of items in the new page desired + */ + public function __construct($pageIndex,$offset,$limit) + { + $this->_pageIndex=$pageIndex; + $this->_offset=$offset; + $this->_limit=$limit; + } + + /** + * @return integer the zero-based index of the new page + */ + public function getNewPageIndex() + { + return $this->_pageIndex; + } + + /** + * @return integer offset of the first item in the new page + */ + public function getOffset() + { + return $this->_offset; + } + + /** + * @return integer number of items in the new page + */ + public function getLimit() + { + return $this->_limit; + } + + /** + * @return mixed new page data + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed new page data + */ + public function setData($value) + { + $this->_data=$value; + } +} + diff --git a/gui/baculum/framework/Collections/TPriorityList.php b/gui/baculum/framework/Collections/TPriorityList.php new file mode 100644 index 0000000000..1ccbd9cec1 --- /dev/null +++ b/gui/baculum/framework/Collections/TPriorityList.php @@ -0,0 +1,743 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPriorityList.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Collections + */ + +/** + * TPriorityList class + * + * TPriorityList implements a priority ordered list collection class. It allows you to specify + * any numeric for priorities down to a specific precision. The lower the numeric, the high the priority of the item in the + * list. Thus -10 has a higher priority than -5, 0, 10 (the default), 18, 10005, etc. Per {@link round}, precision may be negative and + * thus rounding can go by 10, 100, 1000, etc, instead of just .1, .01, .001, etc. The default precision allows for 8 decimal + * places. There is also a default priority of 10, if no different default priority is specified or no item specific priority is indicated. + * If you replace TList with this class it will work exactly the same with items inserted set to the default priority, until you start + * using different priorities than the default priority. + * + * As you access the PHP array features of this class, it flattens and caches the results. If at all possible, this + * will keep the cache fresh even when manipulated. If this is not possible the cache is cleared. + * When an array of items are needed and the cache is outdated, the cache is recreated from the items and their priorities + * + * You can access, append, insert, remove an item by using + * {@link itemAt}, {@link add}, {@link insertAt}, and {@link remove}. + * To get the number of the items in the list, use {@link getCount}. + * TPriorityList can also be used like a regular array as follows, + * + * $list[]=$item; // append with the default priority. It may not be the last item if other items in the list are prioritized after the default priority + * $list[$index]=$item; // $index must be between 0 and $list->Count-1. This sets the element regardless of priority. Priority stays the same. + * $list[$index]=$item; // $index is $list->Count. This appends the item to the end of the list with the same priority as the last item in the list. + * unset($list[$index]); // remove the item at $index + * if(isset($list[$index])) // if the list has an item at $index + * foreach($list as $index=>$item) // traverse each item in the list in proper priority order and add/insert order + * $n=count($list); // returns the number of items in the list + * + * + * To extend TPriorityList for doing your own operations with each addition or removal, + * override {@link insertAtIndexInPriority()} and {@link removeAtIndexInPriority()} and then call the parent. + * + * @author Brad Anderson + * @version $Id: TPriorityList.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Collections + * @since 3.2a + */ +class TPriorityList extends TList +{ + /** + * @var array internal data storage + */ + private $_d=array(); + /** + * @var boolean indicates if the _d is currently ordered. + */ + private $_o=false; + /** + * @var array cached flattened internal data storage + */ + private $_fd=null; + /** + * @var integer number of items contain within the list + */ + private $_c=0; + /** + * @var numeric the default priority of items without specified priorities + */ + private $_dp=10; + /** + * @var integer the precision of the numeric priorities within this priority list. + */ + private $_p=8; + + /** + * Constructor. + * Initializes the list with an array or an iterable object. + * @param array|Iterator the intial data. Default is null, meaning no initial data. + * @param boolean whether the list is read-only + * @param numeric the default priority of items without specified priorities. + * @param integer the precision of the numeric priorities + * @throws TInvalidDataTypeException If data is not null and is neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) + { + parent::__construct(); + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + $this->setPrecision($precision); + $this->setDefaultPriority($defaultPriority); + } + + /** + * Returns the number of items in the list. + * This method is required by Countable interface. + * @return integer number of items in the list. + */ + public function count() + { + return $this->getCount(); + } + + /** + * Returns the total number of items in the list + * @return integer the number of items in the list + */ + public function getCount() + { + return $this->_c; + } + + /** + * Gets the number of items at a priority within the list + * @param numeric optional priority at which to count items. if no parameter, it will be set to the default {@link getDefaultPriority} + * @return integer the number of items in the list at the specified priority + */ + public function getPriorityCount($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if(!isset($this->_d[$priority]) || !is_array($this->_d[$priority])) + return false; + return count($this->_d[$priority]); + } + + /** + * @return numeric gets the default priority of inserted items without a specified priority + */ + public function getDefaultPriority() + { + return $this->_dp; + } + + /** + * This must be called internally or when instantiated. + * @param numeric sets the default priority of inserted items without a specified priority + */ + protected function setDefaultPriority($value) + { + $this->_dp=(string)round(TPropertyValue::ensureFloat($value),$this->_p); + } + + /** + * @return integer The precision of numeric priorities, defaults to 8 + */ + public function getPrecision() + { + return $this->_p; + } + + /** + * This must be called internally or when instantiated. + * @param integer The precision of numeric priorities. + */ + protected function setPrecision($value) + { + $this->_p=TPropertyValue::ensureInteger($value); + } + + /** + * Returns an iterator for traversing the items in the list. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + return new ArrayIterator($this->flattenPriorities()); + } + + /** + * This returns a list of the priorities within this list, ordered lowest to highest. + * @return array the array of priority numerics in decreasing priority order + */ + public function getPriorities() + { + $this->sortPriorities(); + return array_keys($this->_d); + } + + + /** + * This orders the priority list internally. + */ + protected function sortPriorities() { + if(!$this->_o) { + ksort($this->_d,SORT_NUMERIC); + $this->_o=true; + } + } + + /** + * This flattens the priority list into a flat array [0,...,n-1] + * @return array array of items in the list in priority and index order + */ + protected function flattenPriorities() { + if(is_array($this->_fd)) + return $this->_fd; + + $this->sortPriorities(); + $this->_fd=array(); + foreach($this->_d as $priority => $itemsatpriority) + $this->_fd=array_merge($this->_fd,$itemsatpriority); + return $this->_fd; + } + + + /** + * Returns the item at the index of a flattened priority list. + * {@link offsetGet} calls this method. + * @param integer the index of the item to get + * @return mixed the element at the offset + * @throws TInvalidDataValueException Issued when the index is invalid + */ + public function itemAt($index) + { + if($index>=0&&$index<$this->getCount()) { + $arr=$this->flattenPriorities(); + return $arr[$index]; + } else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + + /** + * Gets all the items at a specific priority. + * @param numeric priority of the items to get. Defaults to null, filled in with the default priority, if left blank. + * @return array all items at priority in index order, null if there are no items at that priority + */ + public function itemsAtPriority($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + return isset($this->_d[$priority])?$this->_d[$priority]:null; + } + + /** + * Returns the item at an index within a priority + * @param integer the index into the list of items at priority + * @param numeric the priority which to index. no parameter or null will result in the default priority + * @return mixed the element at the offset, false if no element is found at the offset + */ + public function itemAtIndexInPriority($index,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); + + return !isset($this->_d[$priority])?false:( + isset($this->_d[$priority][$index])?$this->_d[$priority][$index]:false + ); + } + + /** + * Appends an item into the list at the end of the specified priority. The position of the added item may + * not be at the end of the list. + * @param mixed item to add into the list at priority + * @param numeric priority blank or null for the default priority + * @return int the index within the flattened array + * @throws TInvalidOperationException if the map is read-only + */ + public function add($item,$priority=null) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + return $this->insertAtIndexInPriority($item,false,$priority,true); + } + + /** + * Inserts an item at an index. It reads the priority of the item at index within the flattened list + * and then inserts the item at that priority-index. + * @param integer the specified position in the flattened list. + * @param mixed new item to add + * @throws TInvalidDataValueException If the index specified exceeds the bound + * @throws TInvalidOperationException if the list is read-only + */ + public function insertAt($index,$item) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if(($priority=$this->priorityAt($index,true))!==false) + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + + /** + * Inserts an item at the specified index within a priority. Override and call this method to + * insert your own functionality. + * @param mixed item to add within the list. + * @param integer index within the priority to add the item, defaults to false which appends the item at the priority + * @param numeric priority priority of the item. defaults to null, which sets it to the default priority + * @param boolean preserveCache specifies if this is a special quick function or not. This defaults to false. + * @throws TInvalidDataValueException If the index specified exceeds the bound + * @throws TInvalidOperationException if the list is read-only + */ + public function insertAtIndexInPriority($item,$index=false,$priority=null,$preserveCache=false) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); + + if($preserveCache) { + $this->sortPriorities(); + $cc=0; + foreach($this->_d as $prioritykey=>$items) + if($prioritykey>=$priority) + break; + else + $cc+=count($items); + + if($index===false&&isset($this->_d[$priority])) { + $c=count($this->_d[$priority]); + $c+=$cc; + $this->_d[$priority][]=$item; + } else if(isset($this->_d[$priority])) { + $c=$index+$cc; + array_splice($this->_d[$priority],$index,0,array($item)); + } else { + $c = $cc; + $this->_o = false; + $this->_d[$priority]=array($item); + } + + if($this->_fd&&is_array($this->_fd)) // if there is a flattened array cache + array_splice($this->_fd,$c,0,array($item)); + } else { + $c=null; + if($index===false&&isset($this->_d[$priority])) { + $cc=count($this->_d[$priority]); + $this->_d[$priority][]=$item; + } else if(isset($this->_d[$priority])) { + $cc=$index; + array_splice($this->_d[$priority],$index,0,array($item)); + } else { + $cc=0; + $this->_o=false; + $this->_d[$priority]=array($item); + } + if($this->_fd&&is_array($this->_fd)&&count($this->_d)==1) + array_splice($this->_fd,$cc,0,array($item)); + else + $this->_fd=null; + } + + $this->_c++; + + return $c; + + } + + + /** + * Removes an item from the priority list. + * The list will search for the item. The first matching item found will be removed from the list. + * @param mixed item the item to be removed. + * @param numeric priority of item to remove. without this parameter it defaults to false. + * A value of false means any priority. null will be filled in with the default priority. + * @return integer index within the flattened list at which the item is being removed + * @throws TInvalidDataValueException If the item does not exist + */ + public function remove($item,$priority=false) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if(($p=$this->priorityOf($item,true))!==false) + { + if($priority!==false) { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if($p[0]!=$priority) + throw new TInvalidDataValueException('list_item_inexistent'); + } + $this->removeAtIndexInPriority($p[1],$p[0]); + return $p[2]; + } + else + throw new TInvalidDataValueException('list_item_inexistent'); + } + + /** + * Removes an item at the specified index in the flattened list. + * @param integer index of the item to be removed. + * @return mixed the removed item. + * @throws TInvalidDataValueException If the index specified exceeds the bound + * @throws TInvalidOperationException if the list is read-only + */ + public function removeAt($index) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if(($priority=$this->priorityAt($index, true))!==false) + return $this->removeAtIndexInPriority($priority[1],$priority[0]); + throw new TInvalidDataValueException('list_index_invalid',$index); + } + + /** + * Removes the item at a specific index within a priority. Override + * and call this method to insert your own functionality. + * @param integer index of item to remove within the priority. + * @param numeric priority of the item to remove, defaults to null, or left blank, it is then set to the default priority + * @return mixed the removed item. + * @throws TInvalidDataValueException If the item does not exist + */ + public function removeAtIndexInPriority($index, $priority=null) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if(!isset($this->_d[$priority])||$index<0||$index>=count($this->_d[$priority])) + throw new TInvalidDataValueException('list_item_inexistent'); + + // $value is an array of elements removed, only one + $value=array_splice($this->_d[$priority],$index,1); + $value=$value[0]; + + if(!count($this->_d[$priority])) + unset($this->_d[$priority]); + + $this->_c--; + $this->_fd=null; + return $value; + } + + /** + * Removes all items in the priority list by calling removeAtIndexInPriority from the last item to the first. + */ + public function clear() + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + $d=array_reverse($this->_d,true); + foreach($this->_d as $priority=>$items) { + for($index=count($items)-1;$index>=0;$index--) + $this->removeAtIndexInPriority($index,$priority); + unset($this->_d[$priority]); + } + } + + /** + * @param mixed item + * @return boolean whether the list contains the item + */ + public function contains($item) + { + return $this->indexOf($item)>=0; + } + + /** + * @param mixed item + * @return integer the index of the item in the flattened list (0 based), -1 if not found. + */ + public function indexOf($item) + { + if(($index=array_search($item,$this->flattenPriorities(),true))===false) + return -1; + else + return $index; + } + + /** + * Returns the priority of a particular item + * @param mixed the item to look for within the list + * @param boolean withindex this specifies if the full positional data of the item within the list is returned. + * This defaults to false, if no parameter is provided, so only provides the priority number of the item by default. + * @return numeric|array the priority of the item in the list, false if not found. + * if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex, + * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex] + */ + public function priorityOf($item,$withindex = false) + { + $this->sortPriorities(); + + $absindex = 0; + foreach($this->_d as $priority=>$items) { + if(($index=array_search($item,$items,true))!==false) { + $absindex+=$index; + return $withindex?array($priority,$index,$absindex, + 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; + } else + $absindex+=count($items); + } + + return false; + } + + /** + * Retutrns the priority of an item at a particular flattened index. + * @param integer index of the item within the list + * @param boolean withindex this specifies if the full positional data of the item within the list is returned. + * This defaults to false, if no parameter is provided, so only provides the priority number of the item by default. + * @return numeric|array the priority of the item in the list, false if not found. + * if withindex is true, an array is returned of [0 => $priority, 1 => $priorityIndex, 2 => flattenedIndex, + * 'priority' => $priority, 'index' => $priorityIndex, 'absindex' => flattenedIndex] + */ + public function priorityAt($index,$withindex = false) + { + if($index<0||$index>=$this->getCount()) + throw new TInvalidDataValueException('list_index_invalid',$index); + + $absindex=$index; + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) { + if($index>=($c=count($items))) + $index-=$c; + else + return $withindex?array($priority,$index,$absindex, + 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; + } + return false; + } + + /** + * This inserts an item before another item within the list. It uses the same priority as the + * found index item and places the new item before it. + * @param mixed indexitem the item to index + * @param mixed the item to add before indexitem + * @return integer where the item has been inserted in the flattened list + * @throws TInvalidDataValueException If the item does not exist + */ + public function insertBefore($indexitem, $item) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if(($priority=$this->priorityOf($indexitem,true))===false) + throw new TInvalidDataValueException('list_item_inexistent'); + + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); + + return $priority[2]; + } + + /** + * This inserts an item after another item within the list. It uses the same priority as the + * found index item and places the new item after it. + * @param mixed indexitem the item to index + * @param mixed the item to add after indexitem + * @return integer where the item has been inserted in the flattened list + * @throws TInvalidDataValueException If the item does not exist + */ + public function insertAfter($indexitem, $item) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + + if(($priority=$this->priorityOf($indexitem,true))===false) + throw new TInvalidDataValueException('list_item_inexistent'); + + $this->insertAtIndexInPriority($item,$priority[1]+1,$priority[0]); + + return $priority[2]+1; + } + + /** + * @return array the priority list of items in array + */ + public function toArray() + { + return $this->flattenPriorities(); + } + + /** + * @return array the array of priorities keys with values of arrays of items. The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toPriorityArray() + { + $this->sortPriorities(); + return $this->_d; + } + + /** + * Combines the map elements which have a priority below the parameter value + * @param numeric the cut-off priority. All items of priority less than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: false, not inclusive. + * @return array the array of priorities keys with values of arrays of items that are below a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayBelowPriority($priority,$inclusive=false) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) + break; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + /** + * Combines the map elements which have a priority above the parameter value + * @param numeric the cut-off priority. All items of priority greater than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: true, inclusive. + * @return array the array of priorities keys with values of arrays of items that are above a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayAbovePriority($priority,$inclusive=true) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) + continue; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + + /** + * Copies iterable data into the priority list. + * Note, existing data in the map will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function copyFrom($data) + { + if($data instanceof TPriorityList) + { + if($this->getCount()>0) + $this->clear(); + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $index=>$item) + $this->insertAtIndexInPriority($item,$index,$priority); + } + } else if(is_array($data)||$data instanceof Traversable) { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$item) + $this->add($item); + } else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Merges iterable data into the priority list. + * New data will be appended to the end of the existing data. If another TPriorityList is merged, + * the incoming parameter items will be appended at the priorities they are present. These items will be added + * to the end of the existing items with equal priorities, if there are any. + * @param mixed the data to be merged with, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function mergeWith($data) + { + if($data instanceof TPriorityList) + { + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $index=>$item) + $this->insertAtIndexInPriority($item,false,$priority); + } + } + else if(is_array($data)||$data instanceof Traversable) + { + foreach($data as $priority=>$item) + $this->add($item); + + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return ($offset>=0&&$offset<$this->getCount()); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the element at the specified offset. This method is required by the interface ArrayAccess. + * Setting elements in a priority list is not straight forword when appending and setting at the + * end boundary. When appending without an offset (a null offset), the item will be added at + * the default priority. The item may not be the last item in the list. When appending with an + * offset equal to the count of the list, the item will get be appended with the last items priority. + * + * All together, when setting the location of an item, the item stays in that location, but appending + * an item into a priority list doesn't mean the item is at the end of the list. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + if($offset===null) + return $this->add($item); + if($offset===$this->getCount()) { + $priority=$this->priorityAt($offset-1,true); + $priority[1]++; + } else { + $priority=$this->priorityAt($offset,true); + $this->removeAtIndexInPriority($priority[1],$priority[0]); + } + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + $this->removeAt($offset); + } +} diff --git a/gui/baculum/framework/Collections/TPriorityMap.php b/gui/baculum/framework/Collections/TPriorityMap.php new file mode 100644 index 0000000000..46f05e9e00 --- /dev/null +++ b/gui/baculum/framework/Collections/TPriorityMap.php @@ -0,0 +1,606 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPriorityMap.php 2817 2010-04-18 04:25:03Z javalizard $ + * @package System.Collections + */ + +/** + * TPriorityMap class + * + * TPriorityMap implements a collection that takes key-value pairs with + * a priority to allow key-value pairs to be ordered. This ordering is + * important when flattening the map. When flattening the map, if some + * key-value pairs are required to be before or after others, use this + * class to keep order to your map. + * + * You can access, add or remove an item with a key by using + * {@link itemAt}, {@link add}, and {@link remove}. These functions + * can optionally take a priority parameter to allow access to specific + * priorities. TPriorityMap is functionally backward compatible + * with {@link TMap}. + * + * To get the number of the items in the map, use {@link getCount}. + * TPriorityMap can also be used like a regular array as follows, + * + * $map[$key]=$value; // add a key-value pair + * unset($map[$key]); // remove the value with the specified key + * if(isset($map[$key])) // if the map contains the key + * foreach($map as $key=>$value) // traverse the items in the map + * $n=count($map); // returns the number of items in the map + * + * Using standard array access method like these will always use + * the default priority. + * + * An item that doesn't specify a priority will receive the default + * priority. The default priority is set during the instantiation + * of a new TPriorityMap. If no custom default priority is specified, + * the standard default priority of 10 is used. + * + * Priorities with significant digits below precision will be rounded. + * + * A priority may also be a numeric with decimals. This is set + * during the instantiation of a new TPriorityMap. + * The default is 8 decimal places for a priority. If a negative number + * is used, rounding occurs into the integer space rather than in + * the decimal space. See {@link round}. + * + * @author Brad Anderson + * @version $Id: TPriorityMap.php 2817 2010-04-18 04:25:03Z javalizard $ + * @package System.Collections + * @since 3.2a + */ +Prado::using('System.Collections.TMap'); + +class TPriorityMap extends TMap +{ + /** + * @var array internal data storage + */ + private $_d=array(); + /** + * @var boolean whether this list is read-only + */ + private $_r=false; + /** + * @var boolean indicates if the _d is currently ordered. + */ + private $_o=false; + /** + * @var array cached flattened internal data storage + */ + private $_fd=null; + /** + * @var integer number of items contain within the map + */ + private $_c=0; + /** + * @var numeric the default priority of items without specified priorities + */ + private $_dp=10; + /** + * @var integer the precision of the numeric priorities within this priority list. + */ + private $_p=8; + + /** + * Constructor. + * Initializes the array with an array or an iterable object. + * @param map|array|Iterator|TPriorityMap the intial data. Default is null, meaning no initialization. + * @param boolean whether the list is read-only + * @param numeric the default priority of items without specified priorities. + * @param integer the precision of the numeric priorities + * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + $this->setPrecision($precision); + $this->setDefaultPriority($defaultPriority); + } + + /** + * @return boolean whether this map is read-only or not. Defaults to false. + */ + public function getReadOnly() + { + return $this->_r; + } + + /** + * @param boolean whether this list is read-only or not + */ + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + + /** + * @return numeric gets the default priority of inserted items without a specified priority + */ + public function getDefaultPriority() + { + return $this->_dp; + } + + /** + * This must be called internally or when instantiated. + * @param numeric sets the default priority of inserted items without a specified priority + */ + protected function setDefaultPriority($value) + { + $this->_dp = (string)round(TPropertyValue::ensureFloat($value), $this->_p); + } + + /** + * @return integer The precision of numeric priorities, defaults to 8 + */ + public function getPrecision() + { + return $this->_p; + } + + /** + * This must be called internally or when instantiated. + * @param integer The precision of numeric priorities. + */ + protected function setPrecision($value) + { + $this->_p=TPropertyValue::ensureInteger($value); + } + + /** + * Returns an iterator for traversing the items in the map. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the map. + */ + public function getIterator() + { + return new ArrayIterator($this->flattenPriorities()); + } + + + /** + * Orders the priority list internally. + */ + protected function sortPriorities() { + if(!$this->_o) { + ksort($this->_d, SORT_NUMERIC); + $this->_o=true; + } + } + + /** + * This flattens the priority map into a flat array [0,...,n-1] + * @return array array of items in the list in priority and index order + */ + protected function flattenPriorities() { + if(is_array($this->_fd)) + return $this->_fd; + + $this->sortPriorities(); + $this->_fd = array(); + foreach($this->_d as $priority => $itemsatpriority) + $this->_fd = array_merge($this->_fd, $itemsatpriority); + return $this->_fd; + } + + /** + * Returns the number of items in the map. + * This method is required by Countable interface. + * @return integer number of items in the map. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return integer the number of items in the map + */ + public function getCount() + { + return $this->_c; + } + + /** + * Gets the number of items at a priority within the map. + * @param numeric optional priority at which to count items. if no parameter, + * it will be set to the default {@link getDefaultPriority} + * @return integer the number of items in the map at the specified priority + */ + public function getPriorityCount($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if(!isset($this->_d[$priority])||!is_array($this->_d[$priority])) + return false; + return count($this->_d[$priority]); + } + + /** + * This returns a list of the priorities within this map, ordered lowest to highest. + * @return array the array of priority numerics in decreasing priority order + */ + public function getPriorities() + { + $this->sortPriorities(); + return array_keys($this->_d); + } + + /** + * Returns the keys within the map ordered through the priority of each key-value pair + * @return array the key list + */ + public function getKeys() + { + return array_keys($this->flattenPriorities()); + } + + /** + * Returns the item with the specified key. If a priority is specified, only items + * within that specific priority will be selected + * @param mixed the key + * @param mixed the priority. null is the default priority, false is any priority, + * and numeric is a specific priority. default: false, any priority. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key,$priority=false) + { + if($priority===false){ + $map=$this->flattenPriorities(); + return isset($map[$key])?$map[$key]:null; + } else { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + return (isset($this->_d[$priority])&&isset($this->_d[$priority][$key]))?$this->_d[$priority][$key]:null; + } + } + + /** + * This changes an item's priority. Specify the item and the new priority. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the key + * @param numeric|null the priority. default: null, filled in with the default priority numeric. + * @return numeric old priority of the item + */ + public function setPriorityAt($key,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + $oldpriority=$this->priorityAt($key); + if($oldpriority!==false&&$oldpriority!=$priority) { + $value=$this->remove($key,$oldpriority); + $this->add($key,$value,$priority); + } + return $oldpriority; + } + + /** + * Gets all the items at a specific priority. + * @param numeric priority of the items to get. Defaults to null, filled in with the default priority, if left blank. + * @return array all items at priority in index order, null if there are no items at that priority + */ + public function itemsAtPriority($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + return isset($this->_d[$priority])?$this->_d[$priority]:null; + } + + /** + * Returns the priority of a particular item within the map. This searches the map for the item. + * @param mixed item to look for within the map + * @return numeric priority of the item in the map + */ + public function priorityOf($item) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(($index=array_search($item,$items,true))!==false) + return $priority; + return false; + } + + /** + * Retutrns the priority of an item at a particular flattened index. + * @param integer index of the item within the map + * @return numeric priority of the item in the map + */ + public function priorityAt($key) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(array_key_exists($key,$items)) + return $priority; + return false; + } + + /** + * Adds an item into the map. A third parameter may be used to set the priority + * of the item within the map. Priority is primarily used during when flattening + * the map into an array where order may be and important factor of the key-value + * pairs within the array. + * Note, if the specified key already exists, the old value will be overwritten. + * No duplicate keys are allowed regardless of priority. + * @param mixed key + * @param mixed value + * @param numeric|null priority, default: null, filled in with default priority + * @return numeric priority at which the pair was added + * @throws TInvalidOperationException if the map is read-only + */ + public function add($key,$value,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + + if(!$this->_r) + { + foreach($this->_d as $innerpriority=>$items) + if(array_key_exists($key,$items)) + { + unset($this->_d[$innerpriority][$key]); + $this->_c--; + if(count($this->_d[$innerpriority])===0) + unset($this->_d[$innerpriority]); + } + if(!isset($this->_d[$priority])) { + $this->_d[$priority]=array($key=>$value); + $this->_o=false; + } + else + $this->_d[$priority][$key]=$value; + $this->_c++; + $this->_fd=null; + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + return $priority; + } + + /** + * Removes an item from the map by its key. If no priority, or false, is specified + * then priority is irrelevant. If null is used as a parameter for priority, then + * the priority will be the default priority. If a priority is specified, or + * the default priority is specified, only key-value pairs in that priority + * will be affected. + * @param mixed the key of the item to be removed + * @param numeric|false|null priority. False is any priority, null is the + * default priority, and numeric is a specific priority + * @return mixed the removed value, null if no such key exists. + * @throws TInvalidOperationException if the map is read-only + */ + public function remove($key,$priority=false) + { + if(!$this->_r) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + + if($priority===false) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(array_key_exists($key,$items)) + { + $value=$this->_d[$priority][$key]; + unset($this->_d[$priority][$key]); + $this->_c--; + if(count($this->_d[$priority])===0) + { + unset($this->_d[$priority]); + $this->_o=false; + } + $this->_fd=null; + return $value; + } + return null; + } + else + { + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(isset($this->_d[$priority])&&(isset($this->_d[$priority][$key])||array_key_exists($key,$this->_d[$priority]))) + { + $value=$this->_d[$priority][$key]; + unset($this->_d[$priority][$key]); + $this->_c--; + if(count($this->_d[$priority])===0) { + unset($this->_d[$priority]); + $this->_o=false; + } + $this->_fd=null; + return $value; + } + else + return null; + } + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + + /** + * Removes all items in the map. {@link remove} is called on all items. + */ + public function clear() + { + foreach($this->_d as $priority=>$items) + foreach(array_keys($items) as $key) + $this->remove($key); + } + + /** + * @param mixed the key + * @return boolean whether the map contains an item with the specified key + */ + public function contains($key) + { + $map=$this->flattenPriorities(); + return isset($map[$key])||array_key_exists($key,$map); + } + + /** + * When the map is flattened into an array, the priorities are taken into + * account and elements of the map are ordered in the array according to + * their priority. + * @return array the list of items in array + */ + public function toArray() + { + return $this->flattenPriorities(); + } + + /** + * Combines the map elements which have a priority below the parameter value + * @param numeric the cut-off priority. All items of priority less than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: false, not inclusive. + * @return array the array of priorities keys with values of arrays of items that are below a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayBelowPriority($priority,$inclusive=false) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) + break; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + /** + * Combines the map elements which have a priority above the parameter value + * @param numeric the cut-off priority. All items of priority greater than this are returned. + * @param boolean whether or not the input cut-off priority is inclusive. Default: true, inclusive. + * @return array the array of priorities keys with values of arrays of items that are above a specified priority. + * The priorities are sorted so important priorities, lower numerics, are first. + */ + public function toArrayAbovePriority($priority,$inclusive=true) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) + continue; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + + /** + * Copies iterable data into the map. + * Note, existing data in the map will be cleared first. + * @param mixed the data to be copied from, must be an array, object implementing + * Traversable, or a TPriorityMap + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function copyFrom($data) + { + if($data instanceof TPriorityMap) + { + if($this->getCount()>0) + $this->clear(); + foreach($data->getPriorities() as $priority) { + foreach($data->itemsAtPriority($priority) as $key => $value) { + $this->add($key,$value,$priority); + } + } + } + else if(is_array($data)||$data instanceof Traversable) + { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Merges iterable data into the map. + * Existing data in the map will be kept and overwritten if the keys are the same. + * @param mixed the data to be merged with, must be an array, object implementing + * Traversable, or a TPriorityMap + * @throws TInvalidDataTypeException If data is neither an array nor an iterator. + */ + public function mergeWith($data) + { + if($data instanceof TPriorityMap) + { + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $key => $value) + $this->add($key,$value,$priority); + } + } + else if(is_array($data)||$data instanceof Traversable) + { + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return $this->contains($offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Collections/TQueue.php b/gui/baculum/framework/Collections/TQueue.php new file mode 100644 index 0000000000..077b9b2fc4 --- /dev/null +++ b/gui/baculum/framework/Collections/TQueue.php @@ -0,0 +1,263 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Collections + */ + +/** + * TQueue class + * + * TQueue implements a queue. + * + * The typical queue operations are implemented, which include + * {@link enqueue()}, {@link dequeue()} and {@link peek()}. In addition, + * {@link contains()} can be used to check if an item is contained + * in the queue. To obtain the number of the items in the queue, + * check the {@link getCount Count} property. + * + * Items in the queue may be traversed using foreach as follows, + * + * foreach($queue as $item) ... + * + * + * @author Qiang Xue + * @author Knut Urdalen + * @version $Id$ + * @package System.Collections + * @since 3.1 + */ +class TQueue extends TComponent implements IteratorAggregate,Countable +{ + /** + * internal data storage + * @var array + */ + private $_d=array(); + /** + * number of items + * @var integer + */ + private $_c=0; + + /** + * Constructor. + * Initializes the queue with an array or an iterable object. + * @param array|Iterator the intial data. Default is null, meaning no initialization. + * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null) + { + if($data!==null) + $this->copyFrom($data); + } + + /** + * @return array the list of items in queue + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the queue. + * Note, existing data in the list will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor a Traversable. + */ + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + $this->clear(); + foreach($data as $item) + { + $this->_d[]=$item; + ++$this->_c; + } + } + else if($data!==null) + throw new TInvalidDataTypeException('queue_data_not_iterable'); + } + + /** + * Removes all items in the queue. + */ + public function clear() + { + $this->_c=0; + $this->_d=array(); + } + + /** + * @param mixed the item + * @return boolean whether the queue contains the item + */ + public function contains($item) + { + return array_search($item,$this->_d,true)!==false; + } + + /** + * Returns the first item at the front of the queue. + * Unlike {@link dequeue()}, this method does not remove the item from the queue. + * @return mixed item at the top of the queue + * @throws TInvalidOperationException if the queue is empty + */ + public function peek() + { + if($this->_c===0) + throw new TInvalidOperationException('queue_empty'); + else + return $this->_d[0]; + } + + /** + * Removes and returns the object at the beginning of the queue. + * @return mixed the item at the beginning of the queue + * @throws TInvalidOperationException if the queue is empty + */ + public function dequeue() + { + if($this->_c===0) + throw new TInvalidOperationException('queue_empty'); + else + { + --$this->_c; + return array_shift($this->_d); + } + } + + /** + * Adds an object to the end of the queue. + * @param mixed the item to be appended into the queue + */ + public function enqueue($item) + { + ++$this->_c; + $this->_d[] = $item; + } + + /** + * Returns an iterator for traversing the items in the queue. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the queue. + */ + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + + /** + * @return integer the number of items in the queue + */ + public function getCount() + { + return $this->_c; + } + + /** + * Returns the number of items in the queue. + * This method is required by Countable interface. + * @return integer number of items in the queue. + */ + public function count() + { + return $this->getCount(); + } +} + +/** + * TQueueIterator class + * + * TQueueIterator implements Iterator interface. + * + * TQueueIterator is used by TQueue. It allows TQueue to return a new iterator + * for traversing the items in the queue. + * + * @author Qiang Xue + * @version $Id$ + * @package System.Collections + * @since 3.1 + */ +class TQueueIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var integer index of the current item + */ + private $_i; + /** + * @var integer count of the data items + */ + private $_c; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_i=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_i; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_d[$this->_i]; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_i++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_i<$this->_c; + } +} + diff --git a/gui/baculum/framework/Collections/TStack.php b/gui/baculum/framework/Collections/TStack.php new file mode 100644 index 0000000000..06c97046dd --- /dev/null +++ b/gui/baculum/framework/Collections/TStack.php @@ -0,0 +1,263 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TStack.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + */ + +/** + * TStack class + * + * TStack implements a stack. + * + * The typical stack operations are implemented, which include + * {@link push()}, {@link pop()} and {@link peek()}. In addition, + * {@link contains()} can be used to check if an item is contained + * in the stack. To obtain the number of the items in the stack, + * check the {@link getCount Count} property. + * + * Items in the stack may be traversed using foreach as follows, + * + * foreach($stack as $item) ... + * + * + * @author Qiang Xue + * @version $Id: TStack.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TStack extends TComponent implements IteratorAggregate,Countable +{ + /** + * internal data storage + * @var array + */ + private $_d=array(); + /** + * number of items + * @var integer + */ + private $_c=0; + + /** + * Constructor. + * Initializes the stack with an array or an iterable object. + * @param array|Iterator the initial data. Default is null, meaning no initialization. + * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator. + */ + public function __construct($data=null) + { + if($data!==null) + $this->copyFrom($data); + } + + /** + * @return array the list of items in stack + */ + public function toArray() + { + return $this->_d; + } + + /** + * Copies iterable data into the stack. + * Note, existing data in the list will be cleared first. + * @param mixed the data to be copied from, must be an array or object implementing Traversable + * @throws TInvalidDataTypeException If data is neither an array nor a Traversable. + */ + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + $this->clear(); + foreach($data as $item) + { + $this->_d[]=$item; + ++$this->_c; + } + } + else if($data!==null) + throw new TInvalidDataTypeException('stack_data_not_iterable'); + } + + /** + * Removes all items in the stack. + */ + public function clear() + { + $this->_c=0; + $this->_d=array(); + } + + /** + * @param mixed the item + * @return boolean whether the stack contains the item + */ + public function contains($item) + { + return array_search($item,$this->_d,true)!==false; + } + + /** + * Returns the item at the top of the stack. + * Unlike {@link pop()}, this method does not remove the item from the stack. + * @return mixed item at the top of the stack + * @throws TInvalidOperationException if the stack is empty + */ + public function peek() + { + if($this->_c===0) + throw new TInvalidOperationException('stack_empty'); + else + return $this->_d[$this->_c-1]; + } + + /** + * Pops up the item at the top of the stack. + * @return mixed the item at the top of the stack + * @throws TInvalidOperationException if the stack is empty + */ + public function pop() + { + if($this->_c===0) + throw new TInvalidOperationException('stack_empty'); + else + { + --$this->_c; + return array_pop($this->_d); + } + } + + /** + * Pushes an item into the stack. + * @param mixed the item to be pushed into the stack + */ + public function push($item) + { + ++$this->_c; + $this->_d[] = $item; + } + + /** + * Returns an iterator for traversing the items in the stack. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the stack. + */ + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + + /** + * @return integer the number of items in the stack + */ + public function getCount() + { + return $this->_c; + } + + /** + * Returns the number of items in the stack. + * This method is required by Countable interface. + * @return integer number of items in the stack. + */ + public function count() + { + return $this->getCount(); + } +} + +/** + * TStackIterator class + * + * TStackIterator implements Iterator interface. + * + * TStackIterator is used by TStack. It allows TStack to return a new iterator + * for traversing the items in the list. + * + * @deprecated Issue 264 : ArrayIterator should be used instead + * @author Qiang Xue + * @version $Id: TStack.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Collections + * @since 3.0 + */ +class TStackIterator implements Iterator +{ + /** + * @var array the data to be iterated through + */ + private $_d; + /** + * @var integer index of the current item + */ + private $_i; + /** + * @var integer count of the data items + */ + private $_c; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_i=0; + } + + /** + * Returns the key of the current array item. + * This method is required by the interface Iterator. + * @return integer the key of the current array item + */ + public function key() + { + return $this->_i; + } + + /** + * Returns the current array item. + * This method is required by the interface Iterator. + * @return mixed the current array item + */ + public function current() + { + return $this->_d[$this->_i]; + } + + /** + * Moves the internal pointer to the next array item. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_i++; + } + + /** + * Returns whether there is an item at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_i<$this->_c; + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php b/gui/baculum/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php new file mode 100644 index 0000000000..7dd69641f6 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php @@ -0,0 +1,49 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRecordException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + */ + +/** + * Base exception class for Active Records. + * + * @author Wei Zhuo + * @version $Id: TActiveRecordException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordException extends TDbException +{ + /** + * @return string path to the error message file + */ + protected function getErrorMessageFile() + { + $lang=Prado::getPreferredLanguage(); + $path = dirname(__FILE__); + $msgFile=$path.'/messages-'.$lang.'.txt'; + if(!is_file($msgFile)) + $msgFile=$path.'/messages.txt'; + return $msgFile; + } +} + +/** + * TActiveRecordConfigurationException class. + * + * @author Wei Zhuo + * @version $Id: TActiveRecordException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordConfigurationException extends TActiveRecordException +{ + +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Exceptions/messages.txt b/gui/baculum/framework/Data/ActiveRecord/Exceptions/messages.txt new file mode 100644 index 0000000000..0702c8407a --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Exceptions/messages.txt @@ -0,0 +1,25 @@ +ar_readonly_exception = Active Record '{0}' is read only. +ar_object_must_be_retrieved_before_delete = Active Record must be retrieved first before deletion. +ar_object_must_not_be_null = Active record object must not be null. +ar_object_marked_for_removal = Active record object already marked for removed. +ar_column_meta_data_read_only = Column meta is read only. +ar_invalid_database_driver = Active Record does not support database '{0}'. +ar_invalid_finder_method = Unsupported Active Record finder method '{0}'. +ar_no_primary_key_found = Table '{0}' does not contain any primary key fields. +ar_primary_key_is_scalar = Primary key '{1}' in table '{0}' is NOT a composite key, invalid value '{2} used. +ar_invalid_db_connection = Missing or invalid default database connection for ActiveRecord class '{0}', default connection is set by the DbConnection property of TActiveRecordManager. +ar_mismatch_args_exception = ActiveRecord finder method '{0}' expects {1} parameters but found only {2} parameters instead. +ar_invalid_tablename_property = Constant {0}::{1} must be a valid database table name. +ar_invalid_tablename_method = Method {0}::{1} must return a valid database table name. +ar_value_must_not_be_null = Property '{0}::${2}' must not be null as defined by column '{2}' in table '{1}'. +ar_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'. +ar_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'. +ar_must_copy_from_array_or_object = $data in {0}::copyFrom($data) must be an object or an array. +ar_mismatch_column_names = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'. +ar_invalid_table = Missing, invalid or no permission for table/view '{0}'. +ar_invalid_finder_class_name = Class name for finder($className) method must not be 'TActiveRecord', you should override the finder() method in your record class or pass in a valid record class name. +ar_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria. +ar_relations_undefined = Unable to determine Active Record relationships because static array property {0}::${1} is not defined. +ar_undefined_relation_prop = Unable to find {1}::${2}['{0}'], Active Record relationship definition for property "{0}" not found in entries of {1}::${2}. +ar_invalid_relationship = Invalid active record relationship. +ar_relations_missing_fk = Unable to find foreign key relationships in table '{0}' that corresponds to table '{1}'. diff --git a/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php new file mode 100644 index 0000000000..32e277426c --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php @@ -0,0 +1,138 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relationship class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between + * the source objects and the related foreign object. Consider the + * entity relationship between a Team and a Player. + * + * +------+ +--------+ + * | Team | 1 <----- * | Player | + * +------+ +--------+ + * + * Where one team may have 0 or more players and each player belongs to only + * one team. We may model Team-Player object relationship as active record as follows. + * + * class TeamRecord extends TActiveRecord + * { + * // see TActiveRecordHasMany for detailed definition. + * } + * class PlayerRecord extends TActiveRecord + * { + * const TABLE='player'; + * public $player_id; //primary key + * public $team_name; //foreign key player.team_name <-> team.name + * public $age; + * public $team; //foreign object TeamRecord + * + * public static $RELATIONS = array + * ( + * 'team' => array(self::BELONGS_TO, 'TeamRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * The static $RELATIONS property of PlayerRecord defines that the + * property $team belongs to a TeamRecord. + * + * The team object may be fetched as follows. + * + * $players = PlayerRecord::finder()->with_team()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, team) fetchs the corresponding TeamRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord, e.g. + * with_team('location = ?', 'Madrid'). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordBelongsTo extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + + $properties = array_keys($fkeys); + $fields = array_values($fkeys); + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields, $indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($this->getSourceRecord(),$fkObject); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + if(isset($collections[$hash]) && count($collections[$hash]) > 0) + { + if(count($collections[$hash]) > 1) + throw new TActiveRecordException('ar_belongs_to_multiple_result'); + $source->$prop=$collections[$hash][0]; + } + else + $source->$prop=null; + } + + /** + * Updates the source object first. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObject = $obj->getColumnValue($this->getContext()->getProperty()); + if($fkObject!==null) + { + $fkObject->save(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($source, $fkObject); + foreach($fkeys as $srcKey => $fKey) + $source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey)); + return true; + } + return false; + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php new file mode 100644 index 0000000000..e41dc80679 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php @@ -0,0 +1,121 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relations class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or + * more foreign objects. Consider the entity relationship between a Team and a Player. + * + * +------+ +--------+ + * | Team | 1 <----- * | Player | + * +------+ +--------+ + * + * Where one team may have 0 or more players and each player belongs to only + * one team. We may model Team-Player object relationship as active record as follows. + * + * class TeamRecord extends TActiveRecord + * { + * const TABLE='team'; + * public $name; //primary key + * public $location; + * + * public $players=array(); //list of players + * + * public static $RELATIONS=array + * ( + * 'players' => array(self::HAS_MANY, 'PlayerRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class PlayerRecord extends TActiveRecord + * { + * // see TActiveRecordBelongsTo for detailed definition + * } + * + * The static $RELATIONS property of TeamRecord defines that the + * property $players has many PlayerRecords. + * + * The players list may be fetched as follows. + * + * $team = TeamRecord::finder()->with_players()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, players) fetchs the corresponding PlayerRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord, e.g. with_players('age < ?', 35). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasMany extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + + $properties = array_values($fkeys); + $fields = array_keys($fkeys); + + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields,$indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($fkObject, $this->getSourceRecord()); + } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObjects = &$obj->{$this->getContext()->getProperty()}; + $success=true; + if(($total = count($fkObjects))> 0) + { + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($fkObjects[0], $source); + for($i=0;$i<$total;$i++) + { + foreach($fkeys as $fKey => $srcKey) + $fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey)); + $success = $fkObjects[$i]->save() && $success; + } + } + return $success; + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php new file mode 100644 index 0000000000..d845d37269 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php @@ -0,0 +1,376 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relations class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements the M-N (many to many) relationship via association table. + * Consider the entity relationship between Articles and Categories + * via the association table Article_Category. + * + * +---------+ +------------------+ +----------+ + * | Article | * -----> * | Article_Category | * <----- * | Category | + * +---------+ +------------------+ +----------+ + * + * Where one article may have 0 or more categories and each category may have 0 + * or more articles. We may model Article-Category object relationship + * as active record as follows. + * + * class ArticleRecord + * { + * const TABLE='Article'; + * public $article_id; + * + * public $Categories=array(); //foreign object collection. + * + * public static $RELATIONS = array + * ( + * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class CategoryRecord + * { + * const TABLE='Category'; + * public $category_id; + * + * public $Articles=array(); + * + * public static $RELATIONS = array + * ( + * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * + * The static $RELATIONS property of ArticleRecord defines that the + * property $Categories has many CategoryRecords. Similar, the + * static $RELATIONS property of CategoryRecord defines many ArticleRecords. + * + * The articles with categories list may be fetched as follows. + * + * $articles = TeamRecord::finder()->withCategories()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, Categories) fetchs the corresponding CategoryRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasManyAssociation extends TActiveRecordRelation +{ + private $_association; + private $_sourceTable; + private $_foreignTable; + private $_association_columns=array(); + + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects using association table. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys(); + $properties = array_values($sourceKeys); + $indexValues = $this->getIndexValues($properties, $results); + $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys); + } + + /** + * @return array 2 arrays of source keys and foreign keys from the association table. + */ + public function getRelationForeignKeys() + { + $association = $this->getAssociationTable(); + $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true); + $fkObject = $this->getContext()->getForeignRecordFinder(); + $foreignKeys = $this->findForeignKeys($association, $fkObject); + return array($sourceKeys, $foreignKeys); + } + + /** + * @return TDbTableInfo association table information. + */ + protected function getAssociationTable() + { + if($this->_association===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $conn = $this->getSourceRecord()->getDbConnection(); + //table name may include the fk column name separated with a dot. + $table = explode('.', $this->getContext()->getAssociationTable()); + if(count($table)>1) + { + $columns = preg_replace('/^\((.*)\)/', '\1', $table[1]); + $this->_association_columns = preg_split('/\s*[, ]\*/',$columns); + } + $this->_association = $gateway->getTableInfo($conn, $table[0]); + } + return $this->_association; + } + + /** + * @return TDbTableInfo source table information. + */ + protected function getSourceTable() + { + if($this->_sourceTable===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord()); + } + return $this->_sourceTable; + } + + /** + * @return TDbTableInfo foreign table information. + */ + protected function getForeignTable() + { + if($this->_foreignTable===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $fkObject = $this->getContext()->getForeignRecordFinder(); + $this->_foreignTable = $gateway->getRecordTableInfo($fkObject); + } + return $this->_foreignTable; + } + + /** + * @return TDataGatewayCommand + */ + protected function getCommandBuilder() + { + return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord()); + } + + /** + * @return TDataGatewayCommand + */ + protected function getForeignCommandBuilder() + { + $obj = $this->getContext()->getForeignRecordFinder(); + return $this->getSourceRecord()->getRecordGateway()->getCommand($obj); + } + + + /** + * Fetches the foreign objects using TActiveRecord::findAllByIndex() + * @param array field names + * @param array foreign key index values. + */ + protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys) + { + $criteria = $this->getCriteria(); + $finder = $this->getContext()->getForeignRecordFinder(); + $type = get_class($finder); + $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys); + $srcProps = array_keys($sourceKeys); + $collections=array(); + foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row) + { + $hash = $this->getObjectHash($row, $srcProps); + foreach($srcProps as $column) + unset($row[$column]); + $obj = $this->createFkObject($type,$row,$foreignKeys); + $collections[$hash][] = $obj; + } + $this->setResultCollection($results, $collections, array_values($sourceKeys)); + } + + /** + * @param string active record class name. + * @param array row data + * @param array foreign key column names + * @return TActiveRecord + */ + protected function createFkObject($type,$row,$foreignKeys) + { + $obj = TActiveRecord::createRecord($type, $row); + if(count($this->_association_columns) > 0) + { + $i=0; + foreach($foreignKeys as $ref=>$fk) + $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]); + } + return $obj; + } + + /** + * @param TSqlCriteria + * @param TTableInfo association table info + * @param array field names + * @param array field values + */ + public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys) + { + $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys); + $fkTable = $this->getForeignTable()->getTableFullName(); + $srcColumns = $this->getSourceColumns($sourceKeys); + if(($where=$criteria->getCondition())===null) + $where='1=1'; + $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}"; + + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + + $builder = $this->getForeignCommandBuilder()->getBuilder(); + $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset); + $this->getCommandBuilder()->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param array source table column names. + * @return string comma separated source column names. + */ + protected function getSourceColumns($sourceKeys) + { + $columns=array(); + $table = $this->getAssociationTable(); + $tableName = $table->getTableFullName(); + $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns); + foreach($columnNames as $name) + $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName(); + return implode(', ', $columns); + } + + /** + * SQL inner join for M-N relationship via association table. + * @param array foreign table column key names. + * @param array source table index values. + * @param array source table column names. + * @return string inner join condition for M-N relationship via association table. + */ + protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys) + { + $refInfo= $this->getAssociationTable(); + $fkInfo = $this->getForeignTable(); + + $refTable = $refInfo->getTableFullName(); + $fkTable = $fkInfo->getTableFullName(); + + $joins = array(); + $hasAssociationColumns = count($this->_association_columns) > 0; + $i=0; + foreach($foreignKeys as $ref=>$fk) + { + if($hasAssociationColumns) + $refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName(); + else + $refField = $refInfo->getColumn($ref)->getColumnName(); + $fkField = $fkInfo->getColumn($fk)->getColumnName(); + $joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}"; + } + $joinCondition = implode(' AND ', $joins); + $index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues); + return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}"; + } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObjects = &$obj->{$this->getContext()->getProperty()}; + $success=true; + if(($total = count($fkObjects))> 0) + { + $source = $this->getSourceRecord(); + $builder = $this->getAssociationTableCommandBuilder(); + for($i=0;$i<$total;$i++) + $success = $fkObjects[$i]->save() && $success; + return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success; + } + return $success; + } + + /** + * @return TDbCommandBuilder + */ + protected function getAssociationTableCommandBuilder() + { + $conn = $this->getContext()->getSourceRecord()->getDbConnection(); + return $this->getAssociationTable()->createCommandBuilder($conn); + } + + private function hasAssociationData($builder,$data) + { + $condition=array(); + $table = $this->getAssociationTable(); + foreach($data as $name=>$value) + $condition[] = $table->getColumn($name)->getColumnName().' = ?'; + $command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data)); + $result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar())); + return intval($result) > 0; + } + + private function addAssociationData($builder,$data) + { + $command = $builder->createInsertCommand($data); + return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0; + } + + private function updateAssociationTable($obj,$fkObjects, $builder) + { + $source = $this->getSourceRecordValues($obj); + $foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]); + $success=true; + foreach($fkObjects as $fkObject) + { + $data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject)); + if(!$this->hasAssociationData($builder,$data)) + $success = $this->addAssociationData($builder,$data) && $success; + } + return $success; + } + + private function getSourceRecordValues($obj) + { + $sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj); + $indexValues = $this->getIndexValues(array_values($sourceKeys), $obj); + $data = array(); + $i=0; + foreach($sourceKeys as $name=>$srcKey) + $data[$name] = $indexValues[0][$i++]; + return $data; + } + + private function getForeignObjectValues($foreignKeys,$fkObject) + { + $data=array(); + foreach($foreignKeys as $name=>$fKey) + $data[$name] = $fkObject->getColumnValue($fKey); + return $data; + } +} diff --git a/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php new file mode 100644 index 0000000000..4f20f12f3a --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php @@ -0,0 +1,145 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relationship class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * TActiveRecordHasOne models the object relationship that a record (the source object) + * property is an instance of foreign record object having a foreign key + * related to the source object. The HAS_ONE relation is very similar to the + * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view). + * + * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular. + * That is, HAS_MANY will return a collection of records while HAS_ONE returns the + * corresponding record. + * + * Consider the entity relationship between a Car and a Engine. + * + * +-----+ +--------+ + * | Car | 1 <----- 1 | Engine | + * +-----+ +--------+ + * + * Where each engine belongs to only one car, that is, the Engine entity has + * a foreign key to the Car's primary key. We may model + * Engine-Car object relationship as active record as follows. + * + * class CarRecord extends TActiveRecord + * { + * const TABLE='car'; + * public $car_id; //primary key + * public $colour; + * + * public $engine; //engine foreign object + * + * public static $RELATIONS=array + * ( + * 'engine' => array(self::HAS_ONE, 'EngineRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class EngineRecord extends TActiveRecord + * { + * const TABLE='engine'; + * public $engine_id; + * public $capacity; + * public $car_id; //foreign key to cars + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * The static $RELATIONS property of CarRecord defines that the + * property $engine that will reference an EngineRecord instance. + * + * The car record with engine property list may be fetched as follows. + * + * $cars = CarRecord::finder()->with_engine()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, engine) fetchs the corresponding EngineRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord, e.g. with_engine('capacity < ?', 3.8). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasOne extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + $properties = array_values($fkeys); + $fields = array_keys($fkeys); + + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields,$indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($fkObject, $this->getSourceRecord()); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + if(isset($collections[$hash]) && count($collections[$hash]) > 0) + { + if(count($collections[$hash]) > 1) + throw new TActiveRecordException('ar_belongs_to_multiple_result'); + $source->setColumnValue($prop, $collections[$hash][0]); + } + } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $fkObject = $this->getContext()->getPropertyValue(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($fkObject, $source); + foreach($fkeys as $fKey => $srcKey) + $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey)); + return $fkObject->save(); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php new file mode 100644 index 0000000000..2fe6dcb197 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -0,0 +1,254 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Load active record relationship context. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); + +/** + * Base class for active record relationships. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +abstract class TActiveRecordRelation +{ + private $_context; + private $_criteria; + + public function __construct(TActiveRecordRelationContext $context, $criteria) + { + $this->_context = $context; + $this->_criteria = $criteria; + } + + /** + * @return TActiveRecordRelationContext + */ + protected function getContext() + { + return $this->_context; + } + + /** + * @return TActiveRecordCriteria + */ + protected function getCriteria() + { + return $this->_criteria; + } + + /** + * @return TActiveRecord + */ + protected function getSourceRecord() + { + return $this->getContext()->getSourceRecord(); + } + + abstract protected function collectForeignObjects(&$results); + + /** + * Dispatch the method calls to the source record finder object. When + * an instance of TActiveRecord or an array of TActiveRecord is returned + * the corresponding foreign objects are also fetched and assigned. + * + * Multiple relationship calls can be chain together. + * + * @param string method name called + * @param array method arguments + * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called. + */ + public function __call($method,$args) + { + static $stack=array(); + + $results = call_user_func_array(array($this->getSourceRecord(),$method),$args); + $validArray = is_array($results) && count($results) > 0; + if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord) + { + $this->collectForeignObjects($results); + while($obj = array_pop($stack)) + $obj->collectForeignObjects($results); + } + else if($results instanceof TActiveRecordRelation) + $stack[] = $this; //call it later + else if($results === null || !$validArray) + $stack = array(); + return $results; + } + + /** + * Fetch results for current relationship. + * @return boolean always true. + */ + public function fetchResultsInto($obj) + { + $this->collectForeignObjects($obj); + return true; + } + + /** + * Returns foreign keys in $fromRecord with source column names as key + * and foreign column names in the corresponding $matchesRecord as value. + * The method returns the first matching foreign key between these 2 records. + * @param TActiveRecord $fromRecord + * @param TActiveRecord $matchesRecord + * @return array foreign keys with source column names as key and foreign column names as value. + */ + protected function findForeignKeys($from, $matchesRecord, $loose=false) + { + $gateway = $matchesRecord->getRecordGateway(); + $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord); + $matchingTableName = strtolower($recordTableInfo->getTableName()); + $matchingFullTableName = strtolower($recordTableInfo->getTableFullName()); + $tableInfo=$from; + if($from instanceof TActiveRecord) + $tableInfo = $gateway->getRecordTableInfo($from); + //find first non-empty FK + foreach($tableInfo->getForeignKeys() as $fkeys) + { + $fkTable = strtolower($fkeys['table']); + if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName) + { + $hasFkField = !$loose && $this->getContext()->hasFkField(); + $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys']; + if(!empty($key)) + return $key; + } + } + + //none found + $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName(); + throw new TActiveRecordException('ar_relations_missing_fk', + $tableInfo->getTableFullName(), $matching); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + abstract public function getRelationForeignKeys(); + + /** + * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION. + * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot + * are ignored. + */ + private function getFkFields($fkeys) + { + $matching = array(); + preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching); + $fields = array(); + foreach($fkeys as $fkName => $field) + { + if(in_array($fkName, $matching[2])) + $fields[$fkName] = $field; + } + return $fields; + } + + /** + * @param mixed object or array to be hashed + * @param array name of property for hashing the properties. + * @return string object hash using crc32 and serialize. + */ + protected function getObjectHash($obj, $properties) + { + $ids=array(); + foreach($properties as $property) + $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property]; + return serialize($ids); + } + + /** + * Fetches the foreign objects using TActiveRecord::findAllByIndex() + * @param array field names + * @param array foreign key index values. + * @return TActiveRecord[] foreign objects. + */ + protected function findForeignObjects($fields, $indexValues) + { + $finder = $this->getContext()->getForeignRecordFinder(); + return $finder->findAllByIndex($this->_criteria, $fields, $indexValues); + } + + /** + * Obtain the foreign key index values from the results. + * @param array property names + * @param array TActiveRecord results + * @return array foreign key index values. + */ + protected function getIndexValues($keys, $results) + { + if(!is_array($results) && !$results instanceof ArrayAccess) + $results = array($results); + $values=array(); + foreach($results as $result) + { + $value = array(); + foreach($keys as $name) + $value[] = $result->getColumnValue($name); + $values[] = $value; + } + return $values; + } + + /** + * Populate the results with the foreign objects found. + * @param array source results + * @param array source property names + * @param array foreign objects + * @param array foreign object field names. + */ + protected function populateResult(&$results,$properties,&$fkObjects,$fields) + { + $collections=array(); + foreach($fkObjects as $fkObject) + $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject; + $this->setResultCollection($results, $collections, $properties); + } + + /** + * Populates the result array with foreign objects (matched using foreign key hashed property values). + * @param array $results + * @param array $collections + * @param array property names + */ + protected function setResultCollection(&$results, &$collections, $properties) + { + if(is_array($results) || $results instanceof ArrayAccess) + { + for($i=0,$k=count($results);$i<$k;$i++) + $this->setObjectProperty($results[$i], $properties, $collections); + } + else + $this->setObjectProperty($results, $properties, $collections); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array source properties + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array(); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php new file mode 100644 index 0000000000..6c1dcd4f9e --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php @@ -0,0 +1,230 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * TActiveRecordRelationContext holds information regarding record relationships + * such as record relation property name, query criteria and foreign object record + * class names. + * + * This class is use internally by passing a context to the TActiveRecordRelation + * constructor. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordRelationContext +{ + private $_property; + private $_record; + private $_relation; //data from an entry of TActiveRecord::$RELATION + private $_fkeys; + + public function __construct($record, $property=null, $relation=null) + { + $this->_record=$record; + $this->_property=$property; + $this->_relation=$relation; + } + + /** + * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS + * @since 3.1.2 + */ + public function hasRecordRelation() + { + return $this->_relation!==null; + } + + public function getPropertyValue() + { + $obj = $this->getSourceRecord(); + return $obj->getColumnValue($this->getProperty()); + } + + /** + * @return string name of the record property that the relationship results will be assigned to. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @return TActiveRecord the active record instance that queried for its related records. + */ + public function getSourceRecord() + { + return $this->_record; + } + + /** + * @return array foreign key of this relations, the keys is dependent on the + * relationship type. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + if($this->_fkeys===null) + $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys(); + return $this->_fkeys; + } + + /** + * @return string HAS_MANY, HAS_ONE, or BELONGS_TO + */ + public function getRelationType() + { + return $this->_relation[0]; + } + + /** + * @return string foreign record class name. + */ + public function getForeignRecordClass() + { + return $this->_relation[1]; + } + + /** + * @return string foreign key field names, comma delimited. + * @since 3.1.2 + */ + public function getFkField() + { + return $this->_relation[2]; + } + + /** + * @return string the query condition for the relation as specified in RELATIONS + * @since 3.1.2 + */ + public function getCondition() + { + return isset($this->_relation[3])?$this->_relation[3]:null; + } + + /** + * @return array the query parameters for the relation as specified in RELATIONS + * @since 3.1.2 + */ + public function getParameters() + { + return isset($this->_relation[4])?$this->_relation[4]:array(); + } + + /** + * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set. + * @since 3.1.2 + */ + public function hasFkField() + { + $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY; + return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); + } + + /** + * @return string the M-N relationship association table name. + */ + public function getAssociationTable() + { + return $this->_relation[2]; + } + + /** + * @return boolean true if the relationship is HAS_MANY and requires an association table. + */ + public function hasAssociationTable() + { + $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY; + return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); + } + + /** + * @return TActiveRecord corresponding relationship foreign object finder instance. + */ + public function getForeignRecordFinder() + { + return TActiveRecord::finder($this->getForeignRecordClass()); + } + + /** + * Creates and return the TActiveRecordRelation handler for specific relationships. + * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany, + * or TActiveRecordHasManyAssocation will be returned. + * @param TActiveRecordCriteria search criteria + * @return TActiveRecordRelation record relationship handler instnace. + * @throws TActiveRecordException if property is not defined or missing. + */ + public function getRelationHandler($criteria=null) + { + if(!$this->hasRecordRelation()) + { + throw new TActiveRecordException('ar_undefined_relation_prop', + $this->_property, get_class($this->_record), 'RELATIONS'); + } + if($criteria===null) + $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters()); + switch($this->getRelationType()) + { + case TActiveRecord::HAS_MANY: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany'); + return new TActiveRecordHasMany($this, $criteria); + case TActiveRecord::MANY_TO_MANY: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation'); + return new TActiveRecordHasManyAssociation($this, $criteria); + case TActiveRecord::HAS_ONE: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne'); + return new TActiveRecordHasOne($this, $criteria); + case TActiveRecord::BELONGS_TO: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo'); + return new TActiveRecordBelongsTo($this, $criteria); + default: + throw new TActiveRecordException('ar_invalid_relationship'); + } + } + + /** + * @return TActiveRecordRelationCommand + */ + public function updateAssociatedRecords($updateBelongsTo=false) + { + $success=true; + foreach($this->_record->getRecordRelations() as $data) + { + list($property, $relation) = $data; + $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO; + if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo)) + { + $obj = $this->getSourceRecord(); + if(!$this->isEmptyFkObject($obj->getColumnValue($property))) + { + $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation); + $success = $context->getRelationHandler()->updateAssociatedRecords() && $success; + } + } + } + return $success; + } + + protected function isEmptyFkObject($obj) + { + if(is_object($obj)) + return $obj instanceof TList ? $obj->count() === 0 : false; + else if(is_array($obj)) + return count($obj)===0; + else + return empty($obj); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php new file mode 100644 index 0000000000..71f015b8ea --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TIbmScaffoldInput.php @@ -0,0 +1,51 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @package System.Data.ActiveRecord.Scaffold.InputBuilder + */ +Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputCommon'); + +class TIbmScaffoldInput extends TScaffoldInputCommon +{ + protected function createControl($container, $column, $record) + { + switch(strtolower($column->getDbType())) + { + case 'date': + return $this->createDateControl($container, $column, $record); + case 'time': + return $this->createTimeControl($container, $column, $record); + case 'timestamp': + return $this->createDateTimeControl($container, $column, $record); + case 'smallint': case 'integer': case 'bigint': + return $this->createIntegerControl($container, $column, $record); + case 'decimal': case 'numeric': case 'real': case 'float': case 'double': + return $this->createFloatControl($container, $column, $record); + case 'char': case 'varchar': + return $this->createMultiLineControl($container, $column, $record); + default: + return $this->createDefaultControl($container,$column, $record); + } + } + + protected function getControlValue($container, $column, $record) + { + switch(strtolower($column->getDbType())) + { + case 'date': + return $container->findControl(self::DEFAULT_ID)->getDate(); + case 'time': + return $this->getTimeValue($container, $column, $record); + case 'timestamp': + return $this->getDateTimeValue($container, $column, $record); + default: + return $this->getDefaultControlValue($container,$column, $record); + } + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php new file mode 100644 index 0000000000..b9d133a253 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMssqlScaffoldInput.php @@ -0,0 +1,53 @@ +getDbType())) + { + case 'bit': + return $this->createBooleanControl($container, $column, $record); + case 'text': + return $this->createMultiLineControl($container, $column, $record); + case 'smallint': case 'int': case 'bigint': case 'tinyint': + return $this->createIntegerControl($container, $column, $record); + case 'decimal': case 'float': case 'money': case 'numeric': case 'real': case 'smallmoney': + return $this->createFloatControl($container, $column, $record); + case 'datetime': case 'smalldatetime': + return $this->createDateTimeControl($container, $column, $record); + default: + $control = $this->createDefaultControl($container,$column, $record); + if($column->getIsExcluded()) + $control->setEnabled(false); + return $control; + } + } + + protected function getControlValue($container, $column, $record) + { + switch(strtolower($column->getDbType())) + { + case 'boolean': + return $container->findControl(self::DEFAULT_ID)->getChecked(); + case 'datetime': case 'smalldatetime': + return $this->getDateTimeValue($container,$column, $record); + default: + $value = $this->getDefaultControlValue($container,$column, $record); + if(trim($value)==='' && $column->getAllowNull()) + return null; + else + return $value; + } + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php new file mode 100644 index 0000000000..74eddbd80c --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TMysqlScaffoldInput.php @@ -0,0 +1,83 @@ +getDbType()))); + switch($dbtype) + { + case 'date': + return $this->createDateControl($container, $column, $record); + case 'blob': case 'tinyblob': case 'mediumblob': case 'longblob': + case 'text': case 'tinytext': case 'mediumtext': case 'longtext': + return $this->createMultiLineControl($container, $column, $record); + case 'year': + return $this->createYearControl($container, $column, $record); + case 'int': case 'integer': case 'tinyint': case 'smallint': case 'mediumint': case 'bigint': + return $this->createIntegerControl($container, $column, $record); + case 'decimal': case 'double': case 'float': + return $this->createFloatControl($container, $column, $record); + case 'time' : + return $this->createTimeControl($container, $column, $record); + case 'datetime': case 'timestamp': + return $this->createDateTimeControl($container, $column, $record); + case 'set': + return $this->createSetControl($container, $column, $record); + case 'enum': + return $this->createEnumControl($container, $column, $record); + default: + return $this->createDefaultControl($container, $column, $record); + } + } + + protected function getControlValue($container, $column, $record) + { + $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($column->getDbType()))); + switch($dbtype) + { + case 'date': + return $container->findControl(self::DEFAULT_ID)->getDate(); + case 'year': + return $container->findControl(self::DEFAULT_ID)->getSelectedValue(); + case 'time': + return $this->getTimeValue($container, $column, $record); + case 'datetime': case 'timestamp': + return $this->getDateTimeValue($container,$column, $record); + case 'tinyint': + return $this->getIntBooleanValue($container,$column, $record); + case 'set': + return $this->getSetValue($container, $column, $record); + case 'enum': + return $this->getEnumValue($container, $column, $record); + default: + return $this->getDefaultControlValue($container,$column, $record); + } + } + + protected function createIntegerControl($container, $column, $record) + { + if($column->getColumnSize()==1) + return $this->createBooleanControl($container, $column, $record); + else + parent::createIntegerControl($container, $column, $record); + } + + protected function getIntBooleanValue($container,$column, $record) + { + if($column->getColumnSize()==1) + return (int)$container->findControl(self::DEFAULT_ID)->getChecked(); + else + return $this->getDefaultControlValue($container,$column, $record); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php new file mode 100644 index 0000000000..088bbd42e9 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TPgsqlScaffoldInput.php @@ -0,0 +1,54 @@ +getDbType())) + { + case 'boolean': + return $this->createBooleanControl($container, $column, $record); + case 'date': + return $this->createDateControl($container, $column, $record); + case 'text': + return $this->createMultiLineControl($container, $column, $record); + case 'smallint': case 'integer': case 'bigint': + return $this->createIntegerControl($container, $column, $record); + case 'decimal': case 'numeric': case 'real': case 'double precision': + return $this->createFloatControl($container, $column, $record); + case 'time without time zone' : + return $this->createTimeControl($container, $column, $record); + case 'timestamp without time zone': + return $this->createDateTimeControl($container, $column, $record); + default: + return $this->createDefaultControl($container,$column, $record); + } + } + + protected function getControlValue($container, $column, $record) + { + switch(strtolower($column->getDbType())) + { + case 'boolean': + return $container->findControl(self::DEFAULT_ID)->getChecked(); + case 'date': + return $container->findControl(self::DEFAULT_ID)->getDate(); + case 'time without time zone': + return $this->getTimeValue($container, $column, $record); + case 'timestamp without time zone': + return $this->getDateTimeValue($container,$column, $record); + default: + return $this->getDefaultControlValue($container,$column, $record); + } + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php new file mode 100644 index 0000000000..611cfdfb40 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputBase.php @@ -0,0 +1,103 @@ +_parent; + } + + public static function createInputBuilder($record) + { + $record->getDbConnection()->setActive(true); //must be connected before retrieving driver name! + $driver = $record->getDbConnection()->getDriverName(); + switch(strtolower($driver)) + { + case 'sqlite': //sqlite 3 + case 'sqlite2': //sqlite 2 + require_once(dirname(__FILE__).'/TSqliteScaffoldInput.php'); + return new TSqliteScaffoldInput($conn); + case 'mysqli': + case 'mysql': + require_once(dirname(__FILE__).'/TMysqlScaffoldInput.php'); + return new TMysqlScaffoldInput($conn); + case 'pgsql': + require_once(dirname(__FILE__).'/TPgsqlScaffoldInput.php'); + return new TPgsqlScaffoldInput($conn); + case 'mssql': + require_once(dirname(__FILE__).'/TMssqlScaffoldInput.php'); + return new TMssqlScaffoldInput($conn); + case 'ibm': + require_once(dirname(__FILE__).'/TIbmScaffoldInput.php'); + return new TIbmScaffoldInput($conn); + default: + throw new TConfigurationException( + 'scaffold_invalid_database_driver',$driver); + } + } + + public function createScaffoldInput($parent, $item, $column, $record) + { + $this->_parent=$parent; + $item->setCustomData($column->getColumnId()); + $this->createControl($item->_input, $column, $record); + if($item->_input->findControl(self::DEFAULT_ID)) + $this->createControlLabel($item->_label, $column, $record); + } + + protected function createControlLabel($label, $column, $record) + { + $fieldname = ucwords(str_replace('_', ' ', $column->getColumnId())).':'; + $label->setText($fieldname); + $label->setForControl(self::DEFAULT_ID); + } + + public function loadScaffoldInput($parent, $item, $column, $record) + { + $this->_parent=$parent; + if($this->getIsEnabled($column, $record)) + { + $prop = $column->getColumnId(); + $record->setColumnValue($prop, $this->getControlValue($item->_input, $column, $record)); + } + } + + protected function getIsEnabled($column, $record) + { + return !($this->getParent()->getRecordPk() !== null + && $column->getIsPrimaryKey() || $column->hasSequence()); + } + + protected function getRecordPropertyValue($column, $record) + { + $value = $record->getColumnValue($column->getColumnId()); + if($column->getDefaultValue()!==TDbTableColumn::UNDEFINED_VALUE && $value===null) + return $column->getDefaultValue(); + else + return $value; + } + + protected function setRecordPropertyValue($item, $record, $input) + { + $record->setColumnValue($item->getCustomData(), $input->getText()); + } + + protected function createControl($container, $column, $record) + { + } + + protected function getControlValue($container, $column, $record) + { + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php new file mode 100644 index 0000000000..68404adbbd --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TScaffoldInputCommon.php @@ -0,0 +1,309 @@ +setID(self::DEFAULT_ID); + $control->setEnabled($this->getIsEnabled($column, $record)); + $container->Controls[] = $control; + } + + protected function setNotNullProperty($container, $control, $column, $record) + { + $this->setDefaultProperty($container, $control, $column, $record); + if(!$column->getAllowNull() && !$column->hasSequence()) + $this->createRequiredValidator($container, $column, $record); + } + + protected function createBooleanControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $control = new TCheckBox(); + $control->setChecked(TPropertyValue::ensureBoolean($value)); + $control->setCssClass('boolean-checkbox'); + $this->setDefaultProperty($container, $control, $column, $record); + return $control; + } + + protected function createDefaultControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $control = new TTextBox(); + $control->setText($value); + $control->setCssClass('default-textbox scaffold_input'); + if(($len=$column->getColumnSize())!==null) + $control->setMaxLength($len); + $this->setNotNullProperty($container, $control, $column, $record); + return $control; + } + + protected function getDefaultControlValue($container,$column, $record) + { + $control = $container->findControl(self::DEFAULT_ID); + if($control instanceof TCheckBox) + return $control->getChecked(); + else if($control instanceof TControl) + return $control->getText(); + } + + protected function createMultiLineControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $control = new TTextBox(); + $control->setText($value); + $control->setTextMode(TTextBoxMode::MultiLine); + $control->setCssClass('multiline-textbox scaffold_input'); + $this->setNotNullProperty($container, $control, $column, $record); + return $control; + } + + protected function createYearControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $control = new TDropDownList(); + $years = array(); + $current = intval(@date('Y')); + $from = $current-10; $to=$current+10; + for($i = $from; $i <= $to; $i++) + $years[$i] = $i; + $control->setDataSource($years); + $control->setSelectedValue(empty($value) ? $current : $value); + $control->setCssClass('year-dropdown'); + $this->setNotNullProperty($container, $control, $column, $record); + return $control; + } + + protected function createIntegerControl($container, $column, $record) + { + $control = $this->createDefaultControl($container, $column, $record); + $val = $this->createTypeValidator($container, $column, $record); + $val->setDataType(TValidationDataType::Integer); + $val->setErrorMessage('Please entery an integer.'); + return $control; + } + + protected function createFloatControl($container, $column, $record) + { + $control = $this->createDefaultControl($container, $column, $record); + $val = $this->createTypeValidator($container, $column, $record); + $val->setDataType(TValidationDataType::Float); + $val->setErrorMessage('Please entery a decimal number.'); + if(($max= $column->getMaxiumNumericConstraint())!==null) + { + $val = $this->createRangeValidator($container,$column,$record); + $val->setDataType(TValidationDataType::Float); + $val->setMaxValue($max); + $val->setStrictComparison(true); + $val->setErrorMessage('Please entery a decimal number strictly less than '.$max.'.'); + } + return $control; + } + + protected function createRequiredValidator($container, $column, $record) + { + $val = new TRequiredFieldValidator(); + $val->setErrorMessage('*'); + $val->setControlCssClass('required-input'); + $val->setCssClass('required'); + $val->setControlToValidate(self::DEFAULT_ID); + $val->setValidationGroup($this->getParent()->getValidationGroup()); + $val->setDisplay(TValidatorDisplayStyle::Dynamic); + $container->Controls[] = $val; + return $val; + } + + protected function createTypeValidator($container, $column, $record) + { + $val = new TDataTypeValidator(); + $val->setControlCssClass('required-input2'); + $val->setCssClass('required'); + $val->setControlToValidate(self::DEFAULT_ID); + $val->setValidationGroup($this->getParent()->getValidationGroup()); + $val->setDisplay(TValidatorDisplayStyle::Dynamic); + $container->Controls[] = $val; + return $val; + } + + protected function createRangeValidator($container, $column, $record) + { + $val = new TRangeValidator(); + $val->setControlCssClass('required-input3'); + $val->setCssClass('required'); + $val->setControlToValidate(self::DEFAULT_ID); + $val->setValidationGroup($this->getParent()->getValidationGroup()); + $val->setDisplay(TValidatorDisplayStyle::Dynamic); + $container->Controls[] = $val; + return $val; + } + + protected function createTimeControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $hours=array(); + for($i=0;$i<24;$i++) $hours[] = str_pad($i,2,'0',STR_PAD_LEFT); + $mins=array(); + for($i=0;$i<60;$i++) $mins[] = str_pad($i,2,'0',STR_PAD_LEFT); + $hour = intval(@date('H')); + $min = intval(@date('i')); + $sec = intval(@date('s')); + if(!empty($value)) + { + $match=array(); + if(preg_match('/(\d+):(\d+):?(\d+)?/', $value, $match)) + { + $hour = $match[1]; + $min = $match[2]; + if(isset($match[3])) + $sec=$match[3]; + } + } + + $hcontrol = new TDropDownList(); + $hcontrol->setDataSource($hours); + $hcontrol->setID(self::DEFAULT_ID); + $hcontrol->dataBind(); + $hcontrol->setSelectedValue(intval($hour)); + $container->Controls[] = $hcontrol; + $container->Controls[] = ' : '; + + $mcontrol = new TDropDownList(); + $mcontrol->setDataSource($mins); + $mcontrol->dataBind(); + $mcontrol->setID('scaffold_time_min'); + $mcontrol->setSelectedValue(intval($min)); + $container->Controls[] = $mcontrol; + $container->Controls[] = ' : '; + + $scontrol = new TDropDownList(); + $scontrol->setDataSource($mins); + $scontrol->dataBind(); + $scontrol->setID('scaffold_time_sec'); + $scontrol->setSelectedValue(intval($sec)); + $container->Controls[] = $scontrol; + + return array($hcontrol,$mcontrol,$scontrol); + } + + + protected function createDateControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $control = new TDatePicker(); + $control->setFromYear(1900); + $control->setInputMode(TDatePickerInputMode::DropDownList); + $control->setDateFormat('yyyy-MM-dd'); + if(!empty($value)) + $control->setDate(substr($value,0,10)); + $control->setCssClass('date-dropdown'); + $this->setNotNullProperty($container, $control, $column, $record); + return $control; + } + + protected function createDateTimeControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $control = $this->createDateControl($container, $column, $record); + $container->Controls[] = ' @ '; + $time = $this->createTimeControl($container, $column, $record); + if(!empty($value)) + { + $match=array(); + if(preg_match('/(\d+):(\d+):?(\d+)?/', substr($value, 11), $match)) + { + $time[0]->setSelectedValue(intval($match[1])); + $time[1]->setSelectedValue(intval($match[2])); + if(isset($match[3])) + $time[2]->setSelectedValue(intval($match[3])); + } + } + $time[0]->setID('scaffold_time_hour'); + return array($control, $time[0], $time[1], $time[2]); + } + + protected function getDateTimeValue($container, $column, $record) + { + $date = $container->findControl(self::DEFAULT_ID)->getDate(); + $hour = $container->findControl('scaffold_time_hour')->getSelectedValue(); + $mins = $container->findControl('scaffold_time_min')->getSelectedValue(); + $secs = $container->findControl('scaffold_time_sec')->getSelectedValue(); + return "{$date} {$hour}:{$mins}:{$secs}"; + } + + protected function getTimeValue($container, $column, $record) + { + $hour = $container->findControl(self::DEFAULT_ID)->getSelectedValue(); + $mins = $container->findControl('scaffold_time_min')->getSelectedValue(); + $secs = $container->findControl('scaffold_time_sec')->getSelectedValue(); + return "{$hour}:{$mins}:{$secs}"; + } + + protected function createSetControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $selectedValues = preg_split('/\s*,\s*/', $value); + $control = new TCheckBoxList(); + $values = $column->getDbTypeValues(); + $control->setDataSource($values); + $control->dataBind(); + $control->setSelectedIndices($this->getMatchingIndices($values,$selectedValues)); + $control->setID(self::DEFAULT_ID); + $control->setCssClass('set-checkboxes'); + $this->setNotNullProperty($container, $control, $column, $record); + return $control; + } + + protected function getMatchingIndices($checks, $values) + { + $index=array(); + for($i=0, $k=count($checks); $i<$k; $i++) + { + if(in_array($checks[$i], $values)) + $index[] = $i; + } + return $index; + } + + protected function createEnumControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $selectedValues = preg_split('/\s*,\s*/', $value); + $control = new TRadioButtonList(); + $values = $column->getDbTypeValues(); + $control->setDataSource($values); + $control->dataBind(); + $index = $this->getMatchingIndices($values,$selectedValues); + if(count($index) > 0) + $control->setSelectedIndex($index[0]); + $control->setID(self::DEFAULT_ID); + $control->setCssClass('enum-radio-buttons'); + $this->setNotNullProperty($container, $control, $column, $record); + return $control; + } + + protected function getSetValue($container, $column, $record) + { + $value=array(); + foreach($container->findControl(self::DEFAULT_ID)->getItems() as $item) + { + if($item->getSelected()) + $value[] = $item->getText(); + } + return implode(',', $value); + } + + protected function getEnumValue($container, $column, $record) + { + return $container->findControl(self::DEFAULT_ID)->getSelectedItem()->getText(); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php new file mode 100644 index 0000000000..302f320b28 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/InputBuilder/TSqliteScaffoldInput.php @@ -0,0 +1,99 @@ +getDbType())) + { + case 'boolean': + return $this->createBooleanControl($container, $column, $record); + case 'date': + return $this->createDateControl($container, $column, $record); + case 'blob': case 'tinyblob': case 'mediumblob': case 'longblob': + case 'text': case 'tinytext': case 'mediumtext': case 'longtext': + return $this->createMultiLineControl($container, $column, $record); + case 'year': + return $this->createYearControl($container, $column, $record); + case 'int': case 'integer': case 'tinyint': case 'smallint': case 'mediumint': case 'bigint': + return $this->createIntegerControl($container, $column, $record); + case 'decimal': case 'double': case 'float': + return $this->createFloatControl($container, $column, $record); + case 'time' : + return $this->createTimeControl($container, $column, $record); + case 'datetime': case 'timestamp': + return $this->createDateTimeControl($container, $column, $record); + default: + return $this->createDefaultControl($container,$column, $record); + } + } + + protected function getControlValue($container, $column, $record) + { + switch(strtolower($column->getDbType())) + { + case 'boolean': + return $container->findControl(self::DEFAULT_ID)->getChecked(); + case 'date': + return $container->findControl(self::DEFAULT_ID)->getDate(); + case 'year': + return $container->findControl(self::DEFAULT_ID)->getSelectedValue(); + case 'time': + return $this->getTimeValue($container, $column, $record); + case 'datetime': case 'timestamp': + return $this->getDateTimeValue($container,$column, $record); + default: + return $this->getDefaultControlValue($container,$column, $record); + } + } + + protected function createDateControl($container, $column, $record) + { + $control = parent::createDateControl($container, $column, $record); + $value = $this->getRecordPropertyValue($column, $record); + if(!empty($value) && preg_match('/timestamp/i', $column->getDbType())) + $control->setTimestamp(intval($value)); + return $control; + } + + protected function createDateTimeControl($container, $column, $record) + { + $value = $this->getRecordPropertyValue($column, $record); + $time = parent::createDateTimeControl($container, $column, $record); + if(!empty($value) && preg_match('/timestamp/i', $column->getDbType())) + { + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getDate(intval($value)); + $time[1]->setSelectedValue($date['hours']); + $time[2]->setSelectedValue($date['minutes']); + $time[3]->setSelectedValue($date['seconds']); + } + return $time; + } + + protected function getDateTimeValue($container, $column, $record) + { + if(preg_match('/timestamp/i', $column->getDbType())) + { + $time = $container->findControl(self::DEFAULT_ID)->getTimestamp(); + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getDate($time); + $hour = $container->findControl('scaffold_time_hour')->getSelectedValue(); + $mins = $container->findControl('scaffold_time_min')->getSelectedValue(); + $secs = $container->findControl('scaffold_time_sec')->getSelectedValue(); + return $s->getTimeStamp($hour,$mins,$secs,$date['mon'],$date['mday'],$date['year']); + } + else + return parent::getDateTimeValue($container, $column, $record); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php new file mode 100644 index 0000000000..41dbb24016 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php @@ -0,0 +1,207 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TScaffoldBase.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Include the base Active Record class. + */ +Prado::using('System.Data.ActiveRecord.TActiveRecord'); + +/** + * Base class for Active Record scaffold views. + * + * Provides common properties for all scaffold views (such as, TScaffoldListView, + * TScaffoldEditView, TScaffoldListView and TScaffoldView). + * + * During the OnPrRender stage the default css style file (filename style.css) + * is published and registered. To override the default style, provide your own stylesheet + * file explicitly. + * + * @author Wei Zhuo + * @version $Id: TScaffoldBase.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +abstract class TScaffoldBase extends TTemplateControl +{ + /** + * @var TActiveRecord record instance (may be new or retrieved from db) + */ + private $_record; + + /** + * @return TDbMetaData table/view information + */ + protected function getTableInfo() + { + $finder = $this->getRecordFinder(); + $gateway = $finder->getRecordManager()->getRecordGateWay(); + return $gateway->getRecordTableInfo($finder); + } + + /** + * @param TActiveRecord record instance + * @return array record property values + */ + protected function getRecordPropertyValues($record) + { + $data = array(); + foreach($this->getTableInfo()->getColumns() as $name=>$column) + $data[] = $record->getColumnValue($name); + return $data; + } + + /** + * @param TActiveRecord record instance + * @return array record primary key values. + */ + protected function getRecordPkValues($record) + { + $data=array(); + foreach($this->getTableInfo()->getColumns() as $name=>$column) + { + if($column->getIsPrimaryKey()) + $data[] = $record->getColumnValue($name); + } + return $data; + } + + /** + * Name of the Active Record class to be viewed or scaffolded. + * @return string Active Record class name. + */ + public function getRecordClass() + { + return $this->getViewState('RecordClass'); + } + + /** + * Name of the Active Record class to be viewed or scaffolded. + * @param string Active Record class name. + */ + public function setRecordClass($value) + { + $this->setViewState('RecordClass', $value); + } + + /** + * Copy the view details from another scaffold view instance. + * @param TScaffoldBase scaffold view. + */ + protected function copyFrom(TScaffoldBase $obj) + { + $this->_record = $obj->_record; + $this->setRecordClass($obj->getRecordClass()); + $this->setEnableDefaultStyle($obj->getEnableDefaultStyle()); + } + + /** + * Unset the current record instance and table information. + */ + protected function clearRecordObject() + { + $this->_record=null; + } + + /** + * Gets the current Active Record instance. Creates new instance if the + * primary key value is null otherwise the record is fetched from the db. + * @param array primary key value + * @return TActiveRecord record instance + */ + protected function getRecordObject($pk=null) + { + if($this->_record===null) + { + if($pk!==null) + { + $this->_record=$this->getRecordFinder()->findByPk($pk); + if($this->_record===null) + throw new TConfigurationException('scaffold_invalid_record_pk', + $this->getRecordClass(), $pk); + } + else + { + $class = $this->getRecordClass(); + if($class!==null) + $this->_record=Prado::createComponent($class); + else + { + throw new TConfigurationException('scaffold_invalid_record_class', + $this->getRecordClass(),$this->getID()); + } + } + } + return $this->_record; + } + + /** + * @param TActiveRecord Active Record instance. + */ + protected function setRecordObject(TActiveRecord $value) + { + $this->_record=$value; + } + + /** + * @return TActiveRecord Active Record finder instance + */ + protected function getRecordFinder() + { + return TActiveRecord::finder($this->getRecordClass()); + } + + /** + * @return string default scaffold stylesheet name + */ + public function getDefaultStyle() + { + return $this->getViewState('DefaultStyle', 'style'); + } + + /** + * @param string default scaffold stylesheet name + */ + public function setDefaultStyle($value) + { + $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style'); + } + + /** + * @return boolean enable default stylesheet, default is true. + */ + public function getEnableDefaultStyle() + { + return $this->getViewState('EnableDefaultStyle', true); + } + + /** + * @param boolean enable default stylesheet, default is true. + */ + public function setEnableDefaultStyle($value) + { + return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true); + } + + /** + * Publish the default stylesheet file. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getEnableDefaultStyle()) + { + $url = $this->publishAsset($this->getDefaultStyle().'.css'); + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php new file mode 100644 index 0000000000..ff1c65c7bd --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php @@ -0,0 +1,309 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TScaffoldEditView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load scaffold base. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * Template control for editing an Active Record instance. + * The RecordClass determines the Active Record class to be edited. + * A particular record can be edited by specifying the {@link setRecordPk RecordPk} + * value (may be an array for composite keys). + * + * The default editor input controls are created based on the column types. + * The editor layout can be specified by a renderer by set the value + * of the {@link setEditRenderer EditRenderer} property to the class name of a + * class that implements TScaffoldEditRenderer. A renderer is an external + * template control that implements IScaffoldEditRenderer. + * + * The Data of the IScaffoldEditRenderer will be set as the current Active + * Record to be edited. The UpdateRecord() method of IScaffoldEditRenderer + * is called when request to save the record is requested. + * + * Validators in the custom external editor template should have the + * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the + * value of the {@link getValidationGroup} of the TScaffoldEditView instance + * (the edit view instance is the Parent of the IScaffoldEditRenderer in most + * cases. + * + * Cosmetic changes to the default editor should be done using Cascading Stylesheets. + * For example, a particular field/property can be hidden by specifying "display:none" for + * the corresponding style (each field/property has unique Css class name as "property_xxx", where + * xxx is the property name). + * + * @author Wei Zhuo + * @version $Id: TScaffoldEditView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldEditView extends TScaffoldBase +{ + /** + * @var IScaffoldEditRenderer custom scaffold edit renderer + */ + private $_editRenderer; + + /** + * Initialize the editor form if it is Visible. + */ + public function onLoad($param) + { + if($this->getVisible()) + $this->initializeEditForm(); + } + + /** + * @return string the class name for scaffold editor. Defaults to empty, meaning not set. + */ + public function getEditRenderer() + { + return $this->getViewState('EditRenderer', ''); + } + + /** + * @param string the class name for scaffold editor. Defaults to empty, meaning not set. + */ + public function setEditRenderer($value) + { + $this->setViewState('EditRenderer', $value, ''); + } + + /** + * @param array Active Record primary key value to be edited. + */ + public function setRecordPk($value) + { + $this->clearRecordObject(); + $val = TPropertyValue::ensureArray($value); + $this->setViewState('PK', count($val) > 0 ? $val : null); + } + + /** + * @return array Active Record primary key value. + */ + public function getRecordPk() + { + return $this->getViewState('PK'); + } + + /** + * @return TActiveRecord current Active Record instance + */ + protected function getCurrentRecord() + { + return $this->getRecordObject($this->getRecordPk()); + } + + /** + * Initialize the editor form + */ + public function initializeEditForm() + { + $record = $this->getCurrentRecord(); + $classPath = $this->getEditRenderer(); + if($classPath === '') + { + $columns = $this->getTableInfo()->getColumns(); + $this->getInputRepeater()->setDataSource($columns); + $this->getInputRepeater()->dataBind(); + } + else + { + if($this->_editRenderer===null) + $this->createEditRenderer($record, $classPath); + else + $this->_editRenderer->setData($record); + } + } + + /** + * Instantiate the external edit renderer. + * @param TActiveRecord record to be edited + * @param string external edit renderer class name. + * @throws TConfigurationException raised when renderer is not an + * instance of IScaffoldEditRenderer. + */ + protected function createEditRenderer($record, $classPath) + { + $this->_editRenderer = Prado::createComponent($classPath); + if($this->_editRenderer instanceof IScaffoldEditRenderer) + { + $index = $this->getControls()->remove($this->getInputRepeater()); + $this->getControls()->insertAt($index,$this->_editRenderer); + $this->_editRenderer->setData($record); + } + else + { + throw new TConfigurationException( + 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record)); + } + } + + /** + * Initialize the default editor using the scaffold input builder. + */ + protected function createRepeaterEditItem($sender, $param) + { + $type = $param->getItem()->getItemType(); + if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) + { + $item = $param->getItem(); + $column = $item->getDataItem(); + if($column===null) + return; + + $record = $this->getCurrentRecord(); + $builder = $this->getScaffoldInputBuilder($record); + $builder->createScaffoldInput($this, $item, $column, $record); + } + } + + /** + * Bubble the command name event. Stops bubbling when the page validator false. + * Otherwise, the bubble event is continued. + */ + public function bubbleEvent($sender, $param) + { + switch(strtolower($param->getCommandName())) + { + case 'save': + return $this->doSave() ? false : true; + case 'clear': + $this->setRecordPk(null); + $this->initializeEditForm(); + return false; + default: + return false; + } + } + + /** + * Check the validators, then tries to save the record. + * @return boolean true if the validators are true, false otherwise. + */ + protected function doSave() + { + if($this->getPage()->getIsValid()) + { + $record = $this->getCurrentRecord(); + if($this->_editRenderer===null) + { + $table = $this->getTableInfo(); + $builder = $this->getScaffoldInputBuilder($record); + foreach($this->getInputRepeater()->getItems() as $item) + { + $column = $table->getColumn($item->getCustomData()); + $builder->loadScaffoldInput($this, $item, $column, $record); + } + } + else + { + $this->_editRenderer->updateRecord($record); + } + $record->save(); + return true; + } + else if($this->_editRenderer!==null) + { + //preserve the form data. + $this->_editRenderer->updateRecord($this->getCurrentRecord()); + } + + return false; + } + + /** + * @return TRepeater default editor input controls repeater + */ + protected function getInputRepeater() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_repeater'); + } + + /** + * @return TButton Button triggered to save the Active Record. + */ + public function getSaveButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_save'); + } + + /** + * @return TButton Button to clear the editor inputs. + */ + public function getClearButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_clear'); + } + + /** + * @return TButton Button to cancel the edit action (e.g. hide the edit view). + */ + public function getCancelButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_cancel'); + } + + /** + * Create the default scaffold editor control factory. + * @param TActiveRecord record instance. + * @return TScaffoldInputBase scaffold editor control factory. + */ + protected function getScaffoldInputBuilder($record) + { + static $_builders=array(); + $class = get_class($record); + if(!isset($_builders[$class])) + { + Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase'); + $_builders[$class] = TScaffoldInputBase::createInputBuilder($record); + } + return $_builders[$class]; + } + + /** + * @return string editor validation group name. + */ + public function getValidationGroup() + { + return 'group_'.$this->getUniqueID(); + } +} + +/** + * IScaffoldEditRenderer interface. + * + * IScaffoldEditRenderer defines the interface that an edit renderer + * needs to implement. Besides the {@link getData Data} property, an edit + * renderer also needs to provide {@link updateRecord updateRecord} method + * that is called before the save() method is called on the TActiveRecord. + * + * @author Wei Zhuo + * @version $Id: TScaffoldEditView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +interface IScaffoldEditRenderer extends IDataRenderer +{ + /** + * This method should update the record with the user input data. + * @param TActiveRecord record to be saved. + */ + public function updateRecord($record); +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl new file mode 100644 index 0000000000..b3289c0925 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.tpl @@ -0,0 +1,21 @@ +
    +
    + + +
    + + + + +
    +
    +
    +
    + +
    +ValidationGroup %>/> + + +
    +
    \ No newline at end of file diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php new file mode 100644 index 0000000000..2cd2def577 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -0,0 +1,306 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TScaffoldListView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load the scaffold base class. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * TScaffoldListView displays a list of Active Records. + * + * The {@link getHeader Header} property is a TRepeater displaying the + * Active Record property/field names. The {@link getSort Sort} property + * is a drop down list displaying the combination of properties and its possible + * ordering. The {@link getPager Pager} property is a TPager control displaying + * the links and/or buttons that navigate to different pages in the Active Record data. + * The {@link getList List} property is a TRepeater that renders a row of + * Active Record data. + * + * Custom rendering of the each Active Record can be achieved by specifying + * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List} + * repeater. + * + * The TScaffoldListView will listen for two command events named "delete" and + * "edit". A "delete" command will delete a the record for the row where the + * "delete" command is originates. An "edit" command will push + * the record data to be edited by a TScaffoldEditView with ID specified by the + * {@link setEditViewID EditViewID}. + * + * Additional {@link setSearchCondition SearchCondition} and + * {@link setSearchParameters SearchParameters} (takes array values) can be + * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition} + * will be used as the Condition property of TActiveRecordCriteria, and similarly + * the {@link setSearchParameters SearchParameters} will be the corresponding + * Parameters property of TActiveRecordCriteria. + * + * @author Wei Zhuo + * @version $Id: TScaffoldListView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldListView extends TScaffoldBase +{ + /** + * Initialize the sort drop down list and the column names repeater. + */ + protected function initializeSort() + { + $table = $this->getTableInfo(); + $sorts = array('Sort By', str_repeat('-',15)); + $headers = array(); + foreach($table->getColumns() as $name=>$colum) + { + $fname = ucwords(str_replace('_', ' ', $name)); + $sorts[$name.' ASC'] = $fname .' Ascending'; + $sorts[$name.' DESC'] = $fname .' Descending'; + $headers[] = $fname ; + } + $this->_sort->setDataSource($sorts); + $this->_sort->dataBind(); + $this->_header->setDataSource($headers); + $this->_header->dataBind(); + } + + /** + * Loads and display the data. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass()) + { + $this->initializeSort(); + $this->setViewState('CurrentClass', $this->getRecordClass()); + } + $this->loadRecordData(); + } + + /** + * Fetch the records and data bind it to the list. + */ + protected function loadRecordData() + { + $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); + $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search)); + $finder = $this->getRecordFinder(); + $criteria = $this->getRecordCriteria(); + $this->_list->setDataSource($finder->findAll($criteria)); + $this->_list->dataBind(); + } + + /** + * @return TActiveRecordCriteria sort/search/paging criteria + */ + protected function getRecordCriteria() + { + $total = $this->_list->getVirtualItemCount(); + $limit = $this->_list->getPageSize(); + $offset = $this->_list->getCurrentPageIndex()*$limit; + if($offset + $limit > $total) + $limit = $total - $offset; + $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); + if($limit > 0) + { + $criteria->setLimit($limit); + if($offset <= $total) + $criteria->setOffset($offset); + } + $order = explode(' ',$this->_sort->getSelectedValue(), 2); + if(is_array($order) && count($order) === 2) + $criteria->OrdersBy[$order[0]] = $order[1]; + return $criteria; + } + + /** + * @param string search condition, the SQL string after the WHERE clause. + */ + public function setSearchCondition($value) + { + $this->setViewState('SearchCondition', $value); + } + + /** + * @param string SQL search condition for list display. + */ + public function getSearchCondition() + { + return $this->getViewState('SearchCondition'); + } + + /** + * @param array search parameters + */ + public function setSearchParameters($value) + { + $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array()); + } + + /** + * @return array search parameters + */ + public function getSearchParameters() + { + return $this->getViewState('SearchParameters', array()); + } + + /** + * Continue bubbling the "edit" command, "delete" command is handled in this class. + */ + public function bubbleEvent($sender, $param) + { + switch(strtolower($param->getCommandName())) + { + case 'delete': + return $this->deleteRecord($sender, $param); + case 'edit': + $this->initializeEdit($sender, $param); + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + /** + * Initialize the edit view control form when EditViewID is set. + */ + protected function initializeEdit($sender, $param) + { + if(($ctrl=$this->getEditViewControl())!==null) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $ctrl->setRecordPk($pk); + $ctrl->initializeEditForm(); + } + } + } + + /** + * Deletes an Active Record. + */ + protected function deleteRecord($sender, $param) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $this->getRecordFinder()->deleteByPk($pk); + } + } + + /** + * Initialize the default display for each Active Record item. + */ + protected function listItemCreated($sender, $param) + { + $item = $param->getItem(); + if($item instanceof IItemDataRenderer) + { + $type = $item->getItemType(); + if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) + $this->populateField($sender, $param); + } + } + + /** + * Sets the Record primary key to the current repeater item's CustomData. + * Binds the inner repeater with properties of the current Active Record. + */ + protected function populateField($sender, $param) + { + $item = $param->getItem(); + if(($data = $item->getData()) !== null) + { + $item->setCustomData($this->getRecordPkValues($data)); + if(($prop = $item->findControl('_properties'))!==null) + { + $item->_properties->setDataSource($this->getRecordPropertyValues($data)); + $item->_properties->dataBind(); + } + } + } + + /** + * Updates repeater page index with the pager new index value. + */ + protected function pageChanged($sender, $param) + { + $this->_list->setCurrentPageIndex($param->getNewPageIndex()); + } + + /** + * @return TRepeater Repeater control for Active Record instances. + */ + public function getList() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_list'); + } + + /** + * @return TPager List pager control. + */ + public function getPager() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_pager'); + } + + /** + * @return TDropDownList Control that displays and controls the record ordering. + */ + public function getSort() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_sort'); + } + + /** + * @return TRepeater Repeater control for record property names. + */ + public function getHeader() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_header'); + } + + /** + * @return string TScaffoldEditView control ID for editing selected Active Record. + */ + public function getEditViewID() + { + return $this->getViewState('EditViewID'); + } + + /** + * @param string TScaffoldEditView control ID for editing selected Active Record. + */ + public function setEditViewID($value) + { + $this->setViewState('EditViewID', $value); + } + + /** + * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set. + */ + protected function getEditViewControl() + { + if(($id=$this->getEditViewID())!==null) + { + $ctrl = $this->getParent()->findControl($id); + if($ctrl===null) + throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id); + return $ctrl; + } + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl new file mode 100644 index 0000000000..c70e864daf --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.tpl @@ -0,0 +1,57 @@ +
    +
    + + + DataItem %> CssClass="field field_<%# $this->ItemIndex %>"/> + + + + + + + +
    + +
    + + +
    + + + + + <%# htmlspecialchars($this->DataItem) %> + + + + + + NamingContainer->Parent->EditViewID !== Null %> + CommandName="edit" + CssClass="edit-button" + CausesValidation="false" /> + + + +
    +
    +
    +
    + + +
    \ No newline at end of file diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php new file mode 100644 index 0000000000..5505977f03 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php @@ -0,0 +1,150 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Import the scaffold base. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * TScaffoldSearch provide a simple textbox and a button that is used + * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}. + * + * The {@link getSearchText SearchText} property is a TTextBox and the + * {@link getSearchButton SearchButton} property is a TButton with label value "Search". + * + * Searchable fields of the Active Record can be restricted by specifying + * a comma delimited string of allowable fields in the + * {@link setSearchableFields SearchableFields} property. The default is null, + * meaning that most text type fields are searched (the default searchable fields + * are database dependent). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldSearch extends TScaffoldBase +{ + /** + * @var TScaffoldListView the scaffold list view. + */ + private $_list; + + /** + * @return TScaffoldListView the scaffold list view this search box belongs to. + */ + protected function getListView() + { + if($this->_list===null && ($id = $this->getListViewID()) !== null) + { + $this->_list = $this->getParent()->findControl($id); + if($this->_list ===null) + throw new TConfigurationException('scaffold_unable_to_find_list_view', $id); + } + return $this->_list; + } + + /** + * @param string ID of the TScaffoldListView this search control belongs to. + */ + public function setListViewID($value) + { + $this->setViewState('ListViewID', $value); + } + + /** + * @return string ID of the TScaffoldListView this search control belongs to. + */ + public function getListViewID() + { + return $this->getViewState('ListViewID'); + } + + /** + * Sets the SearchCondition of the TScaffoldListView as the search terms + * given by the text of the search text box. + */ + public function bubbleEvent($sender, $param) + { + if(strtolower($param->getCommandName())==='search') + { + if(($list = $this->getListView()) !== null) + { + $list->setSearchCondition($this->createSearchCondition()); + return false; + } + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + /** + * @return string the search criteria for the search terms in the search text box. + */ + protected function createSearchCondition() + { + $table = $this->getTableInfo(); + if(strlen($str=$this->getSearchText()->getText()) > 0) + { + $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection()); + return $builder->getSearchExpression($this->getFields(), $str); + } + } + + /** + * @return array list of fields to be searched. + */ + protected function getFields() + { + if(strlen(trim($str=$this->getSearchableFields()))>0) + $fields = preg_split('/\s*,\s*/', $str); + else + $fields = $this->getTableInfo()->getColumns()->getKeys(); + return $fields; + } + + /** + * @return string comma delimited list of fields that may be searched. + */ + public function getSearchableFields() + { + return $this->getViewState('SearchableFields',''); + } + + /** + * @param string comma delimited list of fields that may be searched. + */ + public function setSearchableFields($value) + { + $this->setViewState('SearchableFields', $value, ''); + } + + /** + * @return TButton button with default label "Search". + */ + public function getSearchButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + /** + * @return TTextBox search text box. + */ + public function getSearchText() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_textbox'); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl new file mode 100644 index 0000000000..a5f56b5559 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.tpl @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php new file mode 100644 index 0000000000..63dea8ddb5 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php @@ -0,0 +1,143 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TScaffoldView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Import scaffold base, list, edit and search controls. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch'); + +/** + * TScaffoldView is a composite control consisting of TScaffoldListView + * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView + * when an "edit" command is raised from the TScaffoldListView (when the + * edit button is clicked). Futher more, the "add" button can be clicked + * that shows an empty data TScaffoldListView for creating new records. + * + * The {@link getListView ListView} property gives a TScaffoldListView for + * display the record data. The {@link getEditView EditView} is the + * TScaffoldEditView that renders the + * inputs for editing and adding records. The {@link getSearchControl SearchControl} + * is a TScaffoldSearch responsible to the search user interface. + * + * Set the {@link setRecordClass RecordClass} property to the name of + * the Active Record class to be displayed/edited/added. + * + * @author Wei Zhuo + * @version $Id: TScaffoldView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.0 + */ +class TScaffoldView extends TScaffoldBase +{ + /** + * Copy basic record details to the list/edit/search controls. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getListView()->copyFrom($this); + $this->getEditView()->copyFrom($this); + $this->getSearchControl()->copyFrom($this); + } + + /** + * @return TScaffoldListView scaffold list view. + */ + public function getListView() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_listView'); + } + + /** + * @return TScaffoldEditView scaffold edit view. + */ + public function getEditView() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_editView'); + } + + /** + * @return TScaffoldSearch scaffold search textbox and button. + */ + public function getSearchControl() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + /** + * @return TButton "Add new record" button. + */ + public function getAddButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_newButton'); + } + + /** + * Handle the "edit" and "new" commands by displaying the edit view. + * Default command shows the list view. + */ + public function bubbleEvent($sender,$param) + { + switch(strtolower($param->getCommandName())) + { + case 'edit': + return $this->showEditView($sender, $param); + case 'new': + return $this->showAddView($sender, $param); + default: + return $this->showListView($sender, $param); + } + return false; + } + + /** + * Shows the edit record view. + */ + protected function showEditView($sender, $param) + { + $this->getListView()->setVisible(false); + $this->getEditView()->setVisible(true); + $this->_panForNewButton->setVisible(false); + $this->_panForSearch->setVisible(false); + $this->getEditView()->getCancelButton()->setVisible(true); + $this->getEditView()->getClearButton()->setVisible(false); + } + + /** + * Shows the view for listing the records. + */ + protected function showListView($sender, $param) + { + $this->getListView()->setVisible(true); + $this->getEditView()->setVisible(false); + $this->_panForNewButton->setVisible(true); + $this->_panForSearch->setVisible(true); + } + + /** + * Shows the add record view. + */ + protected function showAddView($sender, $param) + { + $this->getEditView()->setRecordPk(null); + $this->getEditView()->initializeEditForm(); + $this->showEditView($sender, $param); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl new file mode 100644 index 0000000000..eea3952677 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/TScaffoldView.tpl @@ -0,0 +1,11 @@ +
    + + + + + + + + + +
    \ No newline at end of file diff --git a/gui/baculum/framework/Data/ActiveRecord/Scaffold/style.css b/gui/baculum/framework/Data/ActiveRecord/Scaffold/style.css new file mode 100644 index 0000000000..cd34eb7659 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/Scaffold/style.css @@ -0,0 +1,124 @@ +/* $Id: style.css 1866 2007-04-14 05:02:29Z wei $ */ +body +{ + font-family: Cambria, Georgia, "Times New Roman", Times, serif; +} + +.pager +{ + display: block; + border-top: 1px solid #ccc; + padding: 1em; +} + +.pager span, .pager a +{ + border: 1px solid #ccc; + padding: 0.3em 0.7em; + font-weight: bold; +} + +.pager a +{ + background-color: #E0FFFF; + border-color: #87CEFA; +} + +.pager a:hover +{ + background-color: White; +} + + +.item, .item-header +{ + border-top: 1px dashed #B0C4DE; + padding: 0.5em; + clear: both; +} + +.item-header +{ + border-top: 0 none; + font-weight: bold; +} + +.item_1 +{ + background-color: #F0F8FF; +} + +.field_0, .field +{ + padding: 0.2em; + float: left; + width: 150px; +} + +.field_0 +{ + width: 40px; + text-align: center; +} + +.auxilary-button +{ + padding: 1em; +} + +.edit-inputs label +{ + width: 150px; + float: left; + text-align: right; + padding: 0 0.5em; + font-weight: bold; +} + + +.item-input label +{ + float: none; + font-weight: normal; +} + + +.edit-page-buttons +{ + padding-left: 120px; + padding-top: 20px; +} + +.edit-item +{ + padding: 0.4em; +} + +.edit-inputs .scaffold_input +{ + width: 250px; + border: 1px solid Highlight; + padding: 0.2em; +} + +.edit-inputs .multiline-textbox +{ + width: 500px; + height: 100px; +} + +.edit-inputs .input_0 .scaffold_input +{ + width: 50px; +} + +.edit-inputs .required +{ + font-weight: bold; + padding: 0.2em; +} +.edit-inputs .required-input, .edit-inputs .required-input2 .required-input3 .required-input4 +{ + border: 1px solid red; + background-color: #FFF5EE; +} \ No newline at end of file diff --git a/gui/baculum/framework/Data/ActiveRecord/TActiveRecord.php b/gui/baculum/framework/Data/ActiveRecord/TActiveRecord.php new file mode 100644 index 0000000000..24fa41ce2f --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/TActiveRecord.php @@ -0,0 +1,1086 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRecord.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + */ + +/** + * Load record manager, criteria and relations. + */ +Prado::using('System.Data.ActiveRecord.TActiveRecordManager'); +Prado::using('System.Data.ActiveRecord.TActiveRecordCriteria'); +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); + +/** + * Base class for active records. + * + * An active record creates an object that wraps a row in a database table + * or view, encapsulates the database access, and adds domain logic on that data. + * + * Active record objects are stateful, this is main difference between the + * TActiveRecord implementation and the TTableGateway implementation. + * + * The essence of an Active Record is an object model of the + * domain (e.g. products, items) that incorporates both behavior and + * data in which the classes match very closely the record structure of an + * underlying database. Each Active Record is responsible for saving and + * loading to the database and also for any domain logic that acts on the data. + * + * The Active Record provides methods that do the following: + * 1. Construct an instance of the Active Record from a SQL result set row. + * 2. Construct a new instance for later insertion into the table. + * 3. Finder methods to wrap commonly used SQL queries and return Active Record objects. + * 4. Update the database and insert into it the data in the Active Record. + * + * Example: + * + * class UserRecord extends TActiveRecord + * { + * const TABLE='users'; //optional table name. + * + * public $username; //corresponds to the fieldname in the table + * public $email; + * + * //returns active record finder instance + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * //create a connection and give it to the ActiveRecord manager. + * $dsn = 'pgsql:host=localhost;dbname=test'; + * $conn = new TDbConnection($dsn, 'dbuser','dbpass'); + * TActiveRecordManager::getInstance()->setDbConnection($conn); + * + * //load the user record with username (primary key) 'admin'. + * $user = UserRecord::finder()->findByPk('admin'); + * $user->email = 'admin@example.org'; + * $user->save(); //update the 'admin' record. + * + * + * Since v3.1.1, TActiveRecord starts to support column mapping. The physical + * column names (defined in database) can be mapped to logical column names + * (defined in active classes as public properties.) To use this feature, declare + * a static class variable COLUMN_MAPPING like the following: + * + * class UserRecord extends TActiveRecord + * { + * const TABLE='users'; + * public static $COLUMN_MAPPING=array + * ( + * 'user_id'=>'username', + * 'email_address'=>'email', + * ); + * public $username; + * public $email; + * } + * + * In the above, the 'users' table consists of 'user_id' and 'email_address' columns, + * while the UserRecord class declares 'username' and 'email' properties. + * By using column mapping, we can regularize the naming convention of column names + * in active record. + * + * Since v3.1.2, TActiveRecord enhanced its support to access of foreign objects. + * By declaring a public static variable RELATIONS like the following, one can access + * the corresponding foreign objects easily: + * + * class UserRecord extends TActiveRecord + * { + * const TABLE='users'; + * public static $RELATIONS=array + * ( + * 'department'=>array(self::BELONGS_TO, 'DepartmentRecord', 'department_id'), + * 'contacts'=>array(self::HAS_MANY, 'ContactRecord', 'user_id'), + * ); + * } + * + * In the above, the users table is related with departments table (represented by + * DepartmentRecord) and contacts table (represented by ContactRecord). Now, given a UserRecord + * instance $user, one can access its department and contacts simply by: $user->department and + * $user->contacts. No explicit data fetching is needed. Internally, the foreign objects are + * fetched in a lazy way, which avoids unnecessary overhead if the foreign objects are not accessed + * at all. + * + * Since v3.1.2, new events OnInsert, OnUpdate and OnDelete are available. + * The event OnInsert, OnUpdate and OnDelete methods are executed before + * inserting, updating, and deleting the current record, respectively. You may override + * these methods; a TActiveRecordChangeEventParameter parameter is passed to these methods. + * The property {@link TActiveRecordChangeEventParameter::setIsValid IsValid} of the parameter + * can be set to false to prevent the change action to be executed. This can be used, + * for example, to validate the record before the action is executed. For example, + * in the following the password property is hashed before a new record is inserted. + * + * class UserRecord extends TActiveRecord + * { + * function OnInsert($param) + * { + * //parent method should be called to raise the event + * parent::OnInsert($param); + * $this->nounce = md5(time()); + * $this->password = md5($this->password.$this->nounce); + * } + * } + * + * + * Since v3.1.3 you can also define a method that returns the table name. + * + * class UserRecord extends TActiveRecord + * { + * public function table() + * { + * return 'users'; + * } + * + * } + * + * + * @author Wei Zhuo + * @version $Id: TActiveRecord.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +abstract class TActiveRecord extends TComponent +{ + const BELONGS_TO='BELONGS_TO'; + const HAS_ONE='HAS_ONE'; + const HAS_MANY='HAS_MANY'; + const MANY_TO_MANY='MANY_TO_MANY'; + + const STATE_NEW=0; + const STATE_LOADED=1; + const STATE_DELETED=2; + + /** + * @var integer record state: 0 = new, 1 = loaded, 2 = deleted. + * @since 3.1.2 + */ + protected $_recordState=0; // use protected so that serialization is fine + + /** + * This static variable defines the column mapping. + * The keys are physical column names as defined in database, + * and the values are logical column names as defined as public variable/property names + * for the corresponding active record class. + * @var array column mapping. Keys: physical column names, values: logical column names. + * @since 3.1.1 + */ + public static $COLUMN_MAPPING=array(); + private static $_columnMapping=array(); + + /** + * This static variable defines the relationships. + * The keys are public variable/property names defined in the AR class. + * Each value is an array, e.g. array(self::HAS_MANY, 'PlayerRecord'). + * @var array relationship. + * @since 3.1.1 + */ + public static $RELATIONS=array(); + private static $_relations=array(); + + /** + * @var TDbConnection database connection object. + */ + protected $_connection; // use protected so that serialization is fine + + + /** + * Defaults to 'null' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + protected $_invalidFinderResult = null; // use protected so that serialization is fine + + /** + * Prevent __call() method creating __sleep() when serializing. + */ + public function __sleep() + { + return array_diff(parent::__sleep(),array("\0*\0_connection")); + } + + /** + * Prevent __call() method creating __wakeup() when unserializing. + */ + public function __wakeup() + { + $this->setupColumnMapping(); + $this->setupRelations(); + } + + /** + * Create a new instance of an active record with given $data. The record + * can be saved to the database specified by the $connection object. + * + * @param array optional name value pair record data. + * @param TDbConnection optional database connection this object record use. + */ + public function __construct($data=array(), $connection=null) + { + if($connection!==null) + $this->setDbConnection($connection); + $this->setupColumnMapping(); + $this->setupRelations(); + if(!empty($data)) //$data may be an object + $this->copyFrom($data); + } + + /** + * Magic method for reading properties. + * This method is overriden to provide read access to the foreign objects via + * the key names declared in the RELATIONS array. + * @param string property name + * @return mixed property value. + * @since 3.1.2 + */ + public function __get($name) + { + if($this->hasRecordRelation($name) && !$this->canGetProperty($name)) + { + $this->fetchResultsFor($name); + return $this->$name; + } + return parent::__get($name); + } + + /** + * Magic method for writing properties. + * This method is overriden to provide write access to the foreign objects via + * the key names declared in the RELATIONS array. + * @param string property name + * @param mixed property value. + * @since 3.1.2 + */ + public function __set($name,$value) + { + if($this->hasRecordRelation($name) && !$this->canSetProperty($name)) + $this->$name=$value; + else + parent::__set($name,$value); + } + + /** + * @since 3.1.1 + */ + private function setupColumnMapping() + { + $className=get_class($this); + if(!isset(self::$_columnMapping[$className])) + { + $class=new ReflectionClass($className); + self::$_columnMapping[$className]=$class->getStaticPropertyValue('COLUMN_MAPPING'); + } + } + + /** + * @since 3.1.2 + */ + private function setupRelations() + { + $className=get_class($this); + if(!isset(self::$_relations[$className])) + { + $class=new ReflectionClass($className); + $relations=array(); + foreach($class->getStaticPropertyValue('RELATIONS') as $key=>$value) + $relations[strtolower($key)]=array($key,$value); + self::$_relations[$className]=$relations; + } + } + + /** + * Copies data from an array or another object. + * @throws TActiveRecordException if data is not array or not object. + */ + public function copyFrom($data) + { + if(is_object($data)) + $data=get_object_vars($data); + if(!is_array($data)) + throw new TActiveRecordException('ar_data_invalid', get_class($this)); + foreach($data as $name=>$value) + $this->setColumnValue($name,$value); + } + + + public static function getActiveDbConnection() + { + if(($db=self::getRecordManager()->getDbConnection())!==null) + $db->setActive(true); + return $db; + } + + /** + * Gets the current Db connection, the connection object is obtained from + * the TActiveRecordManager if connection is currently null. + * @return TDbConnection current db connection for this object. + */ + public function getDbConnection() + { + if($this->_connection===null) + $this->_connection=self::getActiveDbConnection(); + return $this->_connection; + } + + /** + * @param TDbConnection db connection object for this record. + */ + public function setDbConnection($connection) + { + $this->_connection=$connection; + } + + /** + * @return TDbTableInfo the meta information of the table associated with this AR class. + */ + public function getRecordTableInfo() + { + return $this->getRecordGateway()->getRecordTableInfo($this); + } + + /** + * Compare two records using their primary key values (all column values if + * table does not defined primary keys). The default uses simple == for + * comparison of their values. Set $strict=true for identity comparison (===). + * @param TActiveRecord another record to compare with. + * @param boolean true to perform strict identity comparison + * @return boolean true if $record equals, false otherwise. + */ + public function equals(TActiveRecord $record, $strict=false) + { + if($record===null || get_class($this)!==get_class($record)) + return false; + $tableInfo = $this->getRecordTableInfo(); + $pks = $tableInfo->getPrimaryKeys(); + $properties = count($pks) > 0 ? $pks : $tableInfo->getColumns()->getKeys(); + $equals=true; + foreach($properties as $prop) + { + if($strict) + $equals = $equals && $this->getColumnValue($prop) === $record->getColumnValue($prop); + else + $equals = $equals && $this->getColumnValue($prop) == $record->getColumnValue($prop); + if(!$equals) + return false; + } + return $equals; + } + + /** + * Returns the instance of a active record finder for a particular class. + * The finder objects are static instances for each ActiveRecord class. + * This means that event handlers bound to these finder instances are class wide. + * Create a new instance of the ActiveRecord class if you wish to bound the + * event handlers to object instance. + * @param string active record class name. + * @return TActiveRecord active record finder instance. + */ + public static function finder($className=__CLASS__) + { + static $finders = array(); + if(!isset($finders[$className])) + { + $f = Prado::createComponent($className); + $finders[$className]=$f; + } + return $finders[$className]; + } + + /** + * Gets the record manager for this object, the default is to call + * TActiveRecordManager::getInstance(). + * @return TActiveRecordManager default active record manager. + */ + public static function getRecordManager() + { + return TActiveRecordManager::getInstance(); + } + + /** + * @return TActiveRecordGateway record table gateway. + */ + public function getRecordGateway() + { + return TActiveRecordManager::getInstance()->getRecordGateway(); + } + + /** + * Saves the current record to the database, insert or update is automatically determined. + * @return boolean true if record was saved successfully, false otherwise. + */ + public function save() + { + $gateway = $this->getRecordGateway(); + $param = new TActiveRecordChangeEventParameter(); + if($this->_recordState===self::STATE_NEW) + { + $this->onInsert($param); + if($param->getIsValid() && $gateway->insert($this)) + { + $this->_recordState = self::STATE_LOADED; + return true; + } + } + else if($this->_recordState===self::STATE_LOADED) + { + $this->onUpdate($param); + if($param->getIsValid() && $gateway->update($this)) + return true; + } + else + throw new TActiveRecordException('ar_save_invalid', get_class($this)); + + return false; + } + + /** + * Deletes the current record from the database. Once deleted, this object + * can not be saved again in the same instance. + * @return boolean true if the record was deleted successfully, false otherwise. + */ + public function delete() + { + if($this->_recordState===self::STATE_LOADED) + { + $gateway = $this->getRecordGateway(); + $param = new TActiveRecordChangeEventParameter(); + $this->onDelete($param); + if($param->getIsValid() && $gateway->delete($this)) + { + $this->_recordState=self::STATE_DELETED; + return true; + } + } + else + throw new TActiveRecordException('ar_delete_invalid', get_class($this)); + + return false; + } + + /** + * Delete records by primary key. Usage: + * + * + * $finder->deleteByPk($primaryKey); //delete 1 record + * $finder->deleteByPk($key1,$key2,...); //delete multiple records + * $finder->deleteByPk(array($key1,$key2,...)); //delete multiple records + * + * + * For composite primary keys (determined from the table definitions): + * + * $finder->deleteByPk(array($key1,$key2)); //delete 1 record + * + * //delete multiple records + * $finder->deleteByPk(array($key1,$key2), array($key3,$key4),...); + * + * //delete multiple records + * $finder->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. )); + * + * + * @param mixed primary key values. + * @return int number of records deleted. + */ + public function deleteByPk($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getRecordGateway()->deleteRecordsByPk($this,(array)$keys); + } + + /** + * Alias for deleteByPk() + */ + public function deleteAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->deleteByPk($keys); + } + /** + * Delete multiple records using a criteria. + * @param string|TActiveRecordCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return int number of records deleted. + */ + public function deleteAll($criteria=null, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getRecordCriteria($criteria,$parameters, $args); + return $this->getRecordGateway()->deleteRecordsByCriteria($this, $criteria); + } + + /** + * Populates a new record with the query result. + * This is a wrapper of {@link createRecord}. + * @param array name value pair of record data + * @return TActiveRecord object record, null if data is empty. + */ + protected function populateObject($data) + { + return self::createRecord(get_class($this), $data); + } + + /** + * @param TDbDataReader data reader + * @return array the AR objects populated by the query result + * @since 3.1.2 + */ + protected function populateObjects($reader) + { + $result=array(); + foreach($reader as $data) + $result[] = $this->populateObject($data); + return $result; + } + + /** + * Create an AR instance specified by the AR class name and initial data. + * If the initial data is empty, the AR object will not be created and null will be returned. + * (You should use the "new" operator to create the AR instance in that case.) + * @param string the AR class name + * @param array initial data to be populated into the AR object. + * @return TActiveRecord the initialized AR object. Null if the initial data is empty. + * @since 3.1.2 + */ + public static function createRecord($type, $data) + { + if(empty($data)) + return null; + $record=new $type($data); + $record->_recordState=self::STATE_LOADED; + return $record; + } + + /** + * Find one single record that matches the criteria. + * + * Usage: + * + * $finder->find('username = :name AND password = :pass', + * array(':name'=>$name, ':pass'=>$pass)); + * $finder->find('username = ? AND password = ?', array($name, $pass)); + * $finder->find('username = ? AND password = ?', $name, $pass); + * //$criteria is of TActiveRecordCriteria + * $finder->find($criteria); //the 2nd parameter for find() is ignored. + * + * + * @param string|TActiveRecordCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return TActiveRecord matching record object. Null if no result is found. + */ + public function find($criteria,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getRecordCriteria($criteria,$parameters, $args); + $criteria->setLimit(1); + $data = $this->getRecordGateway()->findRecordsByCriteria($this,$criteria); + return $this->populateObject($data); + } + + /** + * Same as find() but returns an array of objects. + * + * @param string|TActiveRecordCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return array matching record objects. Empty array if no result is found. + */ + public function findAll($criteria=null,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getRecordCriteria($criteria,$parameters, $args); + $result = $this->getRecordGateway()->findRecordsByCriteria($this,$criteria,true); + return $this->populateObjects($result); + } + + /** + * Find one record using only the primary key or composite primary keys. Usage: + * + * + * $finder->findByPk($primaryKey); + * $finder->findByPk($key1, $key2, ...); + * $finder->findByPk(array($key1,$key2,...)); + * + * + * @param mixed primary keys + * @return TActiveRecord. Null if no result is found. + */ + public function findByPk($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + $data = $this->getRecordGateway()->findRecordByPK($this,$keys); + return $this->populateObject($data); + } + + /** + * Find multiple records matching a list of primary or composite keys. + * + * For scalar primary keys: + * + * $finder->findAllByPk($key1, $key2, ...); + * $finder->findAllByPk(array($key1, $key2, ...)); + * + * + * For composite keys: + * + * $finder->findAllByPk(array($key1, $key2), array($key3, $key4), ...); + * $finder->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...)); + * + * @param mixed primary keys + * @return array matching ActiveRecords. Empty array is returned if no result is found. + */ + public function findAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + $result = $this->getRecordGateway()->findRecordsByPks($this,(array)$keys); + return $this->populateObjects($result); + } + + /** + * Find records using full SQL, returns corresponding record object. + * The names of the column retrieved must be defined in your Active Record + * class. + * @param string select SQL + * @param array $parameters + * @return TActiveRecord, null if no result is returned. + */ + public function findBySql($sql,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getRecordCriteria($sql,$parameters, $args); + $criteria->setLimit(1); + $data = $this->getRecordGateway()->findRecordBySql($this,$criteria); + return $this->populateObject($data); + } + + /** + * Find records using full SQL, returns corresponding record object. + * The names of the column retrieved must be defined in your Active Record + * class. + * @param string select SQL + * @param array $parameters + * @return array matching active records. Empty array is returned if no result is found. + */ + public function findAllBySql($sql,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getRecordCriteria($sql,$parameters, $args); + $result = $this->getRecordGateway()->findRecordsBySql($this,$criteria); + return $this->populateObjects($result); + } + + /** + * Fetches records using the sql clause "(fields) IN (values)", where + * fields is an array of column names and values is an array of values that + * the columns must have. + * + * This method is to be used by the relationship handler. + * + * @param TActiveRecordCriteria additional criteria + * @param array field names to match with "(fields) IN (values)" sql clause. + * @param array matching field values. + * @return array matching active records. Empty array is returned if no result is found. + */ + public function findAllByIndex($criteria,$fields,$values) + { + $result = $this->getRecordGateway()->findRecordsByIndex($this,$criteria,$fields,$values); + return $this->populateObjects($result); + } + + /** + * Find the number of records. + * @param string|TActiveRecordCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return int number of records. + */ + public function count($criteria=null,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getRecordCriteria($criteria,$parameters, $args); + return $this->getRecordGateway()->countRecords($this,$criteria); + } + + /** + * Returns the active record relationship handler for $RELATION with key + * value equal to the $property value. + * @param string relationship/property name corresponding to keys in $RELATION array. + * @param array method call arguments. + * @return TActiveRecordRelation, null if the context or the handler doesn't exist + */ + protected function getRelationHandler($name,$args=array()) + { + if(($context=$this->createRelationContext($name)) !== null) + { + $criteria = $this->getRecordCriteria(count($args)>0 ? $args[0] : null, array_slice($args,1)); + return $context->getRelationHandler($criteria); + } + else + return null; + } + + /** + * Gets a static copy of the relationship context for given property (a key + * in $RELATIONS), returns null if invalid relationship. Keeps a null + * reference to all invalid relations called. + * @param string relationship/property name corresponding to keys in $RELATION array. + * @return TActiveRecordRelationContext object containing information on + * the active record relationships for given property, null if invalid relationship + * @since 3.1.2 + */ + protected function createRelationContext($name) + { + if(($definition=$this->getRecordRelation($name))!==null) + { + list($property, $relation) = $definition; + return new TActiveRecordRelationContext($this,$property,$relation); + } + else + return null; + } + + /** + * Tries to load the relationship results for the given property. The $property + * value should correspond to an entry key in the $RELATION array. + * This method can be used to lazy load relationships. + * + * class TeamRecord extends TActiveRecord + * { + * ... + * + * private $_players; + * public static $RELATION=array + * ( + * 'players' => array(self::HAS_MANY, 'PlayerRecord'), + * ); + * + * public function setPlayers($array) + * { + * $this->_players=$array; + * } + * + * public function getPlayers() + * { + * if($this->_players===null) + * $this->fetchResultsFor('players'); + * return $this->_players; + * } + * } + * Usage example: + * $team = TeamRecord::finder()->findByPk(1); + * var_dump($team->players); //uses lazy load to fetch 'players' relation + * + * @param string relationship/property name corresponding to keys in $RELATION array. + * @return boolean true if relationship exists, false otherwise. + * @since 3.1.2 + */ + protected function fetchResultsFor($property) + { + if( ($context=$this->createRelationContext($property)) !== null) + return $context->getRelationHandler()->fetchResultsInto($this); + else + return false; + } + + /** + * Dynamic find method using parts of method name as search criteria. + * Method name starting with "findBy" only returns 1 record. + * Method name starting with "findAllBy" returns 0 or more records. + * Method name starting with "deleteBy" deletes records by the trail criteria. + * The condition is taken as part of the method name after "findBy", "findAllBy" + * or "deleteBy". + * + * The following are equivalent: + * + * $finder->findByName($name) + * $finder->find('Name = ?', $name); + * + * + * $finder->findByUsernameAndPassword($name,$pass); // OR may be used + * $finder->findBy_Username_And_Password($name,$pass); // _OR_ may be used + * $finder->find('Username = ? AND Password = ?', $name, $pass); + * + * + * $finder->findAllByAge($age); + * $finder->findAll('Age = ?', $age); + * + * + * $finder->deleteAll('Name = ?', $name); + * $finder->deleteByName($name); + * + * @return mixed single record if method name starts with "findBy", 0 or more records + * if method name starts with "findAllBy" + */ + public function __call($method,$args) + { + $delete =false; + if(strncasecmp($method,'with',4)===0) + { + $property= $method[4]==='_' ? substr($method,5) : substr($method,4); + return $this->getRelationHandler($property, $args); + } + else if($findOne=strncasecmp($method,'findby',6)===0) + $condition = $method[6]==='_' ? substr($method,7) : substr($method,6); + else if(strncasecmp($method,'findallby',9)===0) + $condition = $method[9]==='_' ? substr($method,10) : substr($method,9); + else if($delete=strncasecmp($method,'deleteby',8)===0) + $condition = $method[8]==='_' ? substr($method,9) : substr($method,8); + else if($delete=strncasecmp($method,'deleteallby',11)===0) + $condition = $method[11]==='_' ? substr($method,12) : substr($method,11); + else + { + if($this->getInvalidFinderResult() == TActiveRecordInvalidFinderResult::Exception) + throw new TActiveRecordException('ar_invalid_finder_method',$method); + else + return null; + } + + $criteria = $this->getRecordGateway()->getCommand($this)->createCriteriaFromString($method, $condition, $args); + if($delete) + return $this->deleteAll($criteria); + else + return $findOne ? $this->find($criteria) : $this->findAll($criteria); + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @see TActiveRecordManager::getInvalidFinderResult + * @since 3.1.5 + */ + public function getInvalidFinderResult() + { + if($this->_invalidFinderResult !== null) + return $this->_invalidFinderResult; + + return self::getRecordManager()->getInvalidFinderResult(); + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * + * @param TActiveRecordInvalidFinderResult|null + * @see TActiveRecordManager::setInvalidFinderResult + * @since 3.1.5 + */ + public function setInvalidFinderResult($value) + { + if($value === null) + $this->_invalidFinderResult = null; + else + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } + + /** + * Create a new TSqlCriteria object from a string $criteria. The $args + * are additional parameters and are used in place of the $parameters + * if $parameters is not an array and $args is an arrary. + * @param string|TSqlCriteria sql criteria + * @param mixed parameters passed by the user. + * @param array additional parameters obtained from function_get_args(). + * @return TSqlCriteria criteria object. + */ + protected function getRecordCriteria($criteria, $parameters, $args=array()) + { + if(is_string($criteria)) + { + $useArgs = !is_array($parameters) && is_array($args); + return new TActiveRecordCriteria($criteria,$useArgs ? $args : $parameters); + } + else if($criteria instanceof TSqlCriteria) + return $criteria; + else + return new TActiveRecordCriteria(); + //throw new TActiveRecordException('ar_invalid_criteria'); + } + + /** + * Raised when a command is prepared and parameter binding is completed. + * The parameter object is TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * + * Note well that the finder objects obtained from ActiveRecord::finder() + * method are static objects. This means that the event handlers are + * bound to a static finder object and not to each distinct active record object. + * @param TDataGatewayEventParameter + */ + public function onCreateCommand($param) + { + $this->raiseEvent('OnCreateCommand', $this, $param); + } + + /** + * Raised when a command is executed and the result from the database was returned. + * The parameter object is TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * + * Note well that the finder objects obtained from ActiveRecord::finder() + * method are static objects. This means that the event handlers are + * bound to a static finder object and not to each distinct active record object. + * @param TDataGatewayResultEventParameter + */ + public function onExecuteCommand($param) + { + $this->raiseEvent('OnExecuteCommand', $this, $param); + } + + /** + * Raised before the record attempt to insert its data into the database. + * To prevent the insert operation, set the TActiveRecordChangeEventParameter::IsValid parameter to false. + * @param TActiveRecordChangeEventParameter event parameter to be passed to the event handlers + */ + public function onInsert($param) + { + $this->raiseEvent('OnInsert', $this, $param); + } + + /** + * Raised before the record attempt to delete its data from the database. + * To prevent the delete operation, set the TActiveRecordChangeEventParameter::IsValid parameter to false. + * @param TActiveRecordChangeEventParameter event parameter to be passed to the event handlers + */ + public function onDelete($param) + { + $this->raiseEvent('OnDelete', $this, $param); + } + + /** + * Raised before the record attempt to update its data in the database. + * To prevent the update operation, set the TActiveRecordChangeEventParameter::IsValid parameter to false. + * @param TActiveRecordChangeEventParameter event parameter to be passed to the event handlers + */ + public function onUpdate($param) + { + $this->raiseEvent('OnUpdate', $this, $param); + } + + /** + * Retrieves the column value according to column name. + * This method is used internally. + * @param string the column name (as defined in database schema) + * @return mixed the corresponding column value + * @since 3.1.1 + */ + public function getColumnValue($columnName) + { + $className=get_class($this); + if(isset(self::$_columnMapping[$className][$columnName])) + $columnName=self::$_columnMapping[$className][$columnName]; + return $this->$columnName; + } + + /** + * Sets the column value according to column name. + * This method is used internally. + * @param string the column name (as defined in database schema) + * @param mixed the corresponding column value + * @since 3.1.1 + */ + public function setColumnValue($columnName,$value) + { + $className=get_class($this); + if(isset(self::$_columnMapping[$className][$columnName])) + $columnName=self::$_columnMapping[$className][$columnName]; + $this->$columnName=$value; + } + + /** + * @param string relation property name + * @return array relation definition for the specified property + * @since 3.1.2 + */ + public function getRecordRelation($property) + { + $className=get_class($this); + $property=strtolower($property); + return isset(self::$_relations[$className][$property])?self::$_relations[$className][$property]:null; + } + + /** + * @return array all relation definitions declared in the AR class + * @since 3.1.2 + */ + public function getRecordRelations() + { + return self::$_relations[get_class($this)]; + } + + /** + * @param string AR property name + * @return boolean whether a relation is declared for the specified AR property + * @since 3.1.2 + */ + public function hasRecordRelation($property) + { + return isset(self::$_relations[get_class($this)][strtolower($property)]); + } +} + +/** + * TActiveRecordChangeEventParameter class + * + * TActiveRecordChangeEventParameter encapsulates the parameter data for + * ActiveRecord change commit events that are broadcasted. The following change events + * may be raise: {@link TActiveRecord::OnInsert}, {@link TActiveRecord::OnUpdate} and + * {@link TActiveRecord::OnDelete}. The {@link setIsValid IsValid} parameter can + * be set to false to prevent the requested change event to be performed. + * + * @author Wei Zhuo + * @version $Id: TActiveRecord.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1.2 + */ +class TActiveRecordChangeEventParameter extends TEventParameter +{ + private $_isValid=true; + + /** + * @return boolean whether the event should be performed. + */ + public function getIsValid() + { + return $this->_isValid; + } + + /** + * @param boolean set to false to prevent the event. + */ + public function setIsValid($value) + { + $this->_isValid = TPropertyValue::ensureBoolean($value); + } +} + +/** + * TActiveRecordInvalidFinderResult class. + * TActiveRecordInvalidFinderResult defines the enumerable type for possible results + * if an invalid {@link TActiveRecord::__call magic-finder} invoked. + * + * The following enumerable values are defined: + * - Null: return null (default) + * - Exception: throws a TActiveRecordException + * + * @author Yves Berkholz + * @version $Id: TActiveRecord.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @see TActiveRecordManager::setInvalidFinderResult + * @see TActiveRecordConfig::setInvalidFinderResult + * @see TActiveRecord::setInvalidFinderResult + * @since 3.1.5 + */ +class TActiveRecordInvalidFinderResult extends TEnumerable +{ + const Null = 'Null'; + const Exception = 'Exception'; +} diff --git a/gui/baculum/framework/Data/ActiveRecord/TActiveRecordConfig.php b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordConfig.php new file mode 100644 index 0000000000..7421d08b75 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordConfig.php @@ -0,0 +1,201 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRecordConfig.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.TDataSourceConfig'); +Prado::using('System.Data.ActiveRecord.TActiveRecordManager'); + +/** + * TActiveRecordConfig module configuration class. + * + * Database configuration for the default ActiveRecord manager instance. + * + * Example: application.xml configuration + * + * + * + * + * + * + * + * + * MySQL database definition: + * + * CREATE TABLE `blogs` ( + * `blog_id` int(10) unsigned NOT NULL auto_increment, + * `blog_name` varchar(255) NOT NULL, + * `blog_author` varchar(255) NOT NULL, + * PRIMARY KEY (`blog_id`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + * + * + * Record php class: + * + * class Blogs extends TActiveRecord + * { + * public $blog_id; + * public $blog_name; + * public $blog_author; + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * + * Usage example: + * + * class Home extends TPage + * { + * function onLoad($param) + * { + * $blogs = Blogs::finder()->findAll(); + * print_r($blogs); + * } + * } + * + * + * @author Wei Zhuo + * @version $Id: TActiveRecordConfig.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordConfig extends TDataSourceConfig +{ + const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager'; + const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; + + /** + * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} + * @var string + */ + private $_managerClass = self::DEFAULT_MANAGER_CLASS; + + /** + * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + * @var string + */ + private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; + + /** + * @var TActiveRecordManager + */ + private $_manager = null; + + private $_enableCache=false; + + /** + * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; + + /** + * Initialize the active record manager. + * @param TXmlDocument xml configuration. + */ + public function init($xml) + { + parent::init($xml); + $manager = $this -> getManager(); + if($this->getEnableCache()) + $manager->setCache($this->getApplication()->getCache()); + $manager->setDbConnection($this->getDbConnection()); + $manager->setInvalidFinderResult($this->getInvalidFinderResult()); + $manager->setGatewayClass($this->getGatewayClass()); + } + + /** + * @return TActiveRecordManager + */ + public function getManager() { + if($this->_manager === null) + $this->_manager = Prado::createComponent($this -> getManagerClass()); + return TActiveRecordManager::getInstance($this->_manager); + } + + /** + * Set implementation class of ActiveRecordManager + * @param string $value + */ + public function setManagerClass($value) + { + $this->_managerClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} + */ + public function getManagerClass() + { + return $this->_managerClass; + } + + /** + * Set implementation class of ActiveRecordGateway + * @param string $value + */ + public function setGatewayClass($value) + { + $this->_gatewayClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + */ + public function getGatewayClass() + { + return $this->_gatewayClass; + } + + /** + * Set true to cache the table meta data. + * @param boolean true to cache sqlmap instance. + */ + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean true if table meta data should be cached, false otherwise. + */ + public function getEnableCache() + { + return $this->_enableCache; + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @see setInvalidFinderResult + * @since 3.1.5 + */ + public function getInvalidFinderResult() + { + return $this->_invalidFinderResult; + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * + * @param TActiveRecordInvalidFinderResult + * @see getInvalidFinderResult + * @since 3.1.5 + */ + public function setInvalidFinderResult($value) + { + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } +} diff --git a/gui/baculum/framework/Data/ActiveRecord/TActiveRecordCriteria.php b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordCriteria.php new file mode 100644 index 0000000000..afa76c51db --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordCriteria.php @@ -0,0 +1,39 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRecordCriteria.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.DataGateway.TSqlCriteria'); + +/** + * Search criteria for Active Record. + * + * Criteria object for active record finder methods. Usage: + * + * $criteria = new TActiveRecordCriteria; + * $criteria->Condition = 'username = :name AND password = :pass'; + * $criteria->Parameters[':name'] = 'admin'; + * $criteria->Parameters[':pass'] = 'prado'; + * $criteria->OrdersBy['level'] = 'desc'; + * $criteria->OrdersBy['name'] = 'asc'; + * $criteria->Limit = 10; + * $criteria->Offset = 20; + * + * + * @author Wei Zhuo + * @version $Id: TActiveRecordCriteria.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordCriteria extends TSqlCriteria +{ + +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/TActiveRecordGateway.php b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordGateway.php new file mode 100644 index 0000000000..912ab34715 --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordGateway.php @@ -0,0 +1,431 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRecordGateway.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + */ + +/** + * TActiveRecordGateway excutes the SQL command queries and returns the data + * record as arrays (for most finder methods). + * + * @author Wei Zhuo + * @version $Id: TActiveRecordGateway.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordGateway extends TComponent +{ + private $_manager; + private $_tables=array(); //table cache + private $_meta=array(); //meta data cache. + private $_commandBuilders=array(); + private $_currentRecord; + + /** + * Constant name for specifying optional table name in TActiveRecord. + */ + const TABLE_CONST='TABLE'; + /** + * Method name for returning optional table name in in TActiveRecord + */ + const TABLE_METHOD='table'; + + /** + * Record gateway constructor. + * @param TActiveRecordManager $manager + */ + public function __construct(TActiveRecordManager $manager) + { + $this->_manager=$manager; + } + + /** + * @return TActiveRecordManager record manager. + */ + protected function getManager() + { + return $this->_manager; + } + + /** + * Gets the table name from the 'TABLE' constant of the active record + * class if defined, otherwise use the class name as table name. + * @param TActiveRecord active record instance + * @return string table name for the given record class. + */ + protected function getRecordTableName(TActiveRecord $record) + { + $class = new ReflectionClass($record); + + if($class->hasConstant(self::TABLE_CONST)) + { + $value = $class->getConstant(self::TABLE_CONST); + if(empty($value)) + throw new TActiveRecordException('ar_invalid_tablename_property', + get_class($record),self::TABLE_CONST); + return $value; + } + elseif ($class->hasMethod(self::TABLE_METHOD)) + { + $value = $record->{self::TABLE_METHOD}(); + if(empty($value)) + throw new TActiveRecordException('ar_invalid_tablename_method', + get_class($record),self::TABLE_METHOD); + return $value; + } + else + return strtolower(get_class($record)); + } + + /** + * Returns table information, trys the application cache first. + * @param TActiveRecord $record + * @return TDbTableInfo table information. + */ + public function getRecordTableInfo(TActiveRecord $record) + { + $tableName = $this->getRecordTableName($record); + return $this->getTableInfo($record->getDbConnection(), $tableName); + } + + /** + * Returns table information for table in the database connection. + * @param TDbConnection database connection + * @param string table name + * @return TDbTableInfo table details. + */ + public function getTableInfo(TDbConnection $connection, $tableName) + { + $connStr = $connection->getConnectionString(); + $connection->setActive(true); + $driver = $connection->getDriverName(); + if($driver == 'pgsql') { + $tableName = strtolower($tableName); + } + $key = $connStr.$tableName; + if(!isset($this->_tables[$key])) + { + //call this first to ensure that unserializing the cache + //will find the correct driver dependent classes. + if(!isset($this->_meta[$connStr])) + { + Prado::using('System.Data.Common.TDbMetaData'); + $this->_meta[$connStr] = TDbMetaData::getInstance($connection); + } + + $tableInfo = null; + if(($cache=$this->getManager()->getCache())!==null) + $tableInfo = $cache->get($key); + if(empty($tableInfo)) + { + $tableInfo = $this->_meta[$connStr]->getTableInfo($tableName); + if($cache!==null) + $cache->set($key, $tableInfo); + } + $this->_tables[$key] = $tableInfo; + } + return $this->_tables[$key]; + } + + /** + * @param TActiveRecord $record + * @return TDataGatewayCommand + */ + public function getCommand(TActiveRecord $record) + { + $conn = $record->getDbConnection(); + $connStr = $conn->getConnectionString(); + $tableInfo = $this->getRecordTableInfo($record); + if(!isset($this->_commandBuilders[$connStr])) + { + $builder = $tableInfo->createCommandBuilder($record->getDbConnection()); + Prado::using('System.Data.DataGateway.TDataGatewayCommand'); + $command = new TDataGatewayCommand($builder); + $command->OnCreateCommand[] = array($this, 'onCreateCommand'); + $command->OnExecuteCommand[] = array($this, 'onExecuteCommand'); + $this->_commandBuilders[$connStr] = $command; + + } + $this->_commandBuilders[$connStr]->getBuilder()->setTableInfo($tableInfo); + $this->_currentRecord=$record; + return $this->_commandBuilders[$connStr]; + } + + /** + * Raised when a command is prepared and parameter binding is completed. + * The parameter object is TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * This method also raises the OnCreateCommand event on the ActiveRecord + * object calling this gateway. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayEventParameter + */ + public function onCreateCommand($sender, $param) + { + $this->raiseEvent('OnCreateCommand', $this, $param); + if($this->_currentRecord!==null) + $this->_currentRecord->onCreateCommand($param); + } + + /** + * Raised when a command is executed and the result from the database was returned. + * The parameter object is TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * This method also raises the OnCreateCommand event on the ActiveRecord + * object calling this gateway. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayResultEventParameter + */ + public function onExecuteCommand($sender, $param) + { + $this->raiseEvent('OnExecuteCommand', $this, $param); + if($this->_currentRecord!==null) + $this->_currentRecord->onExecuteCommand($param); + } + + /** + * Returns record data matching the given primary key(s). If the table uses + * composite key, specify the name value pairs as an array. + * @param TActiveRecord active record instance. + * @param array primary name value pairs + * @return array record data + */ + public function findRecordByPK(TActiveRecord $record,$keys) + { + $command = $this->getCommand($record); + return $command->findByPk($keys); + } + + /** + * Returns records matching the list of given primary keys. + * @param TActiveRecord active record instance. + * @param array list of primary name value pairs + * @return array matching data. + */ + public function findRecordsByPks(TActiveRecord $record, $keys) + { + return $this->getCommand($record)->findAllByPk($keys); + } + + + /** + * Returns record data matching the given critera. If $iterator is true, it will + * return multiple rows as TDbDataReader otherwise it returns the first row data. + * @param TActiveRecord active record finder instance. + * @param TActiveRecordCriteria search criteria. + * @param boolean true to return multiple rows as iterator, false returns first row. + * @return mixed matching data. + */ + public function findRecordsByCriteria(TActiveRecord $record, $criteria, $iterator=false) + { + $command = $this->getCommand($record); + return $iterator ? $command->findAll($criteria) : $command->find($criteria); + } + + /** + * Return record data from sql query. + * @param TActiveRecord active record finder instance. + * @param TActiveRecordCriteria sql query + * @return array result. + */ + public function findRecordBySql(TActiveRecord $record, $criteria) + { + return $this->getCommand($record)->findBySql($criteria); + } + + /** + * Return record data from sql query. + * @param TActiveRecord active record finder instance. + * @param TActiveRecordCriteria sql query + * @return TDbDataReader result iterator. + */ + public function findRecordsBySql(TActiveRecord $record, $criteria) + { + return $this->getCommand($record)->findAllBySql($criteria); + } + + public function findRecordsByIndex(TActiveRecord $record, $criteria, $fields, $values) + { + return $this->getCommand($record)->findAllByIndex($criteria,$fields,$values); + } + + /** + * Returns the number of records that match the given criteria. + * @param TActiveRecord active record finder instance. + * @param TActiveRecordCriteria search criteria + * @return int number of records. + */ + public function countRecords(TActiveRecord $record, $criteria) + { + return $this->getCommand($record)->count($criteria); + } + + /** + * Insert a new record. + * @param TActiveRecord new record. + * @return int number of rows affected. + */ + public function insert(TActiveRecord $record) + { + //$this->updateAssociatedRecords($record,true); + $result = $this->getCommand($record)->insert($this->getInsertValues($record)); + if($result) + $this->updatePostInsert($record); + //$this->updateAssociatedRecords($record); + return $result; + } + + /** + * Sets the last insert ID to the corresponding property of the record if available. + * @param TActiveRecord record for insertion + */ + protected function updatePostInsert($record) + { + $command = $this->getCommand($record); + $tableInfo = $command->getTableInfo(); + foreach($tableInfo->getColumns() as $name => $column) + { + if($column->hasSequence()) + $record->setColumnValue($name,$command->getLastInsertID($column->getSequenceName())); + } + } + + /** + * @param TActiveRecord record + * @return array insert values. + */ + protected function getInsertValues(TActiveRecord $record) + { + $values=array(); + $tableInfo = $this->getCommand($record)->getTableInfo(); + foreach($tableInfo->getColumns() as $name=>$column) + { + if($column->getIsExcluded()) + continue; + $value = $record->getColumnValue($name); + if(!$column->getAllowNull() && $value===null && !$column->hasSequence() && ($column->getDefaultValue() === TDbTableColumn::UNDEFINED_VALUE)) + { + throw new TActiveRecordException( + 'ar_value_must_not_be_null', get_class($record), + $tableInfo->getTableFullName(), $name); + } + if($value!==null) + $values[$name] = $value; + } + return $values; + } + + /** + * Update the record. + * @param TActiveRecord dirty record. + * @return int number of rows affected. + */ + public function update(TActiveRecord $record) + { + //$this->updateAssociatedRecords($record,true); + list($data, $keys) = $this->getUpdateValues($record); + $result = $this->getCommand($record)->updateByPk($data, $keys); + //$this->updateAssociatedRecords($record); + return $result; + } + + protected function getUpdateValues(TActiveRecord $record) + { + $values=array(); + $tableInfo = $this->getCommand($record)->getTableInfo(); + $primary=array(); + foreach($tableInfo->getColumns() as $name=>$column) + { + if($column->getIsExcluded()) + continue; + $value = $record->getColumnValue($name); + if(!$column->getAllowNull() && $value===null && ($column->getDefaultValue() === TDbTableColumn::UNDEFINED_VALUE)) + { + throw new TActiveRecordException( + 'ar_value_must_not_be_null', get_class($record), + $tableInfo->getTableFullName(), $name); + } + if($column->getIsPrimaryKey()) + $primary[$name] = $value; + else + $values[$name] = $value; + } + return array($values,$primary); + } + + protected function updateAssociatedRecords(TActiveRecord $record,$updateBelongsTo=false) + { + $context = new TActiveRecordRelationContext($record); + return $context->updateAssociatedRecords($updateBelongsTo); + } + + /** + * Delete the record. + * @param TActiveRecord record to be deleted. + * @return int number of rows affected. + */ + public function delete(TActiveRecord $record) + { + return $this->getCommand($record)->deleteByPk($this->getPrimaryKeyValues($record)); + } + + protected function getPrimaryKeyValues(TActiveRecord $record) + { + $tableInfo = $this->getCommand($record)->getTableInfo(); + $primary=array(); + foreach($tableInfo->getColumns() as $name=>$column) + { + if($column->getIsPrimaryKey()) + $primary[$name] = $record->getColumnValue($name); + } + return $primary; + } + + /** + * Delete multiple records using primary keys. + * @param TActiveRecord finder instance. + * @return int number of rows deleted. + */ + public function deleteRecordsByPk(TActiveRecord $record, $keys) + { + return $this->getCommand($record)->deleteByPk($keys); + } + + /** + * Delete multiple records by criteria. + * @param TActiveRecord active record finder instance. + * @param TActiveRecordCriteria search criteria + * @return int number of records. + */ + public function deleteRecordsByCriteria(TActiveRecord $record, $criteria) + { + return $this->getCommand($record)->delete($criteria); + } + + /** + * Raise the corresponding command event, insert, update, delete or select. + * @param string command type + * @param TDbCommand sql command to be executed. + * @param TActiveRecord active record + * @param TActiveRecordCriteria data for the command. + */ + protected function raiseCommandEvent($event,$command,$record,$criteria) + { + if(!($criteria instanceof TSqlCriteria)) + $criteria = new TActiveRecordCriteria(null,$criteria); + $param = new TActiveRecordEventParameter($command,$record,$criteria); + $manager = $record->getRecordManager(); + $manager->{$event}($param); + $record->{$event}($param); + } +} + diff --git a/gui/baculum/framework/Data/ActiveRecord/TActiveRecordManager.php b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordManager.php new file mode 100644 index 0000000000..26c04e87bd --- /dev/null +++ b/gui/baculum/framework/Data/ActiveRecord/TActiveRecordManager.php @@ -0,0 +1,164 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRecordManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.TDbConnection'); +Prado::using('System.Data.ActiveRecord.TActiveRecord'); +Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException'); +Prado::using('System.Data.ActiveRecord.TActiveRecordGateway'); + +/** + * TActiveRecordManager provides the default DB connection, + * default active record gateway, and table meta data inspector. + * + * The default connection can be set as follows: + * + * TActiveRecordManager::getInstance()->setDbConnection($conn); + * + * All new active record created after setting the + * {@link DbConnection setDbConnection()} will use that connection unless + * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection(). + * + * Set the {@link setCache Cache} property to an ICache object to allow + * the active record gateway to cache the table meta data information. + * + * @author Wei Zhuo + * @version $Id: TActiveRecordManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordManager extends TComponent +{ + const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; + + /** + * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + * @var string + */ + private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; + + private $_gateway; + private $_meta=array(); + private $_connection; + + private $_cache; + + /** + * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; + + /** + * @return ICache application cache. + */ + public function getCache() + { + return $this->_cache; + } + + /** + * @param ICache application cache + */ + public function setCache($value) + { + $this->_cache=$value; + } + + /** + * @param TDbConnection default database connection + */ + public function setDbConnection($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection default database connection + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @return TActiveRecordManager static instance of record manager. + */ + public static function getInstance($self=null) + { + static $instance; + if($self!==null) + $instance=$self; + else if($instance===null) + $instance = new self; + return $instance; + } + + /** + * @return TActiveRecordGateway record gateway. + */ + public function getRecordGateway() + { + if($this->_gateway === null) { + $this->_gateway = $this->createRecordGateway(); + } + return $this->_gateway; + } + + /** + * @return TActiveRecordGateway default record gateway. + */ + protected function createRecordGateway() + { + return Prado::createComponent($this->getGatewayClass(), $this); + } + + /** + * Set implementation class of ActiveRecordGateway + * @param string $value + */ + public function setGatewayClass($value) + { + $this->_gateway = null; + $this->_gatewayClass = (string)$value; + } + + /** + * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + */ + public function getGatewayClass() + { + return $this->_gatewayClass; + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @since 3.1.5 + * @see setInvalidFinderResult + */ + public function getInvalidFinderResult() + { + return $this->_invalidFinderResult; + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * @param TActiveRecordInvalidFinderResult + * @since 3.1.5 + * @see getInvalidFinderResult + */ + public function setInvalidFinderResult($value) + { + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } +} diff --git a/gui/baculum/framework/Data/Common/Mssql/TMssqlCommandBuilder.php b/gui/baculum/framework/Data/Common/Mssql/TMssqlCommandBuilder.php new file mode 100644 index 0000000000..efee34f939 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mssql/TMssqlCommandBuilder.php @@ -0,0 +1,173 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + */ + +Prado::using('System.Data.Common.TDbCommandBuilder'); + +/** + * TMssqlCommandBuilder provides specifics methods to create limit/offset query commands + * for MSSQL servers. + * + * @author Wei Zhuo + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TMssqlCommandBuilder extends TDbCommandBuilder +{ + /** + * Overrides parent implementation. Uses "SELECT @@Identity". + * @return integer last insert id, null if none is found. + */ + public function getLastInsertID() + { + foreach($this->getTableInfo()->getColumns() as $column) + { + if($column->hasSequence()) + { + $command = $this->getDbConnection()->createCommand('SELECT @@Identity'); + return intval($command->queryScalar()); + } + } + } + + /** + * Overrides parent implementation. Alters the sql to apply $limit and $offset. + * The idea for limit with offset is done by modifying the sql on the fly + * with numerous assumptions on the structure of the sql string. + * The modification is done with reference to the notes from + * http://troels.arvin.dk/db/rdbms/#select-limit-offset + * + * + * SELECT * FROM ( + * SELECT TOP n * FROM ( + * SELECT TOP z columns -- (z=n+skip) + * FROM tablename + * ORDER BY key ASC + * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything) + * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything) + * + * + * Regular expressions are used to alter the SQL query. The resulting SQL query + * may be malformed for complex queries. The following restrictions apply + * + *
      + *
    • + * In particular, commas should NOT + * be used as part of the ordering expression or identifier. Commas must only be + * used for separating the ordering clauses. + *
    • + *
    • + * In the ORDER BY clause, the column name should NOT be be qualified + * with a table name or view name. Alias the column names or use column index. + *
    • + *
    • + * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses. + *
    • + *
    + * + * @param string SQL query string. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with limit and offset. + */ + public function applyLimitOffset($sql, $limit=-1, $offset=-1) + { + $limit = $limit!==null ? intval($limit) : -1; + $offset = $offset!==null ? intval($offset) : -1; + if ($limit > 0 && $offset <= 0) //just limit + $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql); + else if($limit > 0 && $offset > 0) + $sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset); + return $sql; + } + + /** + * Rewrite sql to apply $limit > and $offset > 0 for MSSQL database. + * See http://troels.arvin.dk/db/rdbms/#select-limit-offset + * @param string sql query + * @param integer $limit > 0 + * @param integer $offset > 0 + * @return sql modified sql query applied with limit and offset. + */ + protected function rewriteLimitOffsetSql($sql, $limit, $offset) + { + $fetch = $limit+$offset; + $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql); + $ordering = $this->findOrdering($sql); + + $orginalOrdering = $this->joinOrdering($ordering); + $reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering)); + $sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner top table__] {$reverseOrdering}) as [__outer top table__] {$orginalOrdering}"; + return $sql; + } + + /** + * Base on simplified syntax http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx + * + * @param string $sql + * @return array ordering expression as key and ordering direction as value + */ + protected function findOrdering($sql) + { + if(!preg_match('/ORDER BY/i', $sql)) + return array(); + $matches=array(); + $ordering=array(); + preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches); + if(count($matches)>1 && count($matches[2]) > 0) + { + $parts = explode(',', $matches[2][0]); + foreach($parts as $part) + { + $subs=array(); + if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs)) + { + if(count($subs) > 1 && count($subs[2]) > 0) + { + $ordering[$subs[1][0]] = $subs[2][0]; + } + //else what? + } + else + $ordering[trim($part)] = 'ASC'; + } + } + return $ordering; + } + + /** + * @param array ordering obtained from findOrdering() + * @return string concat the orderings + */ + protected function joinOrdering($orders) + { + if(count($orders)>0) + { + $str=array(); + foreach($orders as $column => $direction) + $str[] = $column.' '.$direction; + return 'ORDER BY '.implode(', ', $str); + } + } + + /** + * @param array original ordering + * @return array ordering with reversed direction. + */ + protected function reverseDirection($orders) + { + foreach($orders as $column => $direction) + $orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC'; + return $orders; + } +} + diff --git a/gui/baculum/framework/Data/Common/Mssql/TMssqlMetaData.php b/gui/baculum/framework/Data/Common/Mssql/TMssqlMetaData.php new file mode 100644 index 0000000000..3ff7ac7f61 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mssql/TMssqlMetaData.php @@ -0,0 +1,264 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPgsqlMetaData.php 1866 2007-04-14 05:02:29Z wei $ + * @package System.Data.Common.Mssql + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Mssql.TMssqlTableInfo'); + +/** + * TMssqlMetaData loads MSSQL database table and column information. + * + * @author Wei Zhuo + * @version $Id: TPgsqlMetaData.php 1866 2007-04-14 05:02:29Z wei $ + * @package System.Data.Common.Mssql + * @since 3.1 + */ +class TMssqlMetaData extends TDbMetaData +{ + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TMssqlTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, '[', ']'); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '[', ']'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '"', '"'); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TMssqlTableInfo table information. + */ + protected function createTableInfo($table) + { + list($catalogName,$schemaName,$tableName) = $this->getCatalogSchemaTableName($table); + $this->getDbConnection()->setActive(true); + $sql = <<getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + if($schemaName!==null) + $command->bindValue(':schema', $schemaName); + if($catalogName!==null) + $command->bindValue(':catalog', $catalogName); + + $tableInfo=null; + foreach($command->query() as $col) + { + if($tableInfo===null) + $tableInfo = $this->createNewTableInfo($col); + $this->processColumn($tableInfo,$col); + } + if($tableInfo===null) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @param string table name + * @return array tuple($catalogName,$schemaName,$tableName) + */ + protected function getCatalogSchemaTableName($table) + { + //remove possible delimiters + $result = explode('.', preg_replace('/\[|\]|"/', '', $table)); + if(count($result)===1) + return array(null,null,$result[0]); + if(count($result)===2) + return array(null,$result[0],$result[1]); + if(count($result)>2) + return array($result[0],$result[1],$result[2]); + } + + /** + * @param TMssqlTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $columnId = $col['COLUMN_NAME']; + + $info['ColumnName'] = "[$columnId]"; //quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = intval($col['ORDINAL_POSITION'])-1; //zero-based index + if($col['IS_NULLABLE']!=='NO') + $info['AllowNull'] = true; + if($col['COLUMN_DEFAULT']!==null) + $info['DefaultValue'] = $col['COLUMN_DEFAULT']; + + if(in_array($columnId, $tableInfo->getPrimaryKeys())) + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) + $info['IsForeignKey'] = true; + + if($col['IsIdentity']==='1') + $info['AutoIncrement'] = true; + $info['DbType'] = $col['DATA_TYPE']; + if($col['CHARACTER_MAXIMUM_LENGTH']!==null) + $info['ColumnSize'] = intval($col['CHARACTER_MAXIMUM_LENGTH']); + if($col['NUMERIC_PRECISION'] !== null) + $info['NumericPrecision'] = intval($col['NUMERIC_PRECISION']); + if($col['NUMERIC_SCALE']!==null) + $info['NumericScale'] = intval($col['NUMERIC_SCALE']); + $tableInfo->Columns[$columnId] = new TMssqlTableColumn($info); + } + + /** + * @param string table schema name + * @param string table name. + * @return TMssqlTableInfo + */ + protected function createNewTableInfo($col) + { + $info['CatalogName'] = $col['TABLE_CATALOG']; + $info['SchemaName'] = $col['TABLE_SCHEMA']; + $info['TableName'] = $col['TABLE_NAME']; + if($col['TABLE_TYPE']==='VIEW') + $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($col); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * Gets the primary and foreign key column details for the given table. + * @param string schema name + * @param string table name. + * @return array tuple ($primary, $foreign) + */ + protected function getConstraintKeys($col) + { + $sql = <<getDbConnection()->createCommand($sql); + $command->bindValue(':table', $col['TABLE_NAME']); + $primary = array(); + foreach($command->query()->readAll() as $field) + $primary[] = $field['field_name']; + $foreign = $this->getForeignConstraints($col); + return array($primary,$foreign); + } + + /** + * Gets foreign relationship constraint keys and table name + * @param string database name + * @param string table name + * @return array foreign relationship table name and keys. + */ + protected function getForeignConstraints($col) + { + //From http://msdn2.microsoft.com/en-us/library/aa175805(SQL.80).aspx + $sql = <<getDbConnection()->createCommand($sql); + $command->bindValue(':table', $col['TABLE_NAME']); + $fkeys=array(); + $catalogSchema = "[{$col['TABLE_CATALOG']}].[{$col['TABLE_SCHEMA']}]"; + foreach($command->query() as $info) + { + $fkeys[$info['FK_CONSTRAINT_NAME']]['keys'][$info['FK_COLUMN_NAME']] = $info['UQ_COLUMN_NAME']; + $fkeys[$info['FK_CONSTRAINT_NAME']]['table'] = $info['UQ_TABLE_NAME']; + } + return count($fkeys) > 0 ? array_values($fkeys) : $fkeys; + } + + /** + * @param string column name. + * @param TPgsqlTableInfo table information. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $tableInfo) + { + foreach($tableInfo->getForeignKeys() as $fk) + { + if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + return false; + } +} + diff --git a/gui/baculum/framework/Data/Common/Mssql/TMssqlTableColumn.php b/gui/baculum/framework/Data/Common/Mssql/TMssqlTableColumn.php new file mode 100644 index 0000000000..7976c28ad1 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mssql/TMssqlTableColumn.php @@ -0,0 +1,64 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common.Mssql + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a Mssql database table. + * + * @author Wei Zhuo + * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common.Mssql + * @since 3.1 + */ +class TMssqlTableColumn extends TDbTableColumn +{ + private static $types = array(); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + + return 'string'; + } + + /** + * @return boolean true if the column has identity (auto-increment) + */ + public function getAutoIncrement() + { + return $this->getInfo('AutoIncrement',false); + } + + /** + * @return boolean true if auto increments. + */ + public function hasSequence() + { + return $this->getAutoIncrement(); + } + + /** + * @return boolean true if db type is 'timestamp'. + */ + public function getIsExcluded() + { + return strtolower($this->getDbType())==='timestamp'; + } +} + diff --git a/gui/baculum/framework/Data/Common/Mssql/TMssqlTableInfo.php b/gui/baculum/framework/Data/Common/Mssql/TMssqlTableInfo.php new file mode 100644 index 0000000000..0db446b1c1 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mssql/TMssqlTableInfo.php @@ -0,0 +1,64 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Mssql + */ + +/** + * Loads the base TDbTableInfo class and TMssqlTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Mssql.TMssqlTableColumn'); + +/** + * TMssqlTableInfo class provides additional table information for Mssql database. + * + * @author Wei Zhuo + * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Mssql + * @since 3.1 + */ +class TMssqlTableInfo extends TDbTableInfo +{ + /** + * @return string name of the schema this column belongs to. + */ + public function getSchemaName() + { + return $this->getInfo('SchemaName'); + } + + /** + * @return string catalog name (database name) + */ + public function getCatalogName() + { + return $this->getInfo('CatalogName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + //MSSQL alway returns the catalog, schem and table names. + return '['.$this->getCatalogName().'].['.$this->getSchemaName().'].['.$this->getTableName().']'; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Mssql.TMssqlCommandBuilder'); + return new TMssqlCommandBuilder($connection,$this); + } +} + diff --git a/gui/baculum/framework/Data/Common/Mysql/TMysqlCommandBuilder.php b/gui/baculum/framework/Data/Common/Mysql/TMysqlCommandBuilder.php new file mode 100644 index 0000000000..8492c53727 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mysql/TMysqlCommandBuilder.php @@ -0,0 +1,26 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + */ + +Prado::using('System.Data.Common.TDbCommandBuilder'); + +/** + * TMysqlCommandBuilder implements default TDbCommandBuilder + * + * @author Wei Zhuo + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TMysqlCommandBuilder extends TDbCommandBuilder +{ +} + diff --git a/gui/baculum/framework/Data/Common/Mysql/TMysqlMetaData.php b/gui/baculum/framework/Data/Common/Mysql/TMysqlMetaData.php new file mode 100644 index 0000000000..e380075890 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mysql/TMysqlMetaData.php @@ -0,0 +1,386 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMysqlMetaData.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Mysql + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Mysql.TMysqlTableInfo'); + +/** + * TMysqlMetaData loads Mysql version 4.1.x and 5.x database table and column information. + * + * For Mysql version 4.1.x, PHP 5.1.3 or later is required. + * See http://netevil.org/node.php?nid=795&SC=1 + * + * @author Wei Zhuo + * @version $Id: TMysqlMetaData.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Mysql + * @since 3.1 + */ +class TMysqlMetaData extends TDbMetaData +{ + private $_serverVersion=0; + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TMysqlTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, '`', '`'); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '`', '`'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '`', '`'); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TMysqlTableInfo table information. + */ + protected function createTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + $find = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`"; + $this->getDbConnection()->setActive(true); + $sql = "SHOW FULL FIELDS FROM {$find}"; + $command = $this->getDbConnection()->createCommand($sql); + $tableInfo = $this->createNewTableInfo($table); + $index=0; + foreach($command->query() as $col) + { + $col['index'] = $index++; + $this->processColumn($tableInfo,$col); + } + if($index===0) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @return float server version. + */ + protected function getServerVersion() + { + if(!$this->_serverVersion) + { + $version = $this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION); + $digits=array(); + preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits); + $this->_serverVersion=floatval($digits[1].'.'.$digits[2].$digits[3]); + } + return $this->_serverVersion; + } + + /** + * @param TMysqlTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $columnId = $col['field']; + + $info['ColumnName'] = "`$columnId`"; //quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = $col['index']; + if($col['null']==='YES') + $info['AllowNull'] = true; + if(is_int(strpos(strtolower($col['extra']), 'auto_increment'))) + $info['AutoIncrement']=true; + if($col['default']!=="") + $info['DefaultValue'] = $col['default']; + + if($col['key']==='PRI' || in_array($columnId, $tableInfo->getPrimaryKeys())) + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) + $info['IsForeignKey'] = true; + + $info['DbType'] = $col['type']; + $match=array(); + //find SET/ENUM values, column size, precision, and scale + if(preg_match('/\((.*)\)/', $col['type'], $match)) + { + $info['DbType']= preg_replace('/\(.*\)/', '', $col['type']); + + //find SET/ENUM values + if($this->isEnumSetType($info['DbType'])) + $info['DbTypeValues'] = preg_split("/[',]/S", $match[1], -1, PREG_SPLIT_NO_EMPTY); + + //find column size, precision and scale + $pscale = array(); + if(preg_match('/(\d+)(?:,(\d+))?+/', $match[1], $pscale)) + { + if($this->isPrecisionType($info['DbType'])) + { + $info['NumericPrecision'] = intval($pscale[1]); + if(count($pscale) > 2) + $info['NumericScale'] = intval($pscale[2]); + } + else + $info['ColumnSize'] = intval($pscale[1]); + } + } + + $tableInfo->Columns[$columnId] = new TMysqlTableColumn($info); + } + + /** + * @return boolean true if column type if "numeric", "interval" or begins with "time". + */ + protected function isPrecisionType($type) + { + $type = strtolower(trim($type)); + return $type==='decimal' || $type==='dec' + || $type==='float' || $type==='double' + || $type==='double precision' || $type==='real'; + } + + /** + * @return boolean true if column type if "enum" or "set". + */ + protected function isEnumSetType($type) + { + $type = strtolower(trim($type)); + return $type==='set' || $type==='enum'; + } + + /** + * @param string table name, may be quoted with back-ticks and may contain database name. + * @return array tuple ($schema,$table), $schema may be null. + * @throws TDbException when table name contains invalid identifier bytes. + */ + protected function getSchemaTableName($table) + { + //remove the back ticks and separate out the "database.table" + $result = explode('.', str_replace('`', '', $table)); + foreach($result as $name) + { + if(!$this->isValidIdentifier($name)) + { + $ref = 'http://dev.mysql.com/doc/refman/5.0/en/identifiers.html'; + throw new TDbException('dbcommon_invalid_identifier_name', $table, $ref); + } + } + return count($result) > 1 ? $result : array(null, $result[0]); + } + + /** + * http://dev.mysql.com/doc/refman/5.0/en/identifiers.html + * @param string identifier name + * @param boolean true if valid identifier. + */ + protected function isValidIdentifier($name) + { + return !preg_match('#/|\\|.|\x00|\xFF#', $name); + } + + /** + * @param string table schema name + * @param string table name. + * @return TMysqlTableInfo + */ + protected function createNewTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + $info['SchemaName'] = $schemaName; + $info['TableName'] = $tableName; + if($this->getIsView($schemaName,$tableName)) + $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * For MySQL version 5.0.1 or later we can use SHOW FULL TABLES + * http://dev.mysql.com/doc/refman/5.0/en/show-tables.html + * + * For MySQL version 5.0.1 or ealier, this always return false. + * @param string database name, null to use default connection database. + * @param string table or view name. + * @return boolean true if is view, false otherwise. + * @throws TDbException if table or view does not exist. + */ + protected function getIsView($schemaName,$tableName) + { + if($this->getServerVersion()<5.01) + return false; + if($schemaName!==null) + $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table"; + else + $sql = "SHOW FULL TABLES LIKE :table"; + + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + try + { + return count($result = $command->queryRow()) > 0 && $result['table_type']==='VIEW'; + } + catch(TDbException $e) + { + $table = $schemaName===null?$tableName:$schemaName.'.'.$tableName; + throw new TDbException('dbcommon_invalid_table_name',$table,$e->getMessage()); + } + } + + /** + * Gets the primary and foreign key column details for the given table. + * @param string schema name + * @param string table name. + * @return array tuple ($primary, $foreign) + */ + protected function getConstraintKeys($schemaName, $tableName) + { + $table = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`"; + $sql = "SHOW INDEX FROM {$table}"; + $command = $this->getDbConnection()->createCommand($sql); + $primary = array(); + foreach($command->query() as $row) + { + if($row['key_name']==='PRIMARY') + $primary[] = $row['column_name']; + } + // MySQL version was increased to >=5.1.21 instead of 5.x + // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588) + if($this->getServerVersion() >= 5.121) + $foreign = $this->getForeignConstraints($schemaName,$tableName); + else + $foreign = $this->findForeignConstraints($schemaName,$tableName); + return array($primary,$foreign); + } + + /** + * Gets foreign relationship constraint keys and table name + * @param string database name + * @param string table name + * @return array foreign relationship table name and keys. + */ + protected function getForeignConstraints($schemaName, $tableName) + { + $andSchema = $schemaName !== null ? 'AND TABLE_SCHEMA LIKE :schema' : 'AND TABLE_SCHEMA LIKE DATABASE()'; + $sql = <<getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + if($schemaName!==null) + $command->bindValue(':schema', $schemaName); + $fkeys=array(); + foreach($command->query() as $col) + { + $fkeys[$col['con']]['keys'][$col['col']] = $col['fkcol']; + $fkeys[$col['con']]['table'] = $col['fktable']; + } + return count($fkeys) > 0 ? array_values($fkeys) : $fkeys; + } + + /** + * @param string database name + * @param string table name + * @return string SQL command to create the table. + * @throws TDbException if PHP version is less than 5.1.3 + */ + protected function getShowCreateTable($schemaName, $tableName) + { + if(version_compare(PHP_VERSION,'5.1.3','<')) + throw new TDbException('dbmetadata_requires_php_version', 'Mysql 4.1.x', '5.1.3'); + + //See http://netevil.org/node.php?nid=795&SC=1 + $this->getDbConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + if($schemaName!==null) + $sql = "SHOW CREATE TABLE `{$schemaName}`.`{$tableName}`"; + else + $sql = "SHOW CREATE TABLE `{$tableName}`"; + $command = $this->getDbConnection()->createCommand($sql); + $result = $command->queryRow(); + return isset($result['Create Table']) ? $result['Create Table'] : (isset($result['Create View']) ? $result['Create View'] : ''); + } + + /** + * Extract foreign key constraints by extracting the contraints from SHOW CREATE TABLE result. + * @param string database name + * @param string table name + * @return array foreign relationship table name and keys. + */ + protected function findForeignConstraints($schemaName, $tableName) + { + $sql = $this->getShowCreateTable($schemaName, $tableName); + $matches =array(); + $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+`?([^`]+)`?\s\(([^\)]+)\)/mi'; + preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER); + $foreign = array(); + foreach($matches as $match) + { + $fields = array_map('trim',explode(',',str_replace('`','',$match[1]))); + $fk_fields = array_map('trim',explode(',',str_replace('`','',$match[3]))); + $keys=array(); + foreach($fields as $k=>$v) + $keys[$v] = $fk_fields[$k]; + $foreign[] = array('keys' => $keys, 'table' => trim($match[2])); + } + return $foreign; + } + + /** + * @param string column name. + * @param TPgsqlTableInfo table information. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $tableInfo) + { + foreach($tableInfo->getForeignKeys() as $fk) + { + if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + return false; + } +} + diff --git a/gui/baculum/framework/Data/Common/Mysql/TMysqlTableColumn.php b/gui/baculum/framework/Data/Common/Mysql/TMysqlTableColumn.php new file mode 100644 index 0000000000..6f48253785 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mysql/TMysqlTableColumn.php @@ -0,0 +1,72 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMysqlTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Mysql + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a Mysql database table. + * + * @author Wei Zhuo + * @version $Id: TMysqlTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Mysql + * @since 3.1 + */ +class TMysqlTableColumn extends TDbTableColumn +{ + private static $types = array( + 'integer' => array('bit', 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'), + 'boolean' => array('boolean', 'bool'), + 'float' => array('float', 'double', 'double precision', 'decimal', 'dec', 'numeric', 'fixed') + ); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($this->getDbType()))); + if($dbtype==='tinyint' && $this->getColumnSize()===1) + return 'boolean'; + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } + + /** + * @return boolean true if column will auto-increment when the column value is inserted as null. + */ + public function getAutoIncrement() + { + return $this->getInfo('AutoIncrement', false); + } + + /** + * @return boolean true if auto increment is true. + */ + public function hasSequence() + { + return $this->getAutoIncrement(); + } + + public function getDbTypeValues() + { + return $this->getInfo('DbTypeValues'); + } +} + diff --git a/gui/baculum/framework/Data/Common/Mysql/TMysqlTableInfo.php b/gui/baculum/framework/Data/Common/Mysql/TMysqlTableInfo.php new file mode 100644 index 0000000000..b190e9613d --- /dev/null +++ b/gui/baculum/framework/Data/Common/Mysql/TMysqlTableInfo.php @@ -0,0 +1,58 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMysqlTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Mysql + */ + +/** + * Loads the base TDbTableInfo class and TMysqlTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Mysql.TMysqlTableColumn'); + +/** + * TMysqlTableInfo class provides additional table information for MySQL database. + * + * @author Wei Zhuo + * @version $Id: TMysqlTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Mysql + * @since 3.1 + */ +class TMysqlTableInfo extends TDbTableInfo +{ + /** + * @return string name of the schema this column belongs to. + */ + public function getSchemaName() + { + return $this->getInfo('SchemaName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + if(($schema=$this->getSchemaName())!==null) + return '`'.$schema.'`.`'.$this->getTableName().'`'; + else + return '`'.$this->getTableName().'`'; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Mysql.TMysqlCommandBuilder'); + return new TMysqlCommandBuilder($connection,$this); + } +} + diff --git a/gui/baculum/framework/Data/Common/Oracle/TOracleCommandBuilder.php b/gui/baculum/framework/Data/Common/Oracle/TOracleCommandBuilder.php new file mode 100644 index 0000000000..26490d54e3 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Oracle/TOracleCommandBuilder.php @@ -0,0 +1,155 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TOracleCommandBuilder.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + */ + +Prado :: using('System.Data.Common.TDbCommandBuilder'); + +/** + * TOracleCommandBuilder provides specifics methods to create limit/offset query commands + * for Oracle database. + * + * @author Marcos Nobre + * @version $Id: TOracleCommandBuilder.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + * @since 3.1 + */ +class TOracleCommandBuilder extends TDbCommandBuilder { + + /** + * Overrides parent implementation. Only column of type text or character (and its variants) + * accepts the LIKE criteria. + * @param array list of column id for potential search condition. + * @param string string of keywords + * @return string SQL search condition matching on a set of columns. + */ + public function getSearchExpression($fields, $keywords) { + $columns = array (); + foreach ($fields as $field) { + if ($this->isSearchableColumn($this->getTableInfo()->getColumn($field))) + $columns[] = $field; + } + return parent :: getSearchExpression($columns, $keywords); + } + /** + * + * @return boolean true if column can be used for LIKE searching. + */ + protected function isSearchableColumn($column) { + $type = strtolower($column->getDbType()); + return $type === 'character varying' || $type === 'varchar2' || $type === 'character' || $type === 'char' || $type === 'text'; + } + + /** + * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive). + * @param string column name. + * @param array keywords + * @return string search condition for all words in one column. + */ + /* + * + * how Oracle don't implements ILIKE, this method won't be overrided + * + protected function getSearchCondition($column, $words) + { + $conditions=array(); + foreach($words as $word) + $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%'); + return '('.implode(' AND ', $conditions).')'; + } + */ + + /** + * Overrides parent implementation to use Oracle way of get paginated RecordSet instead of using LIMIT sql clause. + * @param string SQL query string. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with limit and offset in Oracle way. + */ + public function applyLimitOffset($sql, $limit = -1, $offset = -1) { + if ((int) $limit <= 0 && (int) $offset <= 0) + return $sql; + + $pradoNUMLIN = 'pradoNUMLIN'; + $fieldsALIAS = 'xyz'; + + $nfimDaSQL = strlen($sql); + $nfimDoWhere = (strpos($sql, 'ORDER') !== false ? strpos($sql, 'ORDER') : $nfimDaSQL); + $niniDoSelect = strpos($sql, 'SELECT') + 6; + $nfimDoSelect = (strpos($sql, 'FROM') !== false ? strpos($sql, 'FROM') : $nfimDaSQL); + + $WhereInSubSelect=""; + if(strpos($sql, 'WHERE')!==false) + $WhereInSubSelect = "WHERE " .substr($sql, strpos($sql, 'WHERE')+5, $nfimDoWhere - $niniDoWhere); + + $sORDERBY = ''; + if (stripos($sql, 'ORDER') !== false) { + $p = stripos($sql, 'ORDER'); + $sORDERBY = substr($sql, $p +8); + } + + $fields = substr($sql, 0, $nfimDoSelect); + $fields = trim(substr($fields, $niniDoSelect)); + $aliasedFields = ', '; + + if (trim($fields) == '*') { + $aliasedFields = ", {$fieldsALIAS}.{$fields}"; + $fields = ''; + $arr = $this->getTableInfo()->getColumns(); + foreach ($arr as $field) { + $fields .= strtolower($field->getColumnName()) . ', '; + } + $fields = str_replace('"', '', $fields); + $fields = trim($fields); + $fields = substr($fields, 0, strlen($fields) - 1); + } else { + if (strpos($fields, ',') !== false) { + $arr = $this->getTableInfo()->getColumns(); + foreach ($arr as $field) { + $field = strtolower($field); + $existAS = str_ireplace(' as ', '-as-', $field); + if (strpos($existAS, '-as-') === false) + $aliasedFields .= "{$fieldsALIAS}." . trim($field) . ", "; + else + $aliasedFields .= "{$field}, "; + } + $aliasedFields = trim($aliasedFields); + $aliasedFields = substr($aliasedFields, 0, strlen($aliasedFields) - 1); + } + } + if ($aliasedFields == ', ') + $aliasedFields = " , $fieldsALIAS.* "; + + /* ************************ + $newSql = " SELECT $fields FROM ". + "( ". + " SELECT rownum as {$pradoNUMLIN} {$aliasedFields} FROM ". + " ($sql) {$fieldsALIAS} WHERE rownum <= {$limit} ". + ") WHERE {$pradoNUMLIN} >= {$offset} "; + + ************************* */ + $offset=(int)$offset; + $toReg = $offset + $limit ; + $fullTableName = $this->getTableInfo()->getTableFullName(); + if (empty ($sORDERBY)) + $sORDERBY="ROWNUM"; + + $newSql = " SELECT $fields FROM " . + "( " . + " SELECT ROW_NUMBER() OVER ( ORDER BY {$sORDERBY} ) -1 as {$pradoNUMLIN} {$aliasedFields} " . + " FROM {$fullTableName} {$fieldsALIAS} $WhereInSubSelect" . + ") nn " . + " WHERE nn.{$pradoNUMLIN} >= {$offset} AND nn.{$pradoNUMLIN} < {$toReg} "; + //echo $newSql."\n
    \n"; + return $newSql; + } + +} diff --git a/gui/baculum/framework/Data/Common/Oracle/TOracleMetaData.php b/gui/baculum/framework/Data/Common/Oracle/TOracleMetaData.php new file mode 100644 index 0000000000..793070eda0 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Oracle/TOracleMetaData.php @@ -0,0 +1,340 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TOracleMetaData.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Oracle + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Oracle.TOracleTableInfo'); +Prado::using('System.Data.Common.Oracle.TOracleTableColumn'); + +/** + * TOracleMetaData loads Oracle database table and column information. + * + * @author Marcos Nobre + * @version $Id: TOracleMetaData.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Oracle + * @since 3.1 + */ +class TOracleMetaData extends TDbMetaData +{ + private $_defaultSchema = 'system'; + + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TOracleTableInfo'; + } + + /** + * @param string default schema. + */ + public function setDefaultSchema($schema) + { + $this->_defaultSchema=$schema; + } + + /** + * @return string default schema. + */ + public function getDefaultSchema() + { + return $this->_defaultSchema; + } + + /** + * @param string table name with optional schema name prefix, uses default schema name prefix is not provided. + * @return array tuple as ($schemaName,$tableName) + */ + protected function getSchemaTableName($table) + { + if(count($parts= explode('.', str_replace('"','',$table))) > 1) + return array($parts[0], $parts[1]); + else + return array($this->getDefaultSchema(),$parts[0]); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TOracleTableInfo table information. + */ + protected function createTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + + // This query is made much more complex by the addition of the 'attisserial' field. + // The subquery to get that field checks to see if there is an internally dependent + // sequence on the field. + $sql = +<<getDbConnection()->setActive(true); + $this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $command = $this->getDbConnection()->createCommand($sql); + //$command->bindValue(':table', $tableName); + //$command->bindValue(':schema', $schemaName); + $tableInfo = $this->createNewTableInfo($schemaName, $tableName); + $index=0; + foreach($command->query() as $col) + { + $col['index'] = $index++; + $this->processColumn($tableInfo, $col); + } + if($index===0) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @param string table schema name + * @param string table name. + * @return TOracleTableInfo + */ + protected function createNewTableInfo($schemaName,$tableName) + { + $info['SchemaName'] = $this->assertIdentifier($schemaName); + $info['TableName'] = $this->assertIdentifier($tableName); + $info['IsView'] = false; + if($this->getIsView($schemaName,$tableName)) $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * @param string table name, schema name or column name. + * @return string a valid identifier. + * @throws TDbException when table name contains a double quote ("). + */ + protected function assertIdentifier($name) + { + if(strpos($name, '"')!==false) + { + $ref = 'http://www.oracle.com'; + throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref); + } + return $name; + } + + /** + * @param string table schema name + * @param string table name. + * @return boolean true if the table is a view. + */ + protected function getIsView($schemaName,$tableName) + { + $this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $sql = +<<getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + //$command->bindValue(':schema',$schemaName); + //$command->bindValue(':table', $tableName); + return intval($command->queryScalar() === 'VIEW'); + } + + /** + * @param TOracleTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $columnId = strtolower($col['attname']); //use column name as column Id + + //$info['ColumnName'] = '"'.$columnId.'"'; //quote the column names! + $info['ColumnName'] = $columnId; //NOT quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = $col['index']; + if(! (bool)$col['attnotnull'] ) $info['AllowNull'] = true; + if(in_array($columnId, $tableInfo->getPrimaryKeys())) $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) $info['IsForeignKey'] = true; + if( (int)$col['atttypmod'] > 0 ) $info['ColumnSize'] = $col['atttypmod']; // - 4; + if( (bool)$col['atthasdef'] ) $info['DefaultValue'] = $col['adsrc']; + // + // For a while Oracle Tables has no associated AutoIncrement Triggers + // + /* + if( $col['attisserial'] ) + { + if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null) + { + $info['SequenceName'] = $sequence; + unset($info['DefaultValue']); + } + } + */ + $matches = array(); + if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches)) + { + $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']); + if($this->isPrecisionType($info['DbType'])) + { + $info['NumericPrecision'] = intval($matches[1]); + if(count($matches) > 2) + $info['NumericScale'] = intval($matches[2]); + } + else + $info['ColumnSize'] = intval($matches[1]); + } + else + $info['DbType'] = $col['type']; + $tableInfo->Columns[$columnId] = new TOracleTableColumn($info); + } + + /** + * @return string serial name if found, null otherwise. + */ + protected function getSequenceName($tableInfo,$src) + { + $matches = array(); + if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches)) + { + if(is_int(strpos($matches[1], '.'))) + return $matches[1]; + else + return $tableInfo->getSchemaName().'.'.$matches[1]; + } + } + + /** + * @return boolean true if column type if "numeric", "interval" or begins with "time". + */ + protected function isPrecisionType($type) + { + $type = strtolower(trim($type)); + return $type==='number'; // || $type==='interval' || strpos($type, 'time')===0; + } + + /** + * Gets the primary and foreign key column details for the given table. + * @param string schema name + * @param string table name. + * @return array tuple ($primary, $foreign) + */ + protected function getConstraintKeys($schemaName, $tableName) + { + $this->getDbConnection()->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); +// select decode( a.CONSTRAINT_TYPE, 'P', 'PRIMARY KEY (', 'FOREIGN KEY (' )||b.COLUMN_NAME||')' as consrc, + $sql = +<<getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + //$command->bindValue(':table', $tableName); + //$command->bindValue(':schema', $schemaName); + $primary = array(); + $foreign = array(); + foreach($command->query() as $row) + { + switch( strtolower( $row['contype'] ) ) + { + case 'p': + $primary = array_merge( $primary, array(strtolower( $row['consrc'] )) ); + /* + $arr = $this->getPrimaryKeys($row['consrc']); + $primary = array_merge( $primary, array(strtolower( $arr[0] )) ); + */ + break; + case 'r': + $foreign = array_merge( $foreign, array(strtolower( $row['consrc'] )) ); + /* + // if(($fkey = $this->getForeignKeys($row['consrc']))!==null) + $fkey = $this->getForeignKeys( $row['consrc'] ); + $foreign = array_merge( $foreign, array(strtolower( $fkey )) ); + */ + break; + } + } + return array($primary,$foreign); + } + + /** + * Gets the primary key field names + * @param string Oracle primary key definition + * @return array primary key field names. + */ + protected function getPrimaryKeys($src) + { + $matches = array(); + if(preg_match('/PRIMARY\s+KEY\s+\(([^\)]+)\)/i', $src, $matches)) + return preg_split('/,\s+/',$matches[1]); + return array(); + } + + /** + * Gets foreign relationship constraint keys and table name + * @param string Oracle foreign key definition + * @return array foreign relationship table name and keys, null otherwise + */ + protected function getForeignKeys($src) + { + $matches = array(); + $brackets = '\(([^\)]+)\)'; + $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i"; + if(preg_match($find, $src, $matches)) + { + $keys = preg_split('/,\s+/', $matches[1]); + $fkeys = array(); + foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey) + $fkeys[$keys[$i]] = $fkey; + return array('table' => str_replace('"','',$matches[2]), 'keys' => $fkeys); + } + } + + /** + * @param string column name. + * @param TOracleTableInfo table information. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $tableInfo) + { + foreach($tableInfo->getForeignKeys() as $fk) + { + if( $fk==$columnId ) + //if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + return false; + } +} + diff --git a/gui/baculum/framework/Data/Common/Oracle/TOracleTableColumn.php b/gui/baculum/framework/Data/Common/Oracle/TOracleTableColumn.php new file mode 100644 index 0000000000..bbd7212cba --- /dev/null +++ b/gui/baculum/framework/Data/Common/Oracle/TOracleTableColumn.php @@ -0,0 +1,50 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TOracleTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Oracle + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a PostgreSQL database table. + * + * @author Marcos Nobre + * @version $Id: TOracleTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Oracle + * @since 3.1 + */ +class TOracleTableColumn extends TDbTableColumn +{ + private static $types=array( + 'numeric' => array( 'numeric' ) +// 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'), +// 'boolean' => array('boolean'), +// 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric') + ); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = strtolower($this->getDbType()); + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } +} + diff --git a/gui/baculum/framework/Data/Common/Oracle/TOracleTableInfo.php b/gui/baculum/framework/Data/Common/Oracle/TOracleTableInfo.php new file mode 100644 index 0000000000..6aa31fd873 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Oracle/TOracleTableInfo.php @@ -0,0 +1,158 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TOracleTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + */ + +/** + * TDbTableInfo class describes the meta data of a database table. + * + * @author Wei Zhuo + * @version $Id: TOracleTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + * @since 3.1 + */ +class TOracleTableInfo extends TComponent +{ + private $_info=array(); + + private $_primaryKeys; + private $_foreignKeys; + + private $_columns; + + private $_lowercase; + + /** + * Sets the database table meta data information. + * @param array table column information. + */ + public function __construct($tableInfo=array(),$primary=array(),$foreign=array()) + { + $this->_info=$tableInfo; + $this->_primaryKeys=$primary; + $this->_foreignKeys=$foreign; + $this->_columns=new TMap; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Oracle.TOracleCommandBuilder'); + return new TOracleCommandBuilder($connection,$this); + } + + /** + * @param string information array key name + * @param mixed default value if information array value is null + * @return mixed information array value. + */ + public function getInfo($name,$default=null) + { + return isset($this->_info[$name]) ? $this->_info[$name] : $default; + } + + /** + * @param string information array key name + * @param mixed new information array value. + */ + protected function setInfo($name,$value) + { + $this->_info[$name]=$value; + } + + /** + * @return string name of the table this column belongs to. + */ + public function getTableName() + { + return $this->getInfo('TableName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return $this->_info['SchemaName'].'.'.$this->getTableName(); + } + + /** + * @return boolean whether the table is a view, default is false. + */ + public function getIsView() + { + return $this->getInfo('IsView',false); + } + + /** + * @return TMap TDbTableColumn column meta data. + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @param string column id + * @return TDbTableColumn column information. + */ + public function getColumn($name) + { + if(($column = $this->_columns->itemAt($name))!==null) + return $column; + throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName()); + } + + /** + * @param array list of column Id, empty to get all columns. + * @return array table column names (identifier quoted) + */ + public function getColumnNames() + { + foreach($this->getColumns() as $column) + $names[] = $column->getColumnName(); + return $names; + } + + /** + * @return string[] names of primary key columns. + */ + public function getPrimaryKeys() + { + return $this->_primaryKeys; + } + + /** + * @return array tuples of foreign table and column name. + */ + public function getForeignKeys() + { + return $this->_foreignKeys; + } + + /** + * @return array lowercased column key names mapped to normal column ids. + */ + public function getLowerCaseColumnNames() + { + if($this->_lowercase===null) + { + $this->_lowercase=array(); + foreach($this->getColumns()->getKeys() as $key) + $this->_lowercase[strtolower($key)] = $key; + } + return $this->_lowercase; + } +} + diff --git a/gui/baculum/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php new file mode 100644 index 0000000000..eb975a1a29 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php @@ -0,0 +1,69 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + */ + +Prado::using('System.Data.Common.TDbCommandBuilder'); + +/** + * TPgsqlCommandBuilder provides specifics methods to create limit/offset query commands + * for Pgsql database. + * + * @author Wei Zhuo + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TPgsqlCommandBuilder extends TDbCommandBuilder +{ + /** + * Overrides parent implementation. Only column of type text or character (and its variants) + * accepts the LIKE criteria. + * @param array list of column id for potential search condition. + * @param string string of keywords + * @return string SQL search condition matching on a set of columns. + */ + public function getSearchExpression($fields, $keywords) + { + $columns = array(); + foreach($fields as $field) + { + if($this->isSearchableColumn($this->getTableInfo()->getColumn($field))) + $columns[] = $field; + } + return parent::getSearchExpression($columns, $keywords); + } + /** + * + * @return boolean true if column can be used for LIKE searching. + */ + protected function isSearchableColumn($column) + { + $type = strtolower($column->getDbType()); + return $type === 'character varying' || $type === 'varchar' || + $type === 'character' || $type === 'char' || $type === 'text'; + } + + /** + * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive). + * @param string column name. + * @param array keywords + * @return string search condition for all words in one column. + */ + protected function getSearchCondition($column, $words) + { + $conditions=array(); + foreach($words as $word) + $conditions[] = $column.' ILIKE '.$this->getDbConnection()->quoteString('%'.$word.'%'); + return '('.implode(' AND ', $conditions).')'; + } + +} + diff --git a/gui/baculum/framework/Data/Common/Pgsql/TPgsqlMetaData.php b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlMetaData.php new file mode 100644 index 0000000000..dd91dfdc20 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlMetaData.php @@ -0,0 +1,422 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPgsqlMetaData.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Pgsql + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Pgsql.TPgsqlTableInfo'); + +/** + * TPgsqlMetaData loads PostgreSQL database table and column information. + * + * @author Wei Zhuo + * @version $Id: TPgsqlMetaData.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Pgsql + * @since 3.1 + */ +class TPgsqlMetaData extends TDbMetaData +{ + private $_defaultSchema = 'public'; + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TPgsqlTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, '"', '"'); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '"', '"'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '"', '"'); + } + + /** + * @param string default schema. + */ + public function setDefaultSchema($schema) + { + $this->_defaultSchema=$schema; + } + + /** + * @return string default schema. + */ + public function getDefaultSchema() + { + return $this->_defaultSchema; + } + + /** + * @param string table name with optional schema name prefix, uses default schema name prefix is not provided. + * @return array tuple as ($schemaName,$tableName) + */ + protected function getSchemaTableName($table) + { + if(count($parts= explode('.', str_replace('"','',$table))) > 1) + return array($parts[0], $parts[1]); + else + return array($this->getDefaultSchema(),$parts[0]); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TPgsqlTableInfo table information. + */ + protected function createTableInfo($table) + { + list($schemaName,$tableName) = $this->getSchemaTableName($table); + + // This query is made much more complex by the addition of the 'attisserial' field. + // The subquery to get that field checks to see if there is an internally dependent + // sequence on the field. + $sql = +<< 0 AND NOT a.attisdropped + ORDER BY a.attnum +EOD; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + $command->bindValue(':schema', $schemaName); + $tableInfo = $this->createNewTableInfo($schemaName, $tableName); + $index=0; + foreach($command->query() as $col) + { + $col['index'] = $index++; + $this->processColumn($tableInfo, $col); + } + if($index===0) + throw new TDbException('dbmetadata_invalid_table_view', $table); + return $tableInfo; + } + + /** + * @param string table schema name + * @param string table name. + * @return TPgsqlTableInfo + */ + protected function createNewTableInfo($schemaName,$tableName) + { + $info['SchemaName'] = $this->assertIdentifier($schemaName); + $info['TableName'] = $this->assertIdentifier($tableName); + if($this->getIsView($schemaName,$tableName)) + $info['IsView'] = true; + list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName); + $class = $this->getTableInfoClass(); + return new $class($info,$primary,$foreign); + } + + /** + * @param string table name, schema name or column name. + * @return string a valid identifier. + * @throws TDbException when table name contains a double quote ("). + */ + protected function assertIdentifier($name) + { + if(strpos($name, '"')!==false) + { + $ref = 'http://www.postgresql.org/docs/7.4/static/sql-syntax.html#SQL-SYNTAX-IDENTIFIERS'; + throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref); + } + return $name; + } + + /** + * @param string table schema name + * @param string table name. + * @return boolean true if the table is a view. + */ + protected function getIsView($schemaName,$tableName) + { + $sql = +<<getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':schema',$schemaName); + $command->bindValue(':table', $tableName); + return intval($command->queryScalar()) === 1; + } + + /** + * @param TPgsqlTableInfo table information. + * @param array column information. + */ + protected function processColumn($tableInfo, $col) + { + $columnId = $col['attname']; //use column name as column Id + + $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = $col['index']; + if(!$col['attnotnull']) + $info['AllowNull'] = true; + if(in_array($columnId, $tableInfo->getPrimaryKeys())) + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $tableInfo)) + $info['IsForeignKey'] = true; + + if($col['atttypmod'] > 0) + $info['ColumnSize'] = $col['atttypmod'] - 4; + if($col['atthasdef']) + $info['DefaultValue'] = $col['adsrc']; + if($col['attisserial'] || substr($col['adsrc'],0,8) === 'nextval(') + { + if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null) + { + $info['SequenceName'] = $sequence; + unset($info['DefaultValue']); + } + } + $matches = array(); + if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches)) + { + $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']); + if($this->isPrecisionType($info['DbType'])) + { + $info['NumericPrecision'] = intval($matches[1]); + if(count($matches) > 2) + $info['NumericScale'] = intval($matches[2]); + } + else + $info['ColumnSize'] = intval($matches[1]); + } + else + $info['DbType'] = $col['type']; + + $tableInfo->Columns[$columnId] = new TPgsqlTableColumn($info); + } + + /** + * @return string serial name if found, null otherwise. + */ + protected function getSequenceName($tableInfo,$src) + { + $matches = array(); + if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches)) + { + if(is_int(strpos($matches[1], '.'))) + return $matches[1]; + else + return $tableInfo->getSchemaName().'.'.$matches[1]; + } + } + + /** + * @return boolean true if column type if "numeric", "interval" or begins with "time". + */ + protected function isPrecisionType($type) + { + $type = strtolower(trim($type)); + return $type==='numeric' || $type==='interval' || strpos($type, 'time')===0; + } + + /** + * Gets the primary and foreign key column details for the given table. + * @param string schema name + * @param string table name. + * @return array tuple ($primary, $foreign) + */ + protected function getConstraintKeys($schemaName, $tableName) + { + $sql = +<<getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + $command->bindValue(':schema', $schemaName); + $primary = array(); + $foreign = array(); + foreach($command->query() as $row) + { + switch($row['contype']) + { + case 'p': + $primary = $this->getPrimaryKeys($tableName, $schemaName, $row['indkey']); + break; + case 'f': + if(($fkey = $this->getForeignKeys($row['consrc']))!==null) + $foreign[] = $fkey; + break; + } + } + return array($primary,$foreign); + } + + /** + * Gets the primary key field names + * @param string pgsql primary key definition + * @return array primary key field names. + */ + protected function getPrimaryKeys($tableName, $schemaName, $columnIndex) + { + $index = join(', ', explode(' ', $columnIndex)); + $sql = +<<getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + $command->bindValue(':schema', $schemaName); +// $command->bindValue(':columnIndex', join(', ', explode(' ', $columnIndex))); + $primary = array(); + foreach($command->query() as $row) + { + $primary[] = $row['attname']; + } + + return $primary; + } + + /** + * Gets foreign relationship constraint keys and table name + * @param string pgsql foreign key definition + * @return array foreign relationship table name and keys, null otherwise + */ + protected function getForeignKeys($src) + { + $matches = array(); + $brackets = '\(([^\)]+)\)'; + $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i"; + if(preg_match($find, $src, $matches)) + { + $keys = preg_split('/,\s+/', $matches[1]); + $fkeys = array(); + foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey) + $fkeys[$keys[$i]] = $fkey; + return array('table' => str_replace('"','',$matches[2]), 'keys' => $fkeys); + } + } + + /** + * @param string column name. + * @param TPgsqlTableInfo table information. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $tableInfo) + { + foreach($tableInfo->getForeignKeys() as $fk) + { + if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + return false; + } +} + diff --git a/gui/baculum/framework/Data/Common/Pgsql/TPgsqlTableColumn.php b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlTableColumn.php new file mode 100644 index 0000000000..da17a7d339 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlTableColumn.php @@ -0,0 +1,49 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPgsqlTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Pgsql + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a PostgreSQL database table. + * + * @author Wei Zhuo + * @version $Id: TPgsqlTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Pgsql + * @since 3.1 + */ +class TPgsqlTableColumn extends TDbTableColumn +{ + private static $types=array( + 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'), + 'boolean' => array('boolean'), + 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric') + ); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = strtolower($this->getDbType()); + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } +} + diff --git a/gui/baculum/framework/Data/Common/Pgsql/TPgsqlTableInfo.php b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlTableInfo.php new file mode 100644 index 0000000000..ef2b457592 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Pgsql/TPgsqlTableInfo.php @@ -0,0 +1,58 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPgsqlTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Pgsql + */ + +/** + * Loads the base TDbTableInfo class and TPgsqlTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Pgsql.TPgsqlTableColumn'); + +/** + * TPgsqlTableInfo class provides additional table information for PostgreSQL database. + * + * @author Wei Zhuo + * @version $Id: TPgsqlTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common.Pgsql + * @since 3.1 + */ +class TPgsqlTableInfo extends TDbTableInfo +{ + /** + * @return string name of the schema this column belongs to. + */ + public function getSchemaName() + { + return $this->getInfo('SchemaName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + if(($schema=$this->getSchemaName())!==null) + return $schema.'.'.$this->getTableName(); + else + return $this->getTableName(); + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Pgsql.TPgsqlCommandBuilder'); + return new TPgsqlCommandBuilder($connection,$this); + } +} + diff --git a/gui/baculum/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php b/gui/baculum/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php new file mode 100644 index 0000000000..396ec7c5ae --- /dev/null +++ b/gui/baculum/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php @@ -0,0 +1,47 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + */ + +Prado::using('System.Data.Common.TDbCommandBuilder'); + +/** + * TSqliteCommandBuilder provides specifics methods to create limit/offset query commands + * for Sqlite database. + * + * @author Wei Zhuo + * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $ + * @package System.Data.Common + * @since 3.1 + */ +class TSqliteCommandBuilder extends TDbCommandBuilder +{ + /** + * Alters the sql to apply $limit and $offset. + * @param string SQL query string. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with limit and offset. + */ + public function applyLimitOffset($sql, $limit=-1, $offset=-1) + { + $limit = $limit!==null ? intval($limit) : -1; + $offset = $offset!==null ? intval($offset) : -1; + if($limit > 0 || $offset > 0) + { + $limitStr = ' LIMIT '.$limit; + $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : ''; + return $sql.$limitStr.$offsetStr; + } + else + return $sql; + } +} + diff --git a/gui/baculum/framework/Data/Common/Sqlite/TSqliteMetaData.php b/gui/baculum/framework/Data/Common/Sqlite/TSqliteMetaData.php new file mode 100644 index 0000000000..3d789500f4 --- /dev/null +++ b/gui/baculum/framework/Data/Common/Sqlite/TSqliteMetaData.php @@ -0,0 +1,210 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + */ + +/** + * Load the base TDbMetaData class. + */ +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.Sqlite.TSqliteTableInfo'); + +/** + * TSqliteMetaData loads SQLite database table and column information. + * + * @author Wei Zhuo + * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Commom.Sqlite + * @since 3.1 + */ +class TSqliteMetaData extends TDbMetaData +{ + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TSqliteTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return parent::quoteTableName($name, "'", "'"); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return parent::quoteColumnName($name, '"', '"'); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return parent::quoteColumnAlias($name, '"', '"'); + } + + /** + * Get the column definitions for given table. + * @param string table name. + * @return TPgsqlTableInfo table information. + */ + protected function createTableInfo($tableName) + { + $tableName = str_replace("'",'',$tableName); + $this->getDbConnection()->setActive(true); + $table = $this->getDbConnection()->quoteString($tableName); + $sql = "PRAGMA table_info({$table})"; + $command = $this->getDbConnection()->createCommand($sql); + $foreign = $this->getForeignKeys($table); + $index=0; + $columns=array(); + $primary=array(); + foreach($command->query() as $col) + { + $col['index'] = $index++; + $column = $this->processColumn($col, $foreign); + $columns[$col['name']] = $column; + if($column->getIsPrimaryKey()) + $primary[] = $col['name']; + } + $info['TableName'] = $tableName; + if($this->getIsView($tableName)) + $info['IsView'] = true; + if(count($columns)===0) + throw new TDbException('dbmetadata_invalid_table_view', $tableName); + $class = $this->getTableInfoClass(); + $tableInfo = new $class($info,$primary,$foreign); + $tableInfo->getColumns()->copyFrom($columns); + return $tableInfo; + } + + /** + * @param string table name. + * @return boolean true if the table is a view. + */ + protected function getIsView($tableName) + { + $sql = 'SELECT count(*) FROM sqlite_master WHERE type="view" AND name= :table'; + $this->getDbConnection()->setActive(true); + $command = $this->getDbConnection()->createCommand($sql); + $command->bindValue(':table', $tableName); + return intval($command->queryScalar()) === 1; + } + + /** + * @param array column information. + * @param array foreign key details. + * @return TSqliteTableColumn column details. + */ + protected function processColumn($col, $foreign) + { + $columnId = $col['name']; //use column name as column Id + + $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names! + $info['ColumnId'] = $columnId; + $info['ColumnIndex'] = $col['index']; + + if($col['notnull']!=='99') + $info['AllowNull'] = true; + + if($col['pk']==='1') + $info['IsPrimaryKey'] = true; + if($this->isForeignKeyColumn($columnId, $foreign)) + $info['IsForeignKey'] = true; + + if($col['dflt_value']!==null) + $info['DefaultValue'] = $col['dflt_value']; + + $type = strtolower($col['type']); + $info['AutoIncrement'] = $type==='integer' && $col['pk']==='1'; + + $info['DbType'] = $type; + $match=array(); + if(is_int($pos=strpos($type, '(')) && preg_match('/\((.*)\)/', $type, $match)) + { + $ps = explode(',', $match[1]); + if(count($ps)===2) + { + $info['NumericPrecision'] = intval($ps[0]); + $info['NumericScale'] = intval($ps[1]); + } + else + $info['ColumnSize']=intval($match[1]); + $info['DbType'] = substr($type,0,$pos); + } + + return new TSqliteTableColumn($info); + } + + /** + * + * + * @param string quoted table name. + * @return array foreign key details. + */ + protected function getForeignKeys($table) + { + $sql = "PRAGMA foreign_key_list({$table})"; + $command = $this->getDbConnection()->createCommand($sql); + $fkeys = array(); + foreach($command->query() as $col) + { + $fkeys[$col['table']]['keys'][$col['from']] = $col['to']; + $fkeys[$col['table']]['table'] = $col['table']; + } + return count($fkeys) > 0 ? array_values($fkeys) : $fkeys; + } + + /** + * @param string column name. + * @param array foreign key column names. + * @return boolean true if column is a foreign key. + */ + protected function isForeignKeyColumn($columnId, $foreign) + { + foreach($foreign as $fk) + { + if(in_array($columnId, array_keys($fk['keys']))) + return true; + } + return false; + } +} + +/** + +CREATE TABLE foo +( + id INTEGER NOT NULL PRIMARY KEY, + id2 CHAR(2) +); + +CREATE TABLE bar +( + id INTEGER NOT NULL PRIMARY KEY, + foo_id INTEGER + CONSTRAINT fk_foo_id REFERENCES foo(id) ON DELETE CASCADE +); +*/ + diff --git a/gui/baculum/framework/Data/Common/Sqlite/TSqliteTableColumn.php b/gui/baculum/framework/Data/Common/Sqlite/TSqliteTableColumn.php new file mode 100644 index 0000000000..d8fd19664b --- /dev/null +++ b/gui/baculum/framework/Data/Common/Sqlite/TSqliteTableColumn.php @@ -0,0 +1,64 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + */ + +/** + * Load common TDbTableCommon class. + */ +Prado::using('System.Data.Common.TDbTableColumn'); + +/** + * Describes the column metadata of the schema for a PostgreSQL database table. + * + * @author Wei Zhuo + * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + * @since 3.1 + */ +class TSqliteTableColumn extends TDbTableColumn +{ + /** + * @TODO add sqlite types. + */ + private static $types = array(); + + /** + * Overrides parent implementation, returns PHP type from the db type. + * @return boolean derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + $dbtype = strtolower($this->getDbType()); + foreach(self::$types as $type => $dbtypes) + { + if(in_array($dbtype, $dbtypes)) + return $type; + } + return 'string'; + } + + /** + * @return boolean true if column will auto-increment when the column value is inserted as null. + */ + public function getAutoIncrement() + { + return $this->getInfo('AutoIncrement', false); + } + + /** + * @return boolean true if auto increment is true. + */ + public function hasSequence() + { + return $this->getAutoIncrement(); + } +} + diff --git a/gui/baculum/framework/Data/Common/Sqlite/TSqliteTableInfo.php b/gui/baculum/framework/Data/Common/Sqlite/TSqliteTableInfo.php new file mode 100644 index 0000000000..52fb3530fc --- /dev/null +++ b/gui/baculum/framework/Data/Common/Sqlite/TSqliteTableInfo.php @@ -0,0 +1,47 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + */ + +/** + * Loads the base TDbTableInfo class and TSqliteTableColumn class. + */ +Prado::using('System.Data.Common.TDbTableInfo'); +Prado::using('System.Data.Common.Sqlite.TSqliteTableColumn'); + +/** + * TSqliteTableInfo class provides additional table information for PostgreSQL database. + * + * @author Wei Zhuo + * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $ + * @package System.Data.Common.Sqlite + * @since 3.1 + */ +class TSqliteTableInfo extends TDbTableInfo +{ + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder'); + return new TSqliteCommandBuilder($connection,$this); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return "'".$this->getTableName()."'"; + } +} + diff --git a/gui/baculum/framework/Data/Common/TDbCommandBuilder.php b/gui/baculum/framework/Data/Common/TDbCommandBuilder.php new file mode 100644 index 0000000000..fdaa3bfe2e --- /dev/null +++ b/gui/baculum/framework/Data/Common/TDbCommandBuilder.php @@ -0,0 +1,507 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCommandBuilder.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + */ + +/** + * TDbCommandBuilder provides basic methods to create query commands for tables + * giving by {@link setTableInfo TableInfo} the property. + * + * @author Wei Zhuo + * @version $Id: TDbCommandBuilder.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + * @since 3.1 + */ +class TDbCommandBuilder extends TComponent +{ + private $_connection; + private $_tableInfo; + + /** + * @param TDbConnection database connection. + * @param TDbTableInfo table information. + */ + public function __construct($connection=null, $tableInfo=null) + { + $this->setDbConnection($connection); + $this->setTableInfo($tableInfo); + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @param TDbConnection database connection. + */ + public function setDbConnection($value) + { + $this->_connection=$value; + } + + /** + * @param TDbTableInfo table information. + */ + public function setTableInfo($value) + { + $this->_tableInfo=$value; + } + + /** + * @param TDbTableInfo table information. + */ + public function getTableInfo() + { + return $this->_tableInfo; + } + + /** + * Iterate through all the columns and returns the last insert id of the + * first column that has a sequence or serial. + * @return mixed last insert id, null if none is found. + */ + public function getLastInsertID() + { + foreach($this->getTableInfo()->getColumns() as $column) + { + if($column->hasSequence()) + return $this->getDbConnection()->getLastInsertID($column->getSequenceName()); + } + } + + /** + * Alters the sql to apply $limit and $offset. Default implementation is applicable + * for PostgreSQL, MySQL and SQLite. + * @param string SQL query string. + * @param integer maximum number of rows, -1 to ignore limit. + * @param integer row offset, -1 to ignore offset. + * @return string SQL with limit and offset. + */ + public function applyLimitOffset($sql, $limit=-1, $offset=-1) + { + $limit = $limit!==null ? (int)$limit : -1; + $offset = $offset!==null ? (int)$offset : -1; + $limitStr = $limit >= 0 ? ' LIMIT '.$limit : ''; + $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : ''; + return $sql.$limitStr.$offsetStr; + } + + /** + * @param string SQL string without existing ordering. + * @param array pairs of column names as key and direction as value. + * @return string modified SQL applied with ORDER BY. + */ + public function applyOrdering($sql, $ordering) + { + $orders=array(); + foreach($ordering as $name => $direction) + { + $direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC'; + if(false !== strpos($name, '(') && false !== strpos($name, ')')) { + // key is a function (bad practice, but we need to handle it) + $key = $name; + } else { + // key is a column + $key = $this->getTableInfo()->getColumn($name)->getColumnName(); + } + $orders[] = $key.' '.$direction; + } + if(count($orders) > 0) + $sql .= ' ORDER BY '.implode(', ', $orders); + return $sql; + } + + /** + * Computes the SQL condition for search a set of column using regular expression + * (or LIKE, depending on database implementation) to match a string of + * keywords (default matches all keywords). + * @param array list of column id for potential search condition. + * @param string string of keywords + * @return string SQL search condition matching on a set of columns. + */ + public function getSearchExpression($fields, $keywords) + { + if(strlen(trim($keywords)) == 0) return ''; + $words = preg_split('/\s/u', $keywords); + $conditions = array(); + foreach($fields as $field) + { + $column = $this->getTableInfo()->getColumn($field)->getColumnName(); + $conditions[] = $this->getSearchCondition($column, $words); + } + return '('.implode(' OR ', $conditions).')'; + } + + /** + * @param string column name. + * @param array keywords + * @return string search condition for all words in one column. + */ + protected function getSearchCondition($column, $words) + { + $conditions=array(); + foreach($words as $word) + $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%'); + return '('.implode(' AND ', $conditions).')'; + } + + /** + * + * Different behavior depends on type of passed data + * string + * usage without modification + * + * null + * will be expanded to full list of quoted table column names (quoting depends on database) + * + * array + * - Column names will be quoted if used as key or value of array + * + * array('col1', 'col2', 'col2') + * // SELECT `col1`, `col2`, `col3` FROM... + * + * + * - Column aliasing + * + * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)') + * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM... + * + * + * - NULL and scalar values (strings will be quoted depending on database) + * + * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL') + * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM... + * + * + * - If the *-wildcard char is used as key or value, add the full list of quoted table column names + * + * array('col1' => 'NULL', '*') + * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM... + * + * @param mixed $value + * @return array of generated fields - use implode(', ', $selectfieldlist) to collapse field list for usage + * @since 3.1.7 + * @todo add support for table aliasing + * @todo add support for quoting of column aliasing + */ + public function getSelectFieldList($data='*') { + if(is_scalar($data)) { + $tmp = explode(',', $data); + $result = array(); + foreach($tmp as $v) + $result[] = trim($v); + return $result; + } + + $bHasWildcard = false; + $result = array(); + if(is_array($data) || $data instanceof Traversable) { + $columns = $this->getTableInfo()->getColumns(); + foreach($data as $key=>$value) { + if($key==='*' || $value==='*') { + $bHasWildcard = true; + continue; + } + + if(strToUpper($key)==='NULL') { + $result[] = 'NULL'; + continue; + } + + if(strpos($key, '(')!==false && strpos($key, ')')!==false) { + $result[] = $key; + continue; + } + + if(stripos($key, 'AS')!==false) { + $result[] = $key; + continue; + } + + if(stripos($value, 'AS')!==false) { + $result[] = $value; + continue; + } + + $v = isset($columns[$value]); + $k = isset($columns[$key]); + if(is_integer($key) && $v) { + $key = $value; + $k = $v; + } + + if(strToUpper($value)==='NULL') { + if($k) + $result[] = 'NULL AS ' . $columns[$key]->getColumnName(); + else + $result[] = 'NULL' . (is_string($key) ? (' AS ' . (string)$key) : ''); + continue; + } + + if(strpos($value, '(')!==false && strpos($value, ')')!==false) { + if($k) + $result[] = $value . ' AS ' . $columns[$key]->getColumnName(); + else + $result[] = $value . (is_string($key) ? (' AS ' . (string)$key) : ''); + continue; + } + + if($v && $key==$value) { + $result[] = $columns[$value]->getColumnName(); + continue; + } + + if($k && $value==null) { + $result[] = $columns[$key]->getColumnName(); + continue; + } + + if(is_string($key) && $v) { + $result[] = $columns[$value]->getColumnName() . ' AS ' . $key; + continue; + } + + if(is_numeric($value) && $k) { + $result[] = $value . ' AS ' . $columns[$key]->getColumnName(); + continue; + } + + if(is_string($value) && $k) { + $result[] = $this->getDbConnection()->quoteString($value) . ' AS ' . $columns[$key]->getColumnName(); + continue; + } + + if(!$v && !$k && is_integer($key)) { + $result[] = is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value); + continue; + } + + $result[] = (is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value)) . ' AS ' . $key; + } + } + + if($data===null || count($result) == 0 || $bHasWildcard) + $result = $result = array_merge($this->getTableInfo()->getColumnNames(), $result); + + return $result; + } + + /** + * Appends the $where condition to the string "SELECT * FROM tableName WHERE ". + * The tableName is obtained from the {@link setTableInfo TableInfo} property. + * @param string query condition + * @param array condition parameters. + * @return TDbCommand query command. + */ + public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1, $select='*') + { + $table = $this->getTableInfo()->getTableFullName(); + $fields = implode(', ', $this -> getSelectFieldList($select)); + $sql = "SELECT {$fields} FROM {$table}"; + if(!empty($where)) + $sql .= " WHERE {$where}"; + return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset); + } + + public function applyCriterias($sql, $parameters=array(),$ordering=array(), $limit=-1, $offset=-1) + { + if(count($ordering) > 0) + $sql = $this->applyOrdering($sql, $ordering); + if($limit>=0 || $offset>=0) + $sql = $this->applyLimitOffset($sql, $limit, $offset); + $command = $this->createCommand($sql); + $this->bindArrayValues($command, $parameters); + return $command; + } + + /** + * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}. + * @param string count condition. + * @param array binding parameters. + * @return TDbCommand count command. + */ + public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1) + { + return $this->createFindCommand($where, $parameters, $ordering, $limit, $offset, 'COUNT(*)'); + } + + /** + * Creates a delete command for the table described in {@link setTableInfo TableInfo}. + * The conditions for delete is given by the $where argument and the parameters + * for the condition is given by $parameters. + * @param string delete condition. + * @param array delete parameters. + * @return TDbCommand delete command. + */ + public function createDeleteCommand($where,$parameters=array()) + { + $table = $this->getTableInfo()->getTableFullName(); + if (!empty($where)) + $where = ' WHERE '.$where; + $command = $this->createCommand("DELETE FROM {$table}".$where); + $this->bindArrayValues($command, $parameters); + return $command; + } + + /** + * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data. + * Each array key in the $data array must correspond to the column name of the table + * (if a column allows to be null, it may be omitted) to be inserted with + * the corresponding array value. + * @param array name-value pairs of new data to be inserted. + * @return TDbCommand insert command + */ + public function createInsertCommand($data) + { + $table = $this->getTableInfo()->getTableFullName(); + list($fields, $bindings) = $this->getInsertFieldBindings($data); + $command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)"); + $this->bindColumnValues($command, $data); + return $command; + } + + /** + * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data. + * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value. + * @param array name-value pairs of data to be updated. + * @param string update condition. + * @param array update parameters. + * @return TDbCommand update command. + */ + public function createUpdateCommand($data, $where, $parameters=array()) + { + $table = $this->getTableInfo()->getTableFullName(); + if($this->hasIntegerKey($parameters)) + $fields = implode(', ', $this->getColumnBindings($data, true)); + else + $fields = implode(', ', $this->getColumnBindings($data)); + + if (!empty($where)) + $where = ' WHERE '.$where; + $command = $this->createCommand("UPDATE {$table} SET {$fields}".$where); + $this->bindArrayValues($command, array_merge($data, $parameters)); + return $command; + } + + /** + * Returns a list of insert field name and a list of binding names. + * @param object array or object to be inserted. + * @return array tuple ($fields, $bindings) + */ + protected function getInsertFieldBindings($values) + { + $fields = array(); $bindings=array(); + foreach(array_keys($values) as $name) + { + $fields[] = $this->getTableInfo()->getColumn($name)->getColumnName(); + $bindings[] = ':'.$name; + } + return array(implode(', ',$fields), implode(', ', $bindings)); + } + + /** + * Create a name-value or position-value if $position=true binding strings. + * @param array data for binding. + * @param boolean true to bind as position values. + * @return string update column names with corresponding binding substrings. + */ + protected function getColumnBindings($values, $position=false) + { + $bindings=array(); + foreach(array_keys($values) as $name) + { + $column = $this->getTableInfo()->getColumn($name)->getColumnName(); + $bindings[] = $position ? $column.' = ?' : $column.' = :'.$name; + } + return $bindings; + } + + /** + * @param string SQL query string. + * @return TDbCommand corresponding database command. + */ + public function createCommand($sql) + { + $this->getDbConnection()->setActive(true); + return $this->getDbConnection()->createCommand($sql); + } + + /** + * Bind the name-value pairs of $values where the array keys correspond to column names. + * @param TDbCommand database command. + * @param array name-value pairs. + */ + public function bindColumnValues($command, $values) + { + foreach($values as $name=>$value) + { + $column = $this->getTableInfo()->getColumn($name); + if($value === null && $column->getAllowNull()) + $command->bindValue(':'.$name, null, PDO::PARAM_NULL); + else + $command->bindValue(':'.$name, $value, $column->getPdoType()); + } + } + + /** + * @param TDbCommand database command + * @param array values for binding. + */ + public function bindArrayValues($command, $values) + { + if($this->hasIntegerKey($values)) + { + $values = array_values($values); + for($i = 0, $max=count($values); $i<$max; $i++) + $command->bindValue($i+1, $values[$i], $this->getPdoType($values[$i])); + } + else + { + foreach($values as $name=>$value) + { + $prop = $name[0]===':' ? $name : ':'.$name; + $command->bindValue($prop, $value, $this->getPdoType($value)); + } + } + } + + /** + * @param mixed PHP value + * @return integer PDO parameter types. + */ + public static function getPdoType($value) + { + switch(gettype($value)) + { + case 'boolean': return PDO::PARAM_BOOL; + case 'integer': return PDO::PARAM_INT; + case 'string' : return PDO::PARAM_STR; + case 'NULL' : return PDO::PARAM_NULL; + } + } + + /** + * @param array + * @return boolean true if any array key is an integer. + */ + protected function hasIntegerKey($array) + { + foreach($array as $k=>$v) + { + if(gettype($k)==='integer') + return true; + } + return false; + } +} diff --git a/gui/baculum/framework/Data/Common/TDbMetaData.php b/gui/baculum/framework/Data/Common/TDbMetaData.php new file mode 100644 index 0000000000..2ad5c5928d --- /dev/null +++ b/gui/baculum/framework/Data/Common/TDbMetaData.php @@ -0,0 +1,184 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbMetaData.php 3284 2013-04-11 07:14:59Z ctrlaltca $ + * @package System.Data.Common + */ + +/** + * TDbMetaData is the base class for retrieving metadata information, such as + * table and columns information, from a database connection. + * + * Use the {@link getTableInfo} method to retrieve a table information. + * + * @author Wei Zhuo + * @version $Id: TDbMetaData.php 3284 2013-04-11 07:14:59Z ctrlaltca $ + * @package System.Data.Common + * @since 3.1 + */ +abstract class TDbMetaData extends TComponent +{ + private $_tableInfoCache=array(); + private $_connection; + + /** + * @var array + */ + protected static $delimiterIdentifier = array('[', ']', '"', '`', "'"); + + /** + * @param TDbConnection database connection. + */ + public function __construct($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * Obtain database specific TDbMetaData class using the driver name of the database connection. + * @param TDbConnection database connection. + * @return TDbMetaData database specific TDbMetaData. + */ + public static function getInstance($conn) + { + $conn->setActive(true); //must be connected before retrieving driver name + $driver = $conn->getDriverName(); + switch(strtolower($driver)) + { + case 'pgsql': + Prado::using('System.Data.Common.Pgsql.TPgsqlMetaData'); + return new TPgsqlMetaData($conn); + case 'mysqli': + case 'mysql': + Prado::using('System.Data.Common.Mysql.TMysqlMetaData'); + return new TMysqlMetaData($conn); + case 'sqlite': //sqlite 3 + case 'sqlite2': //sqlite 2 + Prado::using('System.Data.Common.Sqlite.TSqliteMetaData'); + return new TSqliteMetaData($conn); + case 'mssql': // Mssql driver on windows hosts + case 'sqlsrv': // sqlsrv driver on windows hosts + case 'dblib': // dblib drivers on linux (and maybe others os) hosts + Prado::using('System.Data.Common.Mssql.TMssqlMetaData'); + return new TMssqlMetaData($conn); + case 'oci': + Prado::using('System.Data.Common.Oracle.TOracleMetaData'); + return new TOracleMetaData($conn); +// case 'ibm': +// Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData'); +// return new TIbmDb2MetaData($conn); + default: + throw new TDbException('ar_invalid_database_driver',$driver); + } + } + + /** + * Obtains table meta data information for the current connection and given table name. + * @param string table or view name + * @return TDbTableInfo table information. + */ + public function getTableInfo($tableName=null) + { + $key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName; + if(!isset($this->_tableInfoCache[$key])) + { + $class = $this->getTableInfoClass(); + $tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName); + $this->_tableInfoCache[$key] = $tableInfo; + } + return $this->_tableInfoCache[$key]; + } + + /** + * Creates a command builder for a given table name. + * @param string table name. + * @return TDbCommandBuilder command builder instance for the given table. + */ + public function createCommandBuilder($tableName=null) + { + return $this->getTableInfo($tableName)->createCommandBuilder($this->getDbConnection()); + } + + /** + * This method should be implemented by decendent classes. + * @return TDbTableInfo driver dependent create builder. + */ + abstract protected function createTableInfo($tableName); + + /** + * @return string TDbTableInfo class name. + */ + protected function getTableInfoClass() + { + return 'TDbTableInfo'; + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @param string $lft left delimiter + * @param string $rgt right delimiter + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + $name = str_replace(self::$delimiterIdentifier, '', $name); + + $args = func_get_args(); + $rgt = $lft = isset($args[1]) ? $args[1] : ''; + $rgt = isset($args[2]) ? $args[2] : $rgt; + + if(strpos($name, '.')===false) + return $lft . $name . $rgt; + $names=explode('.', $name); + foreach($names as &$n) + $n = $lft . $n . $rgt; + return implode('.', $names); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @param string $lft left delimiter + * @param string $rgt right delimiter + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + $args = func_get_args(); + $rgt = $lft = isset($args[1]) ? $args[1] : ''; + $rgt = isset($args[2]) ? $args[2] : $rgt; + + return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt; + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column alias + * @param string $lft left delimiter + * @param string $rgt right delimiter + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + $args = func_get_args(); + $rgt = $lft = isset($args[1]) ? $args[1] : ''; + $rgt = isset($args[2]) ? $args[2] : $rgt; + + return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt; + } +} + diff --git a/gui/baculum/framework/Data/Common/TDbTableColumn.php b/gui/baculum/framework/Data/Common/TDbTableColumn.php new file mode 100644 index 0000000000..27cfb7c56f --- /dev/null +++ b/gui/baculum/framework/Data/Common/TDbTableColumn.php @@ -0,0 +1,199 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + */ + +/** + * TDbTableColumn class describes the column meta data of the schema for a database table. + * + * @author Wei Zhuo + * @version $Id: TDbTableColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + * @since 3.1 + */ +class TDbTableColumn extends TComponent +{ + const UNDEFINED_VALUE= INF; //use infinity for undefined value + + private $_info=array(); + + /** + * Sets the table column meta data. + * @param array table column information. + */ + public function __construct($columnInfo) + { + $this->_info=$columnInfo; + } + + /** + * @param string information array key name + * @param mixed default value if information array value is null + * @return mixed information array value. + */ + protected function getInfo($name,$default=null) + { + return isset($this->_info[$name]) ? $this->_info[$name] : $default; + } + + /** + * @param string information array key name + * @param mixed new information array value. + */ + protected function setInfo($name,$value) + { + $this->_info[$name]=$value; + } + + /** + * Returns the derived PHP primitive type from the db type. Default returns 'string'. + * @return string derived PHP primitive type from the column db type. + */ + public function getPHPType() + { + return 'string'; + } + + /** + * @param integer PDO bind param/value types, default returns string. + */ + public function getPdoType() + { + switch($this->getPHPType()) + { + case 'boolean': return PDO::PARAM_BOOL; + case 'integer': return PDO::PARAM_INT; + case 'string' : return PDO::PARAM_STR; + } + return PDO::PARAM_STR; + } + + /** + * @return string name of the column in the table (identifier quoted). + */ + public function getColumnName() + { + return $this->getInfo('ColumnName'); + } + + /** + * @return string name of the column with quoted identifier. + */ + public function getColumnId() + { + return $this->getInfo('ColumnId'); + } + + /** + * @return string size of the column. + */ + public function getColumnSize() + { + return $this->getInfo('ColumnSize'); + } + + /** + * @return integer zero-based ordinal position of the column in the table. + */ + public function getColumnIndex() + { + return $this->getInfo('ColumnIndex'); + } + + /** + * @return string column type. + */ + public function getDbType() + { + return $this->getInfo('DbType'); + } + + /** + * @return boolean specifies whether value Null is allowed, default is false. + */ + public function getAllowNull() + { + return $this->getInfo('AllowNull',false); + } + + /** + * @return mixed default column value if column value was null. + */ + public function getDefaultValue() + { + return $this->getInfo('DefaultValue', self::UNDEFINED_VALUE); + } + + /** + * @return string precision of the column data, if the data is numeric. + */ + public function getNumericPrecision() + { + return $this->getInfo('NumericPrecision'); + } + + /** + * @return string scale of the column data, if the data is numeric. + */ + public function getNumericScale() + { + return $this->getInfo('NumericScale'); + } + + public function getMaxiumNumericConstraint() + { + if(($precision=$this->getNumericPrecision())!==null) + { + $scale=$this->getNumericScale(); + return $scale===null ? pow(10,$precision) : pow(10,$precision-$scale); + } + } + + /** + * @return boolean whether this column is a primary key for the table, default is false. + */ + public function getIsPrimaryKey() + { + return $this->getInfo('IsPrimaryKey',false); + } + + /** + * @return boolean whether this column is a foreign key, default is false. + */ + public function getIsForeignKey() + { + return $this->getInfo('IsForeignKey',false); + } + + /** + * @param string sequence name, only applicable if column is a sequence + */ + public function getSequenceName() + { + return $this->getInfo('SequenceName'); + } + + /** + * @return boolean whether the column is a sequence. + */ + public function hasSequence() + { + return $this->getSequenceName()!==null; + } + + /** + * @return boolean whether this column is excluded from insert and update. + */ + public function getIsExcluded() + { + return false; + } +} + diff --git a/gui/baculum/framework/Data/Common/TDbTableInfo.php b/gui/baculum/framework/Data/Common/TDbTableInfo.php new file mode 100644 index 0000000000..fbfcf46d4e --- /dev/null +++ b/gui/baculum/framework/Data/Common/TDbTableInfo.php @@ -0,0 +1,166 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + */ + +/** + * TDbTableInfo class describes the meta data of a database table. + * + * @author Wei Zhuo + * @version $Id: TDbTableInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.Common + * @since 3.1 + */ +class TDbTableInfo extends TComponent +{ + private $_info=array(); + + private $_primaryKeys; + private $_foreignKeys; + + private $_columns; + + private $_lowercase; + + /** + * @var null|array + * @since 3.1.7 + */ + private $_names = null; + + /** + * Sets the database table meta data information. + * @param array table column information. + */ + public function __construct($tableInfo=array(),$primary=array(),$foreign=array()) + { + $this->_info=$tableInfo; + $this->_primaryKeys=$primary; + $this->_foreignKeys=$foreign; + $this->_columns=new TMap; + } + + /** + * @param TDbConnection database connection. + * @return TDbCommandBuilder new command builder + */ + public function createCommandBuilder($connection) + { + Prado::using('System.Data.Common.TDbCommandBuilder'); + return new TDbCommandBuilder($connection,$this); + } + + /** + * @param string information array key name + * @param mixed default value if information array value is null + * @return mixed information array value. + */ + protected function getInfo($name,$default=null) + { + return isset($this->_info[$name]) ? $this->_info[$name] : $default; + } + + /** + * @param string information array key name + * @param mixed new information array value. + */ + protected function setInfo($name,$value) + { + $this->_info[$name]=$value; + } + + /** + * @return string name of the table this column belongs to. + */ + public function getTableName() + { + return $this->getInfo('TableName'); + } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return $this->getTableName(); + } + + /** + * @return boolean whether the table is a view, default is false. + */ + public function getIsView() + { + return $this->getInfo('IsView',false); + } + + /** + * @return TMap TDbTableColumn column meta data. + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @param string column id + * @return TDbTableColumn column information. + */ + public function getColumn($name) + { + if(($column = $this->_columns->itemAt($name))!==null) + return $column; + throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName()); + } + + /** + * @param array list of column Id, empty to get all columns. + * @return array table column names (identifier quoted) + */ + public function getColumnNames() + { + if($this->_names===null) + { + $this->_names=array(); + foreach($this->getColumns() as $column) + $this->_names[] = $column->getColumnName(); + } + return $this->_names; + } + + /** + * @return string[] names of primary key columns. + */ + public function getPrimaryKeys() + { + return $this->_primaryKeys; + } + + /** + * @return array tuples of foreign table and column name. + */ + public function getForeignKeys() + { + return $this->_foreignKeys; + } + + /** + * @return array lowercased column key names mapped to normal column ids. + */ + public function getLowerCaseColumnNames() + { + if($this->_lowercase===null) + { + $this->_lowercase=array(); + foreach($this->getColumns()->getKeys() as $key) + $this->_lowercase[strtolower($key)] = $key; + } + return $this->_lowercase; + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Data/DataGateway/TDataGatewayCommand.php b/gui/baculum/framework/Data/DataGateway/TDataGatewayCommand.php new file mode 100644 index 0000000000..f51097c72b --- /dev/null +++ b/gui/baculum/framework/Data/DataGateway/TDataGatewayCommand.php @@ -0,0 +1,540 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.DataGateway + */ + +/** + * TDataGatewayCommand is command builder and executor class for + * TTableGateway and TActiveRecordGateway. + * + * TDataGatewayCommand builds the TDbCommand for TTableGateway + * and TActiveRecordGateway commands such as find(), update(), insert(), etc, + * using the TDbCommandBuilder classes (database specific TDbCommandBuilder + * classes are used). + * + * Once the command is built and the query parameters are binded, the + * {@link OnCreateCommand} event is raised. Event handlers for the OnCreateCommand + * event should not alter the Command property nor the Criteria property of the + * TDataGatewayEventParameter. + * + * TDataGatewayCommand excutes the TDbCommands and returns the result obtained from the + * database (returned value depends on the method executed). The + * {@link OnExecuteCommand} event is raised after the command is executed and resulting + * data is set in the TDataGatewayResultEventParameter object's Result property. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TDataGatewayCommand extends TComponent +{ + private $_builder; + + /** + * @param TDbCommandBuilder database specific database command builder. + */ + public function __construct($builder) + { + $this->_builder = $builder; + } + + /** + * @return TDbTableInfo + */ + public function getTableInfo() + { + return $this->_builder->getTableInfo(); + } + + /** + * @return TDbConnection + */ + public function getDbConnection() + { + return $this->_builder->getDbConnection(); + } + + /** + * @return TDbCommandBuilder + */ + public function getBuilder() + { + return $this->_builder; + } + + /** + * Executes a delete command. + * @param TSqlCriteria delete conditions and parameters. + * @return integer number of records affected. + */ + public function delete($criteria) + { + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $command = $this->getBuilder()->createDeleteCommand($where, $parameters); + $this->onCreateCommand($command,$criteria); + $command->prepare(); + return $command->execute(); + } + + /** + * Updates the table with new data. + * @param array date for update. + * @param TSqlCriteria update conditions and parameters. + * @return integer number of records affected. + */ + public function update($data, $criteria) + { + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $command = $this->getBuilder()->createUpdateCommand($data,$where, $parameters); + $this->onCreateCommand($command,$criteria); + $command->prepare(); + return $this->onExecuteCommand($command, $command->execute()); + } + + /** + * @param array update for update + * @param array primary key-value name pairs. + * @return integer number of records affected. + */ + public function updateByPk($data, $keys) + { + list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys); + return $this->update($data, new TSqlCriteria($where, $parameters)); + } + + /** + * Find one record matching the critera. + * @param TSqlCriteria find conditions and parameters. + * @return array matching record. + */ + public function find($criteria) + { + $command = $this->getFindCommand($criteria); + return $this->onExecuteCommand($command, $command->queryRow()); + } + + /** + * Find one or more matching records. + * @param TSqlCriteria $criteria + * @return TDbDataReader record reader. + */ + public function findAll($criteria) + { + $command = $this->getFindCommand($criteria); + return $this->onExecuteCommand($command, $command->query()); + } + + /** + * Build the find command from the criteria. Limit, Offset and Ordering are applied if applicable. + * @param TSqlCriteria $criteria + * @return TDbCommand. + */ + protected function getFindCommand($criteria) + { + if($criteria===null) + return $this->getBuilder()->createFindCommand(); + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + $select = $criteria->getSelect(); + $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset,$select); + $this->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param mixed primary key value, or composite key values as array. + * @return array matching record. + */ + public function findByPk($keys) + { + list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys); + $command = $this->getBuilder()->createFindCommand($where, $parameters); + $this->onCreateCommand($command, new TSqlCriteria($where,$parameters)); + return $this->onExecuteCommand($command, $command->queryRow()); + } + + /** + * @param array multiple primary key values or composite value arrays + * @return TDbDataReader record reader. + */ + public function findAllByPk($keys) + { + $where = $this->getCompositeKeyCondition((array)$keys); + $command = $this->getBuilder()->createFindCommand($where); + $this->onCreateCommand($command, new TSqlCriteria($where,$keys)); + return $this->onExecuteCommand($command,$command->query()); + } + + public function findAllByIndex($criteria,$fields,$values) + { + $index = $this->getIndexKeyCondition($this->getTableInfo(),$fields,$values); + if(strlen($where = $criteria->getCondition())>0) + $criteria->setCondition("({$index}) AND ({$where})"); + else + $criteria->setCondition($index); + $command = $this->getFindCommand($criteria); + $this->onCreateCommand($command, $criteria); + return $this->onExecuteCommand($command,$command->query()); + } + + /** + * @param array multiple primary key values or composite value arrays + * @return integer number of rows affected. + */ + public function deleteByPk($keys) + { + $where = $this->getCompositeKeyCondition((array)$keys); + $command = $this->getBuilder()->createDeleteCommand($where); + $this->onCreateCommand($command, new TSqlCriteria($where,$keys)); + $command->prepare(); + return $this->onExecuteCommand($command,$command->execute()); + } + + public function getIndexKeyCondition($table,$fields,$values) + { + if (!count($values)) + return 'FALSE'; + $columns = array(); + $tableName = $table->getTableFullName(); + foreach($fields as $field) + $columns[] = $tableName.'.'.$table->getColumn($field)->getColumnName(); + return '('.implode(', ',$columns).') IN '.$this->quoteTuple($values); + } + + /** + * Construct a "pk IN ('key1', 'key2', ...)" criteria. + * @param array values for IN predicate + * @param string SQL string for primary keys IN a list. + */ + protected function getCompositeKeyCondition($values) + { + $primary = $this->getTableInfo()->getPrimaryKeys(); + $count = count($primary); + if($count===0) + { + throw new TDbException('dbtablegateway_no_primary_key_found', + $this->getTableInfo()->getTableFullName()); + } + if(!is_array($values) || count($values) === 0) + { + throw new TDbException('dbtablegateway_missing_pk_values', + $this->getTableInfo()->getTableFullName()); + } + if($count>1 && (!isset($values[0]) || !is_array($values[0]))) + $values = array($values); + if($count > 1 && count($values[0]) !== $count) + { + throw new TDbException('dbtablegateway_pk_value_count_mismatch', + $this->getTableInfo()->getTableFullName()); + } + return $this->getIndexKeyCondition($this->getTableInfo(),$primary, $values); + } + + /** + * @param TDbConnection database connection. + * @param array values + * @return string quoted recursive tuple values, e.g. "('val1', 'val2')". + */ + protected function quoteTuple($array) + { + $conn = $this->getDbConnection(); + $data = array(); + foreach($array as $k=>$v) + $data[] = is_array($v) ? $this->quoteTuple($v) : $conn->quoteString($v); + return '('.implode(', ', $data).')'; + } + + /** + * Create the condition and parameters for find by primary. + * @param array primary key values + * @return array tuple($where, $parameters) + */ + protected function getPrimaryKeyCondition($values) + { + $primary = $this->getTableInfo()->getPrimaryKeys(); + if(count($primary)===0) + { + throw new TDbException('dbtablegateway_no_primary_key_found', + $this->getTableInfo()->getTableFullName()); + } + $criteria=array(); + $bindings=array(); + $i = 0; + foreach($primary as $key) + { + $column = $this->getTableInfo()->getColumn($key)->getColumnName(); + $criteria[] = $column.' = :'.$key; + $bindings[$key] = isset($values[$key])?$values[$key]:$values[$i++]; + } + return array(implode(' AND ', $criteria), $bindings); + } + + /** + * Find one matching records for arbituary SQL. + * @param TSqlCriteria $criteria + * @return TDbDataReader record reader. + */ + public function findBySql($criteria) + { + $command = $this->getSqlCommand($criteria); + return $this->onExecuteCommand($command, $command->queryRow()); + } + + /** + * Find zero or more matching records for arbituary SQL. + * @param TSqlCriteria $criteria + * @return TDbDataReader record reader. + */ + public function findAllBySql($criteria) + { + $command = $this->getSqlCommand($criteria); + return $this->onExecuteCommand($command, $command->query()); + } + + /** + * Build sql command from the criteria. Limit, Offset and Ordering are applied if applicable. + * @param TSqlCriteria $criteria + * @return TDbCommand command corresponding to the criteria. + */ + protected function getSqlCommand($criteria) + { + $sql = $criteria->getCondition(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + if(count($ordering) > 0) + $sql = $this->getBuilder()->applyOrdering($sql, $ordering); + if($limit>=0 || $offset>=0) + $sql = $this->getBuilder()->applyLimitOffset($sql, $limit, $offset); + $command = $this->getBuilder()->createCommand($sql); + $this->getBuilder()->bindArrayValues($command, $criteria->getParameters()->toArray()); + $this->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param TSqlCriteria $criteria + * @return integer number of records. + */ + public function count($criteria) + { + if($criteria===null) + return (int)$this->getBuilder()->createCountCommand()->queryScalar(); + $where = $criteria->getCondition(); + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + $command = $this->getBuilder()->createCountCommand($where,$parameters,$ordering,$limit,$offset); + $this->onCreateCommand($command, $criteria); + return $this->onExecuteCommand($command, (int)$command->queryScalar()); + } + + /** + * Inserts a new record into the table. Each array key must + * correspond to a column name in the table unless a null value is permitted. + * @param array new record data. + * @return mixed last insert id if one column contains a serial or sequence, + * otherwise true if command executes successfully and affected 1 or more rows. + */ + public function insert($data) + { + $command=$this->getBuilder()->createInsertCommand($data); + $this->onCreateCommand($command, new TSqlCriteria(null,$data)); + $command->prepare(); + if($this->onExecuteCommand($command, $command->execute()) > 0) + { + $value = $this->getLastInsertId(); + return $value !== null ? $value : true; + } + return false; + } + + /** + * Iterate through all the columns and returns the last insert id of the + * first column that has a sequence or serial. + * @return mixed last insert id, null if none is found. + */ + public function getLastInsertID() + { + return $this->getBuilder()->getLastInsertID(); + } + + /** + * @param string __call method name + * @param string criteria conditions + * @param array method arguments + * @return TActiveRecordCriteria criteria created from the method name and its arguments. + */ + public function createCriteriaFromString($method, $condition, $args) + { + $fields = $this->extractMatchingConditions($method, $condition); + $args=count($args) === 1 && is_array($args[0]) ? $args[0] : $args; + if(count($fields)>count($args)) + { + throw new TDbException('dbtablegateway_mismatch_args_exception', + $method,count($fields),count($args)); + } + return new TSqlCriteria(implode(' ',$fields), $args); + } + + /** + * Calculates the AND/OR condition from dynamic method substrings using + * table meta data, allows for any AND-OR combinations. + * @param string dynamic method name + * @param string dynamic method search criteria + * @return array search condition substrings + */ + protected function extractMatchingConditions($method, $condition) + { + $table = $this->getTableInfo(); + $columns = $table->getLowerCaseColumnNames(); + $regexp = '/('.implode('|', array_keys($columns)).')(and|_and_|or|_or_)?/i'; + $matches = array(); + if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER)) + { + throw new TDbException('dbtablegateway_mismatch_column_name', + $method, implode(', ', $columns), $table->getTableFullName()); + } + + $fields = array(); + foreach($matches as $match) + { + $key = $columns[$match[1]]; + $column = $table->getColumn($key)->getColumnName(); + $sql = $column . ' = ? '; + if(count($match) > 2) + $sql .= strtoupper(str_replace('_', '', $match[2])); + $fields[] = $sql; + } + return $fields; + } + + /** + * Raised when a command is prepared and parameter binding is completed. + * The parameter object is TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayEventParameter + */ + public function onCreateCommand($command, $criteria) + { + $this->raiseEvent('OnCreateCommand', $this, new TDataGatewayEventParameter($command,$criteria)); + } + + /** + * Raised when a command is executed and the result from the database was returned. + * The parameter object is TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayResultEventParameter + */ + public function onExecuteCommand($command, $result) + { + $parameter = new TDataGatewayResultEventParameter($command, $result); + $this->raiseEvent('OnExecuteCommand', $this, $parameter); + return $parameter->getResult(); + } +} + +/** + * TDataGatewayEventParameter class contains the TDbCommand to be executed as + * well as the criteria object. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TDataGatewayEventParameter extends TEventParameter +{ + private $_command; + private $_criteria; + + public function __construct($command,$criteria) + { + $this->_command=$command; + $this->_criteria=$criteria; + } + + /** + * The database command to be executed. Do not rebind the parameters or change + * the sql query string. + * @return TDbCommand command to be executed. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return TSqlCriteria criteria used to bind the sql query parameters. + */ + public function getCriteria() + { + return $this->_criteria; + } +} + +/** + * TDataGatewayResultEventParameter contains the TDbCommand executed and the resulting + * data returned from the database. The data can be changed by changing the + * {@link setResult Result} property. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TDataGatewayResultEventParameter extends TEventParameter +{ + private $_command; + private $_result; + + public function __construct($command,$result) + { + $this->_command=$command; + $this->_result=$result; + } + + /** + * @return TDbCommand database command executed. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return mixed result returned from executing the command. + */ + public function getResult() + { + return $this->_result; + } + + /** + * @param mixed change the result returned by the gateway. + */ + public function setResult($value) + { + $this->_result=$value; + } +} + diff --git a/gui/baculum/framework/Data/DataGateway/TSqlCriteria.php b/gui/baculum/framework/Data/DataGateway/TSqlCriteria.php new file mode 100644 index 0000000000..fb8a3a986b --- /dev/null +++ b/gui/baculum/framework/Data/DataGateway/TSqlCriteria.php @@ -0,0 +1,283 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $ + * @package System.Data.DataGateway + */ + +/** + * Search criteria for TDbDataGateway. + * + * Criteria object for data gateway finder methods. Usage: + * + * $criteria = new TSqlCriteria(); + * $criteria->Parameters[':name'] = 'admin'; + * $criteria->Parameters[':pass'] = 'prado'; + * $criteria->OrdersBy['level'] = 'desc'; + * $criteria->OrdersBy['name'] = 'asc'; + * $criteria->Limit = 10; + * $criteria->Offset = 20; + * + * + * @author Wei Zhuo + * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TSqlCriteria extends TComponent +{ + /** + * @var mixed + * @since 3.1.7 + */ + private $_select='*'; + private $_condition; + private $_parameters; + private $_ordersBy; + private $_limit; + private $_offset; + + /** + * Creates a new criteria with given condition; + * @param string sql string after the WHERE stanza + * @param mixed named or indexed parameters, accepts as multiple arguments. + */ + public function __construct($condition=null, $parameters=array()) + { + if(!is_array($parameters) && func_num_args() > 1) + $parameters = array_slice(func_get_args(),1); + $this->_parameters=new TAttributeCollection; + $this->_parameters->setCaseSensitive(true); + $this->_parameters->copyFrom((array)$parameters); + $this->_ordersBy=new TAttributeCollection; + $this->_ordersBy->setCaseSensitive(true); + + $this->setCondition($condition); + } + + /** + * Gets the field list to be placed after the SELECT in the SQL. Default to '*' + * @return mixed + * @since 3.1.7 + */ + public function getSelect() + { + return $this->_select; + } + + /** + * Sets the field list to be placed after the SELECT in the SQL. + * + * Different behavior depends on type of assigned value + * string + * usage without modification + * + * null + * will be expanded to full list of quoted table column names (quoting depends on database) + * + * array + * - Column names will be quoted if used as key or value of array + * + * array('col1', 'col2', 'col2') + * // SELECT `col1`, `col2`, `col3` FROM... + * + * + * - Column aliasing + * + * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)') + * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM... + * + * + * - NULL and scalar values (strings will be quoted depending on database) + * + * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL') + * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM... + * + * + * - If the *-wildcard char is used as key or value, add the full list of quoted table column names + * + * array('col1' => 'NULL', '*') + * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM... + * + * + * @param mixed + * @since 3.1.7 + * @see TDbCommandBuilder::getSelectFieldList() + */ + public function setSelect($value) + { + $this->_select = $value; + } + + /** + * @return string search conditions. + */ + public function getCondition() + { + return $this->_condition; + } + + /** + * Sets the search conditions to be placed after the WHERE clause in the SQL. + * @param string search conditions. + */ + public function setCondition($value) + { + if(empty($value)) { + return; + } + + // supporting the following SELECT-syntax: + // [ORDER BY {col_name | expr | position} + // [ASC | DESC], ...] + // [LIMIT {[offset,] row_count | row_count OFFSET offset}] + // See: http://dev.mysql.com/doc/refman/5.0/en/select.html + + if(preg_match('/ORDER\s+BY\s+(.*?)(?=LIMIT)|ORDER\s+BY\s+(.*?)$/i', $value, $matches) > 0) { + // condition contains ORDER BY + $value = str_replace($matches[0], '', $value); + if(strlen($matches[1]) > 0) { + $this->setOrdersBy($matches[1]); + } else if(strlen($matches[2]) > 0) { + $this->setOrdersBy($matches[2]); + } + } + + if(preg_match('/LIMIT\s+([\d\s,]+)/i', $value, $matches) > 0) { + // condition contains limit + $value = str_replace($matches[0], '', $value); // remove limit from query + if(strpos($matches[1], ',')) { // both offset and limit given + list($offset, $limit) = explode(',', $matches[1]); + $this->_limit = (int)$limit; + $this->_offset = (int)$offset; + } else { // only limit given + $this->_limit = (int)$matches[1]; + } + } + + if(preg_match('/OFFSET\s+(\d+)/i', $value, $matches) > 0) { + // condition contains offset + $value = str_replace($matches[0], '', $value); // remove offset from query + $this->_offset = (int)$matches[1]; // set offset in criteria + } + + $this->_condition = trim($value); + } + + /** + * @return TAttributeCollection list of named parameters and values. + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @param ArrayAccess named parameters. + */ + public function setParameters($value) + { + if(!(is_array($value) || $value instanceof ArrayAccess)) + throw new TException('value must be array or ArrayAccess'); + $this->_parameters->copyFrom($value); + } + + /** + * @return boolean true if the parameter index are string base, false otherwise. + */ + public function getIsNamedParameters() + { + foreach($this->getParameters() as $k=>$v) + return is_string($k); + } + + /** + * @return TAttributeCollection ordering clause. + */ + public function getOrdersBy() + { + return $this->_ordersBy; + } + + /** + * @param mixed ordering clause. + */ + public function setOrdersBy($value) + { + if(is_array($value) || $value instanceof Traversable) + $this->_ordersBy->copyFrom($value); + else + { + $value=trim(preg_replace('/\s+/',' ',(string)$value)); + $orderBys=array(); + foreach(explode(',',$value) as $orderBy) + { + $vs=explode(' ',trim($orderBy)); + $orderBys[$vs[0]]=isset($vs[1])?$vs[1]:'asc'; + } + $this->_ordersBy->copyFrom($orderBys); + } + } + + /** + * @return int maximum number of records to return. + */ + public function getLimit() + { + return $this->_limit; + } + + /** + * @param int maximum number of records to return. + */ + public function setLimit($value) + { + $this->_limit=$value; + } + + /** + * @return int record offset. + */ + public function getOffset() + { + return $this->_offset; + } + + /** + * @param int record offset. + */ + public function setOffset($value) + { + $this->_offset=$value; + } + + /** + * @return string string representation of the parameters. Useful for debugging. + */ + public function __toString() + { + $str = ''; + if(strlen((string)$this->getCondition()) > 0) + $str .= '"'.(string)$this->getCondition().'"'; + $params = array(); + foreach($this->getParameters() as $k=>$v) + $params[] = "{$k} => ${v}"; + if(count($params) > 0) + $str .= ', "'.implode(', ',$params).'"'; + $orders = array(); + foreach($this->getOrdersBy() as $k=>$v) + $orders[] = "{$k} => ${v}"; + if(count($orders) > 0) + $str .= ', "'.implode(', ',$orders).'"'; + if($this->_limit !==null) + $str .= ', '.$this->_limit; + if($this->_offset !== null) + $str .= ', '.$this->_offset; + return $str; + } +} diff --git a/gui/baculum/framework/Data/DataGateway/TTableGateway.php b/gui/baculum/framework/Data/DataGateway/TTableGateway.php new file mode 100644 index 0000000000..1d6385cf98 --- /dev/null +++ b/gui/baculum/framework/Data/DataGateway/TTableGateway.php @@ -0,0 +1,476 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.DataGateway + */ + +/** + * Loads the data gateway command builder and sql criteria. + */ +Prado::using('System.Data.DataGateway.TSqlCriteria'); +Prado::using('System.Data.DataGateway.TDataGatewayCommand'); + +/** + * TTableGateway class provides several find methods to get data from the database + * and update, insert, and delete methods. + * + * Each method maps the input parameters into a SQL call and executes the SQL + * against a database connection. The TTableGateway is stateless + * (with respect to the data and data objects), as its role is to push data back and forth. + * + * Example usage: + * + * //create a connection + * $dsn = 'pgsql:host=localhost;dbname=test'; + * $conn = new TDbConnection($dsn, 'dbuser','dbpass'); + * + * //create a table gateway for table/view named 'address' + * $table = new TTableGateway('address', $conn); + * + * //insert a new row, returns last insert id (if applicable) + * $id = $table->insert(array('name'=>'wei', 'phone'=>'111111')); + * + * $record1 = $table->findByPk($id); //find inserted record + * + * //finds all records, returns an iterator + * $records = $table->findAll(); + * print_r($records->readAll()); + * + * //update the row + * $table->updateByPk($record1, $id); + * + * + * All methods that may return more than one row of data will return an + * TDbDataReader iterator. + * + * The OnCreateCommand event is raised when a command is prepared and parameter + * binding is completed. The parameter object is a TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * + * The OnExecuteCommand event is raised when a command is executed and the result + * from the database was returned. The parameter object is a + * TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * + * + * $table->OnCreateCommand[] = 'log_it'; //any valid PHP callback statement + * $table->OnExecuteCommand[] = array($obj, 'method_name'); // calls 'method_name' on $obj + * + * function log_it($sender, $param) + * { + * var_dump($param); //TDataGatewayEventParameter object. + * } + * + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.DataGateway + * @since 3.1 + */ +class TTableGateway extends TComponent +{ + private $_command; + private $_connection; + + /** + * Creates a new generic table gateway for a given table or view name + * and a database connection. + * @param string|TDbTableInfo table or view name or table information. + * @param TDbConnection database connection. + */ + public function __construct($table,$connection) + { + $this->_connection=$connection; + if(is_string($table)) + $this->setTableName($table); + else if($table instanceof TDbTableInfo) + $this->setTableInfo($table); + else + throw new TDbException('dbtablegateway_invalid_table_info'); + } + + /** + * @param TDbTableInfo table or view information. + */ + protected function setTableInfo($tableInfo) + { + $builder = $tableInfo->createCommandBuilder($this->getDbConnection()); + $this->initCommandBuilder($builder); + } + + /** + * Sets up the command builder for the given table. + * @param string table or view name. + */ + protected function setTableName($tableName) + { + Prado::using('System.Data.Common.TDbMetaData'); + $meta = TDbMetaData::getInstance($this->getDbConnection()); + $this->initCommandBuilder($meta->createCommandBuilder($tableName)); + } + + public function getTableInfo() + { + return $this->getCommand()->getTableInfo(); + } + + public function getTableName() + { + return $this->getTableInfo()->getTableName(); + } + + /** + * @param TDbCommandBuilder database specific command builder. + */ + protected function initCommandBuilder($builder) + { + $this->_command = new TDataGatewayCommand($builder); + $this->_command->OnCreateCommand[] = array($this, 'onCreateCommand'); + $this->_command->OnExecuteCommand[] = array($this, 'onExecuteCommand'); + } + + /** + * Raised when a command is prepared and parameter binding is completed. + * The parameter object is TDataGatewayEventParameter of which the + * {@link TDataGatewayEventParameter::getCommand Command} property can be + * inspected to obtain the sql query to be executed. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayEventParameter + */ + public function onCreateCommand($sender, $param) + { + $this->raiseEvent('OnCreateCommand', $this, $param); + } + + /** + * Raised when a command is executed and the result from the database was returned. + * The parameter object is TDataGatewayResultEventParameter of which the + * {@link TDataGatewayEventParameter::getResult Result} property contains + * the data return from the database. The data returned can be changed + * by setting the {@link TDataGatewayEventParameter::setResult Result} property. + * @param TDataGatewayCommand originator $sender + * @param TDataGatewayResultEventParameter + */ + public function onExecuteCommand($sender, $param) + { + $this->raiseEvent('OnExecuteCommand', $this, $param); + } + + /** + * @return TDataGatewayCommand command builder and executor. + */ + protected function getCommand() + { + return $this->_command; + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * Execute arbituary sql command with binding parameters. + * @param string SQL query string. + * @param array binding parameters, positional or named. + * @return array query results. + */ + public function findBySql($sql, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($sql,$parameters, $args); + return $this->getCommand()->findBySql($criteria); + } + + /** + * Execute arbituary sql command with binding parameters. + * @param string SQL query string. + * @param array binding parameters, positional or named. + * @return TDbDataReader query results. + */ + public function findAllBySql($sql, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($sql,$parameters, $args); + return $this->getCommand()->findAllBySql($criteria); + } + + /** + * Find one single record that matches the criteria. + * + * Usage: + * + * $table->find('username = :name AND password = :pass', + * array(':name'=>$name, ':pass'=>$pass)); + * $table->find('username = ? AND password = ?', array($name, $pass)); + * $table->find('username = ? AND password = ?', $name, $pass); + * //$criteria is of TSqlCriteria + * $table->find($criteria); //the 2nd parameter for find() is ignored. + * + * + * @param string|TSqlCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return array matching record object. + */ + public function find($criteria, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->find($criteria); + } + + /** + * Accepts same parameters as find(), but returns TDbDataReader instead. + * @param string|TSqlCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return TDbDataReader matching records. + */ + public function findAll($criteria=null, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->findAll($criteria); + } + + /** + * Find one record using only the primary key or composite primary keys. Usage: + * + * + * $table->findByPk($primaryKey); + * $table->findByPk($key1, $key2, ...); + * $table->findByPk(array($key1,$key2,...)); + * + * + * @param mixed primary keys + * @return array matching record. + */ + public function findByPk($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getCommand()->findByPk($keys); + } + + /** + * Similar to findByPk(), but returns TDbDataReader instead. + * + * For scalar primary keys: + * + * $table->findAllByPk($key1, $key2, ...); + * $table->findAllByPk(array($key1, $key2, ...)); + * + * + * For composite keys: + * + * $table->findAllByPk(array($key1, $key2), array($key3, $key4), ...); + * $table->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...)); + * + * @param mixed primary keys + * @return TDbDataReader data reader. + */ + public function findAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getCommand()->findAllByPk($keys); + } + + /** + * Delete records from the table with condition given by $where and + * binding values specified by $parameter argument. + * This method uses additional arguments as $parameters. E.g. + * + * $table->delete('age > ? AND location = ?', $age, $location); + * + * @param string delete condition. + * @param array condition parameters. + * @return integer number of records deleted. + */ + public function deleteAll($criteria, $parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->delete($criteria); + } + + /** + * Delete records by primary key. Usage: + * + * + * $table->deleteByPk($primaryKey); //delete 1 record + * $table->deleteByPk($key1,$key2,...); //delete multiple records + * $table->deleteByPk(array($key1,$key2,...)); //delete multiple records + * + * + * For composite primary keys (determined from the table definitions): + * + * $table->deleteByPk(array($key1,$key2)); //delete 1 record + * + * //delete multiple records + * $table->deleteByPk(array($key1,$key2), array($key3,$key4),...); + * + * //delete multiple records + * $table->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. )); + * + * + * @param mixed primary key values. + * @return int number of records deleted. + */ + public function deleteByPk($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->getCommand()->deleteByPk($keys); + } + + /** + * Alias for deleteByPk() + */ + public function deleteAllByPks($keys) + { + if(func_num_args() > 1) + $keys = func_get_args(); + return $this->deleteByPk($keys); + } + + /** + * Find the number of records. + * @param string|TSqlCriteria SQL condition or criteria object. + * @param mixed parameter values. + * @return int number of records. + */ + public function count($criteria=null,$parameters=array()) + { + $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null; + if($criteria!==null) + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->count($criteria); + } + + /** + * Updates the table with new name-value pair $data. Each array key must + * correspond to a column name in the table. The update condition is + * specified by the $where argument and additional binding values can be + * specified using the $parameter argument. + * This method uses additional arguments as $parameters. E.g. + * + * $gateway->update($data, 'age > ? AND location = ?', $age, $location); + * + * @param array new record data. + * @param string update condition + * @param array additional binding name-value pairs. + * @return integer number of records updated. + */ + public function update($data, $criteria, $parameters=array()) + { + $args = func_num_args() > 2 ? array_slice(func_get_args(),2) : null; + $criteria = $this->getCriteria($criteria,$parameters, $args); + return $this->getCommand()->update($data, $criteria); + } + + /** + * Inserts a new record into the table. Each array key must + * correspond to a column name in the table unless a null value is permitted. + * @param array new record data. + * @return mixed last insert id if one column contains a serial or sequence, + * otherwise true if command executes successfully and affected 1 or more rows. + */ + public function insert($data) + { + return $this->getCommand()->insert($data); + } + + /** + * @return mixed last insert id, null if none is found. + */ + public function getLastInsertId() + { + return $this->getCommand()->getLastInsertId(); + } + + /** + * Create a new TSqlCriteria object from a string $criteria. The $args + * are additional parameters and are used in place of the $parameters + * if $parameters is not an array and $args is an arrary. + * @param string|TSqlCriteria sql criteria + * @param mixed parameters passed by the user. + * @param array additional parameters obtained from function_get_args(). + * @return TSqlCriteria criteria object. + */ + protected function getCriteria($criteria, $parameters, $args) + { + if(is_string($criteria)) + { + $useArgs = !is_array($parameters) && is_array($args); + return new TSqlCriteria($criteria,$useArgs ? $args : $parameters); + } + else if($criteria instanceof TSqlCriteria) + return $criteria; + else + throw new TDbException('dbtablegateway_invalid_criteria'); + } + + /** + * Dynamic find method using parts of method name as search criteria. + * Method name starting with "findBy" only returns 1 record. + * Method name starting with "findAllBy" returns 0 or more records. + * Method name starting with "deleteBy" deletes records by the trail criteria. + * The condition is taken as part of the method name after "findBy", "findAllBy" + * or "deleteBy". + * + * The following are equivalent: + * + * $table->findByName($name) + * $table->find('Name = ?', $name); + * + * + * $table->findByUsernameAndPassword($name,$pass); // OR may be used + * $table->findBy_Username_And_Password($name,$pass); // _OR_ may be used + * $table->find('Username = ? AND Password = ?', $name, $pass); + * + * + * $table->findAllByAge($age); + * $table->findAll('Age = ?', $age); + * + * + * $table->deleteAll('Name = ?', $name); + * $table->deleteByName($name); + * + * @return mixed single record if method name starts with "findBy", 0 or more records + * if method name starts with "findAllBy" + */ + public function __call($method,$args) + { + $delete =false; + if($findOne = substr(strtolower($method),0,6)==='findby') + $condition = $method[6]==='_' ? substr($method,7) : substr($method,6); + else if(substr(strtolower($method),0,9)==='findallby') + $condition = $method[9]==='_' ? substr($method,10) : substr($method,9); + else if($delete = substr(strtolower($method),0,8)==='deleteby') + $condition = $method[8]==='_' ? substr($method,9) : substr($method,8); + else if($delete = substr(strtolower($method),0,11)==='deleteallby') + $condition = $method[11]==='_' ? substr($method,12) : substr($method,11); + else + return null; + + $criteria = $this->getCommand()->createCriteriaFromString($method, $condition, $args); + if($delete) + return $this->deleteAll($criteria); + else + return $findOne ? $this->find($criteria) : $this->findAll($criteria); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TDiscriminator.php b/gui/baculum/framework/Data/SqlMap/Configuration/TDiscriminator.php new file mode 100644 index 0000000000..278ef2d3f2 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TDiscriminator.php @@ -0,0 +1,232 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDiscriminator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * The TDiscriminator corresponds to the tag within a . + * + * TDiscriminator allows inheritance logic in SqlMap result mappings. + * SqlMap compares the data found in the discriminator column to the different + * values using the column value's string equivalence. When the string values + * matches a particular , SqlMap will use the defined by + * {@link resultMapping TSubMap::setResultMapping()} property for loading + * the object data. + * + * @author Wei Zhuo + * @version $Id: TDiscriminator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TDiscriminator extends TComponent +{ + private $_column; + private $_type; + private $_typeHandler=null; + private $_columnIndex; + private $_nullValue; + private $_mapping; + private $_resultMaps=array(); + private $_subMaps=array(); + + /** + * @return string the name of the column in the result set from which the + * value will be used to populate the property. + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @param string the name of the column in the result set from which the + * value will be used to populate the property. + */ + public function setColumn($value) + { + $this->_column = $value; + } + + /** + * @param string property type of the parameter to be set. + */ + public function getType() + { + return $this->_type; + } + + /** + * The type attribute is used to explicitly specify the property type of the + * parameter to be set. If the attribute type is not set and the framework + * cannot otherwise determine the type, the type is assumed from the default + * value of the property. + * @return string property type of the parameter to be set. + */ + public function setType($value) + { + $this->_type = $value; + } + + /** + * @return string custom type handler class name (may use namespace). + */ + public function getTypeHandler() + { + return $this->_typeHandler; + } + + /** + * @param string custom type handler class name (may use namespace). + */ + public function setTypeHandler($value) + { + $this->_typeHandler = $value; + } + + /** + * @return int index of the column in the ResultSet + */ + public function getColumnIndex() + { + return $this->_columnIndex; + } + + /** + * The columnIndex attribute value is the index of the column in the + * ResultSet from which the value will be used to populate the object property. + * @param int index of the column in the ResultSet + */ + public function setColumnIndex($value) + { + $this->_columnIndex = TPropertyValue::ensureInteger($value); + } + + /** + * @return mixed outgoing null value replacement. + */ + public function getNullValue() + { + return $this->_nullValue; + } + + /** + * @param mixed outgoing null value replacement. + */ + public function setNullValue($value) + { + $this->_nullValue = $value; + } + + /** + * @return TResultProperty result property for the discriminator column. + */ + public function getMapping() + { + return $this->_mapping; + } + + /** + * @param TSubMap add new sub mapping. + */ + public function addSubMap($subMap) + { + $this->_subMaps[] = $subMap; + } + + /** + * @param string database value + * @return TResultMap result mapping. + */ + public function getSubMap($value) + { + if(isset($this->_resultMaps[$value])) + return $this->_resultMaps[$value]; + } + + /** + * Copies the discriminator properties to a new TResultProperty. + * @param TResultMap result map holding the discriminator. + */ + public function initMapping($resultMap) + { + $this->_mapping = new TResultProperty($resultMap); + $this->_mapping->setColumn($this->getColumn()); + $this->_mapping->setColumnIndex($this->getColumnIndex()); + $this->_mapping->setType($this->getType()); + $this->_mapping->setTypeHandler($this->getTypeHandler()); + $this->_mapping->setNullValue($this->getNullValue()); + } + + /** + * Set the result maps for particular sub-mapping values. + * @param TSqlMapManager sql map manager instance. + */ + public function initialize($manager) + { + foreach($this->_subMaps as $subMap) + { + $this->_resultMaps[$subMap->getValue()] = + $manager->getResultMap($subMap->getResultMapping()); + } + } +} + +/** + * TSubMap class defines a submapping value and the corresponding + * + * The {@link Value setValue()} property is used for comparison with the + * discriminator column value. When the {@link Value setValue()} matches + * that of the discriminator column value, the corresponding {@link ResultMapping setResultMapping} + * is used inplace of the current result map. + * + * @author Wei Zhuo + * @version $Id: TDiscriminator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSubMap extends TComponent +{ + private $_value; + private $_resultMapping; + + /** + * @return string value for comparison with discriminator column value. + */ + public function getValue() + { + return $this->_value; + } + + /** + * @param string value for comparison with discriminator column value. + */ + public function setValue($value) + { + $this->_value = $value; + } + + /** + * The result map to use when the Value matches the discriminator column value. + * @return string ID of a result map + */ + public function getResultMapping() + { + return $this->_resultMapping; + } + + /** + * @param string ID of a result map + */ + public function setResultMapping($value) + { + $this->_resultMapping = $value; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php b/gui/baculum/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php new file mode 100644 index 0000000000..a2abab3a68 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php @@ -0,0 +1,79 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TInlineParameterMapParser.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TInlineParameterMapParser class. + * + * The inline parameter map syntax lets you embed the property name, + * the property type, the column type, and a null value replacement into a + * parametrized SQL statement. + * + * @author Wei Zhuo + * @version $Id: TInlineParameterMapParser.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TInlineParameterMapParser +{ + /** + * Regular expression for parsing inline parameter maps. + */ + const PARAMETER_TOKEN_REGEXP = '/#([^#]+)#/'; + + /** + * Parse the sql text for inline parameters. + * @param string sql text + * @param array file and node details for exception message. + * @return array 'sql' and 'parameters' name value pairs. + */ + public function parse($sqlText, $scope) + { + $matches = array(); + $mappings = array(); + preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches); + + for($i = 0, $k=count($matches[1]); $i<$k; $i++) + { + $mappings[] = $this->parseMapping($matches[1][$i], $scope); + $sqlText = str_replace($matches[0][$i], '?', $sqlText); + } + return array('sql'=>$sqlText, 'parameters'=>$mappings); + } + + /** + * Parse inline parameter with syntax as + * #propertyName,type=string,dbype=Varchar,nullValue=N/A,handler=string# + * @param string parameter token + * @param array file and node details for exception message. + */ + protected function parseMapping($token, $scope) + { + $mapping = new TParameterProperty; + $properties = explode(',', $token); + $mapping->setProperty(trim(array_shift($properties))); + foreach($properties as $property) + { + $prop = explode('=',$property); + $name = trim($prop[0]); $value=trim($prop[1]); + if($mapping->canSetProperty($name)) + $mapping->{'set'.$name}($value); + else + { + throw new TSqlMapUndefinedException( + 'sqlmap_undefined_property_inline_map', + $name, $scope['file'], $scope['node'], $token); + } + } + return $mapping; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TParameterMap.php b/gui/baculum/framework/Data/SqlMap/Configuration/TParameterMap.php new file mode 100644 index 0000000000..e24648d20e --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TParameterMap.php @@ -0,0 +1,210 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TParameterMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TParameterMap corresponds to the element. + * + * TParameterMap holds one or more parameter child elements that map object + * properties to placeholders in a SQL statement. + * + * A TParameterMap defines an ordered list of values that match up with the + * placeholders of a parameterized query statement. While the attributes + * specified by the map still need to be in the correct order, each parameter + * is named. You can populate the underlying class in any order, and the + * TParameterMap ensures each value is passed in the correct order. + * + * Parameter Maps can be provided as an external element and inline. + * The element accepts two attributes: id (required) and extends (optional). + * + * @author Wei Zhuo + * @version $Id: TParameterMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TParameterMap extends TComponent +{ + private $_extend; + private $_properties; + private $_propertyMap; + private $_extendMap; + private $_ID; + + /** + * Initialize the properties and property map collections. + */ + public function __construct() + { + $this->_properties = new TList; + $this->_propertyMap = new TMap; + } + + /** + * @return string a unique identifier for the . + */ + public function getID() + { + return $this->_ID; + } + + /** + * @param string a unique identifier for the . + */ + public function setID($value) + { + $this->_ID=$value; + } + + /** + * @return TParameterProperty[] list of properties for the parameter map. + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * @return string name of another upon which to base this TParameterMap. + */ + public function getExtends() + { + return $this->_extend; + } + + /** + * @param string name of another upon which to base this TParameterMap. + */ + public function setExtends($value) + { + $this->_extend = $value; + } + + /** + * @param string name of a parameter property. + * @return TParameterProperty parameter property. + * @throws TSqlMapException if index is not string nor integer. + */ + public function getProperty($index) + { + if(is_string($index)) + return $this->_propertyMap->itemAt($index); + else if(is_int($index)) + return $this->_properties->itemAt($index); + else + throw new TSqlMapException('sqlmap_index_must_be_string_or_int', $index); + } + + /** + * @param TParameterProperty new parameter property + */ + public function addProperty(TParameterProperty $property) + { + $this->_propertyMap->add($property->getProperty(), $property); + $this->_properties->add($property); + } + + /** + * @param int parameter property index + * @param TParameterProperty new parameter property. + */ + public function insertProperty($index, TParameterProperty $property) + { + $this->_propertyMap->add($property->getProperty(), $property); + $this->_properties->insertAt($index, $property); + } + + /** + * @return array list of property names. + */ + public function getPropertyNames() + { + return $this->_propertyMap->getKeys(); + } + + /** + * Get the value of a property from the the parameter object. + * @param TSqlMapTypeHandlerRegistry type handler registry. + * @param TParameterProperty parameter proproperty. + * @param mixed parameter object to get the value from. + * @return unknown + */ + public function getPropertyValue($registry, $property, $parameterValue) + { + $value = $this->getObjectValue($parameterValue,$property); + + if(($handler=$this->createTypeHandler($property, $registry))!==null) + $value = $handler->getParameter($value); + + $value = $this->nullifyDefaultValue($property,$value); + + if(($type = $property->getType())!==null) + $value = $registry->convertToType($type, $value); + + return $value; + } + + + /** + * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}. + * @param TParameterProperty parameter property + * @param TSqlMapTypeHandlerRegistry type handler registry + * @return TSqlMapTypeHandler type handler. + */ + protected function createTypeHandler($property, $registry) + { + $type=$property->getTypeHandler() ? $property->getTypeHandler() : $property->getType(); + $handler=$registry->getTypeHandler($type); + if($handler===null && $property->getTypeHandler()) + $handler = Prado::createComponent($type); + return $handler; + } + + + /** + * @param mixed object to obtain the property from. + * @param TParameterProperty parameter property. + * @return mixed property value. + * @throws TSqlMapException if property access is invalid. + */ + protected function getObjectValue($object,$property) + { + try + { + return TPropertyAccess::get($object, $property->getProperty()); + } + catch (TInvalidPropertyException $e) + { + throw new TSqlMapException( + 'sqlmap_unable_to_get_property_for_parameter', + $this->getID(), + $property->getProperty(), + (is_object($object) ? get_class($object) : gettype($object)) + ); + } + } + + /** + * When the actual value matches the {@link NullValue TParameterProperty::setNullValue()}, + * set the current value to null. + * @param TParameterProperty parameter property. + * @param mixed current property value + * @return mixed null if NullValue matches currrent value. + */ + protected function nullifyDefaultValue($property,$value) + { + if(($nullValue = $property->getNullValue())!==null) + { + if($nullValue === $value) + $value = null; + } + return $value; + } +} diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TParameterProperty.php b/gui/baculum/framework/Data/SqlMap/Configuration/TParameterProperty.php new file mode 100644 index 0000000000..255ec69049 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TParameterProperty.php @@ -0,0 +1,150 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TParameterProperty.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TParameterProperty corresponds to the tag and defines + * one object property for the + * + * The {@link NullValue setNullValue()} attribute can be set to any valid + * value (based on property type). The {@link NullValue setNullValue()} attribute + * is used to specify an inbound null value replacement. What this means is + * that when the value is detected in the object property, a NULL will be written + * to the database (the opposite behavior of an inbound null value replacement). + * This allows you to use a magic null number in your application for types that + * do not support null values (such as int, double, float). When these types of + * properties contain a matching null value (for example, say, -9999), a NULL + * will be written to the database instead of the value. + * + * @author Wei Zhuo + * @version $Id: TParameterProperty.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TParameterProperty extends TComponent +{ + private $_typeHandler; + private $_type; + private $_column; + private $_dbType; + private $_property; + private $_nullValue; + + /** + * @return string class name of a custom type handler. + */ + public function getTypeHandler() + { + return $this->_typeHandler; + } + + /** + * @param string class name of a custom type handler. + */ + public function setTypeHandler($value) + { + $this->_typeHandler = $value; + } + + /** + * @return string type of the parameter's property + */ + public function getType() + { + return $this->_type; + } + + /** + * @param string type of the parameter's property + */ + public function setType($value) + { + $this->_type = $value; + } + + /** + * @return string name of a parameter to be used in the SQL statement. + */ + public function getColumn() + { + return $this->_column; + } + + /** + * @param string name of a parameter to be used in the SQL statement. + */ + public function setColumn($value) + { + $this->_column = $value; + } + + /** + * @return string the database column type of the parameter to be set by this property. + */ + public function getDbType() + { + return $this->_dbType; + } + + /** + * @param string the database column type of the parameter to be set by this property. + */ + public function setDbType($value) + { + $this->_dbType = $value; + } + + /** + * @return string name of a property of the parameter object. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @param string name of a property of the parameter object. + */ + public function setProperty($value) + { + $this->_property = $value; + } + + /** + * @return mixed null value replacement + */ + public function getNullValue() + { + return $this->_nullValue; + } + + /** + * The nullValue attribute is used to specify an outgoing null value replacement. + * @param mixed null value replacement. + */ + public function setNullValue($value) + { + $this->_nullValue = $value; + } + + public function __sleep() + { + $exprops = array(); $cn = 'TParameterProperty'; + if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler"; + if ($this->_type===null) $exprops[] = "\0$cn\0_type"; + if ($this->_column===null) $exprops[] = "\0$cn\0_column"; + if ($this->_dbType===null) $exprops[] = "\0$cn\0_dbType"; + if ($this->_property===null) $exprops[] = "\0$cn\0_property"; + if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue"; + return array_diff(parent::__sleep(),$exprops); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TResultMap.php b/gui/baculum/framework/Data/SqlMap/Configuration/TResultMap.php new file mode 100644 index 0000000000..65f149df87 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TResultMap.php @@ -0,0 +1,200 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TResultMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TResultMap corresponds to mapping tag. + * + * A TResultMap lets you control how data is extracted from the result of a + * query, and how the columns are mapped to object properties. A TResultMap + * can describe the column type, a null value replacement, and complex property + * mappings including Collections. + * + * The can contain any number of property mappings that map object + * properties to the columns of a result element. The property mappings are + * applied, and the columns are read, in the order that they are defined. + * Maintaining the element order ensures consistent results between different + * drivers and providers. + * + * The {@link Class setClass()} property must be a PHP class object or array instance. + * + * The optional {@link Extends setExtends()} attribute can be set to the ID of + * another upon which to base this . All properties of the + * "parent" will be included as part of this , and values + * from the "parent" are set before any values specified by this . + * + * @author Wei Zhuo + * @version $Id: TResultMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TResultMap extends TComponent +{ + private $_columns; + private $_class; + private $_extends; + private $_groupBy; + private $_discriminator; + private $_typeHandlers; + private $_ID; + + /** + * Initialize the columns collection. + */ + public function __construct() + { + $this->_columns=new TMap; + } + + /** + * @return string a unique identifier for the . + */ + public function getID() + { + return $this->_ID; + } + + /** + * @param string a unique identifier for the . + */ + public function setID($value) + { + $this->_ID=$value; + } + + /** + * @return string result class name. + */ + public function getClass() + { + return $this->_class; + } + + /** + * @param string result class name. + */ + public function setClass($value) + { + $this->_class = $value; + } + + /** + * @return TMap result columns. + */ + public function getColumns() + { + return $this->_columns; + } + + /** + * @return string result map extends another result map. + */ + public function getExtends() + { + return $this->_extends; + } + + /** + * @param string result map extends another result map. + */ + public function setExtends($value) + { + $this->_extends = $value; + } + + /** + * @return string result map groups by. + */ + public function getGroupBy() + { + return $this->_groupBy; + } + + /** + * @param string result map group by + */ + public function setGroupBy($value) + { + $this->_groupBy = $value; + } + + /** + * @return TDiscriminator result class discriminator. + */ + public function getDiscriminator() + { + return $this->_discriminator; + } + + /** + * @param TDiscriminator result class discriminator. + */ + public function setDiscriminator(TDiscriminator $value) + { + $this->_discriminator = $value; + } + + /** + * Add a TResultProperty to result mapping. + * @param TResultProperty result property. + */ + public function addResultProperty(TResultProperty $property) + { + $this->_columns[$property->getProperty()] = $property; + } + + /** + * Create a new instance of the class of this result map. + * @param TSqlMapTypeHandlerRegistry type handler registry. + * @return mixed new result object. + * @throws TSqlMapException + */ + public function createInstanceOfResult($registry) + { + $handler = $registry->getTypeHandler($this->getClass()); + try + { + if($handler!==null) + return $handler->createNewInstance(); + else + return $registry->createInstanceOf($this->getClass()); + } + catch (TSqlMapException $e) + { + throw new TSqlMapException( + 'sqlmap_unable_to_create_new_instance', + $this->getClass(), get_class($handler), $this->getID()); + } + } + + /** + * Result sub-mappings using the discriminiator column. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param array row data. + * @return TResultMap result sub-map. + */ + public function resolveSubMap($registry,$row) + { + $subMap = $this; + if(($disc = $this->getDiscriminator())!==null) + { + $value = $disc->getMapping()->getPropertyValue($registry,$row); + $subMap = $disc->getSubMap((string)$value); + + if($subMap===null) + $subMap = $this; + else if($subMap !== $this) + $subMap = $subMap->resolveSubMap($registry,$row); + } + return $subMap; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TResultProperty.php b/gui/baculum/framework/Data/SqlMap/Configuration/TResultProperty.php new file mode 100644 index 0000000000..54ba832d9f --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TResultProperty.php @@ -0,0 +1,344 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TResultProperty.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TResultProperty corresponds a tags inside a tag. + * + * The {@link NullValue setNullValue()} attribute can be set to any valid + * value (based on property type). The {@link NullValue setNullValue()} attribute + * is used to specify an outgoing null value replacement. What this means is + * that when a null value is detected in the result, the corresponding value of + * the {@link NullValue getNullValue()} will be used instead. + * + * The {@link Select setSelect()} property is used to describe a relationship + * between objects and to automatically load complex (i.e. user defined) + * property types. The value of the {@link Select setSelect()} property must be + * the name of another mapped statement. The value of the database + * {@link Column setColumn()} that is defined in the same property element as + * this statement attribute will be passed to the related mapped statement as + * the parameter. The {@link LazyLoad setLayLoad()} attribute can be specified + * with the {@link Select setSelect()} . + * + * @author Wei Zhuo + * @version $Id: TResultProperty.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TResultProperty extends TComponent +{ + private $_nullValue; + private $_propertyName; + private $_columnName; + private $_columnIndex=-1; + private $_nestedResultMapName; + private $_nestedResultMap; + private $_valueType; + private $_typeHandler; + private $_isLazyLoad=false; + private $_select; + + private $_hostResultMapID='inplicit internal mapping'; + + const LIST_TYPE = 0; + const ARRAY_TYPE = 1; + + /** + * Gets the containing result map ID. + * @param TResultMap containing result map. + */ + public function __construct($resultMap=null) + { + if($resultMap instanceof TResultMap) + $this->_hostResultMapID = $resultMap->getID(); + } + + /** + * @return mixed null value replacement. + */ + public function getNullValue() + { + return $this->_nullValue; + } + + /** + * @param mixed null value replacement. + */ + public function setNullValue($value) + { + $this->_nullValue = $value; + } + + /** + * @return string name of a property of the result object that will be set to. + */ + public function getProperty() + { + return $this->_propertyName; + } + + /** + * @param string name of a property of the result object that will be set to. + */ + public function setProperty($value) + { + $this->_propertyName = $value; + } + + /** + * @return string name of the column in the result set from which the value + * will be used to populate the property. + */ + public function getColumn() + { + return $this->_columnName; + } + + /** + * @param string name of the column in the result set from which the value + * will be used to populate the property. + */ + public function setColumn($value) + { + $this->_columnName = $value; + } + + /** + * @return int index of the column in the ResultSet from which the value will + * be used to populate the object property + */ + public function getColumnIndex() + { + return $this->_columnIndex; + } + + /** + * @param int index of the column in the ResultSet from which the value will + * be used to populate the object property + */ + public function setColumnIndex($value) + { + $this->_columnIndex = TPropertyValue::ensureInteger($value); + } + + /** + * @return string ID of another used to fill the property. + */ + public function getResultMapping() + { + return $this->_nestedResultMapName; + } + + /** + * @param string ID of another used to fill the property. + */ + public function setResultMapping($value) + { + $this->_nestedResultMapName = $value; + } + + /** + * @return TResultMap nested result map. + */ + public function getNestedResultMap() + { + return $this->_nestedResultMap; + } + + /** + * @param TResult nested result map. + */ + public function setNestedResultMap($value) + { + $this->_nestedResultMap = $value; + } + + /** + * @return string property type of the object property to be set. + */ + public function getType() + { + return $this->_valueType; + } + + /** + * @param string property type of the object property to be set. + */ + public function setType($value) + { + $this->_valueType = $value; + } + + /** + * @return string custom type handler class name (may use namespace). + */ + public function getTypeHandler() + { + return $this->_typeHandler; + } + + /** + * @param string custom type handler class name (may use namespace). + */ + public function setTypeHandler($value) + { + $this->_typeHandler = $value; + } + + /** + * @return string name of another mapped statement + */ + public function getSelect() + { + return $this->_select; + } + + /** + * The select property is used to describe a relationship between objects + * and to automatically load complex (i.e. user defined) property types. + * @param string name of another mapped statement. + */ + public function setSelect($value) + { + $this->_select = $value; + } + + /** + * @return boolean indicate whether or not the select statement's results should be lazy loaded + */ + public function getLazyLoad() + { + return $this->_isLazyLoad; + } + + /** + * @param boolean indicate whether or not the select statement's results should be lazy loaded + */ + public function setLazyLoad($value) + { + $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false); + } + + /** + * Gets the value for the current property, converts to applicable type if necessary. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param array result row + * @return mixed property value. + */ + public function getPropertyValue($registry,$row) + { + $value = null; + $index = $this->getColumnIndex(); + $name = $this->getColumn(); + if($index > 0 && isset($row[$index])) + $value = $this->getTypedValue($registry,$row[$index]); + else if(isset($row[$name])) + $value = $this->getTypedValue($registry,$row[$name]); + if(($value===null) && ($this->getNullValue()!==null)) + $value = $this->getTypedValue($registry,$this->getNullValue()); + return $value; + } + + /** + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param mixed raw property value + * @return mixed property value casted to specific type. + */ + protected function getTypedValue($registry,$value) + { + if(($handler = $this->createTypeHandler($registry))!==null) + return $handler->getResult($value); + else + return $registry->convertToType($this->getType(), $value); + } + + /** + * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @return TSqlMapTypeHandler type handler. + */ + protected function createTypeHandler($registry) + { + $type=$this->getTypeHandler() ? $this->getTypeHandler() : $this->getType(); + $handler=$registry->getTypeHandler($type); + if($handler===null && $this->getTypeHandler()) + $handler = Prado::createComponent($type); + return $handler; + } + + /** + * Determines if the type is an instance of ArrayAccess, TList or an array. + * @return int TResultProperty::LIST_TYPE or TResultProperty::ARRAY_TYPE + */ + protected function getPropertyValueType() + { + if(class_exists($type = $this->getType(), false)) //NO force autoloading + { + if($type==='TList') + return self::LIST_TYPE; + $class = new ReflectionClass($type); + if($class->isSubclassOf('TList')) + return self::LIST_TYPE; + if($class->implementsInterface('ArrayAccess')) + return self::ARRAY_TYPE; + } + if(strtolower($type) == 'array') + return self::ARRAY_TYPE; + } + + /** + * Returns true if the result property {@link Type getType()} is of TList type + * or that the actual result object is an instance of TList. + * @param object result object + * @return boolean true if the result object is an instance of TList + */ + public function instanceOfListType($target) + { + if($this->getType()===null) + return TPropertyAccess::get($target,$this->getProperty()) instanceof TList; + return $this->getPropertyValueType() == self::LIST_TYPE; + } + + /** + * Returns true if the result property {@link Type getType()} is of ArrayAccess + * or that the actual result object is an array or implements ArrayAccess + * @param object result object + * @return boolean true if the result object is an instance of ArrayAccess or is an array. + */ + public function instanceOfArrayType($target) + { + if($this->getType()===null) + { + $prop = TPropertyAccess::get($target,$this->getProperty()); + if(is_object($prop)) + return $prop instanceof ArrayAccess; + return is_array($prop); + } + return $this->getPropertyValueType() == self::ARRAY_TYPE; + } + + public function __sleep() + { + $exprops = array(); $cn = 'TResultProperty'; + if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue"; + if ($this->_propertyName===null) $exprops[] = "\0$cn\0_propertyNama"; + if ($this->_columnName===null) $exprops[] = "\0$cn\0_columnName"; + if ($this->_columnIndex==-1) $exprops[] = "\0$cn\0_columnIndex"; + if ($this->_nestedResultMapName===null) $exprops[] = "\0$cn\0_nestedResultMapName"; + if ($this->_nestedResultMap===null) $exprops[] = "\0$cn\0_nestedResultMap"; + if ($this->_valueType===null) $exprops[] = "\0$cn\0_valueType"; + if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler"; + if ($this->_isLazyLoad===false) $exprops[] = "\0$cn\0_isLazyLoad"; + if ($this->_select===null) $exprops[] = "\0$cn\0_select"; + return array_diff(parent::__sleep(),$exprops); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php b/gui/baculum/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php new file mode 100644 index 0000000000..ac7eb059b4 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php @@ -0,0 +1,45 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSimpleDynamicParser.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TSimpleDynamicParser finds place holders $name$ in the sql text and replaces + * it with a TSimpleDynamicParser::DYNAMIC_TOKEN. + * + * @author Wei Zhuo + * @version $Id: TSimpleDynamicParser.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSimpleDynamicParser +{ + const PARAMETER_TOKEN_REGEXP = '/\$([^\$]+)\$/'; + const DYNAMIC_TOKEN = '`!`'; + + /** + * Parse the sql text for dynamic place holders of the form $name$. + * @param string Sql text. + * @return array name value pairs 'sql' and 'parameters'. + */ + public function parse($sqlText) + { + $matches = array(); + $mappings = array(); + preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches); + for($i = 0, $k=count($matches[1]); $i<$k; $i++) + { + $mappings[] = $matches[1][$i]; + $sqlText = str_replace($matches[0][$i], self::DYNAMIC_TOKEN, $sqlText); + } + return array('sql'=>$sqlText, 'parameters'=>$mappings); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php b/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php new file mode 100644 index 0000000000..b560fe41fd --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php @@ -0,0 +1,246 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapCacheModel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TSqlMapCacheModel corresponds to the sql mapping configuration tag. + * + * The results from a query Mapped Statement can be cached simply by specifying + * the {@link CacheModel TSqlMapStatement::setCacheModel()} property in tag. + * A cache model is a configured cache that is defined within the sql map + * configuration file. Cache models are configured using the element. + * + * The cache model uses a pluggable framework for supporting different types of + * caches. The choice of cache is specified by the {@link Implementation setImplementation()} + * property. The class name specified must be one of {@link TSqlMapCacheTypes}. + * + * The cache implementations, LRU and FIFO cache below do not persist across + * requests. That is, once the request is complete, all cache data is lost. + * These caches are useful queries that results in the same repeated data during + * the current request. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCacheModel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapCacheModel extends TComponent +{ + private $_cache; + private $_hits = 0; + private $_requests = 0; + private $_id; + private $_implementation=TSqlMapCacheTypes::Basic; + private $_properties = array(); + private $_flushInterval = 0; + + private static $_cacheTypes = array(); + + public static function registerCacheType($type, $className) + { + self::$_cacheTypes[$type] = $className; + } + + /** + * @return string unique cache model identifier. + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string unique cache model identifier. + */ + public function setID($value) + { + $this->_id = $value; + } + + /** + * @return string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'. + */ + public function getImplementation() + { + return $this->_implementation; + } + + /** + * @param string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'. + */ + public function setImplementation($value) + { + if (isset(self::$_cacheTypes[$value])) + $this->_implementation = $value; + else + $this->_implementation = TPropertyValue::ensureEnum($value,'TSqlMapCacheTypes'); + } + + /** + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + */ + public function setFlushInterval($value) + { + $this->_flushInterval=TPropertyValue::ensureInteger($value); + } + + /** + * @return integer cache duration. + */ + public function getFlushInterval() + { + return $this->_flushInterval; + } + + /** + * Initialize the cache implementation, sets the actual cache contain if supplied. + * @param ISqLMapCache cache implementation instance. + */ + public function initialize($cache=null) + { + if($cache===null) + $this->_cache= Prado::createComponent($this->getImplementationClass(), $this); + else + $this->_cache=$cache; + } + + /** + * @return string cache implementation class name. + */ + public function getImplementationClass() + { + $implementation = $this->_implementation; + if (isset(self::$_cacheTypes[$implementation])) return self::$_cacheTypes[$implementation]; + + switch(TPropertyValue::ensureEnum($implementation,'TSqlMapCacheTypes')) + { + case TSqlMapCacheTypes::FIFO: return 'TSqlMapFifoCache'; + case TSqlMapCacheTypes::LRU : return 'TSqlMapLruCache'; + case TSqlMapCacheTypes::Basic : return 'TSqlMapApplicationCache'; + } + } + + /** + * Register a mapped statement that will trigger a cache flush. + * @param TMappedStatement mapped statement that may flush the cache. + */ + public function registerTriggerStatement($mappedStatement) + { + $mappedStatement->attachEventHandler('OnExecuteQuery',array($this, 'flush')); + } + + /** + * Clears the cache. + */ + public function flush() + { + $this->_cache->flush(); + } + + /** + * @param TSqlMapCacheKey|string cache key + * @return mixed cached value. + */ + public function get($key) + { + if($key instanceof TSqlMapCacheKey) + $key = $key->getHash(); + + //if flush ? + $value = $this->_cache->get($key); + $this->_requests++; + if($value!==null) + $this->_hits++; + return $value; + } + + /** + * @param TSqlMapCacheKey|string cache key + * @param mixed value to be cached. + */ + public function set($key, $value) + { + if($key instanceof TSqlMapCacheKey) + $key = $key->getHash(); + + if($value!==null) + $this->_cache->set($key, $value, $this->_flushInterval); + } + + /** + * @return float cache hit ratio. + */ + public function getHitRatio() + { + if($this->_requests != 0) + return $this->_hits / $this->_requests; + else + return 0; + } +} + +/** + * TSqlMapCacheTypes enumerable class. + * + * Implemented cache are 'Basic', 'FIFO' and 'LRU'. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCacheModel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapCacheTypes extends TEnumerable +{ + const Basic='Basic'; + const FIFO='FIFO'; + const LRU='LRU'; +} + +/** + * TSqlMapCacheKey class. + * + * Provides a hash of the object to be cached. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCacheModel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapCacheKey +{ + private $_key; + + /** + * @param mixed object to be cached. + */ + public function __construct($object) + { + $this->_key = $this->generateKey(serialize($object)); + } + + /** + * @param string serialized object + * @return string crc32 hash of the serialized object. + */ + protected function generateKey($string) + { + return sprintf('%x',crc32($string)); + } + + /** + * @return string object hash. + */ + public function getHash() + { + return $this->_key; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapStatement.php b/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapStatement.php new file mode 100644 index 0000000000..efa164843b --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapStatement.php @@ -0,0 +1,451 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +/** + * TSqlMapStatement class corresponds to element. + * + * Mapped Statements can hold any SQL statement and can use Parameter Maps + * and Result Maps for input and output. + * + * The element is a general "catch all" element that can be used + * for any type of SQL statement. Generally it is a good idea to use one of the + * more specific statement-type elements. The more specific elements provided + * better error-checking and even more functionality. (For example, the insert + * statement can return a database-generated key.) + * + * @author Wei Zhuo + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapStatement extends TComponent +{ + private $_parameterMapName; + private $_parameterMap; + private $_parameterClassName; + private $_resultMapName; + private $_resultMap; + private $_resultClassName; + private $_cacheModelName; + private $_SQL; + private $_listClass; + private $_typeHandler; + private $_extendStatement; + private $_cache; + private $_ID; + + /** + * @return string name for this statement, unique to each sql map manager. + */ + public function getID() + { + return $this->_ID; + } + + /** + * @param string name for this statement, which must be unique for each sql map manager. + */ + public function setID($value) + { + $this->_ID=$value; + } + + /** + * @return string name of a parameter map. + */ + public function getParameterMap() + { + return $this->_parameterMapName; + } + + /** + * A Parameter Map defines an ordered list of values that match up with + * the "?" placeholders of a standard, parameterized query statement. + * @param string parameter map name. + */ + public function setParameterMap($value) + { + $this->_parameterMapName = $value; + } + + /** + * @return string parameter class name. + */ + public function getParameterClass() + { + return $this->_parameterClassName; + } + + /** + * If a {@link ParameterMap setParameterMap()} property is not specified, + * you may specify a ParameterClass instead and use inline parameters. + * The value of the parameterClass attribute can be any existing PHP class name. + * @param string parameter class name. + */ + public function setParameterClass($value) + { + $this->_parameterClassName = $value; + } + + /** + * @return string result map name. + */ + public function getResultMap() + { + return $this->_resultMapName; + } + + /** + * A Result Map lets you control how data is extracted from the result of a + * query, and how the columns are mapped to object properties. + * @param string result map name. + */ + public function setResultMap($value) + { + $this->_resultMapName = $value; + } + + /** + * @return string result class name. + */ + public function getResultClass() + { + return $this->_resultClassName; + } + + /** + * If a {@link ResultMap setResultMap()} is not specified, you may specify a + * ResultClass instead. The value of the ResultClass property can be the + * name of a PHP class or primitives like integer, string, or array. The + * class specified will be automatically mapped to the columns in the + * result, based on the result metadata. + * @param string result class name. + */ + public function setResultClass($value) + { + $this->_resultClassName = $value; + } + + /** + * @return string cache mode name. + */ + public function getCacheModel() + { + return $this->_cacheModelName; + } + + /** + * @param string cache mode name. + */ + public function setCacheModel($value) + { + $this->_cacheModelName = $value; + } + + /** + * @return TSqlMapCacheModel cache implementation instance for this statement. + */ + public function getCache() + { + return $this->_cache; + } + + /** + * @param TSqlMapCacheModel cache implementation instance for this statement. + */ + public function setCache($value) + { + $this->_cache = $value; + } + + /** + * @return TStaticSql sql text container. + */ + public function getSqlText() + { + return $this->_SQL; + } + + /** + * @param TStaticSql sql text container. + */ + public function setSqlText($value) + { + $this->_SQL = $value; + } + + /** + * @return string name of a PHP class that implements ArrayAccess. + */ + public function getListClass() + { + return $this->_listClass; + } + + /** + * An ArrayAccess class can be specified to handle the type of objects in the collection. + * @param string name of a PHP class that implements ArrayAccess. + */ + public function setListClass($value) + { + $this->_listClass = $value; + } + + /** + * @return string another statement element name. + */ + public function getExtends() + { + return $this->_extendStatement; + } + + /** + * @param string name of another statement element to extend. + */ + public function setExtends($value) + { + $this->_extendStatement = $value; + } + + /** + * @return TResultMap the result map corresponding to the + * {@link ResultMap getResultMap()} property. + */ + public function resultMap() + { + return $this->_resultMap; + } + + /** + * @return TParameterMap the parameter map corresponding to the + * {@link ParameterMap getParameterMap()} property. + */ + public function parameterMap() + { + return $this->_parameterMap; + } + + /** + * @param TInlineParameterMap parameter extracted from the sql text. + */ + public function setInlineParameterMap($map) + { + $this->_parameterMap = $map; + } + + /** + * @param TSqlMapManager initialize the statement, sets the result and parameter maps. + */ + public function initialize($manager) + { + if(strlen($this->_resultMapName) > 0) + $this->_resultMap = $manager->getResultMap($this->_resultMapName); + if(strlen($this->_parameterMapName) > 0) + $this->_parameterMap = $manager->getParameterMap($this->_parameterMapName); + } + + /** + * @param TSqlMapTypeHandlerRegistry type handler registry + * @return ArrayAccess new instance of list class. + */ + public function createInstanceOfListClass($registry) + { + if(strlen($type = $this->getListClass()) > 0) + return $this->createInstanceOf($registry,$type); + return array(); + } + + /** + * Create a new instance of a given type. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param string result class name. + * @param array result data. + * @return mixed result object. + */ + protected function createInstanceOf($registry,$type,$row=null) + { + $handler = $registry->getTypeHandler($type); + if($handler!==null) + return $handler->createNewInstance($row); + else + return $registry->createInstanceOf($type); + } + + /** + * Create a new instance of result class. + * @param TSqlMapTypeHandlerRegistry type handler registry + * @param array result data. + * @return mixed result object. + */ + public function createInstanceOfResultClass($registry,$row) + { + if(strlen($type= $this->getResultClass()) > 0) + return $this->createInstanceOf($registry,$type,$row); + } + + public function __sleep() + { + $cn = __CLASS__; + $exprops = array("\0$cn\0_resultMap"); + if (!$this->_parameterMapName) $exprops[] = "\0$cn\0_parameterMapName"; + if (!$this->_parameterMap) $exprops[] = "\0$cn\0_parameterMap"; + if (!$this->_parameterClassName) $exprops[] = "\0$cn\0_parameterClassName"; + if (!$this->_resultMapName) $exprops[] = "\0$cn\0_resultMapName"; + if (!$this->_resultMap) $exprops[] = "\0$cn\0_resultMap"; + if (!$this->_resultClassName) $exprops[] = "\0$cn\0_resultClassName"; + if (!$this->_cacheModelName) $exprops[] = "\0$cn\0_cacheModelName"; + if (!$this->_SQL) $exprops[] = "\0$cn\0_SQL"; + if (!$this->_listClass) $exprops[] = "\0$cn\0_listClass"; + if (!$this->_typeHandler) $exprops[] = "\0$cn\0_typeHandler"; + if (!$this->_extendStatement) $exprops[] = "\0$cn\0_extendStatement"; + if (!$this->_cache) $exprops[] = "\0$cn\0_cache"; + + return array_diff(parent::__sleep(),$exprops); + } + +} + +/** + * TSqlMapSelect class file. + * + * @author Wei Zhuo + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSqlMapSelect extends TSqlMapStatement +{ + private $_generate; + + public function getGenerate(){ return $this->_generate; } + public function setGenerate($value){ $this->_generate = $value; } +} + +/** + * TSqlMapInsert class corresponds to the element. + * + * The element allows child elements that can be used + * to generate a key to be used for the insert command. + * + * @author Wei Zhuo + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapInsert extends TSqlMapStatement +{ + private $_selectKey=null; + + /** + * @return TSqlMapSelectKey select key element. + */ + public function getSelectKey() + { + return $this->_selectKey; + } + + /** + * @param TSqlMapSelectKey select key. + */ + public function setSelectKey($value) + { + $this->_selectKey = $value; + } +} + +/** + * TSqlMapUpdate class corresponds to element. + * + * @author Wei Zhuo + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapUpdate extends TSqlMapStatement +{ +} + +/** + * TSqlMapDelete class corresponds to the element. + * + * @author Wei Zhuo + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapDelete extends TSqlMapUpdate +{ +} + +/** + * TSqlMapSelect corresponds to the element. + * + * @author Wei Zhuo + * @version $Id: TSqlMapStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapSelectKey extends TSqlMapStatement +{ + private $_type = 'post'; + private $_property; + + /** + * @return string select generated key type, 'post' or 'pre'. + */ + public function getType() + { + return $this->_type; + } + + /** + * @param string select generated key type, 'post' or 'pre'. + */ + public function setType($value) + { + $this->_type = strtolower($value) == 'post' ? 'post' : 'pre'; + } + + /** + * @return string property name for the generated key. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @param string property name for the generated key. + */ + public function setProperty($value) + { + $this->_property = $value; + } + + /** + * @throws TSqlMapConfigurationException extends is unsupported. + */ + public function setExtends($value) + { + throw new TSqlMapConfigurationException('sqlmap_can_not_extend_select_key'); + } + + /** + * @return boolean true if key is generated after insert command, false otherwise. + */ + public function getIsAfter() + { + return $this->_type == 'post'; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php new file mode 100644 index 0000000000..abe9b588be --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php @@ -0,0 +1,805 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapXmlConfiguration.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ + +Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement'); + +/** + * TSqlMapXmlConfig class file. + * + * @author Wei Zhuo + * @version $Id: TSqlMapXmlConfiguration.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + */ +abstract class TSqlMapXmlConfigBuilder +{ + /** + * Create an instance of an object give by the attribute named 'class' in the + * node and set the properties on the object given by attribute names and values. + * @param SimpleXmlNode property node + * @return Object new instance of class with class name given by 'class' attribute value. + */ + protected function createObjectFromNode($node) + { + if(isset($node['class'])) + { + $obj = Prado::createComponent((string)$node['class']); + $this->setObjectPropFromNode($obj,$node,array('class')); + return $obj; + } + throw new TSqlMapConfigurationException( + 'sqlmap_node_class_undef', $node, $this->getConfigFile()); + } + + /** + * For each attributes (excluding attribute named in $except) set the + * property of the $obj given by the name of the attribute with the value + * of the attribute. + * @param Object object instance + * @param SimpleXmlNode property node + * @param array exception property name + */ + protected function setObjectPropFromNode($obj,$node,$except=array()) + { + foreach($node->attributes() as $name=>$value) + { + if(!in_array($name,$except)) + { + if($obj->canSetProperty($name)) + $obj->{$name} = (string)$value; + else + throw new TSqlMapConfigurationException( + 'sqlmap_invalid_property', $name, get_class($obj), + $node, $this->getConfigFile()); + } + } + } + + /** + * Gets the filename relative to the basefile. + * @param string base filename + * @param string relative filename + * @return string absolute filename. + */ + protected function getAbsoluteFilePath($basefile,$resource) + { + $basedir = dirname($basefile); + $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource); + if(!is_string($file) || !is_file($file)) + $file = realpath($resource); + if(is_string($file) && is_file($file)) + return $file; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_resource', $resource); + } + + /** + * Load document using simple xml. + * @param string filename. + * @return SimpleXmlElement xml document. + */ + protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config) + { + if( strpos($filename, '${') !== false) + $filename = $config->replaceProperties($filename); + + if(!is_file($filename)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_config', $filename); + return simplexml_load_string($config->replaceProperties(file_get_contents($filename))); + } + + /** + * Get element node by ID value (try for attribute name ID as case insensitive). + * @param SimpleXmlDocument $document + * @param string tag name. + * @param string id value. + * @return SimpleXmlElement node if found, null otherwise. + */ + protected function getElementByIdValue($document, $tag, $value) + { + //hack to allow upper case and lower case attribute names. + foreach(array('id','ID','Id', 'iD') as $id) + { + $xpath = "//{$tag}[@{$id}='{$value}']"; + foreach($document->xpath($xpath) as $node) + return $node; + } + } + + /** + * @return string configuration file. + */ + protected abstract function getConfigFile(); +} + +/** + * TSqlMapXmlConfig class. + * + * Configures the TSqlMapManager using xml configuration file. + * + * @author Wei Zhuo + * @version $Id: TSqlMapXmlConfiguration.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder +{ + /** + * @var TSqlMapManager manager + */ + private $_manager; + /** + * @var string configuration file. + */ + private $_configFile; + /** + * @var array global properties. + */ + private $_properties=array(); + + /** + * @param TSqlMapManager manager instance. + */ + public function __construct($manager) + { + $this->_manager=$manager; + } + + public function getManager() + { + return $this->_manager; + } + + protected function getConfigFile() + { + return $this->_configFile; + } + + /** + * Configure the TSqlMapManager using the given xml file. + * @param string SqlMap configuration xml file. + */ + public function configure($filename=null) + { + $this->_configFile=$filename; + $document = $this->loadXmlDocument($filename,$this); + + foreach($document->xpath('//property') as $property) + $this->loadGlobalProperty($property); + + foreach($document->xpath('//typeHandler') as $handler) + $this->loadTypeHandler($handler); + + foreach($document->xpath('//connection[last()]') as $conn) + $this->loadDatabaseConnection($conn); + + //try to load configuration in the current config file. + $mapping = new TSqlMapXmlMappingConfiguration($this); + $mapping->configure($filename); + + foreach($document->xpath('//sqlMap') as $sqlmap) + $this->loadSqlMappingFiles($sqlmap); + + $this->resolveResultMapping(); + $this->attachCacheModels(); + } + + /** + * Load global replacement property. + * @param SimpleXmlElement property node. + */ + protected function loadGlobalProperty($node) + { + $this->_properties[(string)$node['name']] = (string)$node['value']; + } + + /** + * Load the type handler configurations. + * @param SimpleXmlElement type handler node + */ + protected function loadTypeHandler($node) + { + $handler = $this->createObjectFromNode($node); + $this->_manager->getTypeHandlers()->registerTypeHandler($handler); + } + + /** + * Load the database connection tag. + * @param SimpleXmlElement connection node. + */ + protected function loadDatabaseConnection($node) + { + $conn = $this->createObjectFromNode($node); + $this->_manager->setDbConnection($conn); + } + + /** + * Load SqlMap mapping configuration. + * @param unknown_type $node + */ + protected function loadSqlMappingFiles($node) + { + if(strlen($resource = (string)$node['resource']) > 0) + { + if( strpos($resource, '${') !== false) + $resource = $this->replaceProperties($resource); + + $mapping = new TSqlMapXmlMappingConfiguration($this); + $filename = $this->getAbsoluteFilePath($this->_configFile, $resource); + $mapping->configure($filename); + } + } + + /** + * Resolve nest result mappings. + */ + protected function resolveResultMapping() + { + $maps = $this->_manager->getResultMaps(); + foreach($maps as $entry) + { + foreach($entry->getColumns() as $item) + { + $resultMap = $item->getResultMapping(); + if(strlen($resultMap) > 0) + { + if($maps->contains($resultMap)) + $item->setNestedResultMap($maps[$resultMap]); + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_result_mapping', + $resultMap, $this->_configFile, $entry->getID()); + } + } + if($entry->getDiscriminator()!==null) + $entry->getDiscriminator()->initialize($this->_manager); + } + } + + /** + * Set the cache for each statement having a cache model property. + */ + protected function attachCacheModels() + { + foreach($this->_manager->getMappedStatements() as $mappedStatement) + { + if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0) + { + $cache = $this->_manager->getCacheModel($model); + $mappedStatement->getStatement()->setCache($cache); + } + } + } + + /** + * Replace the place holders ${name} in text with properties the + * corresponding global property value. + * @param string original string. + * @return string string with global property replacement. + */ + public function replaceProperties($string) + { + foreach($this->_properties as $find => $replace) + $string = str_replace('${'.$find.'}', $replace, $string); + return $string; + } +} + +/** + * Loads the statements, result maps, parameters maps from xml configuration. + * + * description + * + * @author Wei Zhuo + * @version $Id: TSqlMapXmlConfiguration.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Configuration + * @since 3.1 + */ +class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder +{ + private $_xmlConfig; + private $_configFile; + private $_manager; + + private $_document; + + private $_FlushOnExecuteStatements=array(); + + /** + * Regular expressions for escaping simple/inline parameter symbols + */ + const SIMPLE_MARK='$'; + const INLINE_SYMBOL='#'; + const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/'; + const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/'; + const SIMPLE_PLACEHOLDER='`!!`'; + const INLINE_PLACEHOLDER='`!!!`'; + + /** + * @param TSqlMapXmlConfiguration parent xml configuration. + */ + public function __construct(TSqlMapXmlConfiguration $xmlConfig) + { + $this->_xmlConfig=$xmlConfig; + $this->_manager=$xmlConfig->getManager(); + } + + protected function getConfigFile() + { + return $this->_configFile; + } + + /** + * Configure an XML mapping. + * @param string xml mapping filename. + */ + public function configure($filename) + { + $this->_configFile=$filename; + $document = $this->loadXmlDocument($filename,$this->_xmlConfig); + $this->_document=$document; + + static $bCacheDependencies; + if($bCacheDependencies === null) + $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance; + + if($bCacheDependencies) + $this->_manager->getCacheDependencies() + ->getDependencies() + ->add(new TFileCacheDependency($filename)); + + foreach($document->xpath('//resultMap') as $node) + $this->loadResultMap($node); + + foreach($document->xpath('//parameterMap') as $node) + $this->loadParameterMap($node); + + foreach($document->xpath('//statement') as $node) + $this->loadStatementTag($node); + + foreach($document->xpath('//select') as $node) + $this->loadSelectTag($node); + + foreach($document->xpath('//insert') as $node) + $this->loadInsertTag($node); + + foreach($document->xpath('//update') as $node) + $this->loadUpdateTag($node); + + foreach($document->xpath('//delete') as $node) + $this->loadDeleteTag($node); + + foreach($document->xpath('//procedure') as $node) + $this->loadProcedureTag($node); + + foreach($document->xpath('//cacheModel') as $node) + $this->loadCacheModel($node); + + $this->registerCacheTriggers(); + } + + /** + * Load the result maps. + * @param SimpleXmlElement result map node. + */ + protected function loadResultMap($node) + { + $resultMap = $this->createResultMap($node); + + //find extended result map. + if(strlen($extendMap = $resultMap->getExtends()) > 0) + { + if(!$this->_manager->getResultMaps()->contains($extendMap)) + { + $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap); + if($extendNode!==null) + $this->loadResultMap($extendNode); + } + + if(!$this->_manager->getResultMaps()->contains($extendMap)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap); + + $superMap = $this->_manager->getResultMap($extendMap); + $resultMap->getColumns()->mergeWith($superMap->getColumns()); + } + + //add the result map + if(!$this->_manager->getResultMaps()->contains($resultMap->getID())) + $this->_manager->addResultMap($resultMap); + } + + /** + * Create a new result map and its associated result properties, + * disciminiator and sub maps. + * @param SimpleXmlElement result map node + * @return TResultMap SqlMap result mapping. + */ + protected function createResultMap($node) + { + $resultMap = new TResultMap(); + $this->setObjectPropFromNode($resultMap,$node); + + //result nodes + foreach($node->result as $result) + { + $property = new TResultProperty($resultMap); + $this->setObjectPropFromNode($property,$result); + $resultMap->addResultProperty($property); + } + + //create the discriminator + $discriminator = null; + if(isset($node->discriminator)) + { + $discriminator = new TDiscriminator(); + $this->setObjectPropFromNode($discriminator, $node->discriminator); + $discriminator->initMapping($resultMap); + } + + foreach($node->xpath('subMap') as $subMapNode) + { + if($discriminator===null) + throw new TSqlMapConfigurationException( + 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode); + $subMap = new TSubMap; + $this->setObjectPropFromNode($subMap,$subMapNode); + $discriminator->addSubMap($subMap); + } + + if($discriminator!==null) + $resultMap->setDiscriminator($discriminator); + + return $resultMap; + } + + /** + * Load parameter map from xml. + * + * @param SimpleXmlElement parameter map node. + */ + protected function loadParameterMap($node) + { + $parameterMap = $this->createParameterMap($node); + + if(strlen($extendMap = $parameterMap->getExtends()) > 0) + { + if(!$this->_manager->getParameterMaps()->contains($extendMap)) + { + $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap); + if($extendNode!==null) + $this->loadParameterMap($extendNode); + } + + if(!$this->_manager->getParameterMaps()->contains($extendMap)) + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap); + $superMap = $this->_manager->getParameterMap($extendMap); + $index = 0; + foreach($superMap->getPropertyNames() as $propertyName) + $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName)); + } + $this->_manager->addParameterMap($parameterMap); + } + + /** + * Create a new parameter map from xml node. + * @param SimpleXmlElement parameter map node. + * @return TParameterMap new parameter mapping. + */ + protected function createParameterMap($node) + { + $parameterMap = new TParameterMap(); + $this->setObjectPropFromNode($parameterMap,$node); + foreach($node->parameter as $parameter) + { + $property = new TParameterProperty(); + $this->setObjectPropFromNode($property,$parameter); + $parameterMap->addProperty($property); + } + return $parameterMap; + } + + /** + * Load statement mapping from xml configuration file. + * @param SimpleXmlElement statement node. + */ + protected function loadStatementTag($node) + { + $statement = new TSqlMapStatement(); + $this->setObjectPropFromNode($statement,$node); + $this->processSqlStatement($statement, $node); + $mappedStatement = new TMappedStatement($this->_manager, $statement); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load extended SQL statements if application. Replaces global properties + * in the sql text. Extracts inline parameter maps. + * @param TSqlMapStatement mapped statement. + * @param SimpleXmlElement statement node. + */ + protected function processSqlStatement($statement, $node) + { + $commandText = (string)$node; + if(strlen($extend = $statement->getExtends()) > 0) + { + $superNode = $this->getElementByIdValue($this->_document,'*',$extend); + if($superNode!==null) + $commandText = (string)$superNode . $commandText; + else + throw new TSqlMapConfigurationException( + 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node); + } + //$commandText = $this->_xmlConfig->replaceProperties($commandText); + $statement->initialize($this->_manager); + $this->applyInlineParameterMap($statement, $commandText, $node); + } + + /** + * Extract inline parameter maps. + * @param TSqlMapStatement statement object. + * @param string sql text + * @param SimpleXmlElement statement node. + */ + protected function applyInlineParameterMap($statement, $sqlStatement, $node) + { + $scope['file'] = $this->_configFile; + $scope['node'] = $node; + + $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement); + if($statement->parameterMap() === null) + { + // Build a Parametermap with the inline parameters. + // if they exist. Then delete inline infos from sqltext. + $parameterParser = new TInlineParameterMapParser; + $sqlText = $parameterParser->parse($sqlStatement, $scope); + if(count($sqlText['parameters']) > 0) + { + $map = new TParameterMap(); + $map->setID($statement->getID().'-InLineParameterMap'); + $statement->setInlineParameterMap($map); + foreach($sqlText['parameters'] as $property) + $map->addProperty($property); + } + $sqlStatement = $sqlText['sql']; + } + $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement); + + $this->prepareSql($statement, $sqlStatement, $node); + } + + /** + * Prepare the sql text (may extend to dynamic sql). + * @param TSqlMapStatement mapped statement. + * @param string sql text. + * @param SimpleXmlElement statement node. + * @todo Extend to dynamic sql. + */ + protected function prepareSql($statement,$sqlStatement, $node) + { + $simpleDynamic = new TSimpleDynamicParser; + $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement); + $dynamics = $simpleDynamic->parse($sqlStatement); + if(count($dynamics['parameters']) > 0) + { + $sql = new TSimpleDynamicSql($dynamics['parameters']); + $sqlStatement = $dynamics['sql']; + } + else + $sql = new TStaticSql(); + $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement); + $sql->buildPreparedStatement($statement, $sqlStatement); + $statement->setSqlText($sql); + } + + /** + * Load select statement from xml mapping. + * @param SimpleXmlElement select node. + */ + protected function loadSelectTag($node) + { + $select = new TSqlMapSelect; + $this->setObjectPropFromNode($select,$node); + $this->processSqlStatement($select,$node); + $mappedStatement = new TMappedStatement($this->_manager, $select); + if(strlen($select->getCacheModel()) > 0) + $mappedStatement = new TCachingStatement($mappedStatement); + + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load insert statement from xml mapping. + * @param SimpleXmlElement insert node. + */ + protected function loadInsertTag($node) + { + $insert = $this->createInsertStatement($node); + $this->processSqlStatement($insert, $node); + $mappedStatement = new TInsertMappedStatement($this->_manager, $insert); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Create new insert statement from xml node. + * @param SimpleXmlElement insert node. + * @return TSqlMapInsert insert statement. + */ + protected function createInsertStatement($node) + { + $insert = new TSqlMapInsert; + $this->setObjectPropFromNode($insert,$node); + if(isset($node->selectKey)) + $this->loadSelectKeyTag($insert,$node->selectKey); + return $insert; + } + + /** + * Load the selectKey statement from xml mapping. + * @param SimpleXmlElement selectkey node + */ + protected function loadSelectKeyTag($insert, $node) + { + $selectKey = new TSqlMapSelectKey; + $this->setObjectPropFromNode($selectKey,$node); + $selectKey->setID($insert->getID()); + $selectKey->setID($insert->getID().'.SelectKey'); + $this->processSqlStatement($selectKey,$node); + $insert->setSelectKey($selectKey); + $mappedStatement = new TMappedStatement($this->_manager, $selectKey); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load update statement from xml mapping. + * @param SimpleXmlElement update node. + */ + protected function loadUpdateTag($node) + { + $update = new TSqlMapUpdate; + $this->setObjectPropFromNode($update,$node); + $this->processSqlStatement($update, $node); + $mappedStatement = new TUpdateMappedStatement($this->_manager, $update); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load delete statement from xml mapping. + * @param SimpleXmlElement delete node. + */ + protected function loadDeleteTag($node) + { + $delete = new TSqlMapDelete; + $this->setObjectPropFromNode($delete,$node); + $this->processSqlStatement($delete, $node); + $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete); + $this->_manager->addMappedStatement($mappedStatement); + } + + /** + * Load procedure statement from xml mapping. + * @todo Implement loading procedure + * @param SimpleXmlElement procedure node + */ + protected function loadProcedureTag($node) + { + //var_dump('todo: add load procedure'); + } + + /** + * Load cache models from xml mapping. + * @param SimpleXmlElement cache node. + */ + protected function loadCacheModel($node) + { + $cacheModel = new TSqlMapCacheModel; + $properties = array('id','implementation'); + foreach($node->attributes() as $name=>$value) + { + if(in_array(strtolower($name), $properties)) + $cacheModel->{'set'.$name}((string)$value); + } + $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel); + $this->setObjectPropFromNode($cache,$node,$properties); + + foreach($node->xpath('property') as $propertyNode) + { + $name = $propertyNode->attributes()->name; + if($name===null || $name==='') continue; + + $value = $propertyNode->attributes()->value; + if($value===null || $value==='') continue; + + if( !TPropertyAccess::has($cache, $name) ) continue; + + TPropertyAccess::set($cache, $name, $value); + } + + $this->loadFlushInterval($cacheModel,$node); + + $cacheModel->initialize($cache); + $this->_manager->addCacheModel($cacheModel); + foreach($node->xpath('flushOnExecute') as $flush) + $this->loadFlushOnCache($cacheModel,$node,$flush); + } + + /** + * Load the flush interval + * @param TSqlMapCacheModel cache model + * @param SimpleXmlElement cache node + */ + protected function loadFlushInterval($cacheModel, $node) + { + $flushInterval = $node->xpath('flushInterval'); + if($flushInterval === null || count($flushInterval) === 0) return; + $duration = 0; + foreach($flushInterval[0]->attributes() as $name=>$value) + { + switch(strToLower($name)) + { + case 'seconds': + $duration += (integer)$value; + break; + case 'minutes': + $duration += 60 * (integer)$value; + break; + case 'hours': + $duration += 3600 * (integer)$value; + break; + case 'days': + $duration += 86400 * (integer)$value; + break; + case 'duration': + $duration = (integer)$value; + break 2; // switch, foreach + } + } + $cacheModel->setFlushInterval($duration); + } + + /** + * Load the flush on cache properties. + * @param TSqlMapCacheModel cache model + * @param SimpleXmlElement parent node. + * @param SimpleXmlElement flush node. + */ + protected function loadFlushOnCache($cacheModel,$parent,$node) + { + $id = $cacheModel->getID(); + if(!isset($this->_FlushOnExecuteStatements[$id])) + $this->_FlushOnExecuteStatements[$id] = array(); + foreach($node->attributes() as $name=>$value) + { + if(strtolower($name)==='statement') + $this->_FlushOnExecuteStatements[$id][] = (string)$value; + } + } + + /** + * Attach CacheModel to statement and register trigger statements for cache models + */ + protected function registerCacheTriggers() + { + foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs) + { + $cacheModel = $this->_manager->getCacheModel($cacheID); + foreach($statementIDs as $statementID) + { + $statement = $this->_manager->getMappedStatement($statementID); + $cacheModel->registerTriggerStatement($statement); + } + } + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php new file mode 100644 index 0000000000..ae6aa200e3 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php @@ -0,0 +1,89 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $ + * @package System.Data.SqlMap + */ + +/** + * TFastSqlMapApplicationCache class file + * + * Fast SqlMap result cache class with minimal-concurrency get/set and atomic flush operations + * + * @author Berczi Gabor + * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $ + * @package System.Data.SqlMap + * @since 3.2 + */ + +class TFastSqlMapApplicationCache implements ICache +{ + protected $_cacheModel=null; + protected $_cache=null; + + public function __construct($cacheModel=null) + { + $this->_cacheModel = $cacheModel; + } + + protected function getBaseKeyKeyName() + { + return 'SqlMapCacheBaseKey::'.$this->_cacheModel->getId(); + } + + protected function getBaseKey() + { + $cache = $this->getCache(); + $keyname = $this->getBaseKeyKeyName(); + $basekey = $cache->get($keyname); + if (!$basekey) + { + $basekey = DxUtil::generateRandomHash(8); + $cache->set($keyname,$basekey); + } + return $basekey; + } + + protected function getCacheKey($key) + { + return $this->getBaseKey().'###'.$key; + } + + public function delete($key) + { + $this->getCache()->delete($this->getCacheKey($key)); + } + + public function flush() + { + $this->getCache()->delete($this->getBaseKeyKeyName()); + } + + public function get($key) + { + $result = $this->getCache()->get($this->getCacheKey($key)); + return $result === false ? null : $result; + } + + public function set($key, $value,$expire=0,$dependency=null) + { + $this->getCache()->set($this->getCacheKey($key), $value, $expire,$dependency); + } + + protected function getCache() + { + if (!$this->_cache) + $this->_cache = Prado::getApplication()->getCache(); + return $this->_cache; + } + + public function add($id,$value,$expire=0,$dependency=null) + { + throw new TSqlMapException('sqlmap_use_set_to_store_cache'); + } +} diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TLazyLoadList.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TLazyLoadList.php new file mode 100644 index 0000000000..c512ef9232 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TLazyLoadList.php @@ -0,0 +1,144 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLazyLoadList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +/** + * TLazyLoadList executes mapped statements when the proxy collection is first accessed. + * + * @author Wei Zhuo + * @version $Id: TLazyLoadList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TLazyLoadList +{ + private $_param; + private $_target; + private $_propertyName=''; + private $_statement=''; + private $_loaded=false; + private $_innerList; + private $_connection; + + /** + * Create a new proxy list that will execute the mapped statement when any + * of the list's method are accessed for the first time. + * @param TMappedStatement statement to be executed to load the data. + * @param mixed parameter value for the statement. + * @param object result object that contains the lazy collection. + * @param string property of the result object to set the loaded collection. + */ + protected function __construct($mappedStatement, $param, $target, $propertyName) + { + $this->_param = $param; + $this->_target = $target; + $this->_statement = $mappedStatement; + $this->_connection=$mappedStatement->getManager()->getDbConnection(); + $this->_propertyName = $propertyName; + } + + /** + * Create a new instance of a lazy collection. + * @param TMappedStatement statement to be executed to load the data. + * @param mixed parameter value for the statement. + * @param object result object that contains the lazy collection. + * @param string property of the result object to set the loaded collection. + * @return TObjectProxy proxied collection object. + */ + public static function newInstance($mappedStatement, $param, $target, $propertyName) + { + $handler = new self($mappedStatement, $param, $target, $propertyName); + $statement = $mappedStatement->getStatement(); + $registry=$mappedStatement->getManager()->getTypeHandlers(); + $list = $statement->createInstanceOfListClass($registry); + if(!is_object($list)) + throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID()); + return new TObjectProxy($handler, $list); + } + + /** + * Relay the method call to the underlying collection. + * @param string method name. + * @param array method parameters. + */ + public function intercept($method, $arguments) + { + return call_user_func_array(array($this->_innerList, $method), $arguments); + } + + /** + * Load the data by executing the mapped statement. + */ + protected function fetchListData() + { + if($this->_loaded == false) + { + $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param); + $this->_loaded = true; + //replace the target property with real list + TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList); + } + } + + /** + * Try to fetch the data when any of the proxy collection method is called. + * @param string method name. + * @return boolean true if the underlying collection has the corresponding method name. + */ + public function hasMethod($method) + { + $this->fetchListData(); + if(is_object($this->_innerList)) + return in_array($method, get_class_methods($this->_innerList)); + return false; + } +} + +/** + * TObjectProxy sets up a simple object that intercepts method calls to a + * particular object and relays the call to handler object. + * + * @author Wei Zhuo + * @version $Id: TLazyLoadList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TObjectProxy +{ + private $_object; + private $_handler; + + /** + * @param object handler to method calls. + * @param object the object to by proxied. + */ + public function __construct($handler, $object) + { + $this->_handler = $handler; + $this->_object = $object; + } + + /** + * Relay the method call to the handler object (if able to be handled), otherwise + * it calls the proxied object's method. + * @param string method name called + * @param array method arguments + * @return mixed method return value. + */ + public function __call($method,$params) + { + if($this->_handler->hasMethod($method)) + return $this->_handler->intercept($method, $params); + else + return call_user_func_array(array($this->_object, $method), $params); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TPropertyAccess.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TPropertyAccess.php new file mode 100644 index 0000000000..20a853c495 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TPropertyAccess.php @@ -0,0 +1,155 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPropertyAccess.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +/** + * TPropertyAccess class provides dot notation stype property access and setting. + * + * Access object's properties (and subproperties) using dot path notation. + * The following are equivalent. + * + * echo $obj->property1; + * echo $obj->getProperty1(); + * echo $obj['property1']; //$obj may be an array or object + * echo TPropertyAccess($obj, 'property1'); + * + * + * Setting a property value. + * + * $obj1->propert1 = 'hello'; + * $obj->setProperty('hello'); + * $obj['property1'] = 'hello'; //$obj may be an array or object + * TPropertyAccess($obj, 'property1', 'hello'); + * + * + * Subproperties are supported using the dot notation. E.g. + * + * echo $obj->property1->property2->property3 + * echo TPropertyAccess::get($obj, 'property1.property2.property3'); + * + * + * @author Wei Zhuo + * @version $Id: TPropertyAccess.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TPropertyAccess +{ + /** + * Gets the property value. + * @param mixed object or path. + * @param string property path. + * @return mixed property value. + * @throws TInvalidDataValueException if property path is invalid. + */ + public static function get($object,$path) + { + if(!is_array($object) && !is_object($object)) + return $object; + $properties = explode('.', $path); + foreach($properties as $prop) + { + if(is_array($object) || $object instanceof ArrayAccess) + { + if(array_key_exists($prop, $object)) + $object = $object[$prop]; + else + throw new TInvalidPropertyException('sqlmap_invalid_property',$path); + } + else if(is_object($object)) + { + $getter = 'get'.$prop; + if(method_exists($object, $getter) && is_callable(array($object, $getter))) + $object = $object->{$getter}(); + else if(in_array($prop, array_keys(get_object_vars($object)))) + $object = $object->{$prop}; + elseif(method_exists($object, '__get') && is_callable(array($object, '__get'))) + $object = $object->{$prop}; + else + throw new TInvalidPropertyException('sqlmap_invalid_property',$path); + } + else + throw new TInvalidPropertyException('sqlmap_invalid_property',$path); + } + return $object; + } + + /** + * @param mixed object or array + * @param string property path. + * @return boolean true if property path is valid + */ + public static function has($object, $path) + { + if(!is_array($object) && !is_object($object)) + return false; + $properties = explode('.', $path); + foreach($properties as $prop) + { + if(is_array($object) || $object instanceof ArrayAccess) + { + if(array_key_exists($prop, $object)) + $object = $object[$prop]; + else + return false; + } + else if(is_object($object)) + { + $getter = 'get'.$prop; + if(method_exists($object, $getter) && is_callable(array($object, $getter))) + $object = $object->{$getter}(); + else if(in_array($prop, array_keys(get_object_vars($object)))) + $object = $object->{$prop}; + elseif(method_exists($object, '__get') && is_callable(array($object, '__get'))) + $object = $object->{$prop}; + else + return false; + } + else + return false; + } + return true; + } + + /** + * Sets the property value. + * @param mixed object or array + * @param string property path. + * @param mixed new property value. + * @throws TInvalidDataValueException if property path is invalid. + */ + public static function set(&$originalObject, $path, $value) + { + $properties = explode('.', $path); + $prop = array_pop($properties); + if(count($properties) > 0) + $object = self::get($originalObject, implode('.',$properties)); + else + $object = &$originalObject; + + if(is_array($object) || $object instanceof ArrayAccess) + { + $object[$prop] = $value; + } + else if(is_object($object)) + { + $setter = 'set'.$prop; + if (method_exists($object, $setter) && is_callable(array($object, $setter))) + $object->{$setter}($value); + else + $object->{$prop} = $value; + } + else + throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path); + } + +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapCache.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapCache.php new file mode 100644 index 0000000000..15e148fe45 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapCache.php @@ -0,0 +1,295 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +/** + * Allow different implementation of caching strategy. See TSqlMapFifoCache + * for a first-in-first-out implementation. See TSqlMapLruCache for + * a least-recently-used cache implementation. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +abstract class TSqlMapCache implements ICache +{ + protected $_keyList; + protected $_cache; + protected $_cacheSize = 100; + protected $_cacheModel = null; + + /** + * Create a new cache with limited cache size. + * @param TSqlMapCacheModel $cacheModel. + */ + public function __construct($cacheModel=null) + { + $this->_cache = new TMap; + $this->_keyList = new TList; + $this->_cacheModel=$cacheModel; + } + + /** + * Maximum number of items to cache. Default size is 100. + * @param int cache size. + */ + public function setCacheSize($value) + { + $this->_cacheSize=TPropertyValue::ensureInteger($value,100); + } + + /** + * @return int cache size. + */ + public function getCacheSize() + { + return $this->_cacheSize; + } + + /** + * @return object the object removed if exists, null otherwise. + */ + public function delete($key) + { + $object = $this->get($key); + $this->_cache->remove($key); + $this->_keyList->remove($key); + return $object; + } + + /** + * Clears the cache. + */ + public function flush() + { + $this->_keyList->clear(); + $this->_cache->clear(); + } + + /** + * @throws TSqlMapException not implemented. + */ + public function add($id,$value,$expire=0,$dependency=null) + { + throw new TSqlMapException('sqlmap_use_set_to_store_cache'); + } +} + +/** + * First-in-First-out cache implementation, removes + * object that was first added when the cache is full. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapFifoCache extends TSqlMapCache +{ + /** + * @return mixed Gets a cached object with the specified key. + */ + public function get($key) + { + return $this->_cache->itemAt($key); + } + + /** + * Stores a value identified by a key into cache. + * The expire and dependency parameters are ignored. + * @param string cache key + * @param mixed value to cache. + */ + public function set($key, $value,$expire=0,$dependency=null) + { + $this->_cache->add($key, $value); + $this->_keyList->add($key); + if($this->_keyList->getCount() > $this->_cacheSize) + { + $oldestKey = $this->_keyList->removeAt(0); + $this->_cache->remove($oldestKey); + } + } +} + +/** + * Least recently used cache implementation, removes + * object that was accessed last when the cache is full. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapLruCache extends TSqlMapCache +{ + /** + * @return mixed Gets a cached object with the specified key. + */ + public function get($key) + { + if($this->_keyList->contains($key)) + { + $this->_keyList->remove($key); + $this->_keyList->add($key); + return $this->_cache->itemAt($key); + } + } + + /** + * Stores a value identified by a key into cache. + * The expire and dependency parameters are ignored. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function set($key, $value,$expire=0,$dependency=null) + { + $this->_cache->add($key, $value); + $this->_keyList->add($key); + if($this->_keyList->getCount() > $this->_cacheSize) + { + $oldestKey = $this->_keyList->removeAt(0); + $this->_cache->remove($oldestKey); + } + } +} + +/** + * TSqlMapApplicationCache uses the default Prado application cache for + * caching SqlMap results. + * + * @author Wei Zhuo + * @version $Id: TSqlMapCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapApplicationCache implements ICache +{ + protected $_cacheModel=null; + + /** + * Create a new cache with limited cache size. + * @param TSqlMapCacheModel $cacheModel. + */ + public function __construct($cacheModel=null) + { + $this->_cacheModel=$cacheModel; + } + + /** + * + * @return string a KeyListID for the cache model. + */ + protected function getKeyListId() + { + $id='keyList'; + if ($this->_cacheModel instanceof TSqlMapCacheModel) + $id.='_'.$this->_cacheModel->getId(); + return $id; + } + /** + * Retreive keylist from cache or create it if it doesn't exists + * @return TList + */ + protected function getKeyList() + { + if (($keyList=$this->getCache()->get($this->getKeyListId()))===false) + { + $keyList=new TList(); + $this->getCache()->set($this->getKeyListId(), $keyList); + } + return $keyList; + } + + protected function setKeyList($keyList) + { + $this->getCache()->set($this->getKeyListId(), $keyList); + } + + /** + * @param string item to be deleted. + */ + public function delete($key) + { + $keyList=$this->getKeyList(); + $keyList->remove($key); + $this->getCache()->delete($key); + $this->setKeyList($keyList); + } + + /** + * Deletes all items in the cache, only for data cached by sqlmap cachemodel + */ + public function flush() + { + $keyList=$this->getKeyList(); + $cache=$this->getCache(); + foreach ($keyList as $key) + { + $cache->delete($key); + } + // Remove the old keylist + $cache->delete($this->getKeyListId()); + } + + /** + * @return mixed Gets a cached object with the specified key. + */ + public function get($key) + { + $result = $this->getCache()->get($key); + if ($result === false) + { + // if the key has not been found in cache (e.g expired), remove from keylist + $keyList=$this->getKeyList(); + if ($keyList->contains($key)) + { + $keyList->remove($key); + $this->setKeyList($keyList); + } + } + return $result === false ? null : $result; + } + + /** + * Stores a value identified by a key into cache. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + */ + public function set($key, $value,$expire=0,$dependency=null) + { + $this->getCache()->set($key, $value, $expire,$dependency); + $keyList=$this->getKeyList(); + if (!$keyList->contains($key)) + { + $keyList->add($key); + $this->setKeyList($keyList); + } + } + + /** + * @return ICache Application cache instance. + */ + protected function getCache() + { + return Prado::getApplication()->getCache(); + } + + /** + * @throws TSqlMapException not implemented. + */ + public function add($id,$value,$expire=0,$dependency=null) + { + throw new TSqlMapException('sqlmap_use_set_to_store_cache'); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapException.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapException.php new file mode 100644 index 0000000000..d308768f28 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapException.php @@ -0,0 +1,115 @@ + + * @version $Id: TSqlMapException.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapException extends TException +{ + /** + * Constructor, similar to the parent constructor. For parameters that + * are of SimpleXmlElement, the tag name and its attribute names and values + * are expanded into a string. + */ + public function __construct($errorMessage) + { + $this->setErrorCode($errorMessage); + $errorMessage=$this->translateErrorMessage($errorMessage); + $args=func_get_args(); + array_shift($args); + $n=count($args); + $tokens=array(); + for($i=0;$i<$n;++$i) + { + if($args[$i] instanceof SimpleXmlElement) + $tokens['{'.$i.'}']=$this->implodeNode($args[$i]); + else + $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]); + } + parent::__construct(strtr($errorMessage,$tokens)); + } + + /** + * @param SimpleXmlElement node + * @return string tag name and attribute names and values. + */ + protected function implodeNode($node) + { + $attributes=array(); + foreach($node->attributes() as $k=>$v) + $attributes[]=$k.'="'.(string)$v.'"'; + return '<'.$node->getName().' '.implode(' ',$attributes).'>'; + } + + /** + * @return string path to the error message file + */ + protected function getErrorMessageFile() + { + $lang=Prado::getPreferredLanguage(); + $dir=dirname(__FILE__); + $msgFile=$dir.'/messages-'.$lang.'.txt'; + if(!is_file($msgFile)) + $msgFile=$dir.'/messages.txt'; + return $msgFile; + } +} + +/** + * TSqlMapConfigurationException, raised during configuration file parsing. + * + * @author Wei Zhuo + * @version $Id: TSqlMapException.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapConfigurationException extends TSqlMapException +{ + +} + +/** + * TSqlMapUndefinedException, raised when mapped statemented are undefined. + * + * @author Wei Zhuo + * @version $Id: TSqlMapException.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapUndefinedException extends TSqlMapException +{ + +} + +/** + * TSqlMapDuplicateException, raised when a duplicate mapped statement is found. + * + * @author Wei Zhuo + * @version $Id: TSqlMapException.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapDuplicateException extends TSqlMapException +{ +} + +/** + * TInvalidPropertyException, raised when setting or getting an invalid property. + * + * @author Wei Zhuo + * @version $Id: TSqlMapException.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TInvalidPropertyException extends TSqlMapException +{ +} + +class TSqlMapExecutionException extends TSqlMapException +{ +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php new file mode 100644 index 0000000000..f3d67d4724 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php @@ -0,0 +1,208 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapPagedList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +Prado::using('System.Collections.TPagedList'); + +/** + * TSqlMapPagedList implements a list with paging functionality that retrieves + * data from a SqlMap statement. + * + * The maximum number of records fetched is 3 times the page size. It fetches + * the current, the previous and the next page at a time. This allows the paged + * list to determine if the page is a the begin, the middle or the end of the list. + * + * The paged list does not need to know about the total number of records. + * + * @author Wei Zhuo + * @version $Id: TSqlMapPagedList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapPagedList extends TPagedList +{ + private $_statement; + private $_parameter; + private $_prevPageList; + private $_nextPageList; + private $_delegate=null; + + /** + * Create a new SqlMap paged list. + * @param IMappedStatement SqlMap statement. + * @param mixed query parameters + * @param int page size + * @param mixed delegate for each data row retrieved. + * @param int number of page to fetch on initialization + */ + public function __construct(IMappedStatement $statement,$parameter, $pageSize, $delegate=null, $page=0) + { + parent::__construct(); + parent::setCustomPaging(true); + $this->initialize($statement,$parameter, $pageSize, $page); + $this->_delegate=$delegate; + } + + /** + * Initialize the paged list. + * @param IMappedStatement SqlMap statement. + * @param mixed query parameters + * @param int page size. + * @param int number of page. + */ + protected function initialize($statement, $parameter, $pageSize, $page) + { + $this->_statement = $statement; + $this->_parameter = $parameter; + $this->setPageSize($pageSize); + $this->attachEventHandler('OnFetchData', array($this, 'fetchDataFromStatement')); + $this->gotoPage($page); + } + + /** + * @throws TSqlMapException custom paging must be enabled. + */ + public function setCustomPaging($value) + { + throw new TSqlMapException('sqlmap_must_enable_custom_paging'); + } + + /** + * Fetch data by executing the SqlMap statement. + * @param TPageList current object. + * @param TPagedListFetchDataEventParameter fetch parameters + */ + protected function fetchDataFromStatement($sender, $param) + { + $limit = $this->getOffsetAndLimit($param); + $connection = $this->_statement->getManager()->getDbConnection(); + $data = $this->_statement->executeQueryForList($connection, + $this->_parameter, null, $limit[0], $limit[1], $this->_delegate); + $this->populateData($param, $data); + } + + /** + * Switches to the next page. + * @return integer|boolean the new page index, false if next page is not availabe. + */ + public function nextPage() + { + return $this->getIsNextPageAvailable() ? parent::nextPage() : false; + } + + /** + * Switches to the previous page. + * @return integer|boolean the new page index, false if previous page is not availabe. + */ + public function previousPage() + { + return $this->getIsPreviousPageAvailable() ? parent::previousPage() : false; + } + + /** + * Populate the list with the fetched data. + * @param TPagedListFetchDataEventParameter fetch parameters + * @param array fetched data. + */ + protected function populateData($param, $data) + { + $total = $data instanceof TList ? $data->getCount() : count($data); + $pageSize = $this->getPageSize(); + if($total < 1) + { + $param->setData($data); + $this->_prevPageList = null; + $this->_nextPageList = null; + return; + } + + if($param->getNewPageIndex() < 1) + { + $this->_prevPageList = null; + if($total <= $pageSize) + { + $param->setData($data); + $this->_nextPageList = null; + } + else + { + $param->setData(array_slice($data, 0, $pageSize)); + $this->_nextPageList = array_slice($data, $pageSize-1,$total); + } + } + else + { + if($total <= $pageSize) + { + $this->_prevPageList = array_slice($data, 0, $total); + $param->setData(array()); + $this->_nextPageList = null; + } + else if($total <= $pageSize*2) + { + $this->_prevPageList = array_slice($data, 0, $pageSize); + $param->setData(array_slice($data, $pageSize, $total)); + $this->_nextPageList = null; + } + else + { + $this->_prevPageList = array_slice($data, 0, $pageSize); + $param->setData(array_slice($data, $pageSize, $pageSize)); + $this->_nextPageList = array_slice($data, $pageSize*2, $total-$pageSize*2); + } + } + } + + /** + * Calculate the data fetch offsets and limits. + * @param TPagedListFetchDataEventParameter fetch parameters + * @return array 1st element is the offset, 2nd element is the limit. + */ + protected function getOffsetAndLimit($param) + { + $index = $param->getNewPageIndex(); + $pageSize = $this->getPageSize(); + return $index < 1 ? array($index, $pageSize*2) : array(($index-1)*$pageSize, $pageSize*3); + } + + /** + * @return boolean true if the next page is available, false otherwise. + */ + public function getIsNextPageAvailable() + { + return $this->_nextPageList!==null; + } + + /** + * @return boolean true if the previous page is available, false otherwise. + */ + public function getIsPreviousPageAvailable() + { + return $this->_prevPageList!==null; + } + + /** + * @return boolean true if is the very last page, false otherwise. + */ + public function getIsLastPage() + { + return ($this->_nextPageList===null) || $this->_nextPageList->getCount() < 1; + } + + /** + * @return boolean true if is not first nor last page, false otherwise. + */ + public function getIsMiddlePage() + { + return !($this->getIsFirstPage() || $this->getIsLastPage()); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php new file mode 100644 index 0000000000..9d743b8228 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php @@ -0,0 +1,192 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapTypeHandlerRegistry.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +/** + * TTypeHandlerFactory provides type handler classes to convert database field type + * to PHP types and vice versa. + * + * @author Wei Zhuo + * @version $Id: TSqlMapTypeHandlerRegistry.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapTypeHandlerRegistry +{ + private $_typeHandlers=array(); + + /** + * @param string database field type + * @return TSqlMapTypeHandler type handler for give database field type. + */ + public function getDbTypeHandler($dbType='NULL') + { + foreach($this->_typeHandlers as $handler) + if($handler->getDbType()===$dbType) + return $handler; + } + + /** + * @param string type handler class name + * @return TSqlMapTypeHandler type handler + */ + public function getTypeHandler($class) + { + if(isset($this->_typeHandlers[$class])) + return $this->_typeHandlers[$class]; + } + + /** + * @param TSqlMapTypeHandler registers a new type handler + */ + public function registerTypeHandler(TSqlMapTypeHandler $handler) + { + $this->_typeHandlers[$handler->getType()] = $handler; + } + + /** + * Creates a new instance of a particular class (for PHP primative types, + * their corresponding default value for given type is used). + * @param string PHP type name + * @return mixed default type value, if no type is specified null is returned. + * @throws TSqlMapException if class name is not found. + */ + public function createInstanceOf($type='') + { + if(strlen($type) > 0) + { + switch(strtolower($type)) + { + case 'string': return ''; + case 'array': return array(); + case 'float': case 'double': case 'decimal': return 0.0; + case 'integer': case 'int': return 0; + case 'bool': case 'boolean': return false; + } + + if(class_exists('Prado', false)) + return Prado::createComponent($type); + else if(class_exists($type, false)) //NO auto loading + return new $type; + else + throw new TSqlMapException('sqlmap_unable_to_find_class', $type); + } + } + + /** + * Converts the value to given type using PHP's settype() function. + * @param string PHP primative type. + * @param mixed value to be casted + * @return mixed type casted value. + */ + public function convertToType($type, $value) + { + switch(strtolower($type)) + { + case 'integer': case 'int': + $type = 'integer'; break; + case 'float': case 'double': case 'decimal': + $type = 'float'; break; + case 'boolean': case 'bool': + $type = 'boolean'; break; + case 'string' : + $type = 'string'; break; + default: + return $value; + } + settype($value, $type); + return $value; + } +} + +/** + * A simple interface for implementing custom type handlers. + * + * Using this interface, you can implement a type handler that + * will perform customized processing before parameters are set + * on and after values are retrieved from the database. + * Using a custom type handler you can extend + * the framework to handle types that are not supported, or + * handle supported types in a different way. For example, + * you might use a custom type handler to implement proprietary + * BLOB support (e.g. Oracle), or you might use it to handle + * booleans using "Y" and "N" instead of the more typical 0/1. + * + * @author Wei Zhuo + * @version $Id: TSqlMapTypeHandlerRegistry.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +abstract class TSqlMapTypeHandler extends TComponent +{ + private $_dbType='NULL'; + private $_type; + /** + * @param string database field type. + */ + public function setDbType($value) + { + $this->_dbType=$value; + } + + /** + * @return string database field type. + */ + public function getDbType() + { + return $this->_dbType; + } + + public function getType() + { + if($this->_type===null) + return get_class($this); + else + return $this->_type; + } + + public function setType($value) + { + $this->_type=$value; + } + + /** + * Performs processing on a value before it is used to set + * the parameter of a IDbCommand. + * @param object The interface for setting the value. + * @param object The value to be set. + */ + public abstract function getParameter($object); + + + /** + * Performs processing on a value before after it has been retrieved + * from a database + * @param object The interface for getting the value. + * @return mixed The processed value. + */ + public abstract function getResult($string); + + + /** + * Casts the string representation of a value into a type recognized by + * this type handler. This method is used to translate nullValue values + * into types that can be appropriately compared. If your custom type handler + * cannot support nullValues, or if there is no reasonable string representation + * for this type (e.g. File type), you can simply return the String representation + * as it was passed in. It is not recommended to return null, unless null was passed + * in. + * @param array result row. + * @return mixed + */ + public abstract function createNewInstance($row=null); +} + diff --git a/gui/baculum/framework/Data/SqlMap/DataMapper/messages.txt b/gui/baculum/framework/Data/SqlMap/DataMapper/messages.txt new file mode 100644 index 0000000000..0923d606b7 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/DataMapper/messages.txt @@ -0,0 +1,66 @@ + +# TSqlMapManager.php +sqlmap_contains_no_statement = Unable to find SQLMap statement '{0}'. +sqlmap_already_contains_statement = Duplicate SQLMap statement found, '{0}' already exists. +sqlmap_contains_no_result_map = Unable to find SQLMap result map '{0}'. +sqlmap_already_contains_result_map = Duplicate SQLMap result map found, '{0}' already exists. +sqlmap_contains_no_parameter_map = Unable to find SQLMap parameter map '{0}'. +sqlmap_already_contains_parameter_map = Duplicate SQLMap parameter map found, '{0}' already exists. +sqlmap_cache_model_already_exists = This SQLMap already contains cache model '{0}'. +sqlmap_unable_to_find_cache_model = Unable to find cache model '{0}' in this SQLMap. + +# TTypeHandlerFactory.php +sqlmap_dbtype_handler_not_found = Type handler for dbType='{0}' not found. +sqlmap_type_handler_class_not_found = Type handler class '{0}' not found. +sqlmap_unable_to_find_class = Unable to find class '{0}'. + +# TSqlMapXmlConfig.php +sqlmap_node_class_undef = Missing attribute 'class' in tag '{0}' in configuration file '{1}'. +sqlmap_unable_to_find_parent_result_map = Unable to find parent SQLMap result map named '{2}' in file {1} near '{0}'. +sqlmap_undefined_discriminator = The tag not found in ResultMap '{0}' for sub-map '{2}' in file '{1}'. +sqlmap_unable_to_find_parent_sql = Unable to find parent sql statement extension '{0}' near '{2}' in file {1}. +sqlmap_invalid_property = Invalid property '{0}' for class '{1}' for tag '{2}' in configuration file '{3}'. + + +# TInlineParameterMapParser.php +sqlmap_undefined_property_inline_map = Invalid attribute '{0}' in '{3}' for inline parameter in statement '{2}' in file {1}. + +# TSqlMapCacheModel.php +sqlmap_unable_to_find_implemenation = Unable to find cache implementation class '{0}'. + +# TResultProperty.php +sqlmap_error_in_result_property_from_handler = For result map '{0}', error in getting result from type handler '{2}', with value '{1}'. + +# TParameterMap.php +sqlmap_index_must_be_string_or_int = Invalid index '{0}', must be an integes or string to get a SqlMap parameter map property. +sqlmap_unable_to_get_property_for_parameter = Unable to find property '{1}' in object '{2}' for parameter map '{0}'. +sqlmap_error_in_parameter_from_handler = For parameter map '{0}', error in getting parameter from type handler '{2}' with value '{1}': '{3}'. + +# MISC +sqlmap_type_handler_class_undef = Unable to find type handler class named '{1}' in sqlmap configuration file '{0}'. +sqlmap_type_handler_callback_undef = Attributes 'type' and 'callback' must be defined in typeHandler tag in configuration file '{0}'. + +sqlmap_undefined_attribute = {0} attribute '{1}' is not defined for {2} in file {3}. +sqlmap_unable_to_find_parent_parameter_map = Unable to find parent parameter map extension '{0}' in file {1}. +sqlmap_unable_to_find_result_mapping = Unable to resolve SQLMap result mapping '{0}' in Result Map '{2}' using configuration file {1}. + +sqlmap_undefined_input_property = Undefined array index '{0}' in retrieving property in SQLMap parameter map '{1}'. +sqlmap_can_not_instantiate = Type handler '{0}' can not create new objects. +sqlmap_cannot_execute_query_for_map = SQLMap statement class {0} can not query for map in statement '{1}'. +sqlmap_cannot_execute_update = SQLMap statement class {0} can not execute update query in statement '{1}'. +sqlmap_cannot_execute_insert = SQLMap statement class {0} can not execute insert in statement '{1}'. +sqlmap_cannot_execute_query_for_list = SQLMap statement class {0} can not query for list in statement '{1}'. +sqlmap_cannot_execute_query_for_object = SQLMap statement class {0} can not query for object in statement '{1}'. +sqlmap_execution_error_no_record = No record set found in executing statement '{0}': '{1}'. +sqlmap_unable_to_create_new_instance = Unable to create a new instance of '{0}' using type hander '{1}' for SQLMap statement with ID '{2}'. +sqlmap_invalid_property_type = Invalid object type, must be 'Object', unable to set property in path '{0}'. + +sqlmap_unable_to_find_config = Unable to find SQLMap configuration file '{0}'. +sqlmap_unable_to_find_groupby = Unable to find data in result set with column '{0}' in result map with ID '{1}'. +sqlmap_invalid_lazyload_list = Invalid type to lazy load, must specify a valid ListClass in statement '{0}'. +sqlmap_unable_to_find_resource = 'Unable to find SQLMap configuration file '{0}'. +sqlmap_query_execution_error = Error in executing SQLMap statement '{0}' : '{1}'. +sqlmap_invalid_delegate = Invalid callback row delegate '{1}' in mapped statement '{0}'. +sqlmap_invalid_prado_cache = Unable to find Prado cache module for SQLMap cache '{0}'. + +sqlmap_non_groupby_array_list_type = Expecting GroupBy property in result map '{0}' since {1}::{2} is an array or TList type. \ No newline at end of file diff --git a/gui/baculum/framework/Data/SqlMap/Statements/IMappedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/IMappedStatement.php new file mode 100644 index 0000000000..175b6f391b --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/IMappedStatement.php @@ -0,0 +1,82 @@ + + * @version $Id: IMappedStatement.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * Interface for all mapping statements. + * + * @author Wei Zhuo + * @version $Id: IMappedStatement.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +interface IMappedStatement +{ + /** + * @return string Name used to identify the MappedStatement amongst the others. + */ + public function getID(); + + /** + * @return TSqlMapStatement The SQL statment used by this TMappedStatement. + */ + public function getStatement(); + + /** + * @return TSqlMap The TSqlMap used by this TMappedStatement + */ + public function getManager(); + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the $keyProperty parameter. The value at + * each key will be the value of the property specified in the + * $valueProperty parameter. If $valueProperty is + * null, the entire result object will be entered. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value (or null) + * @return TMap A map of object containing the rows keyed by $keyProperty. + */ + public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null); + + + /** + * Execute an update statement. Also used for delete statement. Return the + * number of row effected. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @return integer The number of row effected. + */ + public function executeUpdate($connection, $parameter); + + + /** + * Executes the SQL and retuns a subset of the rows selected. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @param TList A list to populate the result with. + * @param integer The number of rows to skip over. + * @param integer The maximum number of rows to return. + * @return TList A TList of result objects. + */ + public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1); + + + /** + * Executes an SQL statement that returns a single row as an object + * of the type of the $result passed in as a parameter. + * @param IDbConnection database connection to execute the query + * @param mixed The object used to set the parameters in the SQL. + * @param object The result object. + * @return object result. + */ + public function executeQueryForObject($connection,$parameter, $result=null); +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TCachingStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TCachingStatement.php new file mode 100644 index 0000000000..b01280dee4 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TCachingStatement.php @@ -0,0 +1,108 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCachingStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TCacheingStatement class. + * + * @author Wei Zhuo + * @version $Id: TCachingStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TCachingStatement extends TComponent implements IMappedStatement +{ + private $_mappedStatement; + + public function __construct(TMappedStatement $statement) + { + $this->_mappedStatement = $statement; + } + + public function getID() + { + return $this->_mappedStatement->getID(); + } + + public function getStatement() + { + return $this->_mappedStatement->getStatement(); + } + + public function getManager() + { + return $this->_mappedStatement->getManager(); + } + + public function executeQueryForMap($connection, $parameter,$keyProperty, $valueProperty=null, $skip=-1, $max=-1,$delegate=null) + { + $sql = $this->createCommand($connection, $parameter, $skip, $max); + $key = $this->getCacheKey(array(clone($sql), $keyProperty, $valueProperty,$skip, $max)); + $map = $this->getStatement()->getCache()->get($key); + if($map===null) + { + $map = $this->_mappedStatement->runQueryForMap( + $connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate); + $this->getStatement()->getCache()->set($key, $map); + } + return $map; + } + + public function executeUpdate($connection, $parameter) + { + return $this->_mappedStatement->executeUpdate($connection, $parameter); + } + + public function executeInsert($connection, $parameter) + { + return $this->executeInsert($connection, $parameter); + } + + public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->createCommand($connection, $parameter, $skip, $max); + $key = $this->getCacheKey(array(clone($sql), $parameter, $skip, $max)); + $list = $this->getStatement()->getCache()->get($key); + if($list===null) + { + $list = $this->_mappedStatement->runQueryForList( + $connection, $parameter, $sql, $result, $delegate); + $this->getStatement()->getCache()->set($key, $list); + } + return $list; + } + + public function executeQueryForObject($connection, $parameter, $result=null) + { + $sql = $this->createCommand($connection, $parameter); + $key = $this->getCacheKey(array(clone($sql), $parameter)); + $object = $this->getStatement()->getCache()->get($key); + if($object===null) + { + $object = $this->_mappedStatement->runQueryForObject($connection, $sql, $result); + $this->getStatement()->getCache()->set($key, $object); + } + return $object; + } + + protected function getCacheKey($object) + { + $cacheKey = new TSqlMapCacheKey($object); + return $cacheKey->getHash(); + } + + protected function createCommand($connection, $parameter, $skip=null, $max=null) + { + return $this->_mappedStatement->getCommand()->create($this->getManager(), + $connection, $this->getStatement(), $parameter, $skip, $max); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php new file mode 100644 index 0000000000..4b023dc77f --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TDeleteMappedStatement.php @@ -0,0 +1,24 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDeleteMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TDeleteMappedStatement class. + * + * @author Wei Zhuo + * @version $Id: TDeleteMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TDeleteMappedStatement extends TUpdateMappedStatement +{ +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TInsertMappedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TInsertMappedStatement.php new file mode 100644 index 0000000000..02bd4a68a4 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TInsertMappedStatement.php @@ -0,0 +1,49 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TInsertMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TInsertMappedStatement class. + * + * @author Wei Zhuo + * @version $Id: TInsertMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TInsertMappedStatement extends TMappedStatement +{ + public function executeQueryForMap($connection, $parameter, + $keyProperty, $valueProperty=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID()); + } + + public function executeUpdate($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_update', get_class($this), $this->getID()); + } + + public function executeQueryForList($connection, $parameter, $result=null, + $skip=-1, $max=-1) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID()); + } + + public function executeQueryForObject($connection, $parameter, $result=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID()); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TMappedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TMappedStatement.php new file mode 100644 index 0000000000..40caf4f716 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TMappedStatement.php @@ -0,0 +1,1242 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TMappedStatement class executes SQL mapped statements. Mapped Statements can + * hold any SQL statement and use Parameter Maps and Result Maps for input and output. + * + * This class is usualy instantiated during SQLMap configuration by TSqlDomBuilder. + * + * @author Wei Zhuo + * @version $Id: TMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.0 + */ +class TMappedStatement extends TComponent implements IMappedStatement +{ + /** + * @var TSqlMapStatement current SQL statement. + */ + private $_statement; + + /** + * @var TPreparedCommand SQL command prepareer + */ + private $_command; + + /** + * @var TSqlMapper sqlmap used by this mapper. + */ + private $_manager; + + /** + * @var TPostSelectBinding[] post select statement queue. + */ + private $_selectQueue=array(); + + /** + * @var boolean true when data is mapped to a particular row. + */ + private $_IsRowDataFound = false; + + /** + * @var TSQLMapObjectCollectionTree group by object collection tree + */ + private $_groupBy; + + /** + * @var Post select is to query for list. + */ + const QUERY_FOR_LIST = 0; + + /** + * @var Post select is to query for list. + */ + const QUERY_FOR_ARRAY = 1; + + /** + * @var Post select is to query for object. + */ + const QUERY_FOR_OBJECT = 2; + + /** + * @return string Name used to identify the TMappedStatement amongst the others. + * This the name of the SQL statement by default. + */ + public function getID() + { + return $this->_statement->ID; + } + + /** + * @return TSqlMapStatement The SQL statment used by this MappedStatement + */ + public function getStatement() + { + return $this->_statement; + } + + /** + * @return TSqlMapper The SqlMap used by this MappedStatement + */ + public function getManager() + { + return $this->_manager; + } + + /** + * @return TPreparedCommand command to prepare SQL statements. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Empty the group by results cache. + */ + protected function initialGroupByResults() + { + $this->_groupBy = new TSqlMapObjectCollectionTree(); + } + + /** + * Creates a new mapped statement. + * @param TSqlMapper an sqlmap. + * @param TSqlMapStatement An SQL statement. + */ + public function __construct(TSqlMapManager $sqlMap, TSqlMapStatement $statement) + { + $this->_manager = $sqlMap; + $this->_statement = $statement; + $this->_command = new TPreparedCommand(); + $this->initialGroupByResults(); + } + + public function getSqlString() + { + return $this->getStatement()->getSqlText()->getPreparedStatement()->getPreparedSql(); + } + + /** + * Execute SQL Query. + * @param IDbConnection database connection + * @param array SQL statement and parameters. + * @return mixed record set if applicable. + * @throws TSqlMapExecutionException if execution error or false record set. + * @throws TSqlMapQueryExecutionException if any execution error + */ +/* protected function executeSQLQuery($connection, $sql) + { + try + { + if(!($recordSet = $connection->execute($sql['sql'],$sql['parameters']))) + { + throw new TSqlMapExecutionException( + 'sqlmap_execution_error_no_record', $this->getID(), + $connection->ErrorMsg()); + } + return $recordSet; + } + catch (Exception $e) + { + throw new TSqlMapQueryExecutionException($this->getStatement(), $e); + } + }*/ + + /** + * Execute SQL Query with limits. + * @param IDbConnection database connection + * @param array SQL statement and parameters. + * @return mixed record set if applicable. + * @throws TSqlMapExecutionException if execution error or false record set. + * @throws TSqlMapQueryExecutionException if any execution error + */ + protected function executeSQLQueryLimit($connection, $command, $max, $skip) + { + if($max>-1 || $skip > -1) + { + $maxStr=$max>0?' LIMIT '.$max:''; + $skipStr=$skip>0?' OFFSET '.$skip:''; + $command->setText($command->getText().$maxStr.$skipStr); + } + $connection->setActive(true); + return $command->query(); + + /*//var_dump($command); + try + { + $recordSet = $connection->selectLimit($sql['sql'],$max,$skip,$sql['parameters']); + if(!$recordSet) + { + throw new TSqlMapExecutionException( + 'sqlmap_execution_error_query_for_list', + $connection->ErrorMsg()); + } + return $recordSet; + } + catch (Exception $e) + { + throw new TSqlMapQueryExecutionException($this->getStatement(), $e); + }*/ + } + + /** + * Executes the SQL and retuns a List of result objects. + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param object result collection object. + * @param integer The number of rows to skip over. + * @param integer The maximum number of rows to return. + * @return array a list of result objects + * @param callback row delegate handler + * @see executeQueryForList() + */ + public function executeQueryForList($connection, $parameter, $result=null, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter,$skip,$max); + return $this->runQueryForList($connection, $parameter, $sql, $result, $delegate); + } + + /** + * Executes the SQL and retuns a List of result objects. + * + * This method should only be called by internal developers, consider using + * executeQueryForList() first. + * + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param array SQL string and subsititution parameters. + * @param object result collection object. + * @param integer The number of rows to skip over. + * @param integer The maximum number of rows to return. + * @param callback row delegate handler + * @return array a list of result objects + * @see executeQueryForList() + */ + public function runQueryForList($connection, $parameter, $sql, $result, $delegate=null) + { + $registry=$this->getManager()->getTypeHandlers(); + $list = $result instanceof ArrayAccess ? $result : + $this->_statement->createInstanceOfListClass($registry); + $connection->setActive(true); + $reader = $sql->query(); + //$reader = $this->executeSQLQueryLimit($connection, $sql, $max, $skip); + if($delegate!==null) + { + foreach($reader as $row) + { + $obj = $this->applyResultMap($row); + $param = new TResultSetListItemParameter($obj, $parameter, $list); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + //var_dump($sql,$parameter); + foreach($reader as $row) + { +// var_dump($row); + $list[] = $this->applyResultMap($row); + } + } + + if(!$this->_groupBy->isEmpty()) + { + $list = $this->_groupBy->collect(); + $this->initialGroupByResults(); + } + + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + + return $list; + } + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the keyProperty parameter. The value at each key + * will be the value of the property specified in the valueProperty parameter. + * If valueProperty is null, the entire result object will be entered. + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value (or null). + * @param callback row delegate handler + * @return array An array of object containing the rows keyed by keyProperty. + */ + public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty=null, $skip=-1, $max=-1, $delegate=null) + { + $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter, $skip, $max); + return $this->runQueryForMap($connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate); + } + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the keyProperty parameter. The value at each key + * will be the value of the property specified in the valueProperty parameter. + * If valueProperty is null, the entire result object will be entered. + * + * This method should only be called by internal developers, consider using + * executeQueryForMap() first. + * + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @param array SQL string and subsititution parameters. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value (or null). + * @param callback row delegate, a callback function + * @return array An array of object containing the rows keyed by keyProperty. + * @see executeQueryForMap() + */ + public function runQueryForMap($connection, $parameter, $command, $keyProperty, $valueProperty=null, $delegate=null) + { + $map = array(); + //$recordSet = $this->executeSQLQuery($connection, $sql); + $connection->setActive(true); + $reader = $command->query(); + if($delegate!==null) + { + //while($row = $recordSet->fetchRow()) + foreach($reader as $row) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $value = ($valueProperty===null) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + $param = new TResultSetMapItemParameter($key, $value, $parameter, $map); + $this->raiseRowDelegate($delegate, $param); + } + } + else + { + //while($row = $recordSet->fetchRow()) + foreach($reader as $row) + { + $obj = $this->applyResultMap($row); + $key = TPropertyAccess::get($obj, $keyProperty); + $map[$key] = ($valueProperty===null) ? $obj : + TPropertyAccess::get($obj, $valueProperty); + } + } + $this->onExecuteQuery($command); + return $map; + } + + /** + * Raises delegate handler. + * This method is invoked for each new list item. It is the responsibility + * of the handler to add the item to the list. + * @param object event parameter + */ + protected function raiseRowDelegate($handler, $param) + { + if(is_string($handler)) + { + call_user_func($handler,$this,$param); + } + else if(is_callable($handler,true)) + { + // an array: 0 - object, 1 - method name/path + list($object,$method)=$handler; + if(is_string($object)) // static method call + call_user_func($handler,$this,$param); + else + { + if(($pos=strrpos($method,'.'))!==false) + { + $object=$this->getSubProperty(substr($method,0,$pos)); + $method=substr($method,$pos+1); + } + $object->$method($this,$param); + } + } + else + throw new TInvalidDataValueException('sqlmap_invalid_delegate', $this->getID(), $handler); + } + + /** + * Executes an SQL statement that returns a single row as an object of the + * type of the $result passed in as a parameter. + * @param IDbConnection database connection + * @param mixed The parameter data (object, arrary, primitive) used to set the parameters in the SQL + * @param mixed The result object. + * @return ${return} + */ + public function executeQueryForObject($connection, $parameter, $result=null) + { + $sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter); + return $this->runQueryForObject($connection, $sql, $result); + } + + /** + * Executes an SQL statement that returns a single row as an object of the + * type of the $result passed in as a parameter. + * + * This method should only be called by internal developers, consider using + * executeQueryForObject() first. + * + * @param IDbConnection database connection + * @param array SQL string and subsititution parameters. + * @param object The result object. + * @return object the object. + * @see executeQueryForObject() + */ + public function runQueryForObject($connection, $command, &$result) + { + $object = null; + $connection->setActive(true); + foreach($command->query() as $row) + $object = $this->applyResultMap($row, $result); + + if(!$this->_groupBy->isEmpty()) + { + $list = $this->_groupBy->collect(); + $this->initialGroupByResults(); + $object = $list[0]; + } + + $this->executePostSelect($connection); + $this->onExecuteQuery($command); + + return $object; + } + + /** + * Execute an insert statement. Fill the parameter object with the ouput + * parameters if any, also could return the insert generated key. + * @param IDbConnection database connection + * @param mixed The parameter object used to fill the statement. + * @return string the insert generated key. + */ + public function executeInsert($connection, $parameter) + { + $generatedKey = $this->getPreGeneratedSelectKey($connection, $parameter); + + $command = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter); +// var_dump($command,$parameter); + $result = $command->execute(); + + if($generatedKey===null) + $generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter); + + $this->executePostSelect($connection); + $this->onExecuteQuery($command); + return $generatedKey; + } + + /** + * Gets the insert generated ID before executing an insert statement. + * @param IDbConnection database connection + * @param mixed insert statement parameter. + * @return string new insert ID if pre-select key statement was executed, null otherwise. + */ + protected function getPreGeneratedSelectKey($connection, $parameter) + { + if($this->_statement instanceof TSqlMapInsert) + { + $selectKey = $this->_statement->getSelectKey(); + if(($selectKey!==null) && !$selectKey->getIsAfter()) + return $this->executeSelectKey($connection, $parameter, $selectKey); + } + } + + /** + * Gets the inserted row ID after executing an insert statement. + * @param IDbConnection database connection + * @param mixed insert statement parameter. + * @return string last insert ID, null otherwise. + */ + protected function getPostGeneratedSelectKey($connection, $parameter) + { + if($this->_statement instanceof TSqlMapInsert) + { + $selectKey = $this->_statement->getSelectKey(); + if(($selectKey!==null) && $selectKey->getIsAfter()) + return $this->executeSelectKey($connection, $parameter, $selectKey); + } + } + + /** + * Execute the select key statement, used to obtain last insert ID. + * @param IDbConnection database connection + * @param mixed insert statement parameter + * @param TSqlMapSelectKey select key statement + * @return string last insert ID. + */ + protected function executeSelectKey($connection, $parameter, $selectKey) + { + $mappedStatement = $this->getManager()->getMappedStatement($selectKey->getID()); + $generatedKey = $mappedStatement->executeQueryForObject( + $connection, $parameter, null); + if(strlen($prop = $selectKey->getProperty()) > 0) + TPropertyAccess::set($parameter, $prop, $generatedKey); + return $generatedKey; + } + + /** + * Execute an update statement. Also used for delete statement. + * Return the number of rows effected. + * @param IDbConnection database connection + * @param mixed The object used to set the parameters in the SQL. + * @return integer The number of rows effected. + */ + public function executeUpdate($connection, $parameter) + { + $sql = $this->_command->create($this->getManager(),$connection, $this->_statement, $parameter); + $affectedRows = $sql->execute(); + //$this->executeSQLQuery($connection, $sql); + $this->executePostSelect($connection); + $this->onExecuteQuery($sql); + return $affectedRows; + } + + /** + * Process 'select' result properties + * @param IDbConnection database connection + */ + protected function executePostSelect($connection) + { + while(count($this->_selectQueue)) + { + $postSelect = array_shift($this->_selectQueue); + $method = $postSelect->getMethod(); + $statement = $postSelect->getStatement(); + $property = $postSelect->getResultProperty()->getProperty(); + $keys = $postSelect->getKeys(); + $resultObject = $postSelect->getResultObject(); + + if($method == self::QUERY_FOR_LIST || $method == self::QUERY_FOR_ARRAY) + { + $values = $statement->executeQueryForList($connection, $keys, null); + + if($method == self::QUERY_FOR_ARRAY) + $values = $values->toArray(); + TPropertyAccess::set($resultObject, $property, $values); + } + else if($method == self::QUERY_FOR_OBJECT) + { + $value = $statement->executeQueryForObject($connection, $keys, null); + TPropertyAccess::set($resultObject, $property, $value); + } + } + } + + /** + * Raise the execute query event. + * @param array prepared SQL statement and subsititution parameters + */ + public function onExecuteQuery($sql) + { + $this->raiseEvent('OnExecuteQuery', $this, $sql); + } + + /** + * Apply result mapping. + * @param array a result set row retrieved from the database + * @param object the result object, will create if necessary. + * @return object the result filled with data, null if not filled. + */ + protected function applyResultMap($row, &$resultObject=null) + { + if($row === false) return null; + + $resultMapName = $this->_statement->getResultMap(); + $resultClass = $this->_statement->getResultClass(); + + $obj=null; + if($this->getManager()->getResultMaps()->contains($resultMapName)) + $obj = $this->fillResultMap($resultMapName, $row, null, $resultObject); + else if(strlen($resultClass) > 0) + $obj = $this->fillResultClass($resultClass, $row, $resultObject); + else + $obj = $this->fillDefaultResultMap(null, $row, $resultObject); + if(class_exists('TActiveRecord',false) && $obj instanceof TActiveRecord) + //Create a new clean active record. + $obj=TActiveRecord::createRecord(get_class($obj),$obj); + return $obj; + } + + /** + * Fill the result using ResultClass, will creates new result object if required. + * @param string result object class name + * @param array a result set row retrieved from the database + * @param object the result object, will create if necessary. + * @return object result object filled with data + */ + protected function fillResultClass($resultClass, $row, $resultObject) + { + if($resultObject===null) + { + $registry = $this->getManager()->getTypeHandlers(); + $resultObject = $this->_statement->createInstanceOfResultClass($registry,$row); + } + + if($resultObject instanceOf ArrayAccess) + return $this->fillResultArrayList($row, $resultObject); + else if(is_object($resultObject)) + return $this->fillResultObjectProperty($row, $resultObject); + else + return $this->fillDefaultResultMap(null, $row, $resultObject); + } + + /** + * Apply the result to a TList or an array. + * @param array a result set row retrieved from the database + * @param object result object, array or list + * @return object result filled with data. + */ + protected function fillResultArrayList($row, $resultObject) + { + if($resultObject instanceof TList) + foreach($row as $v) + $resultObject[] = $v; + else + foreach($row as $k => $v) + $resultObject[$k] = $v; + return $resultObject; + } + + /** + * Apply the result to an object. + * @param array a result set row retrieved from the database + * @param object result object, array or list + * @return object result filled with data. + */ + protected function fillResultObjectProperty($row, $resultObject) + { + $index = 0; + $registry=$this->getManager()->getTypeHandlers(); + foreach($row as $k=>$v) + { + $property = new TResultProperty; + if(is_string($k) && strlen($k) > 0) + $property->setColumn($k); + $property->setColumnIndex(++$index); + $type = gettype(TPropertyAccess::get($resultObject,$k)); + $property->setType($type); + $value = $property->getPropertyValue($registry,$row); + TPropertyAccess::set($resultObject, $k,$value); + } + return $resultObject; + } + + /** + * Fills the result object according to result mappings. + * @param string result map name. + * @param array a result set row retrieved from the database + * @param object result object to fill, will create new instances if required. + * @return object result object filled with data. + */ + protected function fillResultMap($resultMapName, $row, $parentGroup=null, &$resultObject=null) + { + $resultMap = $this->getManager()->getResultMap($resultMapName); + $registry = $this->getManager()->getTypeHandlers(); + $resultMap = $resultMap->resolveSubMap($registry,$row); + + if($resultObject===null) + $resultObject = $resultMap->createInstanceOfResult($registry); + + if(is_object($resultObject)) + { + if(strlen($resultMap->getGroupBy()) > 0) + return $this->addResultMapGroupBy($resultMap, $row, $parentGroup, $resultObject); + else + foreach($resultMap->getColumns() as $property) + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + } + else + { + $resultObject = $this->fillDefaultResultMap($resultMap, $row, $resultObject); + } + return $resultObject; + } + + /** + * ResultMap with GroupBy property. Save object collection graph in a tree + * and collect the result later. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return object result object. + */ + protected function addResultMapGroupBy($resultMap, $row, $parent, &$resultObject) + { + $group = $this->getResultMapGroupKey($resultMap, $row); + + if(empty($parent)) + { + $rootObject = array('object'=>$resultObject, 'property' => null); + $this->_groupBy->add(null, $group, $rootObject); + } + + foreach($resultMap->getColumns() as $property) + { + //set properties. + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + $nested = $property->getResultMapping(); + + //nested property + if($this->getManager()->getResultMaps()->contains($nested)) + { + $nestedMap = $this->getManager()->getResultMap($nested); + $groupKey = $this->getResultMapGroupKey($nestedMap, $row); + + //add the node reference first + if(empty($parent)) + $this->_groupBy->add($group, $groupKey, ''); + + //get the nested result mapping value + $value = $this->fillResultMap($nested, $row, $groupKey); + + //add it to the object tree graph + $groupObject = array('object'=>$value, 'property' => $property->getProperty()); + if(empty($parent)) + $this->_groupBy->add($group, $groupKey, $groupObject); + else + $this->_groupBy->add($parent, $groupKey, $groupObject); + } + } + return $resultObject; + } + + /** + * Gets the result 'group by' groupping key for each row. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @return string groupping key. + */ + protected function getResultMapGroupKey($resultMap, $row) + { + $groupBy = $resultMap->getGroupBy(); + if(isset($row[$groupBy])) + return $resultMap->getID().$row[$groupBy]; + else + return $resultMap->getID().crc32(serialize($row)); + } + + /** + * Fill the result map using default settings. If $resultMap is null + * the result object returned will be guessed from $resultObject. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return mixed the result object filled with data. + */ + protected function fillDefaultResultMap($resultMap, $row, $resultObject) + { + if($resultObject===null) + $resultObject=''; + + if($resultMap!==null) + $result = $this->fillArrayResultMap($resultMap, $row, $resultObject); + else + $result = $row; + + //if scalar result types + if(count($result) == 1 && ($type = gettype($resultObject))!= 'array') + return $this->getScalarResult($result, $type); + else + return $result; + } + + /** + * Retrieve the result map as an array. + * @param TResultMap result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return array array list of result objects. + */ + protected function fillArrayResultMap($resultMap, $row, $resultObject) + { + $result = array(); + $registry=$this->getManager()->getTypeHandlers(); + foreach($resultMap->getColumns() as $column) + { + if(($column->getType()===null) + && ($resultObject!==null) && !is_object($resultObject)) + $column->setType(gettype($resultObject)); + $result[$column->getProperty()] = $column->getPropertyValue($registry,$row); + } + return $result; + } + + /** + * Converts the first array value to scalar value of given type. + * @param array list of results + * @param string scalar type. + * @return mixed scalar value. + */ + protected function getScalarResult($result, $type) + { + $scalar = array_shift($result); + settype($scalar, $type); + return $scalar; + } + + /** + * Set a property of the result object with appropriate value. + * @param TResultMap result mapping details. + * @param TResultProperty the result property to fill. + * @param array a result set row retrieved from the database + * @param object the result object + */ + protected function setObjectProperty($resultMap, $property, $row, &$resultObject) + { + $select = $property->getSelect(); + $key = $property->getProperty(); + $nested = $property->getNestedResultMap(); + $registry=$this->getManager()->getTypeHandlers(); + if($key === '') + { + $resultObject = $property->getPropertyValue($registry,$row); + } + else if(strlen($select) == 0 && ($nested===null)) + { + $value = $property->getPropertyValue($registry,$row); + + $this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null); + if(is_array($resultObject) || is_object($resultObject)) + TPropertyAccess::set($resultObject, $key, $value); + else + $resultObject = $value; + } + else if($nested!==null) + { + if($property->instanceOfListType($resultObject) || $property->instanceOfArrayType($resultObject)) + { + if(strlen($resultMap->getGroupBy()) <= 0) + throw new TSqlMapExecutionException( + 'sqlmap_non_groupby_array_list_type', $resultMap->getID(), + get_class($resultObject), $key); + } + else + { + $obj = $nested->createInstanceOfResult($this->getManager()->getTypeHandlers()); + if($this->fillPropertyWithResultMap($nested, $row, $obj) == false) + $obj = null; + TPropertyAccess::set($resultObject, $key, $obj); + } + } + else //'select' ResultProperty + { + $this->enquequePostSelect($select, $resultMap, $property, $row, $resultObject); + } + } + + /** + * Add nested result property to post select queue. + * @param string post select statement ID + * @param TResultMap current result mapping details. + * @param TResultProperty current result property. + * @param array a result set row retrieved from the database + * @param object the result object + */ + protected function enquequePostSelect($select, $resultMap, $property, $row, $resultObject) + { + $statement = $this->getManager()->getMappedStatement($select); + $key = $this->getPostSelectKeys($resultMap, $property, $row); + $postSelect = new TPostSelectBinding; + $postSelect->setStatement($statement); + $postSelect->setResultObject($resultObject); + $postSelect->setResultProperty($property); + $postSelect->setKeys($key); + + if($property->instanceOfListType($resultObject)) + { + $values = null; + if($property->getLazyLoad()) + { + $values = TLazyLoadList::newInstance($statement, $key, + $resultObject, $property->getProperty()); + TPropertyAccess::set($resultObject, $property->getProperty(), $values); + } + else + $postSelect->setMethod(self::QUERY_FOR_LIST); + } + else if($property->instanceOfArrayType($resultObject)) + $postSelect->setMethod(self::QUERY_FOR_ARRAY); + else + $postSelect->setMethod(self::QUERY_FOR_OBJECT); + + if(!$property->getLazyLoad()) + $this->_selectQueue[] = $postSelect; + } + + /** + * Finds in the post select property the SQL statement primary selection keys. + * @param TResultMap result mapping details + * @param TResultProperty result property + * @param array current row data. + * @return array list of primary key values. + */ + protected function getPostSelectKeys($resultMap, $property,$row) + { + $value = $property->getColumn(); + if(is_int(strpos($value.',',0)) || is_int(strpos($value, '=',0))) + { + $keys = array(); + foreach(explode(',', $value) as $entry) + { + $pair =explode('=',$entry); + $keys[trim($pair[0])] = $row[trim($pair[1])]; + } + return $keys; + } + else + { + $registry=$this->getManager()->getTypeHandlers(); + return $property->getPropertyValue($registry,$row); + } + } + + /** + * Fills the property with result mapping results. + * @param TResultMap nested result mapping details. + * @param array a result set row retrieved from the database + * @param object the result object + * @return boolean true if the data was found, false otherwise. + */ + protected function fillPropertyWithResultMap($resultMap, $row, &$resultObject) + { + $dataFound = false; + foreach($resultMap->getColumns() as $property) + { + $this->_IsRowDataFound = false; + $this->setObjectProperty($resultMap, $property, $row, $resultObject); + $dataFound = $dataFound || $this->_IsRowDataFound; + } + $this->_IsRowDataFound = $dataFound; + return $dataFound; + } + + public function __wakeup() + { + parent::__wakeup(); + if (is_null($this->_selectQueue)) $this->_selectQueue = array(); + } + + public function __sleep() + { + $exprops = array(); $cn = __CLASS__; + if (!count($this->_selectQueue)) $exprops[] = "\0$cn\0_selectQueue"; + if (is_null($this->_groupBy)) $exprops[] = "\0$cn\0_groupBy"; + if (!$this->_IsRowDataFound) $exprops[] = "\0$cn\0_IsRowDataFound"; + return array_diff(parent::__sleep(),$exprops); + } +} + +/** + * TPostSelectBinding class. + * + * @author Wei Zhuo + * @version $Id: TMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPostSelectBinding +{ + private $_statement=null; + private $_property=null; + private $_resultObject=null; + private $_keys=null; + private $_method=TMappedStatement::QUERY_FOR_LIST; + + public function getStatement(){ return $this->_statement; } + public function setStatement($value){ $this->_statement = $value; } + + public function getResultProperty(){ return $this->_property; } + public function setResultProperty($value){ $this->_property = $value; } + + public function getResultObject(){ return $this->_resultObject; } + public function setResultObject($value){ $this->_resultObject = $value; } + + public function getKeys(){ return $this->_keys; } + public function setKeys($value){ $this->_keys = $value; } + + public function getMethod(){ return $this->_method; } + public function setMethod($value){ $this->_method = $value; } +} + +/** + * TSQLMapObjectCollectionTree class. + * + * Maps object collection graphs as trees. Nodes in the collection can + * be {@link add} using parent relationships. The object collections can be + * build using the {@link collect} method. + * + * @author Wei Zhuo + * @version $Id: TMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSqlMapObjectCollectionTree extends TComponent +{ + /** + * @var array object graph as tree + */ + private $_tree = array(); + /** + * @var array tree node values + */ + private $_entries = array(); + /** + * @var array resulting object collection + */ + private $_list = array(); + + /** + * @return boolean true if the graph is empty + */ + public function isEmpty() + { + return count($this->_entries) == 0; + } + + /** + * Add a new node to the object tree graph. + * @param string parent node id + * @param string new node id + * @param mixed node value + */ + public function add($parent, $node, $object='') + { + if(isset($this->_entries[$parent]) && ($this->_entries[$parent]!==null) + && isset($this->_entries[$node]) && ($this->_entries[$node]!==null)) + { + $this->_entries[$node] = $object; + return; + } + $this->_entries[$node] = $object; + if(empty($parent)) + { + if(isset($this->_entries[$node])) + return; + $this->_tree[$node] = array(); + } + $found = $this->addNode($this->_tree, $parent, $node); + if(!$found && !empty($parent)) + { + $this->_tree[$parent] = array(); + if(!isset($this->_entries[$parent]) || $object !== '') + $this->_entries[$parent] = $object; + $this->addNode($this->_tree, $parent, $node); + } + } + + /** + * Find the parent node and add the new node as its child. + * @param array list of nodes to check + * @param string parent node id + * @param string new node id + * @return boolean true if parent node is found. + */ + protected function addNode(&$childs, $parent, $node) + { + $found = false; + reset($childs); + for($i = 0, $k = count($childs); $i < $k; $i++) + { + $key = key($childs); + next($childs); + if($key == $parent) + { + $found = true; + $childs[$key][$node] = array(); + } + else + { + $found = $found || $this->addNode($childs[$key], $parent, $node); + } + } + return $found; + } + + /** + * @return array object collection + */ + public function collect() + { + while(count($this->_tree) > 0) + $this->collectChildren(null, $this->_tree); + return $this->getCollection(); + } + + /** + * @param array list of nodes to check + * @return boolean true if all nodes are leaf nodes, false otherwise + */ + protected function hasChildren(&$nodes) + { + $hasChildren = false; + foreach($nodes as $node) + if(count($node) != 0) + return true; + return $hasChildren; + } + + /** + * Visit all the child nodes and collect them by removing. + * @param string parent node id + * @param array list of child nodes. + */ + protected function collectChildren($parent, &$nodes) + { + $noChildren = !$this->hasChildren($nodes); + $childs = array(); + for(reset($nodes); $key = key($nodes);) + { + next($nodes); + if($noChildren) + { + $childs[] = $key; + unset($nodes[$key]); + } + else + $this->collectChildren($key, $nodes[$key]); + } + if(count($childs) > 0) + $this->onChildNodesVisited($parent, $childs); + } + + /** + * Set the object properties for all the child nodes visited. + * @param string parent node id + * @param array list of child nodes visited. + */ + protected function onChildNodesVisited($parent, $nodes) + { + if(empty($parent) || empty($this->_entries[$parent])) + return; + + $parentObject = $this->_entries[$parent]['object']; + $property = $this->_entries[$nodes[0]]['property']; + + $list = TPropertyAccess::get($parentObject, $property); + + foreach($nodes as $node) + { + if($list instanceof TList) + $parentObject->{$property}[] = $this->_entries[$node]['object']; + else if(is_array($list)) + $list[] = $this->_entries[$node]['object']; + else + throw new TSqlMapExecutionException( + 'sqlmap_property_must_be_list'); + } + + if(is_array($list)) + TPropertyAccess::set($parentObject, $property, $list); + + if($this->_entries[$parent]['property'] === null) + $this->_list[] = $parentObject; + } + + /** + * @return array object collection. + */ + protected function getCollection() + { + return $this->_list; + } + + public function __sleep() + { + $exprops = array(); $cn = __CLASS__; + if (!count($this->_tree)) $exprops[] = "\0$cn\0_tree"; + if (!count($this->_entries)) $exprops[] = "\0$cn\0_entries"; + if (!count($this->_list)) $exprops[] = "\0$cn\0_list"; + return array_diff(parent::__sleep(),$exprops); + } +} + +/** + * TResultSetListItemParameter class + * + * @author Wei Zhuo + * @version $Id: TMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TResultSetListItemParameter extends TComponent +{ + private $_resultObject; + private $_parameterObject; + private $_list; + + public function __construct($result, $parameter, &$list) + { + $this->_resultObject = $result; + $this->_parameterObject = $parameter; + $this->_list = &$list; + } + + public function getResult() + { + return $this->_resultObject; + } + + public function getParameter() + { + return $this->_parameterObject; + } + + public function &getList() + { + return $this->_list; + } +} + +/** + * TResultSetMapItemParameter class. + * + * @author Wei Zhuo + * @version $Id: TMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TResultSetMapItemParameter extends TComponent +{ + private $_key; + private $_value; + private $_parameterObject; + private $_map; + + public function __construct($key, $value, $parameter, &$map) + { + $this->_key = $key; + $this->_value = $value; + $this->_parameterObject = $parameter; + $this->_map = &$map; + } + + public function getKey() + { + return $this->_key; + } + + public function getValue() + { + return $this->_value; + } + + public function getParameter() + { + return $this->_parameterObject; + } + + public function &getMap() + { + return $this->_map; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TPreparedCommand.php b/gui/baculum/framework/Data/SqlMap/Statements/TPreparedCommand.php new file mode 100644 index 0000000000..1e6906a1b3 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TPreparedCommand.php @@ -0,0 +1,68 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPreparedCommand.php 3261 2013-01-22 22:36:51Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +Prado::using('System.Data.Common.TDbMetaData'); +Prado::using('System.Data.Common.TDbCommandBuilder'); + +/** + * TPreparedCommand class. + * + * @author Wei Zhuo + * @version $Id: TPreparedCommand.php 3261 2013-01-22 22:36:51Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPreparedCommand +{ + public function create(TSqlMapManager $manager, $connection, $statement, $parameterObject,$skip=null,$max=null) + { + $sqlText = $statement->getSQLText(); + + $prepared = $sqlText->getPreparedStatement($parameterObject); + $connection->setActive(true); + $sql = $prepared->getPreparedSql(); + + if($sqlText instanceof TSimpleDynamicSql) + $sql = $sqlText->replaceDynamicParameter($sql, $parameterObject); + + if($max!==null || $skip!==null) + { + $builder = TDbMetaData::getInstance($connection)->createCommandBuilder(); + $sql = $builder->applyLimitOffset($sql,$max,$skip); + } + $command = $connection->createCommand($sql); + $this->applyParameterMap($manager, $command, $prepared, $statement, $parameterObject); + + return $command; + } + + protected function applyParameterMap($manager,$command,$prepared, $statement, $parameterObject) + { + $properties = $prepared->getParameterNames(false); + //$parameters = $prepared->getParameterValues(); + $registry=$manager->getTypeHandlers(); + if ($properties) + for($i = 0, $k=$properties->getCount(); $i<$k; $i++) + { + $property = $statement->parameterMap()->getProperty($i); + $value = $statement->parameterMap()->getPropertyValue($registry,$property, $parameterObject); + $dbType = $property->getDbType(); + if($dbType=='') //relies on PHP lax comparison + $command->bindValue($i+1,$value, TDbCommandBuilder::getPdoType($value)); + else if(strpos($dbType, 'PDO::')===0) + $command->bindValue($i+1,$value, constant($property->getDbType())); //assumes PDO types, e.g. PDO::PARAM_INT + else + $command->bindValue($i+1,$value); + } + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TPreparedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TPreparedStatement.php new file mode 100644 index 0000000000..4127eae06d --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TPreparedStatement.php @@ -0,0 +1,56 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPreparedStatement.php 3261 2013-01-22 22:36:51Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TpreparedStatement class. + * + * @author Wei Zhuo + * @version $Id: TPreparedStatement.php 3261 2013-01-22 22:36:51Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPreparedStatement extends TComponent +{ + private $_sqlString=''; + private $_parameterNames; + private $_parameterValues; + + public function getPreparedSql(){ return $this->_sqlString; } + public function setPreparedSql($value){ $this->_sqlString = $value; } + + public function getParameterNames($needed = true) + { + if (!$this->_parameterNames and $needed) + $this->_parameterNames = new TList; + return $this->_parameterNames; + } + + public function setParameterNames($value){ $this->_parameterNames = $value; } + + public function getParameterValues($needed = true) + { + if (!$this->_parameterValues and $needed) + $this->_parameterValues=new TMap; + return $this->_parameterValues; + } + + public function setParameterValues($value){ $this->_parameterValues = $value; } + + public function __sleep() + { + $exprops = array(); $cn = __CLASS__; + if (!$this->_parameterNames or !$this->_parameterNames->getCount()) $exprops[] = "\0$cn\0_parameterNames"; + if (!$this->_parameterValues or !$this->_parameterValues->getCount()) $exprops[] = "\0$cn\0_parameterValues"; + return array_diff(parent::__sleep(),$exprops); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php b/gui/baculum/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php new file mode 100644 index 0000000000..5fdd16b5c4 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TPreparedStatementFactory.php @@ -0,0 +1,49 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPreparedStatementFactory.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TPreparedStatementFactory class. + * + * @author Wei Zhuo + * @version $Id: TPreparedStatementFactory.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TPreparedStatementFactory +{ + private $_statement; + private $_preparedStatement; + private $_parameterPrefix = 'param'; + private $_commandText; + + public function __construct($statement, $sqlString) + { + $this->_statement = $statement; + $this->_commandText = $sqlString; + } + + public function prepare() + { + $this->_preparedStatement = new TPreparedStatement(); + $this->_preparedStatement->setPreparedSql($this->_commandText); + if($this->_statement->parameterMap()!==null) + $this->createParametersForTextCommand(); + return $this->_preparedStatement; + } + + protected function createParametersForTextCommand() + { + foreach($this->_statement->ParameterMap()->getProperties() as $prop) + $this->_preparedStatement->getParameterNames()->add($prop->getProperty()); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TSelectMappedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TSelectMappedStatement.php new file mode 100644 index 0000000000..b0926c41b1 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TSelectMappedStatement.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSelectMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TSelectMappedStatment class. + * + * @author Wei Zhuo + * @version $Id: TSelectMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSelectMappedStatement extends TMappedStatement +{ + public function executeInsert($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_insert', get_class($this), $this->getID()); + } + + public function executeUpdate($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_update', get_class($this), $this->getID()); + } + +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php b/gui/baculum/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php new file mode 100644 index 0000000000..3fce2e3a0d --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TSimpleDynamicSql.php @@ -0,0 +1,40 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSimpleDynamicSql.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TSimpleDynamicSql class. + * + * @author Wei Zhuo + * @version $Id: TSimpleDynamicSql.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TSimpleDynamicSql extends TStaticSql +{ + private $_mappings=array(); + + public function __construct($mappings) + { + $this->_mappings = $mappings; + } + + public function replaceDynamicParameter($sql, $parameter) + { + foreach($this->_mappings as $property) + { + $value = TPropertyAccess::get($parameter, $property); + $sql = preg_replace('/'.TSimpleDynamicParser::DYNAMIC_TOKEN.'/', str_replace('$', '\$', $value), $sql, 1); + } + return $sql; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TStaticSql.php b/gui/baculum/framework/Data/SqlMap/Statements/TStaticSql.php new file mode 100644 index 0000000000..1da6330e5d --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TStaticSql.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TStaticSql.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TStaticSql class. + * + * @author Wei Zhuo + * @version $Id: TStaticSql.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TStaticSql extends TComponent +{ + private $_preparedStatement; + + public function buildPreparedStatement($statement, $sqlString) + { + $factory = new TPreparedStatementFactory($statement, $sqlString); + $this->_preparedStatement = $factory->prepare(); + } + + public function getPreparedStatement($parameter=null) + { + return $this->_preparedStatement; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php b/gui/baculum/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php new file mode 100644 index 0000000000..d3c3acc41c --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/Statements/TUpdateMappedStatement.php @@ -0,0 +1,49 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TUpdateMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + */ + +/** + * TUpdateMappedStatement class. + * + * @author Wei Zhuo + * @version $Id: TUpdateMappedStatement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap.Statements + * @since 3.1 + */ +class TUpdateMappedStatement extends TMappedStatement +{ + public function executeInsert($connection, $parameter) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_insert', get_class($this), $this->getID()); + } + + public function executeQueryForMap($connection, $parameter, $keyProperty, + $valueProperty=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_map', get_class($this), $this->getID()); + } + + public function executeQueryForList($connection, $parameter, $result=null, + $skip=-1, $max=-1) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_list', get_class($this), $this->getID()); + } + + public function executeQueryForObject($connection, $parameter, $result=null) + { + throw new TSqlMapExecutionException( + 'sqlmap_cannot_execute_query_for_object', get_class($this), $this->getID()); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/TSqlMapConfig.php b/gui/baculum/framework/Data/SqlMap/TSqlMapConfig.php new file mode 100644 index 0000000000..c6bd475474 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/TSqlMapConfig.php @@ -0,0 +1,181 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapConfig.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +Prado::using('System.Data.TDataSourceConfig'); + +/** + * TSqlMapConfig module configuration class. + * + * Database connection and TSqlMapManager configuration. + * + * @author Wei Zhuo + * @version $Id: TSqlMapConfig.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapConfig extends TDataSourceConfig +{ + private $_configFile; + private $_sqlmap; + private $_enableCache=false; + + /** + * File extension of external configuration file + */ + const CONFIG_FILE_EXT='.xml'; + + /** + * @return string module ID + configuration file path. + */ + private function getCacheKey() + { + return $this->getID().$this->getConfigFile(); + } + + /** + * Deletes the configuration cache. + */ + public function clearCache() + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) { + $cache->delete($this->getCacheKey()); + } + } + + /** + * Create and configure the data mapper using sqlmap configuration file. + * Or if cache is enabled and manager already cached load from cache. + * If cache is enabled, the data mapper instance is cached. + * + * @return TSqlMapManager SqlMap manager instance + * @since 3.1.7 + */ + public function getSqlMapManager() { + Prado::using('System.Data.SqlMap.TSqlMapManager'); + if(($manager = $this->loadCachedSqlMapManager())===null) + { + $manager = new TSqlMapManager($this->getDbConnection()); + if(strlen($file=$this->getConfigFile()) > 0) + { + $manager->configureXml($file); + $this->cacheSqlMapManager($manager); + } + } + elseif($this->getConnectionID() !== '') { + $manager->setDbConnection($this->getDbConnection()); + } + return $manager; + } + + /** + * Saves the current SqlMap manager to cache. + * @return boolean true if SqlMap manager was cached, false otherwise. + */ + protected function cacheSqlMapManager($manager) + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) { + $dependencies = null; + if($this->getApplication()->getMode() !== TApplicationMode::Performance) + $dependencies = $manager->getCacheDependencies(); + return $cache->set($this->getCacheKey(), $manager, 0, $dependencies); + } + } + return false; + } + + /** + * Loads SqlMap manager from cache. + * @return TSqlMapManager SqlMap manager intance if load was successful, null otherwise. + */ + protected function loadCachedSqlMapManager() + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) + { + $manager = $cache->get($this->getCacheKey()); + if($manager instanceof TSqlMapManager) + return $manager; + } + } + return null; + } + + /** + * @return string SqlMap configuration file. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * extension must be '.xml'. + * @throws TConfigurationException if the file is invalid. + */ + public function setConfigFile($value) + { + if(is_file($value)) + $this->_configFile=$value; + else + { + $file = Prado::getPathOfNamespace($value,self::CONFIG_FILE_EXT); + if($file === null || !is_file($file)) + throw new TConfigurationException('sqlmap_configfile_invalid',$value); + else + $this->_configFile = $file; + } + } + + /** + * Set true to cache sqlmap instances. + * @param boolean true to cache sqlmap instance. + */ + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean true if configuration should be cached, false otherwise. + */ + public function getEnableCache() + { + return $this->_enableCache; + } + + /** + * @return TSqlMapGateway SqlMap gateway instance. + */ + protected function createSqlMapGateway() + { + return $this->getSqlMapManager()->getSqlmapGateway(); + } + + /** + * Initialize the sqlmap if necessary, returns the TSqlMapGateway instance. + * @return TSqlMapGateway SqlMap gateway instance. + */ + public function getClient() + { + if($this->_sqlmap===null ) + $this->_sqlmap=$this->createSqlMapGateway(); + return $this->_sqlmap; + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/TSqlMapGateway.php b/gui/baculum/framework/Data/SqlMap/TSqlMapGateway.php new file mode 100644 index 0000000000..a0cebd9c0e --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/TSqlMapGateway.php @@ -0,0 +1,261 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapGateway.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +Prado::using('System.Data.SqlMap.TSqlMapManager'); + +/** + * DataMapper client, a fascade to provide access the rest of the DataMapper + * framework. It provides three core functions: + * + * # execute an update query (including insert and delete). + * # execute a select query for a single object + * # execute a select query for a list of objects + * + * This class should be instantiated from a TSqlMapManager instance. + * + * @author Wei Zhuo + * @version $Id: TSqlMapGateway.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapGateway extends TComponent +{ + /** + * @var TSqlMapManager manager + */ + private $_manager; + + public function __construct($manager) + { + $this->_manager=$manager; + } + + /** + * @return TSqlMapManager sqlmap manager. + */ + public function getSqlMapManager() + { + return $this->_manager; + } + + /** + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + return $this->getSqlMapManager()->getDbConnection(); + } + + /** + * Executes a Sql SELECT statement that returns that returns data + * to populate a single object instance. + * + * The parameter object is generally used to supply the input + * data for the WHERE clause parameter(s) of the SELECT statement. + * + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param mixed An object of the type to be returned. + * @return object A single result object populated with the result set data. + */ + public function queryForObject($statementName, $parameter=null, $result=null) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForObject($this->getDbConnection(), $parameter, $result); + } + + /** + * Executes a Sql SELECT statement that returns data to populate a number + * of result objects. + * + * The parameter object is generally used to supply the input + * data for the WHERE clause parameter(s) of the SELECT statement. + * + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param TList An Ilist object used to hold the objects, + * pass in null if want to return a list instead. + * @param int The number of rows to skip over. + * @param int The maximum number of rows to return. + * @return TList A List of result objects. + */ + public function queryForList($statementName, $parameter=null, $result=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForList($this->getDbConnection(),$parameter, $result, $skip, $max); + } + + /** + * Runs a query for list with a custom object that gets a chance to deal + * with each row as it is processed. + * + * Example: $sqlmap->queryWithRowDelegate('getAccounts', array($this, 'rowHandler')); + * + * @param string The name of the sql statement to execute. + * @param callback Row delegate handler, a valid callback required. + * @param mixed The object used to set the parameters in the SQL. + * @param TList An Ilist object used to hold the objects, + * pass in null if want to return a list instead. + * @param int The number of rows to skip over. + * @param int The maximum number of rows to return. + * @return TList A List of result objects. + */ + public function queryWithRowDelegate($statementName, $delegate, $parameter=null, $result=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForList($this->getDbConnection(), $parameter, $result, $skip, $max, $delegate); + } + + /** + * Executes the SQL and retuns a subset of the results in a dynamic + * TPagedList that can be used to automatically scroll through results + * from a database table. + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param integer The maximum number of objects to store in each page. + * @param integer The number of the page to initially load into the list. + * @return TPagedList A PaginatedList of beans containing the rows. + */ + public function queryForPagedList($statementName, $parameter=null, $pageSize=10, $page=0) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return new TSqlMapPagedList($statement, $parameter, $pageSize, null, $page); + } + + /** + * Executes the SQL and retuns a subset of the results in a dynamic + * TPagedList that can be used to automatically scroll through results + * from a database table. + * + * Runs paged list query with row delegate + * Example: $sqlmap->queryForPagedListWithRowDelegate('getAccounts', array($this, 'rowHandler')); + * + * @param string The name of the sql statement to execute. + * @param callback Row delegate handler, a valid callback required. + * @param mixed The object used to set the parameters in the SQL. + * @param integer The maximum number of objects to store in each page. + * @param integer The number of the page to initially load into the list. + * @return TPagedList A PaginatedList of beans containing the rows. + */ + public function queryForPagedListWithRowDelegate($statementName,$delegate, $parameter=null, $pageSize=10, $page=0) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return new TSqlMapPagedList($statement, $parameter, $pageSize, $delegate,$page); + } + + + /** + * Executes the SQL and retuns all rows selected in a map that is keyed on + * the property named in the keyProperty parameter. The value at each key + * will be the value of the property specified in the valueProperty + * parameter. If valueProperty is null, the entire result object will be + * entered. + * @param string The name of the sql statement to execute. + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value. + * @return TMap Array object containing the rows keyed by keyProperty. + */ + public function queryForMap($statementName, $parameter=null, $keyProperty=null, $valueProperty=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $skip, $max); + } + + /** + * Runs a query with a custom object that gets a chance to deal + * with each row as it is processed. + * + * Example: $sqlmap->queryForMapWithRowDelegate('getAccounts', array($this, 'rowHandler')); + * + * @param string The name of the sql statement to execute. + * @param callback Row delegate handler, a valid callback required. + * @param mixed The object used to set the parameters in the SQL. + * @param string The property of the result object to be used as the key. + * @param string The property of the result object to be used as the value. + * @return TMap Array object containing the rows keyed by keyProperty. + */ + public function queryForMapWithRowDelegate($statementName, $delegate, $parameter=null, $keyProperty=null, $valueProperty=null, $skip=-1, $max=-1) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeQueryForMap($this->getDbConnection(), $parameter, $keyProperty, $valueProperty, $skip, $max, $delegate); + } + + /** + * Executes a Sql INSERT statement. + * + * Insert is a bit different from other update methods, as it provides + * facilities for returning the primary key of the newly inserted row + * (rather than the effected rows), + * + * The parameter object is generally used to supply the input data for the + * INSERT values. + * + * @param string The name of the statement to execute. + * @param string The parameter object. + * @return mixed The primary key of the newly inserted row. + * This might be automatically generated by the RDBMS, + * or selected from a sequence table or other source. + */ + public function insert($statementName, $parameter=null) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeInsert($this->getDbConnection(), $parameter); + } + + /** + * Executes a Sql UPDATE statement. + * + * Update can also be used for any other update statement type, such as + * inserts and deletes. Update returns the number of rows effected. + * + * The parameter object is generally used to supply the input data for the + * UPDATE values as well as the WHERE clause parameter(s). + * + * @param string The name of the statement to execute. + * @param mixed The parameter object. + * @return integer The number of rows effected. + */ + public function update($statementName, $parameter=null) + { + $statement = $this->getSqlMapManager()->getMappedStatement($statementName); + return $statement->executeUpdate($this->getDbConnection(), $parameter); + } + + /** + * Executes a Sql DELETE statement. Delete returns the number of rows effected. + * @param string The name of the statement to execute. + * @param mixed The parameter object. + * @return integer The number of rows effected. + */ + public function delete($statementName, $parameter=null) + { + return $this->update($statementName, $parameter); + } + + /** + * Flushes all cached objects that belong to this SqlMap + */ + public function flushCaches() + { + $this->getSqlMapManager()->flushCacheModels(); + } + + /** + * @param TSqlMapTypeHandler new type handler. + */ + public function registerTypeHandler($typeHandler) + { + $this->getSqlMapManager()->getTypeHandlers()->registerTypeHandler($typeHandler); + } +} + diff --git a/gui/baculum/framework/Data/SqlMap/TSqlMapManager.php b/gui/baculum/framework/Data/SqlMap/TSqlMapManager.php new file mode 100644 index 0000000000..2923492090 --- /dev/null +++ b/gui/baculum/framework/Data/SqlMap/TSqlMapManager.php @@ -0,0 +1,274 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSqlMapManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + */ + +Prado::using('System.Data.SqlMap.TSqlMapGateway'); +Prado::using('System.Data.SqlMap.DataMapper.TSqlMapException'); +Prado::using('System.Data.SqlMap.DataMapper.TSqlMapTypeHandlerRegistry'); +Prado::using('System.Data.SqlMap.DataMapper.TSqlMapCache'); +Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement'); +Prado::using('System.Data.SqlMap.Configuration.*'); +Prado::using('System.Data.SqlMap.DataMapper.*'); +Prado::using('System.Data.SqlMap.Statements.*'); +Prado::using('System.Caching.TCache'); + + +/** + * TSqlMapManager class holds the sqlmap configuation result maps, statements + * parameter maps and a type handler factory. + * + * Use {@link SqlMapGateway getSqlMapGateway()} property to obtain the gateway + * instance used for querying statements defined in the SqlMap configuration files. + * + * + * $conn = new TDbConnection($dsn,$dbuser,$dbpass); + * $manager = new TSqlMapManager($conn); + * $manager->configureXml('mydb-sqlmap.xml'); + * $sqlmap = $manager->getSqlMapGateway(); + * $result = $sqlmap->queryForObject('Products'); + * + * + * @author Wei Zhuo + * @version $Id: TSqlMapManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data.SqlMap + * @since 3.1 + */ +class TSqlMapManager extends TComponent +{ + private $_mappedStatements; + private $_resultMaps; + private $_parameterMaps; + private $_typeHandlers; + private $_cacheModels; + + private $_connection; + private $_gateway; + private $_cacheDependencies; + + /** + * Constructor, create a new SqlMap manager. + * @param TDbConnection database connection + * @param string configuration file. + */ + public function __construct($connection=null) + { + $this->_connection=$connection; + + $this->_mappedStatements=new TMap; + $this->_resultMaps=new TMap; + $this->_parameterMaps=new TMap; + $this->_cacheModels=new TMap; + } + + /** + * @param TDbConnection default database connection + */ + public function setDbConnection($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection default database connection + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @return TTypeHandlerFactory The TypeHandlerFactory + */ + public function getTypeHandlers() + { + if($this->_typeHandlers===null) + $this->_typeHandlers= new TSqlMapTypeHandlerRegistry(); + return $this->_typeHandlers; + } + + /** + * @return TSqlMapGateway SqlMap gateway. + */ + public function getSqlmapGateway() + { + if($this->_gateway===null) + $this->_gateway=$this->createSqlMapGateway(); + return $this->_gateway; + } + + /** + * Loads and parses the SqlMap configuration file. + * @param string xml configuration file. + */ + public function configureXml($file) + { + $config = new TSqlMapXmlConfiguration($this); + $config->configure($file); + } + + /** + * @return TChainedCacheDependency + * @since 3.1.5 + */ + public function getCacheDependencies() + { + if($this->_cacheDependencies === null) + $this->_cacheDependencies=new TChainedCacheDependency(); + + return $this->_cacheDependencies; + } + + /** + * Configures the current TSqlMapManager using the given xml configuration file + * defined in {@link ConfigFile setConfigFile()}. + * @return TSqlMapGateway create and configure a new TSqlMapGateway. + */ + protected function createSqlMapGateway() + { + return new TSqlMapGateway($this); + } + + /** + * @return TMap mapped statements collection. + */ + public function getMappedStatements() + { + return $this->_mappedStatements; + } + + /** + * Gets a MappedStatement by name. + * @param string The name of the statement. + * @return IMappedStatement The MappedStatement + * @throws TSqlMapUndefinedException + */ + public function getMappedStatement($name) + { + if($this->_mappedStatements->contains($name) == false) + throw new TSqlMapUndefinedException('sqlmap_contains_no_statement', $name); + return $this->_mappedStatements[$name]; + } + + /** + * Adds a (named) MappedStatement. + * @param string The key name + * @param IMappedStatement The statement to add + * @throws TSqlMapDuplicateException + */ + public function addMappedStatement(IMappedStatement $statement) + { + $key = $statement->getID(); + if($this->_mappedStatements->contains($key) == true) + throw new TSqlMapDuplicateException('sqlmap_already_contains_statement', $key); + $this->_mappedStatements->add($key, $statement); + } + + /** + * @return TMap result maps collection. + */ + public function getResultMaps() + { + return $this->_resultMaps; + } + + /** + * Gets a named result map + * @param string result name. + * @return TResultMap the result map. + * @throws TSqlMapUndefinedException + */ + public function getResultMap($name) + { + if($this->_resultMaps->contains($name) == false) + throw new TSqlMapUndefinedException('sqlmap_contains_no_result_map', $name); + return $this->_resultMaps[$name]; + } + + /** + * @param TResultMap add a new result map to this SQLMap + * @throws TSqlMapDuplicateException + */ + public function addResultMap(TResultMap $result) + { + $key = $result->getID(); + if($this->_resultMaps->contains($key) == true) + throw new TSqlMapDuplicateException('sqlmap_already_contains_result_map', $key); + $this->_resultMaps->add($key, $result); + } + + /** + * @return TMap parameter maps collection. + */ + public function getParameterMaps() + { + return $this->_parameterMaps; + } + + /** + * @param string parameter map ID name. + * @return TParameterMap the parameter with given ID. + * @throws TSqlMapUndefinedException + */ + public function getParameterMap($name) + { + if($this->_parameterMaps->contains($name) == false) + throw new TSqlMapUndefinedException('sqlmap_contains_no_parameter_map', $name); + return $this->_parameterMaps[$name]; + } + + /** + * @param TParameterMap add a new parameter map to this SQLMap. + * @throws TSqlMapDuplicateException + */ + public function addParameterMap(TParameterMap $parameter) + { + $key = $parameter->getID(); + if($this->_parameterMaps->contains($key) == true) + throw new TSqlMapDuplicateException('sqlmap_already_contains_parameter_map', $key); + $this->_parameterMaps->add($key, $parameter); + } + + /** + * Adds a named cache. + * @param TSqlMapCacheModel the cache to add. + * @throws TSqlMapConfigurationException + */ + public function addCacheModel(TSqlMapCacheModel $cacheModel) + { + if($this->_cacheModels->contains($cacheModel->getID())) + throw new TSqlMapConfigurationException('sqlmap_cache_model_already_exists', $cacheModel->getID()); + else + $this->_cacheModels->add($cacheModel->getID(), $cacheModel); + } + + /** + * Gets a cache by name + * @param string the name of the cache to get. + * @return TSqlMapCacheModel the cache object. + * @throws TSqlMapConfigurationException + */ + public function getCacheModel($name) + { + if(!$this->_cacheModels->contains($name)) + throw new TSqlMapConfigurationException('sqlmap_unable_to_find_cache_model', $name); + return $this->_cacheModels[$name]; + } + + /** + * Flushes all cached objects that belong to this SqlMap + */ + public function flushCacheModels() + { + foreach($this->_cacheModels as $cache) + $cache->flush(); + } +} + diff --git a/gui/baculum/framework/Data/TDataSourceConfig.php b/gui/baculum/framework/Data/TDataSourceConfig.php new file mode 100644 index 0000000000..aff5f1af1f --- /dev/null +++ b/gui/baculum/framework/Data/TDataSourceConfig.php @@ -0,0 +1,167 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataSourceConfig.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + */ + +Prado::using('System.Data.TDbConnection'); + +/** + * TDataSourceConfig module class provides configuration for database connections. + * + * Example usage: mysql connection + * + * + * + * + * + * + * + * + * Usage in php: + * + * class Home extends TPage + * { + * function onLoad($param) + * { + * $db = $this->Application->Modules['db1']->DbConnection; + * $db->createCommand('...'); //... + * } + * } + * + * + * The properties of are those of the class TDbConnection. + * Set {@link setConnectionClass} attribute for a custom database connection class + * that extends the TDbConnection class. + * + * @author Wei Zhuo + * @version $Id: TDataSourceConfig.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.1 + */ +class TDataSourceConfig extends TModule +{ + private $_connID=''; + private $_conn; + private $_connClass='System.Data.TDbConnection'; + + /** + * Initalize the database connection properties from attributes in tag. + * @param TXmlDocument xml configuration. + */ + public function init($xml) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(isset($xml['database']) && is_array($xml['database'])) + { + $db=$this->getDbConnection(); + foreach($xml['database'] as $name=>$value) + $db->setSubProperty($name,$value); + } + } + else + { + if($prop=$xml->getElementByTagName('database')) + { + $db=$this->getDbConnection(); + foreach($prop->getAttributes() as $name=>$value) + $db->setSubproperty($name,$value); + } + } + } + + /** + * The module ID of another TDataSourceConfig. The {@link getDbConnection DbConnection} + * property of this configuration will equal to {@link getDbConnection DbConnection} + * of the given TDataSourceConfig module. + * @param string module ID. + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return string connection module ID. + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Gets the TDbConnection from another module if {@link setConnectionID ConnectionID} + * is supplied and valid. Otherwise, a connection of type given by + * {@link setConnectionClass ConnectionClass} is created. + * @return TDbConnection database connection. + */ + public function getDbConnection() + { + if($this->_conn===null) + { + if($this->_connID!=='') + $this->_conn = $this->findConnectionByID($this->getConnectionID()); + else + $this->_conn = Prado::createComponent($this->getConnectionClass()); + } + return $this->_conn; + } + + /** + * Alias for getDbConnection(). + * @return TDbConnection database connection. + */ + public function getDatabase() + { + return $this->getDbConnection(); + } + + /** + * @param string Database connection class name to be created. + */ + public function getConnectionClass() + { + return $this->_connClass; + } + + /** + * The database connection class name to be created when {@link getDbConnection} + * method is called and {@link setConnectionID ConnectionID} is null. The + * {@link setConnectionClass ConnectionClass} property must be set before + * calling {@link getDbConnection} if you wish to create the connection using the + * given class name. + * @param string Database connection class name. + * @throws TConfigurationException when database connection is already established. + */ + public function setConnectionClass($value) + { + if($this->_conn!==null) + throw new TConfigurationException('datasource_dbconnection_exists', $value); + $this->_connClass=$value; + } + + /** + * Finds the database connection instance from the Application modules. + * @param string Database connection module ID. + * @return TDbConnection database connection. + * @throws TConfigurationException when module is not of TDbConnection or TDataSourceConfig. + */ + protected function findConnectionByID($id) + { + $conn = $this->getApplication()->getModule($id); + if($conn instanceof TDbConnection) + return $conn; + else if($conn instanceof TDataSourceConfig) + return $conn->getDbConnection(); + else + throw new TConfigurationException('datasource_dbconnection_invalid',$id); + } +} diff --git a/gui/baculum/framework/Data/TDbCommand.php b/gui/baculum/framework/Data/TDbCommand.php new file mode 100644 index 0000000000..e80d7704b3 --- /dev/null +++ b/gui/baculum/framework/Data/TDbCommand.php @@ -0,0 +1,308 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbCommand.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + */ + +/** + * TDbCommand class. + * + * TDbCommand represents an SQL statement to execute against a database. + * It is usually created by calling {@link TDbConnection::createCommand}. + * The SQL statement to be executed may be set via {@link setText Text}. + * + * To execute a non-query SQL (such as insert, delete, update), call + * {@link execute}. To execute an SQL statement that returns result data set + * (such as select), use {@link query} or its convenient versions {@link queryRow} + * and {@link queryScalar}. + * + * If an SQL statement returns results (such as a SELECT SQL), the results + * can be accessed via the returned {@link TDbDataReader}. + * + * TDbCommand supports SQL statment preparation and parameter binding. + * Call {@link bindParameter} to bind a PHP variable to a parameter in SQL. + * Call {@link bindValue} to bind a value to an SQL parameter. + * When binding a parameter, the SQL statement is automatically prepared. + * You may also call {@link prepare} to explicitly prepare an SQL statement. + * + * @author Qiang Xue + * @version $Id: TDbCommand.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.0 + */ +class TDbCommand extends TComponent +{ + private $_connection; + private $_text=''; + private $_statement=null; + + /** + * Constructor. + * @param TDbConnection the database connection + * @param string the SQL statement to be executed + */ + public function __construct(TDbConnection $connection,$text) + { + $this->_connection=$connection; + $this->setText($text); + } + + /** + * Set the statement to null when serializing. + */ + public function __sleep() + { + return array_diff(parent::__sleep(),array("\0TDbCommand\0_statement")); + } + + /** + * @return string the SQL statement to be executed + */ + public function getText() + { + return $this->_text; + } + + /** + * Specifies the SQL statement to be executed. + * Any previous execution will be terminated or cancel. + * @param string the SQL statement to be executed + */ + public function setText($value) + { + $this->_text=$value; + $this->cancel(); + } + + /** + * @return TDbConnection the connection associated with this command + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return PDOStatement the underlying PDOStatement for this command + * It could be null if the statement is not prepared yet. + */ + public function getPdoStatement() + { + return $this->_statement; + } + + /** + * Prepares the SQL statement to be executed. + * For complex SQL statement that is to be executed multiple times, + * this may improve performance. + * For SQL statement with binding parameters, this method is invoked + * automatically. + */ + public function prepare() + { + if($this->_statement==null) + { + try + { + $this->_statement=$this->getConnection()->getPdoInstance()->prepare($this->getText()); + } + catch(Exception $e) + { + throw new TDbException('dbcommand_prepare_failed',$e->getMessage(),$this->getText()); + } + } + } + + /** + * Cancels the execution of the SQL statement. + */ + public function cancel() + { + $this->_statement=null; + } + + /** + * Binds a parameter to the SQL statement to be executed. + * @param mixed Parameter identifier. For a prepared statement + * using named placeholders, this will be a parameter name of + * the form :name. For a prepared statement using question mark + * placeholders, this will be the 1-indexed position of the parameter. + * Unlike {@link bindValue}, the variable is bound as a reference and will + * only be evaluated at the time that {@link execute} or {@link query} is called. + * @param mixed Name of the PHP variable to bind to the SQL statement parameter + * @param int SQL data type of the parameter + * @param int length of the data type + * @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php + */ + public function bindParameter($name, &$value, $dataType=null, $length=null) + { + $this->prepare(); + if($dataType===null) + $this->_statement->bindParam($name,$value); + else if($length===null) + $this->_statement->bindParam($name,$value,$dataType); + else + $this->_statement->bindParam($name,$value,$dataType,$length); + } + + /** + * Binds a value to a parameter. + * @param mixed Parameter identifier. For a prepared statement + * using named placeholders, this will be a parameter name of + * the form :name. For a prepared statement using question mark + * placeholders, this will be the 1-indexed position of the parameter. + * @param mixed The value to bind to the parameter + * @param int SQL data type of the parameter + * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php + */ + public function bindValue($name, $value, $dataType=null) + { + $this->prepare(); + if($dataType===null) + $this->_statement->bindValue($name,$value); + else + $this->_statement->bindValue($name,$value,$dataType); + } + + /** + * Executes the SQL statement. + * This method is meant only for executing non-query SQL statement. + * No result set will be returned. + * @return integer number of rows affected by the execution. + * @throws TDbException execution failed + */ + public function execute() + { + try + { + // Do not trace because it will remain even in + // Performance mode or when pradolite.php is used + // Prado::trace('Execute Command: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + { + $this->_statement->execute(); + return $this->_statement->rowCount(); + } + else + return $this->getConnection()->getPdoInstance()->exec($this->getText()); + } + catch(Exception $e) + { + throw new TDbException('dbcommand_execute_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * @return String prepared SQL text for debugging purposes. + */ + public function getDebugStatementText() + { + if(Prado::getApplication()->getMode() === TApplicationMode::Debug) + return $this->_statement instanceof PDOStatement ? + $this->_statement->queryString + : $this->getText(); + } + + /** + * Executes the SQL statement and returns query result. + * This method is for executing an SQL query that returns result set. + * @return TDbDataReader the reader object for fetching the query result + * @throws TDbException execution failed + */ + public function query() + { + try + { + // Prado::trace('Query: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + return new TDbDataReader($this); + } + catch(Exception $e) + { + throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * Executes the SQL statement and returns the first row of the result. + * This is a convenient method of {@link query} when only the first row of data is needed. + * @param boolean whether the row should be returned as an associated array with + * column names as the keys or the array keys are column indexes (0-based). + * @return array the first row of the query result, false if no result. + * @throws TDbException execution failed + */ + public function queryRow($fetchAssociative=true) + { + try + { + // Prado::trace('Query Row: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + $result=$this->_statement->fetch($fetchAssociative ? PDO::FETCH_ASSOC : PDO::FETCH_NUM); + $this->_statement->closeCursor(); + return $result; + } + catch(Exception $e) + { + throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * Executes the SQL statement and returns the value of the first column in the first row of data. + * This is a convenient method of {@link query} when only a single scalar + * value is needed (e.g. obtaining the count of the records). + * @return mixed the value of the first column in the first row of the query result. False is returned if there is no value. + * @throws TDbException execution failed + */ + public function queryScalar() + { + try + { + // Prado::trace('Query Scalar: '.$this->getDebugStatementText(), 'System.Data'); + if($this->_statement instanceof PDOStatement) + $this->_statement->execute(); + else + $this->_statement=$this->getConnection()->getPdoInstance()->query($this->getText()); + $result=$this->_statement->fetchColumn(); + $this->_statement->closeCursor(); + if(is_resource($result) && get_resource_type($result)==='stream') + return stream_get_contents($result); + else + return $result; + } + catch(Exception $e) + { + throw new TDbException('dbcommand_query_failed',$e->getMessage(),$this->getDebugStatementText()); + } + } + + /** + * Executes the SQL statement and returns the first column of the result. + * This is a convenient method of {@link query} when only the first column of data is needed. + * Note, the column returned will contain the first element in each row of result. + * @return array the first column of the query result. Empty array if no result. + * @throws TDbException execution failed + * @since 3.1.2 + */ + public function queryColumn() + { + $rows=$this->query()->readAll(); + $column=array(); + foreach($rows as $row) + $column[]=current($row); + return $column; + } +} + diff --git a/gui/baculum/framework/Data/TDbConnection.php b/gui/baculum/framework/Data/TDbConnection.php new file mode 100644 index 0000000000..f973790a60 --- /dev/null +++ b/gui/baculum/framework/Data/TDbConnection.php @@ -0,0 +1,685 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbConnection.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + */ + +Prado::using('System.Data.TDbTransaction'); +Prado::using('System.Data.TDbCommand'); + +/** + * TDbConnection class + * + * TDbConnection represents a connection to a database. + * + * TDbConnection works together with {@link TDbCommand}, {@link TDbDataReader} + * and {@link TDbTransaction} to provide data access to various DBMS + * in a common set of APIs. They are a thin wrapper of the {@link http://www.php.net/manual/en/ref.pdo.php PDO} + * PHP extension. + * + * To establish a connection, set {@link setActive Active} to true after + * specifying {@link setConnectionString ConnectionString}, {@link setUsername Username} + * and {@link setPassword Password}. + * + * Since 3.1.2, the connection charset can be set (for MySQL and PostgreSQL databases only) + * using the {@link setCharset Charset} property. The value of this property is database dependant. + * e.g. for mysql, you can use 'latin1' for cp1252 West European, 'utf8' for unicode, ... + * + * The following example shows how to create a TDbConnection instance and establish + * the actual connection: + * + * $connection=new TDbConnection($dsn,$username,$password); + * $connection->Active=true; + * + * + * After the DB connection is established, one can execute an SQL statement like the following: + * + * $command=$connection->createCommand($sqlStatement); + * $command->execute(); // a non-query SQL statement execution + * // or execute an SQL query and fetch the result set + * $reader=$command->query(); + * + * // each $row is an array representing a row of data + * foreach($reader as $row) ... + * + * + * One can do prepared SQL execution and bind parameters to the prepared SQL: + * + * $command=$connection->createCommand($sqlStatement); + * $command->bindParameter($name1,$value1); + * $command->bindParameter($name2,$value2); + * $command->execute(); + * + * + * To use transaction, do like the following: + * + * $transaction=$connection->beginTransaction(); + * try + * { + * $connection->createCommand($sql1)->execute(); + * $connection->createCommand($sql2)->execute(); + * //.... other SQL executions + * $transaction->commit(); + * } + * catch(Exception $e) + * { + * $transaction->rollBack(); + * } + * + * + * TDbConnection provides a set of methods to support setting and querying + * of certain DBMS attributes, such as {@link getNullConversion NullConversion}. + * + * @author Qiang Xue + * @version $Id: TDbConnection.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.0 + */ +class TDbConnection extends TComponent +{ + /** + * + * @since 3.1.7 + */ + const DEFAULT_TRANSACTION_CLASS = 'System.Data.TDbTransaction'; + + private $_dsn=''; + private $_username=''; + private $_password=''; + private $_charset=''; + private $_attributes=array(); + private $_active=false; + private $_pdo=null; + private $_transaction; + + /** + * @var TDbMetaData + */ + private $_dbMeta = null; + + /** + * @var string + * @since 3.1.7 + */ + private $_transactionClass=self::DEFAULT_TRANSACTION_CLASS; + + /** + * Constructor. + * Note, the DB connection is not established when this connection + * instance is created. Set {@link setActive Active} property to true + * to establish the connection. + * Since 3.1.2, you can set the charset for MySql connection + * + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @param string The user name for the DSN string. + * @param string The password for the DSN string. + * @param string Charset used for DB Connection (MySql & pgsql only). If not set, will use the default charset of your database server + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public function __construct($dsn='',$username='',$password='', $charset='') + { + $this->_dsn=$dsn; + $this->_username=$username; + $this->_password=$password; + $this->_charset=$charset; + } + + /** + * Close the connection when serializing. + */ + public function __sleep() + { +// $this->close(); - DO NOT CLOSE the current connection as serializing doesn't neccessarily mean we don't this connection anymore in the current session + return array_diff(parent::__sleep(),array("\0TDbConnection\0_pdo","\0TDbConnection\0_active")); + } + + /** + * @return array list of available PDO drivers + * @see http://www.php.net/manual/en/function.PDO-getAvailableDrivers.php + */ + public static function getAvailableDrivers() + { + return PDO::getAvailableDrivers(); + } + + /** + * @return boolean whether the DB connection is established + */ + public function getActive() + { + return $this->_active; + } + + /** + * Open or close the DB connection. + * @param boolean whether to open or close DB connection + * @throws TDbException if connection fails + */ + public function setActive($value) + { + $value=TPropertyValue::ensureBoolean($value); + if($value!==$this->_active) + { + if($value) + $this->open(); + else + $this->close(); + } + } + + /** + * Opens DB connection if it is currently not + * @throws TDbException if connection fails + */ + protected function open() + { + if($this->_pdo===null) + { + try + { + $this->_pdo=new PDO($this->getConnectionString(),$this->getUsername(), + $this->getPassword(),$this->_attributes); + // This attribute is only useful for PDO::MySql driver. + // Ignore the warning if a driver doesn't understand this. + @$this->_pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->_pdo->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $this->_active=true; + $this->setConnectionCharset(); + } + catch(PDOException $e) + { + throw new TDbException('dbconnection_open_failed',$e->getMessage()); + } + } + } + + /** + * Closes the currently active DB connection. + * It does nothing if the connection is already closed. + */ + protected function close() + { + $this->_pdo=null; + $this->_active=false; + } + + /* + * Set the database connection charset. + * Only MySql databases are supported for now. + * @since 3.1.2 + */ + protected function setConnectionCharset() + { + if ($this->_charset === '' || $this->_active === false) + return; + switch ($this->_pdo->getAttribute(PDO::ATTR_DRIVER_NAME)) + { + case 'mysql': + case 'sqlite': + $stmt = $this->_pdo->prepare('SET NAMES ?'); + break; + case 'pgsql': + $stmt = $this->_pdo->prepare('SET client_encoding TO ?'); + break; + default: + throw new TDbException('dbconnection_unsupported_driver_charset', $driver); + } + $stmt->execute(array($this->_charset)); + } + + /** + * @return string The Data Source Name, or DSN, contains the information required to connect to the database. + */ + public function getConnectionString() + { + return $this->_dsn; + } + + /** + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public function setConnectionString($value) + { + $this->_dsn=$value; + } + + /** + * @return string the username for establishing DB connection. Defaults to empty string. + */ + public function getUsername() + { + return $this->_username; + } + + /** + * @param string the username for establishing DB connection + */ + public function setUsername($value) + { + $this->_username=$value; + } + + /** + * @return string the password for establishing DB connection. Defaults to empty string. + */ + public function getPassword() + { + return $this->_password; + } + + /** + * @param string the password for establishing DB connection + */ + public function setPassword($value) + { + $this->_password=$value; + } + + /** + * @return string the charset used for database connection. Defaults to emtpy string. + */ + public function getCharset () + { + return $this->_charset; + } + + /** + * @param string the charset used for database connection + */ + public function setCharset ($value) + { + $this->_charset=$value; + $this->setConnectionCharset(); + } + + /** + * @return PDO the PDO instance, null if the connection is not established yet + */ + public function getPdoInstance() + { + return $this->_pdo; + } + + /** + * Creates a command for execution. + * @param string SQL statement associated with the new command. + * @return TDbCommand the DB command + * @throws TDbException if the connection is not active + */ + public function createCommand($sql) + { + if($this->getActive()) + return new TDbCommand($this,$sql); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * @return TDbTransaction the currently active transaction. Null if no active transaction. + */ + public function getCurrentTransaction() + { + if($this->_transaction!==null) + { + if($this->_transaction->getActive()) + return $this->_transaction; + } + return null; + } + + /** + * Starts a transaction. + * @return TDbTransaction the transaction initiated + * @throws TDbException if the connection is not active + */ + public function beginTransaction() + { + if($this->getActive()) + { + $this->_pdo->beginTransaction(); + return $this->_transaction=Prado::createComponent($this->getTransactionClass(), $this); + } + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * @return string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. Defaults to 'System.Data.TDbTransaction'. + * @since 3.1.7 + */ + public function getTransactionClass() + { + return $this->_transactionClass; + } + + + /** + * @param string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. + * @since 3.1.7 + */ + public function setTransactionClass($value) + { + $this->_transactionClass = (string)$value; + } + + /** + * Returns the ID of the last inserted row or sequence value. + * @param string name of the sequence object (required by some DBMS) + * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object + * @see http://www.php.net/manual/en/function.PDO-lastInsertId.php + */ + public function getLastInsertID($sequenceName='') + { + if($this->getActive()) + return $this->_pdo->lastInsertId($sequenceName); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * Quotes a string for use in a query. + * @param string string to be quoted + * @return string the properly quoted string + * @see http://www.php.net/manual/en/function.PDO-quote.php + */ + public function quoteString($str) + { + if($this->getActive()) + return $this->_pdo->quote($str); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * Quotes a table name for use in a query. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteTableName($name) + { + return $this->getDbMetaData()->quoteTableName($name); + } + + /** + * Quotes a column name for use in a query. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteColumnName($name) + { + return $this->getDbMetaData()->quoteColumnName($name); + } + + /** + * Quotes a column alias for use in a query. + * @param string $name column name + * @return string the properly quoted column alias + */ + public function quoteColumnAlias($name) + { + return $this->getDbMetaData()->quoteColumnAlias($name); + } + + /** + * @return TDbMetaData + */ + public function getDbMetaData() + { + if($this->_dbMeta===null) + { + Prado::using('System.Data.Common.TDbMetaData'); + $this->_dbMeta = TDbMetaData::getInstance($this); + } + return $this->_dbMeta; + } + + /** + * @return TDbColumnCaseMode the case of the column names + */ + public function getColumnCase() + { + switch($this->getAttribute(PDO::ATTR_CASE)) + { + case PDO::CASE_NATURAL: + return TDbColumnCaseMode::Preserved; + case PDO::CASE_LOWER: + return TDbColumnCaseMode::LowerCase; + case PDO::CASE_UPPER: + return TDbColumnCaseMode::UpperCase; + } + } + + /** + * @param TDbColumnCaseMode the case of the column names + */ + public function setColumnCase($value) + { + switch(TPropertyValue::ensureEnum($value,'TDbColumnCaseMode')) + { + case TDbColumnCaseMode::Preserved: + $value=PDO::CASE_NATURAL; + break; + case TDbColumnCaseMode::LowerCase: + $value=PDO::CASE_LOWER; + break; + case TDbColumnCaseMode::UpperCase: + $value=PDO::CASE_UPPER; + break; + } + $this->setAttribute(PDO::ATTR_CASE,$value); + } + + /** + * @return TDbNullConversionMode how the null and empty strings are converted + */ + public function getNullConversion() + { + switch($this->getAttribute(PDO::ATTR_ORACLE_NULLS)) + { + case PDO::NULL_NATURAL: + return TDbNullConversionMode::Preserved; + case PDO::NULL_EMPTY_STRING: + return TDbNullConversionMode::EmptyStringToNull; + case PDO::NULL_TO_STRING: + return TDbNullConversionMode::NullToEmptyString; + } + } + + /** + * @param TDbNullConversionMode how the null and empty strings are converted + */ + public function setNullConversion($value) + { + switch(TPropertyValue::ensureEnum($value,'TDbNullConversionMode')) + { + case TDbNullConversionMode::Preserved: + $value=PDO::NULL_NATURAL; + break; + case TDbNullConversionMode::EmptyStringToNull: + $value=PDO::NULL_EMPTY_STRING; + break; + case TDbNullConversionMode::NullToEmptyString: + $value=PDO::NULL_TO_STRING; + break; + } + $this->setAttribute(PDO::ATTR_ORACLE_NULLS,$value); + } + + /** + * @return boolean whether creating or updating a DB record will be automatically committed. + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getAutoCommit() + { + return $this->getAttribute(PDO::ATTR_AUTOCOMMIT); + } + + /** + * @param boolean whether creating or updating a DB record will be automatically committed. + * Some DBMS (such as sqlite) may not support this feature. + */ + public function setAutoCommit($value) + { + $this->setAttribute(PDO::ATTR_AUTOCOMMIT,TPropertyValue::ensureBoolean($value)); + } + + /** + * @return boolean whether the connection is persistent or not + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getPersistent() + { + return $this->getAttribute(PDO::ATTR_PERSISTENT); + } + + /** + * @param boolean whether the connection is persistent or not + * Some DBMS (such as sqlite) may not support this feature. + */ + public function setPersistent($value) + { + return $this->setAttribute(PDO::ATTR_PERSISTENT,TPropertyValue::ensureBoolean($value)); + } + + /** + * @return string name of the DB driver + */ + public function getDriverName() + { + return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + } + + /** + * @return string the version information of the DB driver + */ + public function getClientVersion() + { + return $this->getAttribute(PDO::ATTR_CLIENT_VERSION); + } + + /** + * @return string the status of the connection + * Some DBMS (such as sqlite) may not support this feature. + */ + public function getConnectionStatus() + { + return $this->getAttribute(PDO::ATTR_CONNECTION_STATUS); + } + + /** + * @return boolean whether the connection performs data prefetching + */ + public function getPrefetch() + { + return $this->getAttribute(PDO::ATTR_PREFETCH); + } + + /** + * @return string the information of DBMS server + */ + public function getServerInfo() + { + return $this->getAttribute(PDO::ATTR_SERVER_INFO); + } + + /** + * @return string the version information of DBMS server + */ + public function getServerVersion() + { + return $this->getAttribute(PDO::ATTR_SERVER_VERSION); + } + + /** + * @return int timeout settings for the connection + */ + public function getTimeout() + { + return $this->getAttribute(PDO::ATTR_TIMEOUT); + } + + /** + * Obtains a specific DB connection attribute information. + * @param int the attribute to be queried + * @return mixed the corresponding attribute information + * @see http://www.php.net/manual/en/function.PDO-getAttribute.php + */ + public function getAttribute($name) + { + if($this->getActive()) + return $this->_pdo->getAttribute($name); + else + throw new TDbException('dbconnection_connection_inactive'); + } + + /** + * Sets an attribute on the database connection. + * @param int the attribute to be set + * @param mixed the attribute value + * @see http://www.php.net/manual/en/function.PDO-setAttribute.php + */ + public function setAttribute($name,$value) + { + if($this->_pdo instanceof PDO) + $this->_pdo->setAttribute($name,$value); + else + $this->_attributes[$name]=$value; + } +} + +/** + * TDbColumnCaseMode + * + * @author Qiang Xue + * @version $Id: TDbConnection.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.0 + */ +class TDbColumnCaseMode extends TEnumerable +{ + /** + * Column name cases are kept as is from the database + */ + const Preserved='Preserved'; + /** + * Column names are converted to lower case + */ + const LowerCase='LowerCase'; + /** + * Column names are converted to upper case + */ + const UpperCase='UpperCase'; +} + +/** + * TDbNullConversionMode + * + * @author Qiang Xue + * @version $Id: TDbConnection.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.0 + */ +class TDbNullConversionMode extends TEnumerable +{ + /** + * No conversion is performed for null and empty values. + */ + const Preserved='Preserved'; + /** + * NULL is converted to empty string + */ + const NullToEmptyString='NullToEmptyString'; + /** + * Empty string is converted to NULL + */ + const EmptyStringToNull='EmptyStringToNull'; +} + diff --git a/gui/baculum/framework/Data/TDbDataReader.php b/gui/baculum/framework/Data/TDbDataReader.php new file mode 100644 index 0000000000..718d355af1 --- /dev/null +++ b/gui/baculum/framework/Data/TDbDataReader.php @@ -0,0 +1,225 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbDataReader.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + */ + +/** + * TDbDataReader class. + * + * TDbDataReader represents a forward-only stream of rows from a query result set. + * + * To read the current row of data, call {@link read}. The method {@link readAll} + * returns all the rows in a single array. + * + * One can also retrieve the rows of data in TDbDataReader by using foreach: + * + * foreach($reader as $row) + * // $row represents a row of data + * + * Since TDbDataReader is a forward-only stream, you can only traverse it once. + * + * It is possible to use a specific mode of data fetching by setting + * {@link setFetchMode FetchMode}. See {@link http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php} + * for more details. + * + * @author Qiang Xue + * @version $Id: TDbDataReader.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.0 + */ +class TDbDataReader extends TComponent implements Iterator +{ + private $_statement; + private $_closed=false; + private $_row; + private $_index=-1; + + /** + * Constructor. + * @param TDbCommand the command generating the query result + */ + public function __construct(TDbCommand $command) + { + $this->_statement=$command->getPdoStatement(); + $this->_statement->setFetchMode(PDO::FETCH_ASSOC); + } + + /** + * Binds a column to a PHP variable. + * When rows of data are being fetched, the corresponding column value + * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND. + * @param mixed Number of the column (1-indexed) or name of the column + * in the result set. If using the column name, be aware that the name + * should match the case of the column, as returned by the driver. + * @param mixed Name of the PHP variable to which the column will be bound. + * @param int Data type of the parameter + * @see http://www.php.net/manual/en/function.PDOStatement-bindColumn.php + */ + public function bindColumn($column, &$value, $dataType=null) + { + if($dataType===null) + $this->_statement->bindColumn($column,$value); + else + $this->_statement->bindColumn($column,$value,$dataType); + } + + /** + * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php + */ + public function setFetchMode($mode) + { + $params=func_get_args(); + call_user_func_array(array($this->_statement,'setFetchMode'),$params); + } + + /** + * Advances the reader to the next row in a result set. + * @return array|false the current row, false if no more row available + */ + public function read() + { + return $this->_statement->fetch(); + } + + /** + * Returns a single column from the next row of a result set. + * @param int zero-based column index + * @return mixed|false the column of the current row, false if no more row available + */ + public function readColumn($columnIndex) + { + return $this->_statement->fetchColumn($columnIndex); + } + + /** + * Returns a single column from the next row of a result set. + * @param string class name of the object to be created and populated + * @param array list of column names whose values are to be passed as parameters in the constructor of the class being created + * @return mixed|false the populated object, false if no more row of data available + */ + public function readObject($className,$fields) + { + return $this->_statement->fetchObject($className,$fields); + } + + /** + * Reads the whole result set into an array. + * @return array the result set (each array element represents a row of data). + * An empty array will be returned if the result contains no row. + */ + public function readAll() + { + return $this->_statement->fetchAll(); + } + + /** + * Advances the reader to the next result when reading the results of a batch of statements. + * This method is only useful when there are multiple result sets + * returned by the query. Not all DBMS support this feature. + */ + public function nextResult() + { + return $this->_statement->nextRowset(); + } + + /** + * Closes the reader. + * Any further data reading will result in an exception. + */ + public function close() + { + $this->_statement->closeCursor(); + $this->_closed=true; + } + + /** + * @return boolean whether the reader is closed or not. + */ + public function getIsClosed() + { + return $this->_closed; + } + + /** + * @return int number of rows contained in the result. + * Note, most DBMS may not give a meaningful count. + * In this case, use "SELECT COUNT(*) FROM tableName" to obtain the number of rows. + */ + public function getRowCount() + { + return $this->_statement->rowCount(); + } + + /** + * @return int the number of columns in the result set. + * Note, even there's no row in the reader, this still gives correct column number. + */ + public function getColumnCount() + { + return $this->_statement->columnCount(); + } + + /** + * Resets the iterator to the initial state. + * This method is required by the interface Iterator. + * @throws TDbException if this method is invoked twice + */ + public function rewind() + { + if($this->_index<0) + { + $this->_row=$this->_statement->fetch(); + $this->_index=0; + } + else + throw new TDbException('dbdatareader_rewind_invalid'); + } + + /** + * Returns the index of the current row. + * This method is required by the interface Iterator. + * @return integer the index of the current row. + */ + public function key() + { + return $this->_index; + } + + /** + * Returns the current row. + * This method is required by the interface Iterator. + * @return mixed the current row. + */ + public function current() + { + return $this->_row; + } + + /** + * Moves the internal pointer to the next row. + * This method is required by the interface Iterator. + */ + public function next() + { + $this->_row=$this->_statement->fetch(); + $this->_index++; + } + + /** + * Returns whether there is a row of data at current position. + * This method is required by the interface Iterator. + * @return boolean whether there is a row of data at current position. + */ + public function valid() + { + return $this->_row!==false; + } +} + diff --git a/gui/baculum/framework/Data/TDbTransaction.php b/gui/baculum/framework/Data/TDbTransaction.php new file mode 100644 index 0000000000..f1be4e0b9b --- /dev/null +++ b/gui/baculum/framework/Data/TDbTransaction.php @@ -0,0 +1,112 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbTransaction.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + */ + +Prado::using('System.Data.TDbDataReader'); + +/** + * TDbTransaction class. + * + * TDbTransaction represents a DB transaction. + * It is usually created by calling {@link TDbConnection::beginTransaction}. + * + * The following code is a common scenario of using transactions: + * + * try + * { + * $transaction=$connection->beginTransaction(); + * $connection->createCommand($sql1)->execute(); + * $connection->createCommand($sql2)->execute(); + * //.... other SQL executions + * $transaction->commit(); + * } + * catch(Exception $e) + * { + * $transaction->rollBack(); + * } + * + * + * @author Qiang Xue + * @version $Id: TDbTransaction.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Data + * @since 3.0 + */ +class TDbTransaction extends TComponent +{ + private $_connection=null; + private $_active; + + /** + * Constructor. + * @param TDbConnection the connection associated with this transaction + * @see TDbConnection::beginTransaction + */ + public function __construct(TDbConnection $connection) + { + $this->_connection=$connection; + $this->setActive(true); + } + + /** + * Commits a transaction. + * @throws TDbException if the transaction or the DB connection is not active. + */ + public function commit() + { + if($this->_active && $this->_connection->getActive()) + { + $this->_connection->getPdoInstance()->commit(); + $this->_active=false; + } + else + throw new TDbException('dbtransaction_transaction_inactive'); + } + + /** + * Rolls back a transaction. + * @throws TDbException if the transaction or the DB connection is not active. + */ + public function rollback() + { + if($this->_active && $this->_connection->getActive()) + { + $this->_connection->getPdoInstance()->rollBack(); + $this->_active=false; + } + else + throw new TDbException('dbtransaction_transaction_inactive'); + } + + /** + * @return TDbConnection the DB connection for this transaction + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * @return boolean whether this transaction is active + */ + public function getActive() + { + return $this->_active; + } + + /** + * @param boolean whether this transaction is active + */ + protected function setActive($value) + { + $this->_active=TPropertyValue::ensureBoolean($value); + } +} + diff --git a/gui/baculum/framework/Exceptions/TErrorHandler.php b/gui/baculum/framework/Exceptions/TErrorHandler.php new file mode 100644 index 0000000000..3c9d8514d2 --- /dev/null +++ b/gui/baculum/framework/Exceptions/TErrorHandler.php @@ -0,0 +1,413 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TErrorHandler.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + */ + +/** + * TErrorHandler class + * + * TErrorHandler handles all PHP user errors and exceptions generated during + * servicing user requests. It displays these errors using different templates + * and if possible, using languages preferred by the client user. + * Note, PHP parsing errors cannot be caught and handled by TErrorHandler. + * + * The templates used to format the error output are stored under System.Exceptions. + * You may choose to use your own templates, should you not like the templates + * provided by Prado. Simply set {@link setErrorTemplatePath ErrorTemplatePath} + * to the path (in namespace format) storing your own templates. + * + * There are two sets of templates, one for errors to be displayed to client users + * (called external errors), one for errors to be displayed to system developers + * (called internal errors). The template file name for the former is + * error[StatusCode][-LanguageCode].html, and for the latter it is + * exception[-LanguageCode].html, where StatusCode refers to response status + * code (e.g. 404, 500) specified when {@link THttpException} is thrown, + * and LanguageCode is the client user preferred language code (e.g. en, zh, de). + * The templates error.html and exception.html are default ones + * that are used if no other appropriate templates are available. + * Note, these templates are not Prado control templates. They are simply + * html files with keywords (e.g. %%ErrorMessage%%, %%Version%%) + * to be replaced with the corresponding information. + * + * By default, TErrorHandler is registered with {@link TApplication} as the + * error handler module. It can be accessed via {@link TApplication::getErrorHandler()}. + * You seldom need to deal with the error handler directly. It is mainly used + * by the application object to handle errors. + * + * TErrorHandler may be configured in application configuration file as follows + * + * + * @author Qiang Xue + * @version $Id: TErrorHandler.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TErrorHandler extends TModule +{ + /** + * error template file basename + */ + const ERROR_FILE_NAME='error'; + /** + * exception template file basename + */ + const EXCEPTION_FILE_NAME='exception'; + /** + * number of lines before and after the error line to be displayed in case of an exception + */ + const SOURCE_LINES=12; + + /** + * @var string error template directory + */ + private $_templatePath=null; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + $this->getApplication()->setErrorHandler($this); + } + + /** + * @return string the directory containing error template files. + */ + public function getErrorTemplatePath() + { + if($this->_templatePath===null) + $this->_templatePath=Prado::getFrameworkPath().'/Exceptions/templates'; + return $this->_templatePath; + } + + /** + * Sets the path storing all error and exception template files. + * The path must be in namespace format, such as System.Exceptions (which is the default). + * @param string template path in namespace format + * @throws TConfigurationException if the template path is invalid + */ + public function setErrorTemplatePath($value) + { + if(($templatePath=Prado::getPathOfNamespace($value))!==null && is_dir($templatePath)) + $this->_templatePath=$templatePath; + else + throw new TConfigurationException('errorhandler_errortemplatepath_invalid',$value); + } + + /** + * Handles PHP user errors and exceptions. + * This is the event handler responding to the Error event + * raised in {@link TApplication}. + * The method mainly uses appropriate template to display the error/exception. + * It terminates the application immediately after the error is displayed. + * @param mixed sender of the event + * @param mixed event parameter (if the event is raised by TApplication, it refers to the exception instance) + */ + public function handleError($sender,$param) + { + static $handling=false; + // We need to restore error and exception handlers, + // because within error and exception handlers, new errors and exceptions + // cannot be handled properly by PHP + restore_error_handler(); + restore_exception_handler(); + // ensure that we do not enter infinite loop of error handling + if($handling) + $this->handleRecursiveError($param); + else + { + $handling=true; + if(($response=$this->getResponse())!==null) + $response->clear(); + if(!headers_sent()) + header('Content-Type: text/html; charset=UTF-8'); + if($param instanceof THttpException) + $this->handleExternalError($param->getStatusCode(),$param); + else if($this->getApplication()->getMode()===TApplicationMode::Debug) + $this->displayException($param); + else + $this->handleExternalError(500,$param); + } + } + + + /** + * @param string $value + * @param Exception|null$exception + * @return string + * @since 3.1.6 + */ + protected static function hideSecurityRelated($value, $exception=null) + { + $aRpl = array(); + if($exception !== null && $exception instanceof Exception) + { + $aTrace = $exception->getTrace(); + foreach($aTrace as $item) + { + if(isset($item['file'])) + $aRpl[dirname($item['file']) . DIRECTORY_SEPARATOR] = '' . DIRECTORY_SEPARATOR; + } + } + $aRpl[$_SERVER['DOCUMENT_ROOT']] = '${DocumentRoot}'; + $aRpl[str_replace('/', DIRECTORY_SEPARATOR, $_SERVER['DOCUMENT_ROOT'])] = '${DocumentRoot}'; + $aRpl[PRADO_DIR . DIRECTORY_SEPARATOR] = '${PradoFramework}' . DIRECTORY_SEPARATOR; + if(isset($aRpl[DIRECTORY_SEPARATOR])) unset($aRpl[DIRECTORY_SEPARATOR]); + $aRpl = array_reverse($aRpl, true); + + return str_replace(array_keys($aRpl), $aRpl, $value); + } + + /** + * Displays error to the client user. + * THttpException and errors happened when the application is in Debug + * mode will be displayed to the client user. + * @param integer response status code + * @param Exception exception instance + */ + protected function handleExternalError($statusCode,$exception) + { + if(!($exception instanceof THttpException)) + error_log($exception->__toString()); + + $content=$this->getErrorTemplate($statusCode,$exception); + + $serverAdmin=isset($_SERVER['SERVER_ADMIN'])?$_SERVER['SERVER_ADMIN']:''; + + $isDebug = $this->getApplication()->getMode()===TApplicationMode::Debug; + + $errorMessage = $exception->getMessage(); + if($isDebug) + $version=$_SERVER['SERVER_SOFTWARE'].' PRADO/'.Prado::getVersion(); + else + { + $version=''; + $errorMessage = self::hideSecurityRelated($errorMessage, $exception); + } + $tokens=array( + '%%StatusCode%%' => "$statusCode", + '%%ErrorMessage%%' => htmlspecialchars($errorMessage), + '%%ServerAdmin%%' => $serverAdmin, + '%%Version%%' => $version, + '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) + ); + + $CGI=substr(php_sapi_name(), 0, 3) == 'cgi'; // FastCGI / IIS + if($isDebug) + { + if ($CGI) + header("Status: $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode)); + else + header("HTTP/1.0 $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode)); + } else { + if ($CGI) + header("Status: $statusCode", true, TPropertyValue::ensureInteger($statusCode)); + else + header("HTTP/1.0 $statusCode", true, TPropertyValue::ensureInteger($statusCode)); + } + + echo strtr($content,$tokens); + } + + /** + * Handles error occurs during error handling (called recursive error). + * THttpException and errors happened when the application is in Debug + * mode will be displayed to the client user. + * Error is displayed without using existing template to prevent further errors. + * @param Exception exception instance + */ + protected function handleRecursiveError($exception) + { + if($this->getApplication()->getMode()===TApplicationMode::Debug) + { + echo "Recursive Error\n"; + echo "

    Recursive Error

    \n"; + echo "
    ".$exception->__toString()."
    \n"; + echo ""; + } + else + { + error_log("Error happened while processing an existing error:\n".$exception->__toString()); + header('HTTP/1.0 500 Internal Error'); + } + } + + /** + * Displays exception information. + * Exceptions are displayed with rich context information, including + * the call stack and the context source code. + * This method is only invoked when application is in Debug mode. + * @param Exception exception instance + */ + protected function displayException($exception) + { + if(php_sapi_name()==='cli') + { + echo $exception->getMessage()."\n"; + echo $exception->getTraceAsString(); + return; + } + + if($exception instanceof TTemplateException) + { + $fileName=$exception->getTemplateFile(); + $lines=empty($fileName)?explode("\n",$exception->getTemplateSource()):@file($fileName); + $source=$this->getSourceCode($lines,$exception->getLineNumber()); + if($fileName==='') + $fileName='---embedded template---'; + $errorLine=$exception->getLineNumber(); + } + else + { + if(($trace=$this->getExactTrace($exception))!==null) + { + $fileName=$trace['file']; + $errorLine=$trace['line']; + } + else + { + $fileName=$exception->getFile(); + $errorLine=$exception->getLine(); + } + $source=$this->getSourceCode(@file($fileName),$errorLine); + } + + if($this->getApplication()->getMode()===TApplicationMode::Debug) + $version=$_SERVER['SERVER_SOFTWARE'].' PRADO/'.Prado::getVersion(); + else + $version=''; + + $tokens=array( + '%%ErrorType%%' => get_class($exception), + '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())), + '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')', + '%%SourceCode%%' => $source, + '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()), + '%%Version%%' => $version, + '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) + ); + + $content=$this->getExceptionTemplate($exception); + + echo strtr($content,$tokens); + } + + /** + * Retrieves the template used for displaying internal exceptions. + * Internal exceptions will be displayed with source code causing the exception. + * This occurs when the application is in debug mode. + * @param Exception the exception to be displayed + * @return string the template content + */ + protected function getExceptionTemplate($exception) + { + $lang=Prado::getPreferredLanguage(); + $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html'; + if(!is_file($exceptionFile)) + $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'.html'; + if(($content=@file_get_contents($exceptionFile))===false) + die("Unable to open exception template file '$exceptionFile'."); + return $content; + } + + /** + * Retrieves the template used for displaying external exceptions. + * External exceptions are those displayed to end-users. They do not contain + * error source code. Therefore, you might want to override this method + * to provide your own error template for displaying certain external exceptions. + * The following tokens in the template will be replaced with corresponding content: + * %%StatusCode%% : the status code of the exception + * %%ErrorMessage%% : the error message (HTML encoded). + * %%ServerAdmin%% : the server admin information (retrieved from Web server configuration) + * %%Version%% : the version information of the Web server. + * %%Time%% : the time the exception occurs at + * + * @param integer status code (such as 404, 500, etc.) + * @param Exception the exception to be displayed + * @return string the template content + */ + protected function getErrorTemplate($statusCode,$exception) + { + $base=$this->getErrorTemplatePath().DIRECTORY_SEPARATOR.self::ERROR_FILE_NAME; + $lang=Prado::getPreferredLanguage(); + if(is_file("$base$statusCode-$lang.html")) + $errorFile="$base$statusCode-$lang.html"; + else if(is_file("$base$statusCode.html")) + $errorFile="$base$statusCode.html"; + else if(is_file("$base-$lang.html")) + $errorFile="$base-$lang.html"; + else + $errorFile="$base.html"; + if(($content=@file_get_contents($errorFile))===false) + die("Unable to open error template file '$errorFile'."); + return $content; + } + + private function getExactTrace($exception) + { + $trace=$exception->getTrace(); + $result=null; + // if PHP exception, we want to show the 2nd stack level context + // because the 1st stack level is of little use (it's in error handler) + if($exception instanceof TPhpErrorException) + $result=isset($trace[0]['file'])?$trace[0]:$trace[1]; + else if($exception instanceof TInvalidOperationException) + { + // in case of getter or setter error, find out the exact file and row + if(($result=$this->getPropertyAccessTrace($trace,'__get'))===null) + $result=$this->getPropertyAccessTrace($trace,'__set'); + } + if($result!==null && strpos($result['file'],': eval()\'d code')!==false) + return null; + + return $result; + } + + private function getPropertyAccessTrace($trace,$pattern) + { + $result=null; + foreach($trace as $t) + { + if(isset($t['function']) && $t['function']===$pattern) + $result=$t; + else + break; + } + return $result; + } + + private function getSourceCode($lines,$errorLine) + { + $beginLine=$errorLine-self::SOURCE_LINES>=0?$errorLine-self::SOURCE_LINES:0; + $endLine=$errorLine+self::SOURCE_LINES<=count($lines)?$errorLine+self::SOURCE_LINES:count($lines); + + $source=''; + for($i=$beginLine;$i<$endLine;++$i) + { + if($i===$errorLine-1) + { + $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + $source.="
    ".$line."
    "; + } + else + $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + } + return $source; + } + + private function addLink($message) + { + $baseUrl='http://www.pradosoft.com/docs/classdoc'; + return preg_replace('/\b(T[A-Z]\w+)\b/',"\${1}",$message); + } +} + diff --git a/gui/baculum/framework/Exceptions/TException.php b/gui/baculum/framework/Exceptions/TException.php new file mode 100644 index 0000000000..c4fe62b4f8 --- /dev/null +++ b/gui/baculum/framework/Exceptions/TException.php @@ -0,0 +1,424 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + */ + +/** + * TException class + * + * TException is the base class for all PRADO exceptions. + * + * TException provides the functionality of translating an error code + * into a descriptive error message in a language that is preferred + * by user browser. Additional parameters may be passed together with + * the error code so that the translated message contains more detailed + * information. + * + * By default, TException looks for a message file by calling + * {@link getErrorMessageFile()} method, which uses the "message-xx.txt" + * file located under "System.Exceptions" folder, where "xx" is the + * code of the user preferred language. If such a file is not found, + * "message.txt" will be used instead. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TException extends Exception +{ + private $_errorCode=''; + static $_messageCache=array(); + + /** + * Constructor. + * @param string error message. This can be a string that is listed + * in the message file. If so, the message in the preferred language + * will be used as the error message. Any rest parameters will be used + * to replace placeholders ({0}, {1}, {2}, etc.) in the message. + */ + public function __construct($errorMessage) + { + $this->_errorCode=$errorMessage; + $errorMessage=$this->translateErrorMessage($errorMessage); + $args=func_get_args(); + array_shift($args); + $n=count($args); + $tokens=array(); + for($i=0;$i<$n;++$i) + $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]); + parent::__construct(strtr($errorMessage,$tokens)); + } + + /** + * Translates an error code into an error message. + * @param string error code that is passed in the exception constructor. + * @return string the translated error message + */ + protected function translateErrorMessage($key) + { + $msgFile=$this->getErrorMessageFile(); + + // Cache messages + if (!isset(self::$_messageCache[$msgFile])) + { + if(($entries=@file($msgFile))!==false) + { + foreach($entries as $entry) + { + @list($code,$message)=explode('=',$entry,2); + self::$_messageCache[$msgFile][trim($code)]=trim($message); + } + } + } + return isset(self::$_messageCache[$msgFile][$key]) ? self::$_messageCache[$msgFile][$key] : $key; + } + + /** + * @return string path to the error message file + */ + protected function getErrorMessageFile() + { + $lang=Prado::getPreferredLanguage(); + $msgFile=Prado::getFrameworkPath().'/Exceptions/messages/messages-'.$lang.'.txt'; + if(!is_file($msgFile)) + $msgFile=Prado::getFrameworkPath().'/Exceptions/messages/messages.txt'; + return $msgFile; + } + + /** + * @return string error code + */ + public function getErrorCode() + { + return $this->_errorCode; + } + + /** + * @param string error code + */ + public function setErrorCode($code) + { + $this->_errorCode=$code; + } + + /** + * @return string error message + */ + public function getErrorMessage() + { + return $this->getMessage(); + } + + /** + * @param string error message + */ + protected function setErrorMessage($message) + { + $this->message=$message; + } +} + +/** + * TSystemException class + * + * TSystemException is the base class for all framework-level exceptions. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TSystemException extends TException +{ +} + +/** + * TApplicationException class + * + * TApplicationException is the base class for all user application-level exceptions. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TApplicationException extends TException +{ +} + +/** + * TInvalidOperationException class + * + * TInvalidOperationException represents an exception caused by invalid operations. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TInvalidOperationException extends TSystemException +{ +} + +/** + * TInvalidDataTypeException class + * + * TInvalidDataTypeException represents an exception caused by invalid data type. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TInvalidDataTypeException extends TSystemException +{ +} + +/** + * TInvalidDataValueException class + * + * TInvalidDataValueException represents an exception caused by invalid data value. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TInvalidDataValueException extends TSystemException +{ +} + +/** + * TConfigurationException class + * + * TConfigurationException represents an exception caused by invalid configurations, + * such as error in an application configuration file or control template file. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TConfigurationException extends TSystemException +{ +} + +/** + * TTemplateException class + * + * TTemplateException represents an exception caused by invalid template syntax. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.1 + */ +class TTemplateException extends TConfigurationException +{ + private $_template=''; + private $_lineNumber=0; + private $_fileName=''; + + /** + * @return string the template source code that causes the exception. This is empty if {@link getTemplateFile TemplateFile} is not empty. + */ + public function getTemplateSource() + { + return $this->_template; + } + + /** + * @param string the template source code that causes the exception + */ + public function setTemplateSource($value) + { + $this->_template=$value; + } + + /** + * @return string the template file that causes the exception. This could be empty if the template is an embedded template. In this case, use {@link getTemplateSource TemplateSource} to obtain the actual template content. + */ + public function getTemplateFile() + { + return $this->_fileName; + } + + /** + * @param string the template file that causes the exception + */ + public function setTemplateFile($value) + { + $this->_fileName=$value; + } + + /** + * @return integer the line number at which the template has error + */ + public function getLineNumber() + { + return $this->_lineNumber; + } + + /** + * @param integer the line number at which the template has error + */ + public function setLineNumber($value) + { + $this->_lineNumber=TPropertyValue::ensureInteger($value); + } +} + +/** + * TIOException class + * + * TIOException represents an exception related with improper IO operations. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TIOException extends TSystemException +{ +} + +/** + * TDbException class + * + * TDbException represents an exception related with DB operations. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TDbException extends TSystemException +{ +} + +/** + * TDbConnectionException class + * + * TDbConnectionException represents an exception caused by DB connection failure. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TDbConnectionException extends TDbException +{ +} + +/** + * TNotSupportedException class + * + * TNotSupportedException represents an exception caused by using an unsupported PRADO feature. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TNotSupportedException extends TSystemException +{ +} + +/** + * TPhpErrorException class + * + * TPhpErrorException represents an exception caused by a PHP error. + * This exception is mainly thrown within a PHP error handler. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class TPhpErrorException extends TSystemException +{ + /** + * Constructor. + * @param integer error number + * @param string error string + * @param string error file + * @param integer error line number + */ + public function __construct($errno,$errstr,$errfile,$errline) + { + static $errorTypes=array( + E_ERROR => "Error", + E_WARNING => "Warning", + E_PARSE => "Parsing Error", + E_NOTICE => "Notice", + E_CORE_ERROR => "Core Error", + E_CORE_WARNING => "Core Warning", + E_COMPILE_ERROR => "Compile Error", + E_COMPILE_WARNING => "Compile Warning", + E_USER_ERROR => "User Error", + E_USER_WARNING => "User Warning", + E_USER_NOTICE => "User Notice", + E_STRICT => "Runtime Notice" + ); + $errorType=isset($errorTypes[$errno])?$errorTypes[$errno]:'Unknown Error'; + parent::__construct("[$errorType] $errstr (@line $errline in file $errfile)."); + } +} + + +/** + * THttpException class + * + * THttpException represents an exception that is caused by invalid operations + * of end-users. The {@link getStatusCode StatusCode} gives the type of HTTP error. + * It is used by {@link TErrorHandler} to provide different error output to users. + * + * @author Qiang Xue + * @version $Id: TException.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Exceptions + * @since 3.0 + */ +class THttpException extends TSystemException +{ + private $_statusCode; + + /** + * Constructor. + * @param integer HTTP status code, such as 404, 500, etc. + * @param string error message. This can be a string that is listed + * in the message file. If so, the message in the preferred language + * will be used as the error message. Any rest parameters will be used + * to replace placeholders ({0}, {1}, {2}, etc.) in the message. + */ + public function __construct($statusCode,$errorMessage) + { + $this->_statusCode=$statusCode; + $this->setErrorCode($errorMessage); + $errorMessage=$this->translateErrorMessage($errorMessage); + $args=func_get_args(); + array_shift($args); + array_shift($args); + $n=count($args); + $tokens=array(); + for($i=0;$i<$n;++$i) + $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]); + parent::__construct(strtr($errorMessage,$tokens)); + } + + /** + * @return integer HTTP status code, such as 404, 500, etc. + */ + public function getStatusCode() + { + return $this->_statusCode; + } +} + diff --git a/gui/baculum/framework/Exceptions/messages/messages-fr.txt b/gui/baculum/framework/Exceptions/messages/messages-fr.txt new file mode 100644 index 0000000000..4e6a396b92 --- /dev/null +++ b/gui/baculum/framework/Exceptions/messages/messages-fr.txt @@ -0,0 +1,418 @@ +prado_application_singleton_required = Il ne peut y avoir qu'une instance de : Prado.Application. +prado_component_unknown = Le type du composant '{0}' est inconnu. Cela peut être causé par l'erreur de syntaxe dans le fichier {0} : {1} +prado_using_invalid = Le namespace '{0}' n'est pas valide. Assurez-vous que '.*' a été ajouté si vous souhaitez utiliser un namespace qui fasse référence à ce dossier +prado_alias_redefined = L'alias '{0}' ne peut pas être redéfini. +prado_alias_invalid = L'alias '{0}' fait référence à un chemin de dossier non valide : '{1}'. Seul les dossiers existants peuvent être mis en alias. +prado_aliasname_invalid = L'alias '{0}' contient un caractère non autorisé : '.'. + +component_property_undefined = La proriété de l'objet '{0}.{1}' n'est pas définie. +component_property_readonly = La proriété de l'objet '{0}.{1}' est en lecture seule. +component_event_undefined = L'événement de l'objet '{0}.{1}' n'est pas défini. +component_eventhandler_invalid = L'événement de l'objet '{0}.{1}' n'est pas rataché à un handler valide '{2}'. +component_expression_invalid = L'objet '{0}' essaye d'évaluer un expression invalide '{1}' : {2}. +component_statements_invalid = L'objet '{0}' essaye d'évaluer une déclaration PHP invalide '{1}' : {2}. + +propertyvalue_enumvalue_invalid = La valeur '{0}' n'est pas une valeur possible pour l'énumération ({1}). + +list_index_invalid = L'indice '{0}' est hors limite. +list_item_inexistent = L'élément ne peut être trouvé dans la liste. +list_data_not_iterable = Data must be either an array or an object implementing Traversable interface. +list_readonly = {0} est en lecture seule. + +map_addition_disallowed = Le nouveau élément ne peut être ajouter dans la liste(map). +map_item_unremovable = L'élément ne peut être supprimé de la liste (map). +map_data_not_iterable = Data must be either an array or an object implementing Traversable interface. +map_readonly = {0} est en lecture seule. + +application_includefile_invalid = Impossible de trouver les paramètres de configuration de l'application {0}. Assurez-vous qu'il soit dans le bon espace d'adressage (namespace format) et que l'extension du fichier soit en ".xml". +application_basepath_invalid = Le dossier de base de l'application '{0}' n'existe pas ou n'est pas un répertoire. +application_runtimepath_invalid = Le dossier "runtime" de l'application '{0}' n'existe pas ou le serveur Web n'a pas les droits en écriture sur ce dossier. +application_service_invalid = Le service '{0}' doit implémenter l'interface "IService". +application_service_unknown = Le service '{0}' que vous demandez n'est pas défini. +application_unavailable = L'application n'est pas disponible actuellement. +application_service_unavailable = Le service '{0}' n'est pas disponible actuellement. +application_moduleid_duplicated = Le module d'ID '{0}' de l'application n'est pas unique. +application_runtimepath_failed = Impossible de créer le dossier "runtime" '{0}'. Assurez-vous que le dossier parent existe et qu'il est accessible en écriture par le serveur Web. + +appconfig_aliaspath_invalid = Configuration de l'application : l'élement pointe vers un chemin invalide "{1}". +appconfig_alias_invalid = Confirugation de l'application : l'élement doit avoir un attribut "id" et un attribut "path". +appconfig_alias_redefined = Configuration de l'application : l'élement ne peut pas être redéfini. +appconfig_using_invalid = Configuration de l'application : l'élément doit avoir un attribut "namespace". +appconfig_moduleid_required = Configuration de l'application : l'élément doit avoir un attribut "id". +appconfig_moduletype_required = Configuration de l'application : l'élement doit avoir un attribut "class". +appconfig_serviceid_required = Configuration de l'application : l'élément doit avoir un attribut "id". +appconfig_servicetype_required = Configuration de l'application : l'élément doit avoit un attribut "class". +appconfig_parameterid_required = Configuration de l'application : l'élément doit avoir un attribut "id". +appconfig_includefile_required = Configuration de l'application : l'élément doit avoir un attribut "file". + +securitymanager_validationkey_invalid = TSecurityManager.ValidationKey ne doit pas être vide. +securitymanager_encryptionkey_invalid = TSecurityManager.EncryptionKey ne doit pas être vide. +securitymanager_mcryptextension_required = L'extension PHP Mcrypt est nécessaier pour utiliser les fonctions de cryptage de TSecurityManager. + +uri_format_invalid = '{0}' n'est pas une URI valide. + +httprequest_separator_invalid = THttpRequest.UrlParamSeparator ne peut contenir qu'un seul caractère. +httprequest_urlmanager_inexist = THttpRequest.UrlManager '{0}' ne pointe pas vers un momdule exitant. +httprequest_urlmanager_invalid = THttpRequest.UrlManager '{0}' doit pointer vers un module héritant de TUrlManager. + +httpcookiecollection_httpcookie_required = THttpCookieCollection n'accepte que des objets de type THttpCookie. + +httpresponse_bufferoutput_unchangeable = THttpResponse.BufferOutput ne peut pas être modifié après que THttpResponse ait été initialisé. +httpresponse_file_inexistent = THttpResponse ne peut pas envoyer le fichier '{0}'. Ce fichier n'existe pas. + +httpsession_sessionid_unchangeable = THttpSession.SessionID ne peut pas être modifié après que la session ait démarré. +httpsession_sessionname_unchangeable = THttpSession.SessionName ne peut pas être modifié après que la session ait démarré. +httpsession_sessionname_invalid = THttpSession.SessionName ne peut contenir que des caractères alphanumériques. +httpsession_savepath_unchangeable = THttpSession.SavePath ne peut pas être modifié après que la session ait démarré. +httpsession_savepath_invalid = THttpSession.SavePath '{0}' est invalide. +httpsession_storage_unchangeable = THttpSession.Storage ne peut pas être modifié après que la session ait démarré. +httpsession_cookiemode_unchangeable = THttpSession.CookieMode ne peut pas être modifié après que la session ait démarré. +httpsession_autostart_unchangeable = THttpSession.AutoStart ne peut pas être modifié après que le module de session ait été initialisé. +httpsession_gcprobability_unchangeable = THttpSession.GCProbability ne peut pas être modifié après que la session ait démarré. +httpsession_gcprobability_invalid = THttpSession.GCProbability doit être un entier compris entre 0 et 100. +httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID ne peut pas être modifié après que la session ait démarré. +httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID ne peut pas être utilisé quand THttpSession.CookieMode est fixé à "Only". +httpsession_maxlifetime_unchangeable = THttpSession.Timeout ne peut pas être modifié après que la session ait démarré. + +assetmanager_basepath_invalid = TAssetManager.BasePath '{0}' est invalide. Vérifier qu'il est bien au format 'namespace' et qu'il pointe bien vers un répertoire accessible en écriture par le propriétaire du processus serveur Web +assetmanager_basepath_unchangeable = TAssetManager.BasePath ne peut pas être modifié après l'initialisation du module. +assetmanager_baseurl_unchangeable = TAssetManager.BaseUrl ne peut pas être modifié après l'initialisation du module. +assetmanager_filepath_invalid = TAssetManager essaye de publier un fichier invalide '{0}'. +assetmanager_tarchecksum_invalid = TAssetManager essaye de publier une archive 'tar' avec un checksum invalide '{0}'. +assetmanager_tarfile_invalid = TAssetManager essaye de publier une archive 'tar' invalide '{0}'. +assetmanager_source_directory_invalid = TAssetManager essaye de copier un répertoire invalide '{0}'. + +cache_primary_duplicated = Un seul module au maximum de cache primaire est autorisé. {0} essaye de déclarer un autre module de cache primaire. +sqlitecache_extension_required = TSqliteCache nécessite l'extension PHP SQLLite. +sqlitecache_dbfile_required = TSqliteCache.DbFile est nécessaire. +sqlitecache_connection_failed = Echec de la connexion à la base de données TSqliteCache. {0}. +sqlitecache_table_creation_failed = Echec de la créatin de la base de données TSqliteCache. {0}. +sqlitecache_dbfile_unchangeable = TSqliteCache.DbFile ne peut pas être modifié après l'initialisation du module. +sqlitecache_dbfile_invalid = TSqliteCache.DbFile est invalide. Vérifier qu'il est écrit au format 'Namespace'. + +memcache_extension_required = TMemCache nécessite l'extension PHP memcache. +memcache_connection_failed = TMemCache ne peut pas se connecter au serveur memcache {0}:{1}. +memcache_host_unchangeable = TMemCache.Host ne peut pas être modifié après l'initialisation du module. +memcache_port_unchangeable = TMemCache.Port ne peut pas être modifié après l'initialisation du module. + +apccache_extension_required = TAPCCache nécessite l'extension PHP APC. +apccache_add_unsupported = TAPCCache.add() n'est pas supporté. +apccache_replace_unsupported = TAPCCache.replace() n'est pas supporté. +apccache_extension_not_enabled = TAPCCache nécessite apc.enabled = 1 dans php.ini. +apccache_extension_not_enabled_cli = TAPCCache nécessite apc.enable_cli = 1 dans php.ini pour fonctionner en ligne de commande. + +errorhandler_errortemplatepath_invalid = TErrorHandler.ErrorTemplatePath '{0}' est invalide. Vérifier qu'il est écrit au format 'namespace' et qu'il pointe bien vers un répertoire valide + +pageservice_page_unknown = La page '{0}' n'a pas été trouvée +pageservice_pageclass_unknown = La classe de la page '{0}' est inconnue. +pageservice_basepath_invalid = TPageService.BasePath '{0}' n'est pas un répertoire valide. +pageservice_page_required = Le nom de la page est requis. +pageservice_defaultpage_unchangeable = TPageService.DefaultPage ne peut pas être modifié après l'initialisation du service. +pageservice_basepath_unchangeable = TPageService.BasePath ne peut pas être modifié après l'initialisation du service. +pageservice_pageclass_invalid = La classe de la page {0} est invalide. Cela devrait être TPage ou un héritage de TPage. +pageservice_includefile_invalid = Impossible de trouver le fichier de configuration du service de page {0}. Verifier qu'il est au format 'namespace' et que l'extension du fichier est '.xml'. + +pageserviceconf_file_invalid = Unable to open page directory configuration file '{0}'. +pageserviceconf_aliaspath_invalid = uses an invalid file path "{1}" in page directory configuration file '{2}'. +pageserviceconf_alias_invalid = element must have an "id" attribute and a "path" attribute in page directory configuration file '{0}'. +pageserviceconf_using_invalid = element must have a "namespace" attribute in page directory configuration file '{0}'. +pageserviceconf_module_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_moduletype_required = must have a "class" attribute in page directory configuration file '{1}'. +pageserviceconf_parameter_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_page_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_includefile_required = Page configuration element must have a "file" attribute. + +template_closingtag_unexpected = Unexpected closing tag '{0}' is found. +template_closingtag_expected = Closing tag '{0}' is expected. +template_directive_nonunique = Directive '<%@ ... %>' must appear at the beginning of the template and can appear at most once. +template_comments_forbidden = Template comments are not allowed within property tags. +template_matching_unexpected = Unexpected matching. +template_property_unknown = {0} has no property called '{1}'. +template_event_unknown = {0} has no event called '{1}'. +template_property_readonly = {0} has a read-only property '{1}'. +template_event_forbidden = {0} is a non-control component. No handler can be attached to its event '{1}' in a template. +template_databind_forbidden = {0} is a non-control component. Expressions cannot be bound to its property '{1}'. +template_component_required = '{0}' is not a component. Only components can appear in a template. +template_format_invalid = Invalid template syntax: {0} +template_property_duplicated = Property {0} is configured twice or more. +template_eventhandler_invalid = {0}.{1} can only accept a static string. +template_controlid_invalid = {0}.ID can only accept a static text string. +template_controlskinid_invalid = {0}.SkinID can only accept a static text string. +template_content_unexpected = Unexpected content is encountered when instantiating template: {0}. +template_include_invalid = Invalid template inclusion. Make sure {0} is a valid namespace pointing to an existing template file whose extension is .tpl. +template_tag_unexpected = Initialization for property {0} contains an unknown tag type {1}. + +xmldocument_file_read_failed = TXmlDocument is unable to read file '{0}'. +xmldocument_file_write_failed = TXmlDocument is unable to write file '{0}'. + +xmlelementlist_xmlelement_required = TXmlElementList can only accept TXmlElement objects. + +authorizationrule_action_invalid = TAuthorizationRule.Action can only take 'allow' or 'deny' as the value. +authorizationrule_verb_invalid = TAuthorizationRule.Verb can only take 'get' or 'post' as the value. + +authorizationrulecollection_authorizationrule_required = TAuthorizationRuleCollection can only accept TAuthorizationRule objects. + +usermanager_userfile_invalid = TUserManager.UserFile '{0}' is not a valid file. +usermanager_userfile_unchangeable = TUserManager.UserFile cannot be modified. The user module has been initialized already. + +authmanager_usermanager_required = TAuthManager.UserManager must be assigned a value. +authmanager_usermanager_inexistent = TAuthManager.UserManager '{0}' does not refer to an ID of application module. +authmanager_usermanager_invalid = TAuthManager.UserManager '{0}' does not refer to a valid TUserManager application module. +authmanager_usermanager_unchangeable = TAuthManager.UserManager cannot be modified after the module is initialized. +authmanager_session_required = TAuthManager requires a session application module. + +thememanager_service_unavailable = TThemeManager requires TPageService to be available. This error often occurs when you configure TThemeManager outside of the page service configuration. +thememanager_basepath_invalid = TThemeManager.BasePath '{0}' is not a valid path alias. Make sure you have defined this alias in configuration and it points to a valid directory. +thememanager_basepath_invalid2 = TThemeManager.BasePath '{0}' is not a valid directory. +thememanager_basepath_unchangeable = TThemeManager.BasePath cannot be modified after the module is initialized. + +theme_baseurl_required = TThemeManager.BasePath is required. By default, a directory named 'themes' under the directory containing the application entry script is assumed. +theme_path_inexistent = Theme path '{0}' does not exist. +theme_control_nested = Skin for control type '{0}' in theme '{1}' cannot be within another skin. +theme_skinid_duplicated = SkinID '{0}.{1}' is duplicated in theme '{2}'. +theme_databind_forbidden = Databind cannot be used in theme '{0}' for control skin '{1}.{2}' about property '{3}'. +theme_property_readonly = Skin is being applied to a read-only control property '{0}.{1}'. +theme_property_undefined = Skin is being applied to an inexistent control property '{0}.{1}'. +theme_tag_unexpected = Initialization for property {0} contains an unknown tag type {1}. + +control_object_reregistered = Duplicated object ID '{0}' found. +control_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character. +control_skinid_unchangeable = {0}.SkinID cannot be modified after a skin has been applied to the control or the child controls have been created. +control_enabletheming_unchangeable = {0}.EnableTheming cannot be modified after the child controls have been created. +control_stylesheet_applied = StyleSheet skin has already been applied to {0}. +control_id_nonunique = {0}.ID '{1}' is not unique among all controls under the same naming container. + +templatecontrol_mastercontrol_invalid = Master control must be of type TTemplateControl or a child class. +templatecontrol_mastercontrol_required = Control '{0}' requires a master control since the control uses TContent. +templatecontrol_contentid_duplicated = TContent ID '{0}' is duplicated. +templatecontrol_placeholderid_duplicated= TContentPlaceHolder ID '{0}' is duplicated. +templatecontrol_directive_invalid = {0}.{1} can only accept a static text string through a template directive. +templatecontrol_placeholder_inexistent = TContent '{0}' does not have a matching TContentPlaceHolder. + +page_form_duplicated = A page can contain at most one TForm. Use regular HTML form tags for the rest forms. +page_isvalid_unknown = TPage.IsValid has not been evaluated yet. +page_postbackcontrol_invalid = Unable to determine postback control '{0}'. +page_control_outofform = {0} '{1}' must be enclosed within TForm. +page_head_duplicated = A page can contain at most one THead. +page_statepersister_invalid = Page state persister must implement IPageStatePersister interface. + +csmanager_pradoscript_invalid = Unknown Prado script library name '{0}'. +csmanager_invalid_packages = Unkownn packages '{1}' for javascript packages defined in '{0}'. Valid packages are '{2}'. + +contentplaceholder_id_required = TContentPlaceHolder must have an ID. + +content_id_required = TContent must have an ID. + +controlcollection_control_required = TControlList can only accept strings or TControl objects. + +webcontrol_accesskey_invalid = {0}.AccessKey '{1}' is invalid. It must be a single character only. +webcontrol_style_invalid = {0}.Style must take string value only. + +listcontrol_selection_invalid = {0} has an invalid selection that is set before performing databinding. +listcontrol_selectedindex_invalid = {0}.SelectedIndex has an invalid value {1}. +listcontrol_selectedvalue_invalid = {0}.SelectedValue has an invalid value '{1}'. +listcontrol_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2} +listcontrol_multiselect_unsupported = {0} does not support multiselection. + +label_associatedcontrol_invalid = TLabel.AssociatedControl '{0}' cannot be found. + +hiddenfield_focus_unsupported = THiddenField does not support setting input focus. +hiddenfield_theming_unsupported = THiddenField does not support theming. +hiddenfield_skinid_unsupported = THiddenField does not support control skin. + +panel_defaultbutton_invalid = TPanel.DefaultButton '{0}' does not refer to an existing button control. + +tablestyle_cellpadding_invalid = TTableStyle.CellPadding must take an integer equal to or greater than -1. +tablestyle_cellspacing_invalid = TTableStyle.CellSpacing must take an integer equal to or greater than -1. + +pagestatepersister_pagestate_corrupted = Page state is corrupted. + +sessionpagestatepersister_pagestate_corrupted = Page state is corrupted. +sessionpagestatepersister_historysize_invalid = TSessionPageStatePersister.History must be an integer greater than 0. + +listitemcollection_item_invalid = TListItemCollection can only take strings or TListItem objects. + +dropdownlist_selectedindices_unsupported= TDropDownList.SelectedIndices is read-only. + +bulletedlist_autopostback_unsupported = TBulletedList.AutoPostBack is read-only. +bulletedlist_selectedindex_unsupported = TBulletedList.SelectedIndex is read-only. +bulletedlist_selectedindices_unsupported= TBulletedList.SelectedIndices is read-only. +bulletedlist_selectedvalue_unsupported = TBulletedList.SelectedValue is read-only. + +radiobuttonlist_selectedindices_unsupported = TRadioButtonList.SelectedIndices is read-only. + +logrouter_configfile_invalid = TLogRouter.ConfigFile '{0}' does not exist. +logrouter_routeclass_required = Class attribute is required in configuration. +logrouter_routetype_required = Log route must be an instance of TLogRoute or its derived class. + +filelogroute_logpath_invalid = TFileLogRoute.LogPath '{0}' must be a directory in namespace format and must be writable by the Web server process. +filelogroute_maxfilesize_invalid = TFileLogRoute.MaxFileSize must be greater than 0. +filelogroute_maxlogfiles_invalid = TFileLogRoute.MaxLogFiles must be greater than 0. + +emaillogroute_sentfrom_required = TEmailLogRoute.SentFrom cannot be empty. + +repeatinfo_repeatcolumns_invalid = TRepeatInfo.RepeatColumns must be no less than 0. + +basevalidator_controltovalidate_invalid = {0}.ControlToValidate is empty or contains an invalid control ID path. +basevalidator_validatable_required = {0}.ControlToValidate must point to a control implementing IValidatable interface. +basevalidator_forcontrol_unsupported = {0}.ForControl is not supported. + +comparevalidator_controltocompare_invalid = TCompareValidator.ControlToCompare contains an invalid control ID path. + +listcontrolvalidator_invalid_control = {0}.ControlToValidate contains an invalid TListControl ID path, "{1}" is a {2}. + +repeater_template_required = TRepeater.{0} requires a template instance implementing ITemplate interface. +repeater_itemtype_unknown = Unknow repeater item type {0}. +repeateritemcollection_item_invalid = TRepeaterItemCollection can only accept objects that are instance of TControl or its descendant class. + +datalist_template_required = TDataList.{0} requires a template instance implementing ITemplate interface. +datalistitemcollection_datalistitem_required = TDataListItemCollection can only accept TDataListItem objects. + +datagrid_template_required = TDataGrid.{0} requires a template instance implementing ITemplate interface. +templatecolumn_template_required = TTemplateColumn.{0} requires a template instance implementing ITemplate interface. +datagrid_currentpageindex_invalid = TDataGrid.CurrentPageIndex must be no less than 0. +datagrid_pagesize_invalid = TDataGrid.PageSize must be greater than 0. +datagrid_virtualitemcount_invalid = TDataGrid.VirtualItemCount must be no less than 0. +datagriditemcollection_datagriditem_required = TDataGridItemCollection can only accept TDataGridItem objects. +datagridcolumncollection_datagridcolumn_required = TDataGridColumnCollection can only accept TDataGridColumn objects. +datagridpagerstyle_pagebuttoncount_invalid = TDataGridPagerStyle.PageButtonCount must be greater than 0. + +datafieldaccessor_data_invalid = TDataFieldAccessor is trying to evaluate a field value of an invalid data. Make sure the data is an array, TMap, TList, or object that contains the specified field '{0}'. +datafieldaccessor_datafield_invalid = TDataFieldAccessor is trying to evaluate data value of an unknown field '{0}'. + +tablerowcollection_tablerow_required = TTableRowCollection can only accept TTableRow objects. + +tablecellcollection_tablerow_required = TTableCellCollection can only accept TTableCell objects. + +multiview_view_required = TMultiView can only accept TView as child. +multiview_activeviewindex_invalid = TMultiView.ActiveViewIndex has an invalid index '{0}'. +multiview_view_inexistent = TMultiView cannot find the specified view. +multiview_viewid_invalid = TMultiView cannot find the view '{0}' to switch to. + +viewcollection_view_required = TViewCollection can only accept TView as its element. + +view_visible_readonly = TView.Visible is read-only. Use TView.Active to toggle its visibility. + +wizard_step_invalid = The step to be activated cannot be found in wizard step collection. +wizard_command_invalid = Invalid wizard navigation command '{0}'. + +table_tablesection_outoforder = TTable table sections must be in the order of: Header, Body and Footer. + +completewizardstep_steptype_readonly = TCompleteWizardStep.StepType is read-only. + +wizardstepcollection_wizardstep_required = TWizardStepCollection can only accept objects of TWizardStep or its derived classes. + +texthighlighter_stylesheet_invalid = Unable to find the stylesheet file for TTextHighlighter. + +hotspotcollection_hotspot_required = THotSpotCollection can only accept instance of THotSpot or its derived classes. + +htmlarea_textmode_readonly = THtmlArea.TextMode is read-only. +htmlarea_tarfile_invalid = THtmlArea is unable to locate the TinyMCE tar file. + +parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile is not changeable because the module is already initialized. +parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' is invalid. Make sure it is in namespace format and the file extension is '.xml'. +parametermodule_parameterid_required = Parameter element must have 'id' attribute. + +datagridcolumn_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character. +datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2} + +outputcache_cachemoduleid_invalid = TOutputCache.CacheModuleID is set with an invalid cache module ID {0}. Either the module does not exist or does not implement ICache interface. +outputcache_duration_invalid = {0}.Duration must be an integer no less than 0. + +stack_data_not_iterable = TStack can only fetch data from an array or a traversable object. +stack_empty = TStack is empty. + +queue_data_not_iterable = TQueue can only fetch data from an array or a traversable object. +queue_empty = TQueue is empty. + +pager_pagebuttoncount_invalid = TPager.PageButtonCount must be an integer no less than 1. +pager_currentpageindex_invalid = TPager.CurrentPageIndex is out of range. +pager_pagecount_invalid = TPager.PageCount cannot be smaller than 0. +pager_controltopaginate_invalid = TPager.ControlToPaginate {0} must be a valid ID path pointing to a TDataBoundControl-derived control. + +databoundcontrol_pagesize_invalid = {0}.PageSize must be an integer no smaller than 1. +databoundcontrol_virtualitemcount_invalid = {0}.VirtualItemCount must be an integer no smaller than 0. +databoundcontrol_currentpageindex_invalid = {0}.CurrentPageIndex is out of range. +databoundcontrol_datasource_invalid = {0}.DataSource is not valid. +databoundcontrol_datasourceid_inexistent = databoundcontrol_datasourceid_inexistent. +databoundcontrol_datasourceid_invalid = databoundcontrol_datasourceid_invalid +databoundcontrol_datamember_invalid = databoundcontrol_datamember_invalid + +clientscript_invalid_file_position = Invalid file position '{1}' for TClientScript control '{0}', must be 'Head', 'Here' or 'Begin'. +clientscript_invalid_package_path = Invalid PackagePath '{0}' for TClientScript control '{1}'. + +tdatepicker_autopostback_unsupported = '{0}' does not support AutoPostBack. +globalization_cache_path_failed = Unable to create translation message cache path '{0}'. Make sure the parent directory exists and is writable by the Web process. +globalization_source_path_failed = Unable to create translation message path '{0}'. Make sure the parent directory exists and is writable by the Web process. +callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_callback_options = '{1}' is not a valid TCallbackOptions control for Callback control '{0}'. +callback_invalid_clientside_options = Callback ClientSide property must be either a string that is the ID of a TCallbackOptions control or an instance of TCallbackClientSideOptions.======= +callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_handler = Invalid callback handler, control {0} must implement ICallbackEventHandler. +callback_invalid_target = Invalid callback target, no such control with ID {0}. + +callback_interval_be_positive = Interval for TCallbackTimer "{0}" must be strictly greater than zero seconds. +callback_decay_be_not_negative = Decay rate for TCallbackTimer "{0}" must be not negative. + +callback_no_autopostback = Control "{0}" can not enable AutoPostBack. + +xmltransform_xslextension_required = TXmlTransform requires the PHP's XSL extension. +xmltransform_transformpath_invalid = TXmlTransform.TransformPath '{0}' is invalid. +xmltransform_documentpath_invalid = TXmlTransform.DocumentPath '{0}' is invalid. +xmltransform_transform_required = Either TransformContent or TransformPath property must be set for TXmlTransform. + +ttriggeredcallback_invalid_controlid = ControlID property for '{0}' must not be empty. +tactivecustomvalidator_clientfunction_unsupported = {0} does not support client side validator function. + +dbconnection_open_failed = TDbConnection failed to establish DB connection: {0} +dbconnection_connection_inactive = TDbConnection is inactive. +dbconnection_unsupported_driver_charset = Le pilote de base de données '{0}' ne supporte pas la modification du jeu de caractères. + +dbcommand_prepare_failed = TDbCommand failed to prepare the SQL statement "{1}": {0} +dbcommand_execute_failed = TDbCommand failed to execute the SQL statement "{1}": {0} +dbcommand_query_failed = TDbCommand failed to execute the query SQL "{1}": {0} +dbcommand_column_empty = TDbCommand returned an empty result and could not obtain the scalar. +dbdatareader_rewind_invalid = TDbDataReader is a forward-only stream. It can only be traversed once. +dbtransaction_transaction_inactive = TDbTransaction is inactive. + +dbcommandbuilder_value_must_not_be_null = Property {0} must not be null as defined by column '{2}' in table '{1}'. + +dbcommon_invalid_table_name = Database table '{0}' not found. Error message: {1}. +dbcommon_invalid_identifier_name = Invalid database identifier name '{0}', see {1} for details. +dbtableinfo_invalid_column_name = Invalid column name '{0}' for database table '{1}'. +dbmetadata_invalid_table_view = Invalid table/view name '{0}', or that table/view '{0}' contains no accessible column/field definitions. +dbmetadata_requires_php_version = PHP version {1} or later is required for using {0} database. + +dbtablegateway_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria. +dbtablegateway_no_primary_key_found = Table '{0}' does not contain any primary key fields. +dbtablegateway_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'. +dbtablegateway_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'. +dbtablegateway_mismatch_args_exception = TTableGateway finder method '{0}' expects {1} parameters but found only {2} parameters instead. +dbtablegateway_mismatch_column_name = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'. +dbtablegateway_invalid_table_info = Table must be a string or an instanceof TDbTableInfo. + +directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} does not refer to a valid directory. +cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList. + +soapservice_configfile_invalid = TSoapService.ConfigFile '{0}' does not exist. Note, it has to be specified in a namespace format and the file extension must be '.xml'. +soapservice_request_invalid = SOAP server '{0}' not found. +soapservice_serverid_required = element must have 'id' attribute. +soapservice_serverid_duplicated = SOAP server ID '{0}' is duplicated. +soapserver_id_invalid = Invalid SOAP server ID '{0}'. It should not end with '.wsdl'. +soapserver_version_invalid = Invalid SOAP version '{0}'. It must be either '1.1' or '1.2'. + +dbusermanager_userclass_required = TDbUserManager.UserClass is required. +dbusermanager_userclass_invalid = TDbUserManager.UserClass '{0}' is not a valid user class. The class must extend TDbUser. +dbusermanager_connectionid_invalid = TDbUserManager.ConnectionID '{0}' does not point to a valid TDataSourceConfig module. +dbusermanager_connectionid_required = TDbUserManager.ConnectionID is required. + +feedservice_id_required = TFeedService requires 'id' attribute in its feed elements. +feedservice_feedtype_invalid = The class feed '{0}' must implement IFeedContentProvider interface. +feedservice_class_required = TFeedService requires 'class' attribute in its feed elements. +feedservice_feed_unknown = Unknown feed '{0}' requested. + +tactivetablecell_control_outoftable = {0} '{1}' must be enclosed within a TTableRow control. +tactivetablecell_control_notincollection = {0} '{1}' no member of the TTableCellCollection of the parent TTableRow control. + +tactivetablerow_control_outoftable = {0} '{1}' must be enclosed within a TTable control. +tactivetablerow_control_notincollection = {0} '{1}' no member of the TTableRowCollection of the parent TTable control. \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/messages/messages-id.txt b/gui/baculum/framework/Exceptions/messages/messages-id.txt new file mode 100644 index 0000000000..687d4f308c --- /dev/null +++ b/gui/baculum/framework/Exceptions/messages/messages-id.txt @@ -0,0 +1,464 @@ +prado_application_singleton_required = Prado.Application harus disetel hanya sekali. +prado_component_unknown = Tipe komponen '{0}' tidak dikenal. Ini bisa disebabkan oleh kesalahan penguraian dalam file kelas {0}: {1} +prado_using_invalid = '{0}' bukan namespace yang benar untuk dipakai. Pastikan '.*' ditambahkan jika anda ingin menggunakan namespace merujuk ke sebuah direktori. +prado_alias_redefined = Alias '{0}' tidak bisa didefinisikan ulang. +prado_alias_invalid = Alias '{0}' merujuk ke path '{1}' yang tidak benar. Hanya direktori yang sudah ada bisa dialiaskan. +prado_aliasname_invalid = Alias '{0}' berisi karakter tidak benar '.'. + +component_property_undefined = Properti komponen '{0}.{1}' tidak didefinisikan. +component_property_readonly = Properti komponen '{0}.{1}' hanya-baca. +component_event_undefined = Event komponen '{0}.{1}' tidak didefinsikan. +component_eventhandler_invalid = Event komponen '{0}.{1}' dilampirkan dengan pengendali event tidak benar '{2}'. +component_expression_invalid = Komponen '{0}' mengevaluasi ekspresi yang tidak benar '{1}' : {2}. +component_statements_invalid = Komponen '{0}' mengevaluasi pernyataan PHP yang tidak benar '{1}' : {2}. + +propertyvalue_enumvalue_invalid = Nilai '{0}' bukan nilai enumerasi yang benar ({1}). + +list_index_invalid = Indeks '{0}' diluar jangkauan. +list_item_inexistent = Item tidak bisa ditemukan dalam daftar. +list_data_not_iterable = Data harus berupa array atau obyek yang mengimplementasikan antarmuka Traversable. +list_readonly = {0} hanya-baca. + +map_addition_disallowed = Item baru tidak bisa ditambahkan ke peta. +map_item_unremovable = Item tidak bisas dihapus dari peta. +map_data_not_iterable = Data harus berupa array atau obyek yang mengimplementasikan antarmuka Traversable. +map_readonly = {0} hanya-baca. + +application_includefile_invalid = Tidak bisa menemukan konfigurasi aplikasi {0}. Pastikan ia ada dalam format namespace dan file berakhir dengan ".xml". +application_basepath_invalid = Path basis aplikasi '{0}' tidak ada atau bukan sebuah direktori. +application_runtimepath_invalid = Path runtime aplikasi '{0}' tidak ada atau tidak bisa ditulis oleh proses server Web. +application_service_invalid = Layanan '{0}' harus mengimplementasikan antarmuka IService. +application_service_unknown = Layanan yang diminta '{0}' tidak didefinisikan. +application_unavailable = Aplikasi tidak tersedia saat ini. +application_service_unavailable = Layanan '{0}' tidak tersedia saat ini. +application_moduleid_duplicated = ID modul aplikasi '{0}' tidak unik. +application_runtimepath_failed = Tidak bisa membuat path runtime '{0}'. Pastikan direktori leluhur ada dan bisa ditulis oleh proses Web. + +appconfig_aliaspath_invalid = Konfigurasi aplikasi menggunakan path file tidak benar "{1}". +appconfig_alias_invalid = Konfigurasi aplikasi elemen harus mempunyai atribut "id" dan atribut "path". +appconfig_alias_redefined = Konfigurasi aplikasi tidak bisa didefinisikan ulang. +appconfig_using_invalid = Konfigurasi aplikasi elemen harus mempunyai atribut "namespace". +appconfig_moduleid_required = Konfigurasi aplikasi elemen harus mempunyai atribut "id". +appconfig_moduletype_required = Konfigurasi aplikasi harus mempunyai atribut "class". +appconfig_serviceid_required = Konfigurasi aplikasi elemen harus mempunyai atribut "id". +appconfig_servicetype_required = Konfigurasi aplikasi harus mempunyai atribut "class". +appconfig_parameterid_required = Konfigurasi aplikasi elemen harus mempunyai atribut "id". +appconfig_includefile_required = Konfigurasi aplikasi elemen harus mempunyai atribut "file". + +securitymanager_validationkey_invalid = TSecurityManager.ValidationKey tidak boleh kosong. +securitymanager_encryptionkey_invalid = TSecurityManager.EncryptionKey tidak boleh kosong. +securitymanager_mcryptextension_required = Ekstensy mcrypt PHP diperlukan agar bisa menggunakan fitur enkripsi TSecurityManager. + +uri_format_invalid = '{0}' bukan URI yang benar. + +httprequest_separator_invalid = THttpRequest.UrlParamSeparator hanya bisa berisi karakter tunggal. +httprequest_urlmanager_inexist = THttpRequest.UrlManager '{0}' tidak mengarah ke modul yang sudah ada. +httprequest_urlmanager_invalid = THttpRequest.UrlManager '{0}' harus mengarah ke modul yang diperluas dari TUrlManager. + +httpcookiecollection_httpcookie_required = THttpCookieCollection hanya bisa menerima obyek THttpCookie. + +httpresponse_bufferoutput_unchangeable = THttpResponse.BufferOutput tidak bisa diubah setelah THttpResponse diinisialisasi. +httpresponse_file_inexistent = THttpResponse tidak bisa mengirimkan file '{0}'. File tidak ada. + +httpsession_sessionid_unchangeable = THttpSession.SessionID tidak bisa diubah setelah sesi dimulai. +httpsession_sessionname_unchangeable = THttpSession.SessionName tidak bisa diubah setelah sesi dimulai. +httpsession_sessionname_invalid = THttpSession.SessionName harus berisi hanya karakter alfanumerik. +httpsession_savepath_unchangeable = THttpSession.SavePath tidak bisa diubah setelah sesi dimulai. +httpsession_savepath_invalid = THttpSession.SavePath '{0}' tidak benar. +httpsession_storage_unchangeable = THttpSession.Storage tidak bisa diubah setelah sesi dimulai. +httpsession_cookiemode_unchangeable = THttpSession.CookieMode tidak bisa diubah setelah sesi dimulai. +httpsession_autostart_unchangeable = THttpSession.AutoStart tidak bisa diubah setelah modul sesi diinisialisasi. +httpsession_gcprobability_unchangeable = THttpSession.GCProbability tidak bisa diubah setelah sesi dimulai. +httpsession_gcprobability_invalid = THttpSession.GCProbability harus integer antara 0 dan 100. +httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID tidak bisa diubah setelah sesi dimulai. +httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID cannot be set when THttpSession.CookieMode is set to Only. +httpsession_maxlifetime_unchangeable = THttpSession.Timeout tidak bisa diubah setelah sesi dimulai. + +assetmanager_basepath_invalid = TAssetManager.BasePath '{0}' tidak benar. Pastikan ia dalam bentuk namespace dan mengarah ke direktori yang bisa ditulis oleh proses server Web. +assetmanager_basepath_unchangeable = TAssetManager.BasePath tidak bisa diubah setelah modul diinisialisasi. +assetmanager_baseurl_unchangeable = TAssetManager.BaseUrl tidak bisa diubah setelah modul diinisialisasi. +assetmanager_filepath_invalid = TAssetManager menerbitkan file yang tidak benar '{0}'. +assetmanager_tarchecksum_invalid = TAssetManager menerbitkan file tar dengan checksum '{0}' yang salah. +assetmanager_tarfile_invalid = TAssetManager menerbitkan file tar yang tidak benar '{0}'. +assetmanager_source_directory_invalid = TAssetManager meng-copy direktori '{0}' yang tidak benar. + +cache_primary_duplicated = Paling banyak satu modul cache primer yang dibolehkan. {0} mencoba meregistrasi cache primer lain. +sqlitecache_extension_required = TSqliteCache memerlukan ekstensi SQLite PHP. +sqlitecache_dbfile_required = TSqliteCache.DbFile diperlukan. +sqlitecache_connection_failed = TSqliteCache koneksi database gagal. {0}. +sqlitecache_table_creation_failed = TSqliteCache gagal untuk membuat cache database. {0}. +sqlitecache_dbfile_unchangeable = TSqliteCache.DbFile tidak bisa diubah setelah modul diinisialisasi. +sqlitecache_dbfile_invalid = TSqliteCache.DbFile tidak benar. Pastikan ia ada dalam format namespace yang benar. + +memcache_extension_required = TMemCache memerlukan ekstensi memcache PHP. +memcache_connection_failed = TMemCache gagal menghubungi server memcache {0}:{1}. +memcache_host_unchangeable = TMemCache.Host tidak bisa diubah setelah modul diinisialisasi. +memcache_port_unchangeable = TMemCache.Port tidak bisa diubah setelah modul diinisialisasi. + +apccache_extension_required = TAPCCache memerlukan ekstensi APC PHP. +apccache_add_unsupported = TAPCCache.add() tidak didukung. +apccache_replace_unsupported = TAPCCache.replace() tidak didukung. +apccache_extension_not_enabled = TAPCCache memerlukan apc.enabled = 1 dalam php.ini agar bekerja. +apccache_extension_not_enabled_cli = TAPCCache memerlukan apc.enable_cli = 1 dalam php.ini agar bekerja dengan PHP dari baris perintah. + +errorhandler_errortemplatepath_invalid = TErrorHandler.ErrorTemplatePath '{0}' tidak benar. Pastikan ia ada dalam bentuk namespace dan mengarah ke direktori yang berisi file template kesalahan. + +pageservice_page_unknown = Halaman '{0}' Tidak Ditemukan +pageservice_pageclass_unknown = Kelas Halaman '{0}' tidak dikenal. +pageservice_basepath_invalid = TPageService.BasePath '{0}' bukan direktori yang benar. +pageservice_page_required = Nama Halaman Diperlukan +pageservice_defaultpage_unchangeable = TPageService.DefaultPage tidak bisa diubah setelah layanan diinisialisasi. +pageservice_basepath_unchangeable = TPageService.BasePath tidak bisa diubah setelah layanan diinisialisasi. +pageservice_pageclass_invalid = Kelas halaman {0} tidak benar. Ia harus berupa TPage atau diperluas dari TPage. +pageservice_includefile_invalid = Tidak bisa menemukan konfigurasi layanan {0}. Pastikan ia ada dalam format namespace dan file berakhir dengan ".xml". + +pageserviceconf_file_invalid = Tidak bisa membuka file konfigurasi direktori halaman '{0}'. +pageserviceconf_aliaspath_invalid = menggunakan path file tidak benar "{1}" dalam file konfigurasi direktori halaman '{2}'. +pageserviceconf_alias_invalid = Elemen harus mempunyai atribut "id" dan atribut "path" dalam file konfigurasi direktori halaman '{0}'. +pageserviceconf_using_invalid = Elemen harus mempunyai atribut "namespace" dalam file konfigurasi direktori halaman '{0}'. +pageserviceconf_module_invalid = Elemen harus mempunyai atribut "id" dalam file konfigurasi direktori halaman '{0}'. +pageserviceconf_moduletype_required = harus mempunyai atribut "class" dalam file konfigurasi direktori halaman '{1}'. +pageserviceconf_parameter_invalid = Elemen harus mempunyai atribut "id" dalam file konfigurasi direktori halaman '{0}'. +pageserviceconf_page_invalid = Elemen harus mempunyai atribut "id" dalam file konfigurasi direktori halaman '{0}'. +pageserviceconf_includefile_required = Konfigurasi halaman elemen harus mempunyai atribut "file". + +template_closingtag_unexpected = Tag penutup '{0}' yang tidak diharapkan ditemukan. +template_closingtag_expected = Tag penutup '{0}' diharapkan. +template_directive_nonunique = Direktif '<%@ ... %>' harus muncul di awal template dan bisa muncul paling banyak satu kali. +template_comments_forbidden = Komentar template tidak dibolehkan dalam tag properti. +template_matching_unexpected = Penyamaan tidak diharapkan. +template_property_unknown = {0} tidak memiliki properti bernama '{1}'. +template_event_unknown = {0} tidak memiliki event bernama '{1}'. +template_property_readonly = {0} memiliki properti hanya-baca '{1}'. +template_event_forbidden = {0} bukan kontrol komponen. Tidak ada pengendali yang dapat dilampirkan ke event '{1}' dalam template. +template_databind_forbidden = {0} bukan kontrol komponen. Ekspresi tidak bisa diikat ke propertinya '{1}'. +template_component_required = '{0}' bukan komponen. Hanya komponen yang dapat terlihat dalam template. +template_format_invalid = Sintaks template salah: {0} +template_property_duplicated = Properti {0} dikonfigurasi dua kali atau lebih. +template_eventhandler_invalid = {0}.{1} hanya bisa menerima string statis. +template_controlid_invalid = {0}.ID hanya bisa menerima string teks statis. +template_controlskinid_invalid = {0}.SkinID hanya bisa menerima string teks statis. +template_content_unexpected = Konten tidak diharapkan ditemukan saat menurunkan template: {0}. +template_include_invalid = Inklusi template tidak benar. Pastikan {0} adalah namespace yang benar mengarah ke file template yang sudah ada yang mempunyai ekstensi .tpl. +template_tag_unexpected = Inisialisasi properti {0} berisi tipe tag tidak dikenal {1}. + +xmldocument_file_read_failed = TXmlDocument tidak bisa membaca file '{0}'. +xmldocument_file_write_failed = TXmlDocument tidak bisa menulis file '{0}'. + +xmlelementlist_xmlelement_required = TXmlElementList hanya bisa menerima obyek TXmlElement. + +authorizationrule_action_invalid = TAuthorizationRule.Action hanya bisa mengambil 'allow' atau 'deny' sebagai nilainya. +authorizationrule_verb_invalid = TAuthorizationRule.Verb hanya bisa mengambil 'get' atau 'post' sebagai nilainya. + +authorizationrulecollection_authorizationrule_required = TAuthorizationRuleCollection hanya bisa menerima obyek TAuthorizationRule. + +usermanager_userfile_invalid = TUserManager.UserFile '{0}' bukan file yang benar. +usermanager_userfile_unchangeable = TUserManager.UserFile tidak bisa diubah. Modul pengguna sudah diinisialisasi. + +authmanager_usermanager_required = TAuthManager.UserManager harus menempatkan nilai. +authmanager_usermanager_inexistent = TAuthManager.UserManager '{0}' tidak merujuk ke ID modul aplikasi. +authmanager_usermanager_invalid = TAuthManager.UserManager '{0}' tidak merujuk ke modul aplikasi TUserManager yang benar. +authmanager_usermanager_unchangeable = TAuthManager.UserManager tidak bisa diubah setelah modul diinisialisasi. +authmanager_session_required = TAuthManager memerlukan modul aplikasi sesi. + +thememanager_service_unavailable = TThemeManager memerlukan ketersediaan TPageService. Kesalahan ini sering terjadi saat anda mengkonfigurasi TThemeManager di luar konfigurasi layanan halaman. +thememanager_basepath_invalid = TThemeManager.BasePath '{0}' bukan alias path yang benar. Pastikan anda telah mendefinisikan alias ini dalam konfigurasi dan mengarahkan ke direktori yang benar. +thememanager_basepath_invalid2 = TThemeManager.BasePath '{0}' bukan direktori yang benar. +thememanager_basepath_unchangeable = TThemeManager.BasePath tidak bisa diubah setelah modul diinisialisasi. + +theme_baseurl_required = TThemeManager.BasePath diperlukan. Standarnya direktori bernama 'themes' di bawah direktori berisi naskah entri aplikasi diasumsikan. +theme_path_inexistent = Path tema '{0}' tidak ada. +theme_control_nested = Skin untuk tipe kontrol '{0}' dalam tema '{1}' tidak bisa di dalam skin lain. +theme_skinid_duplicated = SkinID '{0}.{1}' diduplikasi dalam tema '{2}'. +theme_databind_forbidden = Databind tidak bisa dipakai dalam tema '{0}' untuk skin kontrol '{1}.{2}' mengenai properti '{3}'. +theme_property_readonly = Skin diterapkan ke properti kontrol hanya-baca '{0}.{1}'. +theme_property_undefined = Skin diterapkan ke properti kontrol yang tidak ada '{0}.{1}'. +theme_tag_unexpected = Inisialisasi properti {0} berisi tipe tag tidak dikenal {1}. + +control_object_reregistered = Ditemukan duplikasi obyek ID '{0}'. +control_id_invalid = {0}.ID '{1}' tidak benar. Hanya alfanumerik dan karakter bergaris bawah dibolehkan. Karakter pertama harus alfabetikatau karakter garis bawah. +control_skinid_unchangeable = {0}.SkinID tidak bisa diubah setelah skin diterapkan ke kontrol atau kontrol anak sudah dibuat. +control_enabletheming_unchangeable = {0}.EnableTheming tidak bisa diubah setelah kontrol anak dibuat. +control_stylesheet_applied = StyleSheet skin sudah diterapkan ke {0}. +control_id_nonunique = {0}.ID '{1}' tidak unik diantara semua kontrol di bawah tempat penamaan yang sama. + +templatecontrol_mastercontrol_invalid = Kontrol master harus bertipe TTemplateControl atau kelas anak. +templatecontrol_mastercontrol_required = Kontrol '{0}' memerlukan kontrol master karena kontrol menggunakan TContent. +templatecontrol_contentid_duplicated = TContent ID '{0}' duplikasi. +templatecontrol_placeholderid_duplicated= TContentPlaceHolder ID '{0}' duplikasi. +templatecontrol_directive_invalid = {0}.{1} hanya menerima teks statis melalui direktif template. +templatecontrol_placeholder_inexistent = TContent '{0}' tidak mempunyai TContentPlaceHolder yang sama. + +page_form_duplicated = Halaman bisa berisi paling banyak satu TForm. Gunakan tag form HTML reguler untuk sisa form-nya. +page_isvalid_unknown = TPage.IsValid belum dievaluasi. +page_postbackcontrol_invalid = Tidak bisa menentukan kontrol postback '{0}'. +page_control_outofform = {0} '{1}' harus dikurung di dalam TForm. +page_head_duplicated = Halaman dapat berisi paling banyak satu THead. +page_head_required = Kontrol THead dibutuhkan dalam template halaman untuk menyajikan CSS dan JS dalam seksi head HTML. +page_statepersister_invalid = Persister kondisi halaman harus mengimplementasikan antarmuka IPageStatePersister. + +csmanager_pradoscript_invalid = Nama librari naskah Prado '{0}' tidak dikenal. +csmanager_invalid_packages = Paket '{1}' tidak dikenal untuk paket javascript yang didefinisikan dalam '{0}'. Paket yang benar adalah '{2}'. + +contentplaceholder_id_required = TContentPlaceHolder harus mempunyai ID. + +content_id_required = TContent harus mempunyai ID. + +controlcollection_control_required = TControlList hanya bisa menerima string atau obyek TControl. + +webcontrol_accesskey_invalid = {0}.AccessKey '{1}' tidak benar. Ia harus berupa hanya karakter tunggal. +webcontrol_style_invalid = {0}.Style harus berisi hanya nilai string. + +listcontrol_selection_invalid = {0} mempunyai pilihan tidak benar yang disetel sebelum melakukan penyatuan data. +listcontrol_selectedindex_invalid = {0}.SelectedIndex mempunyai nilai '{1}' yang tidak benar. +listcontrol_selectedvalue_invalid = {0}.SelectedValue mempunyai nilai '{1}' yang tidak benar. +listcontrol_expression_invalid = {0} mengevaluasi ekspresi yang tidak benar '{1}' : {2} +listcontrol_multiselect_unsupported = {0} tidak mendukung multi pilihan. + +label_associatedcontrol_invalid = TLabel.AssociatedControl '{0}' tidak bisa ditemukan. + +hiddenfield_focus_unsupported = THiddenField tidak mendukung setelan fokus input. +hiddenfield_theming_unsupported = THiddenField tidak mendukung tema. +hiddenfield_skinid_unsupported = THiddenField tidak mendukung skin kontrol. + +panel_defaultbutton_invalid = TPanel.DefaultButton '{0}' tidak merujuk ke kontrol tombol yang sudah ada. + +tablestyle_cellpadding_invalid = TTableStyle.CellPadding harus mengambil integer sama dengan atau lebih besar dari. +tablestyle_cellspacing_invalid = TTableStyle.CellSpacing harus mengambil integer sama dengan atau lebih besar dari -1. + +pagestatepersister_pagestate_corrupted = Kondisi halaman rusak. + +sessionpagestatepersister_pagestate_corrupted = Kondisi halaman rusak. +sessionpagestatepersister_historysize_invalid = TSessionPageStatePersister.History harus integer lebih besar dari 0. + +listitemcollection_item_invalid = TListItemCollection hanya bisa mengambil strings atau obyek TListItem. + +dropdownlist_selectedindices_unsupported= TDropDownList.SelectedIndices hanya-baca. + +bulletedlist_autopostback_unsupported = TBulletedList.AutoPostBack hanya-baca. +bulletedlist_selectedindex_unsupported = TBulletedList.SelectedIndex hanya-baca. +bulletedlist_selectedindices_unsupported= TBulletedList.SelectedIndices hanya-baca. +bulletedlist_selectedvalue_unsupported = TBulletedList.SelectedValue hanya-baca. + +radiobuttonlist_selectedindices_unsupported = TRadioButtonList.SelectedIndices hanya-baca. + +logrouter_configfile_invalid = TLogRouter.ConfigFile '{0}' tidak ada. +logrouter_routeclass_required = Atribut Class diperlukan dalam konfigurasi . +logrouter_routetype_required = Rute catatan harus turunan dari TLogRoute atau kelas turunannya. + +filelogroute_logpath_invalid = TFileLogRoute.LogPath '{0}' harus berupa direktori dalam format namespace dan harus bisa ditulis oleh proses server Web. +filelogroute_maxfilesize_invalid = TFileLogRoute.MaxFileSize harus lebih besar dari 0. +filelogroute_maxlogfiles_invalid = TFileLogRoute.MaxLogFiles harus lebih besar dari 0. + +emaillogroute_sentfrom_required = TEmailLogRoute.SentFrom tidak boleh kosong. + +repeatinfo_repeatcolumns_invalid = TRepeatInfo.RepeatColumns tidak boleh kurang dari 0. + +basevalidator_controltovalidate_invalid = {0}.ControlToValidate kosong atau berisi path ID kontrol yang tidak benar. +basevalidator_validatable_required = {0}.ControlToValidate harus mengarah ke sebuah kontrol yang mengimplementasikan antarmuka IValidatable. +basevalidator_forcontrol_unsupported = {0}.ForControl tidak didukung. + +comparevalidator_controltocompare_invalid = TCompareValidator.ControlToCompare berisi path ID kontrol tidak benar. + +listcontrolvalidator_invalid_control = {0}.ControlToValidate berisi path ID TListControl tidak benar, "{1}" adalah {2}. + +repeater_template_required = TRepeater.{0} memerlukan turunan template yang mengimplementasikan antarmuka ITemplate. +repeater_itemtype_unknown = Tipe item repeater {0} tidak dikenal. +repeateritemcollection_item_invalid = TRepeaterItemCollection hanya bisa menerima obyek yang adalah turunan dari TControl atau kelas turunannya. + +datalist_template_required = TDataList.{0} requires a template instance implementing ITemplate interface. +datalistitemcollection_datalistitem_required = TDataListItemCollection hanya bisa menerima obyek TDataListItem. + +datagrid_template_required = TDataGrid.{0} memerlukan turunan template yang mengimplementasikan antarmuka ITemplate. +templatecolumn_template_required = TTemplateColumn.{0} memerlukan turunan template yang mengimplementasikan antarmuka ITemplate. +datagrid_currentpageindex_invalid = TDataGrid.CurrentPageIndex harus tidak kurang dari0. +datagrid_pagesize_invalid = TDataGrid.PageSize harus lebih besar dari 0. +datagrid_virtualitemcount_invalid = TDataGrid.VirtualItemCount harus tidak kurang dari0. +datagriditemcollection_datagriditem_required = TDataGridItemCollection hanya bisas menerima obyek TDataGridItem. +datagridcolumncollection_datagridcolumn_required = TDataGridColumnCollection hanya bisa menerima obyek TDataGridColumn. +datagridpagerstyle_pagebuttoncount_invalid = TDataGridPagerStyle.PageButtonCount harus lebih besar dari 0. + +datafieldaccessor_data_invalid = TDataFieldAccessor mencoba untuk mengevaluasi nilai field dari data yang tidak benar. Pastikan data adalah sebuah array, TMap, TList, atau obyek yang berisi field yang ditetapkan '{0}'. +datafieldaccessor_datafield_invalid = TDataFieldAccessor mencoba untuk mengevaluasi nilai data dari field '{0}' yang tidak dikenal. + +tablerowcollection_tablerow_required = TTableRowCollection hanya bisa menerima obyek TTableRow. + +tablecellcollection_tablerow_required = TTableCellCollection hanya bisa menerima obyek TTableCell. + +multiview_view_required = TMultiView hanya bisa menerima TView sebagai anak. +multiview_activeviewindex_invalid = TMultiView.ActiveViewIndex mempunyai indeks'{0}' yang tidak benar. +multiview_view_inexistent = TMultiView tidak bisa menemukan tampilan yang ditetapkan. +multiview_viewid_invalid = TMultiView tidak bisa menemukan tampilan '{0}' untu beralih ke sana. + +viewcollection_view_required = TViewCollection hanya bisa menerima TView sebagai elemennya. + +view_visible_readonly = TView.Visible hanya baca. Gunakan TView.Active untuk menghidup-matikan visibilitasnya. + +wizard_step_invalid = Langkah yang diaktifkan tidak bisa ditemukan dalam koleksi langkah bimbingan. +wizard_command_invalid = Perintah navigasi bimbingan '{0}' tidak benar. + +table_tablesection_outoforder = Seksi tabel TTable harus dalam urutan: Header, Body dan Footer. + +completewizardstep_steptype_readonly = TCompleteWizardStep.StepType hanya-baca. + +wizardstepcollection_wizardstep_required = TWizardStepCollection hanya bisa menerima obyek dari TWizardStep atau kelas anaknya. + +texthighlighter_stylesheet_invalid = Tidak bisa menemukan file stylesheet untuk TTextHighlighter. + +hotspotcollection_hotspot_required = THotSpotCollection hanya bisa meneruma turunan dari THotSpot atau kelas anaknya. + +htmlarea_textmode_readonly = THtmlArea.TextMode hanya-baca. +htmlarea_tarfile_invalid = THtmlArea tidak bisa mencari file tar TinyMCE. + +parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile tidak bisa diubah karena modul sudah diinisialisasi. +parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' tidak benar. Pastikan ia ada dalam format namespace dan ekstensi file adalah '.xml'. +parametermodule_parameterid_required = Elemen parameter harus mempunyai atribut 'id'. + +datagridcolumn_id_invalid = {0}.ID '{1}' tidak benar. Hanya alfanumerik dan karakter bergaris bawah yang dibolehkan. Karakter pertama harus alfabetik atau karakter garis bawah. +datagridcolumn_expression_invalid = {0} mengevaluasi ekspresi yang tidak benar '{1}' : {2} + +outputcache_cachemoduleid_invalid = TOutputCache.CacheModuleID disetel dengan ID modul cacheID {0} yang tidak benar. Baik modul yang tidak ada ataupun tidak mengimplementasikan antarmuka ICache. +outputcache_duration_invalid = {0}.Duration harus integer tidak kurang dari 0. + +stack_data_not_iterable = TStack hanya bisa mengambil data dari array atau obyek yang dapat dijelajahi. +stack_empty = TStack kosong. + +queue_data_not_iterable = TQueue hanya bisa mengambil data dari array atau obyek yang dapat dijelajahi. +queue_empty = TQueue kosong. + +pager_pagebuttoncount_invalid = TPager.PageButtonCount harus integer tidak kurang dari 1. +pager_currentpageindex_invalid = TPager.CurrentPageIndex diluar jangkauan. +pager_pagecount_invalid = TPager.PageCount tidak bisa lebih kecil dari 0. +pager_controltopaginate_invalid = TPager.ControlToPaginate {0} harus path ID yang merngarah ke kontrol turunan-TDataBoundControl. + +databoundcontrol_pagesize_invalid = {0}.PageSize harus integer tidak lebih kecil dari 1. +databoundcontrol_virtualitemcount_invalid = {0}.VirtualItemCount harus integer tidak lebih kecil dari 0. +databoundcontrol_currentpageindex_invalid = {0}.CurrentPageIndex diluar jangkauan. +databoundcontrol_datasource_invalid = {0}.DataSource tidak benar. +databoundcontrol_datasourceid_inexistent = databoundcontrol_datasourceid_inexistent. +databoundcontrol_datasourceid_invalid = databoundcontrol_datasourceid_invalid +databoundcontrol_datamember_invalid = databoundcontrol_datamember_invalid + +clientscript_invalid_file_position = Posisi file '{1}' tidak benar untuk kontrol TClientScript '{0}', harus 'Head', 'Here' atau 'Begin'. +clientscript_invalid_package_path = PackagePath '{0}' tidak benar untuk kontrol TClientScript '{1}'. + +tdatepicker_autopostback_unsupported = '{0}' tidak mendukung AutoPostBack. +globalization_cache_path_failed = Tidak bisa membuat path cache pesan terjemahan '{0}'. Pastikan direktori leluhur ada dan bisa ditulis oleh proses Web. +globalization_source_path_failed = Tidak bisa membuat path pesan terjemahan '{0}'. Pastikan direktori leluhur ada dan bisa ditulis oleh proses Web. +callback_not_support_no_priority_state_update = Permintaan callback tidak mendukung pemutakhiran kondisi halam yang tidak diprioritaskan. +callback_invalid_callback_options = '{1}' bukan kontrol TCallbackOptions yang benar untuk kontrol Callback '{0}'. +callback_invalid_clientside_options = Properti ClientSide Callback harus berupa string yakni ID dari kontrol TCallbackOptions atau turunan dari TCallbackClientSideOptions.======= +callback_not_support_no_priority_state_update = Permintaan callback tidak mendukung pemutakhiran kondisi halaman yang tidak diprioritaskan. +callback_invalid_handler = Pengendali callback tidak benar, kontrol {0} harus mengimplementasikan ICallbackEventHandler. +callback_invalid_target = Target callback tidak benar, tidak ada kontrol dengan ID {0}. + +callback_interval_be_positive = Interval TCallbackTimer "{0}" harus tepat lebih besar dari nol detik. +callback_decay_be_not_negative = Rata-rata kekurangan TCallbackTimer "{0}" tidak boleh negatif. + +callback_no_autopostback = Kontrol "{0}" tidak bisa menghidupkan AutoPostBack. + +xmltransform_xslextension_required = TXmlTransform memerlukan ekstensi XSL PHP. +xmltransform_transformpath_invalid = TXmlTransform.TransformPath '{0}' tidak benar. +xmltransform_documentpath_invalid = TXmlTransform.DocumentPath '{0}' tidak benar. +xmltransform_transform_required = Baik properti TransformContent ataupun TransformPath harus disetel untuk TXmlTransform. + +ttriggeredcallback_invalid_controlid = Properti ControlID '{0}' tidak boleh kosong. +tactivecustomvalidator_clientfunction_unsupported = {0} tidak mendukung fungsi validator sisi klien. + +dbconnection_open_failed = TDbConnection gagal untuk menyelesaikan koneksi DB: {0} +dbconnection_connection_inactive = TDbConnection tidak aktif. +dbconnection_unsupported_driver_charset = Database driver '{0}' doesn't support setting charset. + +dbcommand_prepare_failed = TDbCommand gagal untuk menyiapkan pernyataan SQL "{1}": {0} +dbcommand_execute_failed = TDbCommand gagal untuk menjalankan pernyataan SQL "{1}": {0} +dbcommand_query_failed = TDbCommand gagal untuk menjalankan query SQL "{1}": {0} +dbcommand_column_empty = TDbCommand mengembalikan hasil kosong dan tidak bisa memperoleh skalar. +dbdatareader_rewind_invalid = TDbDataReader adalah stream hanya-maju. Ia hanya bisa sekali menjelajah. +dbtransaction_transaction_inactive = TDbTransaction tidak aktif. + +dbcommandbuilder_value_must_not_be_null = Properti {0} tidak boleh null seperti didefinisikan oleh kolom '{2}' dalam tabel '{1}'. + +dbcommon_invalid_table_name = Tabel database '{0}' tidak ditemukan. Pesan kesalahan: {1}. +dbcommon_invalid_identifier_name = Nama pengenal database '{0}' tidak benar, lihat {1} untuk lebih jelasnya. +dbtableinfo_invalid_column_name = Nama kolom '{0}' tidak benar untuk tabel database '{1}'. +dbmetadata_invalid_table_view = Nama table/view '{0}', atau table/view '{0}' itu tidak berisi definisi kolom/field yang bisa diakses. +dbmetadata_requires_php_version = Versi PHP {1} atau yang terbaru diperlukan untuk menggunakan database {0}. + +dbtablegateway_invalid_criteria = Obyek kriteria tidak benar, harus berupa string atau turunan dari TSqlCriteria. +dbtablegateway_no_primary_key_found = Tabel '{0}' tidak berisi field kunci primer manapun. +dbtablegateway_missing_pk_values = Nilai kunci primer tidak ada dalam bentuk IN(key1, key2, ...) untuk tabel '{0}'. +dbtablegateway_pk_value_count_mismatch = Jumlah nilai kunci gabungan tidak sama dalam bentuk IN( (key1, key2, ..), (key3, key4, ..)) untuk tabel '{0}'. +dbtablegateway_mismatch_args_exception = Metode finder TTableGateway '{0}' mengharapkan parameter {1} tapi sebaliknya hanya menemukan parameter {2}. +dbtablegateway_mismatch_column_name = Dalam metode dynamic __call() '{0}', tidak ada kolom yang ditemukan, kolom yang benar untuk tabel '{2}' adalah '{1}'. +dbtablegateway_invalid_table_info = Tabel harus string atau turunan dari TDbTableInfo. + +directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} tidak merujuk ke direktori yang benar. +cachedependencylist_cachedependency_required = Hanya obyek yang mengimplementasikan ICacheDependency yang dapat ditambahkan ke dalam TCacheDependencyList. + +soapservice_configfile_invalid = TSoapService.ConfigFile '{0}' tidak ada. Catatan, ia harus ditetapkan dalam format namespace dan mempunyai ekstensi file '.xml'. +soapservice_request_invalid = Server SOAP '{0}' tidak ditemukan. +soapservice_serverid_required = Elemen harus memiliki atribut 'id'. +soapservice_serverid_duplicated = ID server SOAP '{0}' duplikasi. +soapserver_id_invalid = ID Server SOAP '{0}'. Ia tidak boleh diakhiri dengan '.wsdl'. +soapserver_version_invalid = Versi SOAP tidak benar '{0}'. Ia harus berupa '1.1' atau '1.2'. + +dbusermanager_userclass_required = TDbUserManager.UserClass diperlukan. +dbusermanager_userclass_invalid = TDbUserManager.UserClass '{0}' bukan kelas pengguna yang benar. Kelas harus memperluas TDbUser. +dbusermanager_connectionid_invalid = TDbUserManager.ConnectionID '{0}' tidak mengarah ke modul TDataSourceConfig yang benar. +dbusermanager_connectionid_required = TDbUserManager.ConnectionID diperlukan. + +feedservice_id_required = TFeedService memerlukan atribut 'id' dalam elemen feed-nya. +feedservice_feedtype_invalid = Kelas feed '{0}' harus mengimplementasikan antarmuka IFeedContentProvider. +feedservice_class_required = TFeedService memerlukan atribut 'class' dalam elemen feed-nya. +feedservice_feed_unknown = Feed '{0}' yang diminta tidak dikenal. + +tabviewcollection_tabview_required = TTabPanel hanya menerima TTabView sebagai anaknya. +tabpanel_activeviewid_invalid = TTabPanel.ActiveViewID mempunyai ID '{0}' yang tidak benar. +tabpanel_activeviewindex_invalid = TTabPanel.ActiveViewIndex mempunyai Indeks '{0}' yang tidak benar. +tabpanel_view_inexistent = TTabPanel tidak bisa menemukan tampilan yang ditetapkan. + +cachesession_cachemoduleid_required = TCacheHttpSession.CacheModuleID diperlukan. +cachesession_cachemodule_inexistent = TCacheHttpSession.CacheModuleID '{0}' mengarah ke modul yang tidak ada. +cachesession_cachemodule_invalid = TCacheHttpSession.CacheModuleID '{0}' mengarah ke modul yang tidak mengimplementasikan antarmuka ICache. + +urlmapping_urlmappingpattern_required = TUrlMapping can hanya berisi TUrlMappingPattern atau kelas anaknya. +urlmapping_global_required = TUrlMapping harus dikonfigurasi sebagai modul global. +urlmapping_configfile_inexistent = TUrlMapping.ConfigFile '{0}' bukan sebuah file. +urlmapping_configfile_invalid = TUrlMapping.ConfigFile '{0}' harus menunjuk ke file XML dalam format namespace. + +urlmappingpattern_serviceparameter_required = TUrlMappingPattern.ServiceParameter diperlukan untuk pola '{0}'. +urlmapping_global_required = TUrlMapping harus dikonfigurasi sebagai modul global. +urlmapping_configfile_inexistent = TUrlMapping.ConfigFile '{0}' bukan sebuah file. +urlmapping_configfile_invalid = TUrlMapping.ConfigFile '{0}' harus mengarah ke file XML file dalam format namespace. + +urlmappingpattern_serviceparameter_required = TUrlMappingPattern.ServiceParameter diperlukan untuk pola '{0}'. + +keyboard_forcontrol_required = TKeyboard.ForControl tidak boleh kosong. +keyboard_forcontrol_invalid = TKeyboard.ForControl '{0}' tidak benar. + +captcha_tokenimagetheme_invalid = TCaptcha.TokenImageTheme harus integer antara {0} dan {1}. +captcha_tokenfontsize_invalid = TCaptcha.TokenFontSize harus integer antara {0} dan {1}. +captcha_mintokenlength_invalid = TCaptcha.MinTokenLength harus integer antara {0} dan {1}. +captcha_maxtokenlength_invalid = TCaptcha.MaxTokenLength harus integer antara {0} dan {1}. +captcha_tokenalphabet_invalid = TCaptcha.TokenAlphabet harus berupa string yang terdiri dari setidaknya 2 karakter. +captcha_privatekey_unknown = TCaptcha.PrivateKey tidak dikenal. Pastikan bahwa direktori assets anda bisa ditulisi oleh proses server Web. +captcha_gd2_required = TCaptcha memerlukan ekstensi GD2 PHP. +captcha_imagettftext_required = TCaptcha memerlukan ekstensi GD2 PHP dengan dukungan font TrueType. +captcha_imagepng_required = TCaptcha memerlukan ekstensi GD2 PHP dengan dukungan format gambar PNG. + +slider_handle_class_invalid = TSlider.HandleClass '{0}' bukan kelas pengguna yang benar. Kelas harus memperluas TSliderHandle. + +cachepagestatepersister_cachemoduleid_invalid = TCachePageStatePersister.CacheModuleID '{0}' tidak mengarah ke modul cache yang benar. +cachepagestatepersister_cache_required = TCachePageStatePersister memerlukan modul cache untuk diambil. +cachepagestatepersister_timeout_invalid = TCachePageStatePersister.Timeout harus berupa integer tidak kurang dari nol. +cachepagestatepersister_pagestate_corrupted = Kondisi halaman rusak. + +conditional_condition_invalid = TConditional.Condition '{0}' bukan ekspresi PHP yang benar: {1} + +db_cachetable_inexistent = TDbCache tidak bisa menemukan tabel DB '{0}' untuk menyimpan data yang di-cache. + +ar_data_invalid = {0}.copyFrom() hanya bisa menggunakan obyek atay array sebagai parameter. +ar_save_invalid = Turunan {0} tidak bisa disimpan karena kondisi sudah dihapus ataupun tidak dikenal. +ar_delete_invalid = Turunan {0} tidak bisa dihapus karena ada rekaman baru atau rekaman sudah dihapus. + +datasource_dbconnection_invalid = TDataSourceConfig.DbConnection '{0}' tidak benar. ia merujuk ke modul aplikasi yang benar. \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/messages/messages-zh.txt b/gui/baculum/framework/Exceptions/messages/messages-zh.txt new file mode 100644 index 0000000000..9dc14a8b82 --- /dev/null +++ b/gui/baculum/framework/Exceptions/messages/messages-zh.txt @@ -0,0 +1,434 @@ +prado_application_singleton_required = Prado.Application只能被设置一次。 +prado_component_unknown = 未知组件类型“{0}”。这有可能是因为{0}类文件里有如下错误:{1} +prado_using_invalid = “{0}”不是一个合法的命名空间。如果命名空间指向一个目录,请确认命名空间以“.*”结尾。 +prado_alias_redefined = 路径别名“{0}”不能被重定义。 +prado_alias_invalid = 路径别名“{0}”所指的文件目录{1}不存在。 +prado_aliasname_invalid = 路径别名“{0}”不允许包含“.”字符。 + +component_property_undefined = 组件属性“{0}.{1}”未定义。 +component_property_readonly = 组件属性“{0}.{1}”是只读的。 +component_event_undefined = 组件事件“{0}.{1}”未定义。 +component_eventhandler_invalid = 组件事件“{0}.{1}”所指事件响应函数“{2}”非法。 +component_expression_invalid = 组件{0}执行了一个非法的表达式“{1}”:{2} +component_statements_invalid = 组件{0}执行了一段非法的PHP代码“{1}”:{2} + +propertyvalue_enumvalue_invalid = 枚举类型“{1}”不存在枚举值“{0}”。 + +list_index_invalid = 列表下标“{0}”越界。 +list_item_inexistent = 无法在列表里找到要找的项目。 +list_data_not_iterable = 所传参数必须是一个数组或是一个实现Traversable接口的对象。 +list_readonly = 列表“{0}”是只读的。 + +map_addition_disallowed = Map无法添加新项目。 +map_item_unremovable = Map无法删除项目。 +map_data_not_iterable = Map数据必须是个数组或是实现Traversable接口的对象。 +map_readonly = Map类型“{0}”是只读的。 + +application_includefile_invalid = 无法找到应用配置文件“{0}”。注意,配置文件应该以命名空间的形式指定;文件名必须以.xml结尾。 +application_basepath_invalid = 应用的基本路径“{0}”不存在或不是一个目录。 +application_runtimepath_invalid = 应用的runtime路径“{0}”不存在,或Web服务进程无法写入该目录。 +application_service_invalid = 服务“{0}”必须实现IService接口。 +application_service_unknown = 请求的服务“{0}”未定义。 +application_unavailable = 应用暂时不可用。 +application_service_unavailable = 服务“{0}”暂时不可用。 +application_moduleid_duplicated = 应用模块的ID “{0}”不唯一。 +application_runtimepath_failed = 无法创建runtime路径“{0}”。请确认父目录是否存在,是否可被Web服务进程写入。 + +appconfig_aliaspath_invalid = 应用配置使用了一个非法的文件目录“{1}”。 +appconfig_alias_invalid = 应用配置元素必须指定“id”和“path”属性。 +appconfig_alias_redefined = 应用配置不允许重复定义。 +appconfig_using_invalid = 应用配置元素必须指定“namespace”属性。 +appconfig_moduleid_required = 应用配置元素必须指定“id”属性。 +appconfig_moduletype_required = 应用配置元素必须指定“class”属性。 +appconfig_serviceid_required = 应用配置元素必须指定“id”属性。 +appconfig_servicetype_required = 应用配置元素必须指定“class”属性。 +appconfig_parameterid_required = 应用配置元素必须指定“id”属性。 +appconfig_includefile_required = 应用配置元素必须指定“file”属性。 +appconfig_paths_invalid = 应用配置不允许包含<{0}>元素。 +appconfig_modules_invalid = 应用配置不允许包含<{0}>元素。 +appconfig_services_invalid = 应用配置不允许包含<{0}>元素。 +appconfig_parameters_invalid = 应用配置不允许包含<{0}>元素。 +appconfig_tag_invalid = 应用配置不允许包含<{0}>元素。 + +securitymanager_validationkey_invalid = TSecurityManager.ValidationKey不能为空。 +securitymanager_encryptionkey_invalid = TSecurityManager.EncryptionKey不能为空。 +securitymanager_mcryptextension_required = TSecurityManager的加密功能需要使用Mcrypt的PHP扩展模块。 + +uri_format_invalid = “{0}”不是一个合法的URI。 + +httprequest_separator_invalid = THttpRequest.UrlParamSeparator只能包含一个字符。 +httprequest_urlmanager_inexist = THttpRequest.UrlManager “{0}”所指的模块不存在。 +httprequest_urlmanager_invalid = THttpRequest.UrlManager “{0}”所指的模块必须继承TUrlManager。 + +httpcookiecollection_httpcookie_required = THttpCookieCollection只能包含THttpCookie对象。 + +httpresponse_bufferoutput_unchangeable = THttpResponse.BufferOutput无法被修改,因为THttpResponse已经初始化完毕。 +httpresponse_file_inexistent = THttpResponse无法发送文件“{0}”。该文件不存在。 + +httpsession_sessionid_unchangeable = THttpSession.SessionID无法被修改,因为session已经启动了。 +httpsession_sessionname_unchangeable = THttpSession.SessionName无法被修改,因为session已经启动了。 +httpsession_sessionname_invalid = THttpSession.SessionName只能包含字母或数字字符。 +httpsession_savepath_unchangeable = THttpSession.SavePath无法被修改,因为session已经启动了。 +httpsession_savepath_invalid = THttpSession.SavePath所指目录“{0}”不存在。 +httpsession_storage_unchangeable = THttpSession.Storage无法被修改,因为session已经启动了。 +httpsession_cookiemode_unchangeable = THttpSession.CookieMode无法被修改,因为session已经启动了。 +httpsession_autostart_unchangeable = THttpSession.AutoStart无法被修改,因为session已经启动了。 +httpsession_gcprobability_unchangeable = THttpSession.GCProbability无法被修改,因为session已经启动了。 +httpsession_gcprobability_invalid = THttpSession.GCProbability必须是个0到100之间的整数。 +httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID无法被修改,因为session已经启动了。 +httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID cannot be set when THttpSession.CookieMode is set to Only. +httpsession_maxlifetime_unchangeable = THttpSession.Timeout无法被修改,因为session已经启动了。 + +assetmanager_basepath_invalid = TAssetManager.BasePath所指路径“{0}”非法。请确认它以命名空间方式指定,并且它所对应的文件目录可以被Web服务器进程写入。 +assetmanager_basepath_unchangeable = TAssetManager.BasePath无法被修改,因为该模块已经初始化完毕。 +assetmanager_baseurl_unchangeable = TAssetManager.BaseUrl无法被修改,因为该模块已经初始化完毕。 +assetmanager_filepath_invalid = TAssetManager试图发布一个不存在的文件“{0}”。 +assetmanager_tarchecksum_invalid = TAssetManager试图发布一个校验值不正确的tar文件。 +assetmanager_tarfile_invalid = TAssetManager试图发布一个不存在的tar文件“{0}”。 +assetmanager_source_directory_invalid = TAssetManager试图复制一个不存在的文件目录“{0}”。 + +cache_primary_duplicated = 一个应用最多只能指定一个主缓存模块。模块“{0}”正在被注册为第二个主缓存模块。 +sqlitecache_extension_required = TSqliteCache需要SQLite PHP扩展模块。 +sqlitecache_dbfile_required = TSqliteCache.DbFile必须指定一个值。 +sqlitecache_connection_failed = TSqliteCache连接数据库失败:{0} +sqlitecache_table_creation_failed = TSqliteCache无法创建缓存数据库:{0} +sqlitecache_dbfile_unchangeable = TSqliteCache.DbFile无法被修改,因为该模块已经初始化完毕。 +sqlitecache_dbfile_invalid = TSqliteCache.DbFile所指文件不存在。请确认它以命名空间方式指定。 + +memcache_extension_required = TMemCache需要memcache PHP扩展模块。 +memcache_connection_failed = TMemCache连接memcache服务器“{0}”失败:{1} +memcache_host_unchangeable = TMemCache.Host无法被修改,因为该模块已经初始化完毕。 +memcache_port_unchangeable = TMemCache.Port无法被修改,因为该模块已经初始化完毕。 + +apccache_extension_required = TAPCCache需要APC PHP扩展模块。 +apccache_add_unsupported = TAPCCache不支持add()函数。 +apccache_replace_unsupported = TAPCCache不支持replace()函数。 +apccache_extension_not_enabled = TAPCCache需要在php.ini里指定apc.enabled = 1。 +apccache_extension_not_enabled_cli = TAPCCache需要在php.ini里指定apc.enable_cli = 1。 + +errorhandler_errortemplatepath_invalid = TErrorHandler.ErrorTemplatePath所指路径“{0}”不存在。请确认它以命名空间的方式指定,并且它指向一个包含错误信息模板的文件目录。 + +pageservice_page_unknown = 无法找到页面“{0}”。 +pageservice_pageclass_unknown = 未知页面类“{0}”。 +pageservice_basepath_invalid = TPageService.BasePath所指路径“{0}”不存在。 +pageservice_page_required = 请提供页面名字。 +pageservice_defaultpage_unchangeable = TPageService.DefaultPage无法被修改,因为页面服务已经初始化完毕。 +pageservice_basepath_unchangeable = TPageService.BasePath无法被修改,因为页面服务已经初始化完毕。 +pageservice_pageclass_invalid = 页面类“{0}”必须继承TPage。 +pageservice_includefile_invalid = 无法找到页面服务配置“{0}”。请确认它以命名空间方式指定,并且文件名以“.xml”结尾。 + +pageserviceconf_file_invalid = 无法打开页面配置文件“{0}”。 +pageserviceconf_aliaspath_invalid = uses an invalid file path "{1}" in page directory configuration file '{2}'. +pageserviceconf_alias_invalid = element must have an "id" attribute and a "path" attribute in page directory configuration file '{0}'. +pageserviceconf_using_invalid = element must have a "namespace" attribute in page directory configuration file '{0}'. +pageserviceconf_module_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_moduletype_required = must have a "class" attribute in page directory configuration file '{1}'. +pageserviceconf_parameter_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_page_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_includefile_required = Page configuration element must have a "file" attribute. + +template_closingtag_unexpected = Unexpected closing tag '{0}' is found. +template_closingtag_expected = Closing tag '{0}' is expected. +template_directive_nonunique = Directive '<%@ ... %>' must appear at the beginning of the template and can appear at most once. +template_comments_forbidden = Template comments are not allowed within property tags. +template_matching_unexpected = Unexpected matching. +template_property_unknown = {0} has no property called '{1}'. +template_event_unknown = {0} has no event called '{1}'. +template_property_readonly = {0} has a read-only property '{1}'. +template_event_forbidden = {0} is a non-control component. No handler can be attached to its event '{1}' in a template. +template_databind_forbidden = {0} is a non-control component. Expressions cannot be bound to its property '{1}'. +template_component_required = '{0}' is not a component. Only components can appear in a template. +template_format_invalid = Invalid template syntax: {0} +template_property_duplicated = Property {0} is configured twice or more. +template_eventhandler_invalid = {0}.{1} can only accept a static string. +template_controlid_invalid = {0}.ID can only accept a static text string. +template_controlskinid_invalid = {0}.SkinID can only accept a static text string. +template_content_unexpected = Unexpected content is encountered when instantiating template: {0}. +template_include_invalid = Invalid template inclusion. Make sure {0} is a valid namespace pointing to an existing template file whose extension is .tpl. +template_tag_unexpected = Initialization for property {0} contains an unknown tag type {1}. + +xmldocument_file_read_failed = TXmlDocument is unable to read file '{0}'. +xmldocument_file_write_failed = TXmlDocument is unable to write file '{0}'. + +xmlelementlist_xmlelement_required = TXmlElementList can only accept TXmlElement objects. + +authorizationrule_action_invalid = TAuthorizationRule.Action can only take 'allow' or 'deny' as the value. +authorizationrule_verb_invalid = TAuthorizationRule.Verb can only take 'get' or 'post' as the value. + +authorizationrulecollection_authorizationrule_required = TAuthorizationRuleCollection can only accept TAuthorizationRule objects. + +usermanager_userfile_invalid = TUserManager.UserFile '{0}' is not a valid file. +usermanager_userfile_unchangeable = TUserManager.UserFile cannot be modified. The user module has been initialized already. + +authmanager_usermanager_required = TAuthManager.UserManager must be assigned a value. +authmanager_usermanager_inexistent = TAuthManager.UserManager '{0}' does not refer to an ID of application module. +authmanager_usermanager_invalid = TAuthManager.UserManager '{0}' does not refer to a valid TUserManager application module. +authmanager_usermanager_unchangeable = TAuthManager.UserManager cannot be modified after the module is initialized. +authmanager_session_required = TAuthManager requires a session application module. + +thememanager_service_unavailable = TThemeManager requires TPageService to be available. This error often occurs when you configure TThemeManager outside of the page service configuration. +thememanager_basepath_invalid = TThemeManager.BasePath '{0}' is not a valid path alias. Make sure you have defined this alias in configuration and it points to a valid directory. +thememanager_basepath_invalid2 = TThemeManager.BasePath '{0}' is not a valid directory. +thememanager_basepath_unchangeable = TThemeManager.BasePath cannot be modified after the module is initialized. + +theme_baseurl_required = TThemeManager.BasePath is required. By default, a directory named 'themes' under the directory containing the application entry script is assumed. +theme_path_inexistent = Theme path '{0}' does not exist. +theme_control_nested = Skin for control type '{0}' in theme '{1}' cannot be within another skin. +theme_skinid_duplicated = SkinID '{0}.{1}' is duplicated in theme '{2}'. +theme_databind_forbidden = Databind cannot be used in theme '{0}' for control skin '{1}.{2}' about property '{3}'. +theme_property_readonly = Skin is being applied to a read-only control property '{0}.{1}'. +theme_property_undefined = Skin is being applied to an inexistent control property '{0}.{1}'. +theme_tag_unexpected = Initialization for property {0} contains an unknown tag type {1}. + +control_object_reregistered = Duplicated object ID '{0}' found. +control_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character. +control_skinid_unchangeable = {0}.SkinID cannot be modified after a skin has been applied to the control or the child controls have been created. +control_enabletheming_unchangeable = {0}.EnableTheming cannot be modified after the child controls have been created. +control_stylesheet_applied = StyleSheet skin has already been applied to {0}. +control_id_nonunique = {0}.ID '{1}' is not unique among all controls under the same naming container. + +templatecontrol_mastercontrol_invalid = Master control must be of type TTemplateControl or a child class. +templatecontrol_mastercontrol_required = Control '{0}' requires a master control since the control uses TContent. +templatecontrol_contentid_duplicated = TContent ID '{0}' is duplicated. +templatecontrol_placeholderid_duplicated= TContentPlaceHolder ID '{0}' is duplicated. +templatecontrol_directive_invalid = {0}.{1} can only accept a static text string through a template directive. +templatecontrol_placeholder_inexistent = TContent '{0}' does not have a matching TContentPlaceHolder. + +page_form_duplicated = A page can contain at most one TForm. Use regular HTML form tags for the rest forms. +page_isvalid_unknown = TPage.IsValid has not been evaluated yet. +page_postbackcontrol_invalid = Unable to determine postback control '{0}'. +page_control_outofform = {0} '{1}' must be enclosed within TForm. +page_head_duplicated = A page can contain at most one THead. +page_head_required = A THead control is needed in page template in order to render CSS and js in the HTML head section. +page_statepersister_invalid = Page state persister must implement IPageStatePersister interface. + +csmanager_pradoscript_invalid = Unknown Prado script library name '{0}'. +csmanager_invalid_packages = Unkownn packages '{1}' for javascript packages defined in '{0}'. Valid packages are '{2}'. + +contentplaceholder_id_required = TContentPlaceHolder must have an ID. + +content_id_required = TContent must have an ID. + +controlcollection_control_required = TControlList can only accept strings or TControl objects. + +webcontrol_accesskey_invalid = {0}.AccessKey '{1}' is invalid. It must be a single character only. +webcontrol_style_invalid = {0}.Style must take string value only. + +listcontrol_selection_invalid = {0} has an invalid selection that is set before performing databinding. +listcontrol_selectedindex_invalid = {0}.SelectedIndex has an invalid value {1}. +listcontrol_selectedvalue_invalid = {0}.SelectedValue has an invalid value '{1}'. +listcontrol_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2} +listcontrol_multiselect_unsupported = {0} does not support multiselection. + +label_associatedcontrol_invalid = TLabel.AssociatedControl '{0}' cannot be found. + +hiddenfield_focus_unsupported = THiddenField does not support setting input focus. +hiddenfield_theming_unsupported = THiddenField does not support theming. +hiddenfield_skinid_unsupported = THiddenField does not support control skin. + +panel_defaultbutton_invalid = TPanel.DefaultButton '{0}' does not refer to an existing button control. + +tablestyle_cellpadding_invalid = TTableStyle.CellPadding must take an integer equal to or greater than -1. +tablestyle_cellspacing_invalid = TTableStyle.CellSpacing must take an integer equal to or greater than -1. + +pagestatepersister_pagestate_corrupted = Page state is corrupted. + +sessionpagestatepersister_pagestate_corrupted = Page state is corrupted. +sessionpagestatepersister_historysize_invalid = TSessionPageStatePersister.History must be an integer greater than 0. + +listitemcollection_item_invalid = TListItemCollection can only take strings or TListItem objects. + +dropdownlist_selectedindices_unsupported= TDropDownList.SelectedIndices is read-only. + +bulletedlist_autopostback_unsupported = TBulletedList.AutoPostBack is read-only. +bulletedlist_selectedindex_unsupported = TBulletedList.SelectedIndex is read-only. +bulletedlist_selectedindices_unsupported= TBulletedList.SelectedIndices is read-only. +bulletedlist_selectedvalue_unsupported = TBulletedList.SelectedValue is read-only. + +radiobuttonlist_selectedindices_unsupported = TRadioButtonList.SelectedIndices is read-only. + +logrouter_configfile_invalid = TLogRouter.ConfigFile '{0}' does not exist. +logrouter_routeclass_required = Class attribute is required in configuration. +logrouter_routetype_required = Log route must be an instance of TLogRoute or its derived class. + +filelogroute_logpath_invalid = TFileLogRoute.LogPath '{0}' must be a directory in namespace format and must be writable by the Web server process. +filelogroute_maxfilesize_invalid = TFileLogRoute.MaxFileSize must be greater than 0. +filelogroute_maxlogfiles_invalid = TFileLogRoute.MaxLogFiles must be greater than 0. + +emaillogroute_sentfrom_required = TEmailLogRoute.SentFrom cannot be empty. + +repeatinfo_repeatcolumns_invalid = TRepeatInfo.RepeatColumns must be no less than 0. + +basevalidator_controltovalidate_invalid = {0}.ControlToValidate is empty or contains an invalid control ID path. +basevalidator_validatable_required = {0}.ControlToValidate must point to a control implementing IValidatable interface. +basevalidator_forcontrol_unsupported = {0}.ForControl is not supported. + +comparevalidator_controltocompare_invalid = TCompareValidator.ControlToCompare contains an invalid control ID path. + +listcontrolvalidator_invalid_control = {0}.ControlToValidate contains an invalid TListControl ID path, "{1}" is a {2}. + +repeater_template_required = TRepeater.{0} requires a template instance implementing ITemplate interface. +repeater_itemtype_unknown = Unknow repeater item type {0}. +repeateritemcollection_item_invalid = TRepeaterItemCollection can only accept objects that are instance of TControl or its descendant class. + +datalist_template_required = TDataList.{0} requires a template instance implementing ITemplate interface. +datalistitemcollection_datalistitem_required = TDataListItemCollection can only accept TDataListItem objects. + +datagrid_template_required = TDataGrid.{0} requires a template instance implementing ITemplate interface. +templatecolumn_template_required = TTemplateColumn.{0} requires a template instance implementing ITemplate interface. +datagrid_currentpageindex_invalid = TDataGrid.CurrentPageIndex must be no less than 0. +datagrid_pagesize_invalid = TDataGrid.PageSize must be greater than 0. +datagrid_virtualitemcount_invalid = TDataGrid.VirtualItemCount must be no less than 0. +datagriditemcollection_datagriditem_required = TDataGridItemCollection can only accept TDataGridItem objects. +datagridcolumncollection_datagridcolumn_required = TDataGridColumnCollection can only accept TDataGridColumn objects. +datagridpagerstyle_pagebuttoncount_invalid = TDataGridPagerStyle.PageButtonCount must be greater than 0. + +datafieldaccessor_data_invalid = TDataFieldAccessor is trying to evaluate a field value of an invalid data. Make sure the data is an array, TMap, TList, or object that contains the specified field '{0}'. +datafieldaccessor_datafield_invalid = TDataFieldAccessor is trying to evaluate data value of an unknown field '{0}'. + +tablerowcollection_tablerow_required = TTableRowCollection can only accept TTableRow objects. + +tablecellcollection_tablerow_required = TTableCellCollection can only accept TTableCell objects. + +multiview_view_required = TMultiView can only accept TView as child. +multiview_activeviewindex_invalid = TMultiView.ActiveViewIndex has an invalid index '{0}'. +multiview_view_inexistent = TMultiView cannot find the specified view. +multiview_viewid_invalid = TMultiView cannot find the view '{0}' to switch to. + +viewcollection_view_required = TViewCollection can only accept TView as its element. + +view_visible_readonly = TView.Visible is read-only. Use TView.Active to toggle its visibility. + +wizard_step_invalid = The step to be activated cannot be found in wizard step collection. +wizard_command_invalid = Invalid wizard navigation command '{0}'. + +table_tablesection_outoforder = TTable table sections must be in the order of: Header, Body and Footer. + +completewizardstep_steptype_readonly = TCompleteWizardStep.StepType is read-only. + +wizardstepcollection_wizardstep_required = TWizardStepCollection can only accept objects of TWizardStep or its derived classes. + +texthighlighter_stylesheet_invalid = Unable to find the stylesheet file for TTextHighlighter. + +hotspotcollection_hotspot_required = THotSpotCollection can only accept instance of THotSpot or its derived classes. + +htmlarea_textmode_readonly = THtmlArea.TextMode is read-only. +htmlarea_tarfile_invalid = THtmlArea is unable to locate the TinyMCE tar file. + +parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile is not changeable because the module is already initialized. +parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' is invalid. Make sure it is in namespace format and the file extension is '.xml'. +parametermodule_parameterid_required = Parameter element must have 'id' attribute. + +datagridcolumn_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character. +datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2} + +outputcache_cachemoduleid_invalid = TOutputCache.CacheModuleID is set with an invalid cache module ID {0}. Either the module does not exist or does not implement ICache interface. +outputcache_duration_invalid = {0}.Duration must be an integer no less than 0. + +stack_data_not_iterable = TStack can only fetch data from an array or a traversable object. +stack_empty = TStack is empty. + +queue_data_not_iterable = TQueue can only fetch data from an array or a traversable object. +queue_empty = TQueue is empty. + +pager_pagebuttoncount_invalid = TPager.PageButtonCount must be an integer no less than 1. +pager_currentpageindex_invalid = TPager.CurrentPageIndex is out of range. +pager_pagecount_invalid = TPager.PageCount cannot be smaller than 0. +pager_controltopaginate_invalid = TPager.ControlToPaginate {0} must be a valid ID path pointing to a TDataBoundControl-derived control. + +databoundcontrol_pagesize_invalid = {0}.PageSize must be an integer no smaller than 1. +databoundcontrol_virtualitemcount_invalid = {0}.VirtualItemCount must be an integer no smaller than 0. +databoundcontrol_currentpageindex_invalid = {0}.CurrentPageIndex is out of range. +databoundcontrol_datasource_invalid = {0}.DataSource is not valid. +databoundcontrol_datasourceid_inexistent = databoundcontrol_datasourceid_inexistent. +databoundcontrol_datasourceid_invalid = databoundcontrol_datasourceid_invalid +databoundcontrol_datamember_invalid = databoundcontrol_datamember_invalid + +clientscript_invalid_file_position = Invalid file position '{1}' for TClientScript control '{0}', must be 'Head', 'Here' or 'Begin'. +clientscript_invalid_package_path = Invalid PackagePath '{0}' for TClientScript control '{1}'. + +tdatepicker_autopostback_unsupported = '{0}' does not support AutoPostBack. +globalization_cache_path_failed = Unable to create translation message cache path '{0}'. Make sure the parent directory exists and is writable by the Web process. +globalization_source_path_failed = Unable to create translation message path '{0}'. Make sure the parent directory exists and is writable by the Web process. +callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_callback_options = '{1}' is not a valid TCallbackOptions control for Callback control '{0}'. +callback_invalid_clientside_options = Callback ClientSide property must be either a string that is the ID of a TCallbackOptions control or an instance of TCallbackClientSideOptions.======= +callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_handler = Invalid callback handler, control {0} must implement ICallbackEventHandler. +callback_invalid_target = Invalid callback target, no such control with ID {0}. + +callback_interval_be_positive = Interval for TCallbackTimer "{0}" must be strictly greater than zero seconds. +callback_decay_be_not_negative = Decay rate for TCallbackTimer "{0}" must be not negative. + +callback_no_autopostback = Control "{0}" can not enable AutoPostBack. + +xmltransform_xslextension_required = TXmlTransform requires the PHP's XSL extension. +xmltransform_transformpath_invalid = TXmlTransform.TransformPath '{0}' is invalid. +xmltransform_documentpath_invalid = TXmlTransform.DocumentPath '{0}' is invalid. +xmltransform_transform_required = Either TransformContent or TransformPath property must be set for TXmlTransform. + +ttriggeredcallback_invalid_controlid = ControlID property for '{0}' must not be empty. +tactivecustomvalidator_clientfunction_unsupported = {0} does not support client side validator function. + +dbconnection_open_failed = TDbConnection failed to establish DB connection: {0} +dbconnection_connection_inactive = TDbConnection is inactive. +dbconnection_unsupported_driver_charset = Database driver '{0}' doesn't support setting charset. + +dbcommand_prepare_failed = TDbCommand failed to prepare the SQL statement "{1}": {0} +dbcommand_execute_failed = TDbCommand failed to execute the SQL statement "{1}": {0} +dbcommand_query_failed = TDbCommand failed to execute the query SQL "{1}": {0} +dbcommand_column_empty = TDbCommand returned an empty result and could not obtain the scalar. +dbdatareader_rewind_invalid = TDbDataReader is a forward-only stream. It can only be traversed once. +dbtransaction_transaction_inactive = TDbTransaction is inactive. + +dbcommandbuilder_value_must_not_be_null = Property {0} must not be null as defined by column '{2}' in table '{1}'. + +dbcommon_invalid_table_name = Database table '{0}' not found. Error message: {1}. +dbcommon_invalid_identifier_name = Invalid database identifier name '{0}', see {1} for details. +dbtableinfo_invalid_column_name = Invalid column name '{0}' for database table '{1}'. +dbmetadata_invalid_table_view = Invalid table/view name '{0}', or that table/view '{0}' contains no accessible column/field definitions. +dbmetadata_requires_php_version = PHP version {1} or later is required for using {0} database. + +dbtablegateway_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria. +dbtablegateway_no_primary_key_found = Table '{0}' does not contain any primary key fields. +dbtablegateway_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'. +dbtablegateway_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'. +dbtablegateway_mismatch_args_exception = TTableGateway finder method '{0}' expects {1} parameters but found only {2} parameters instead. +dbtablegateway_mismatch_column_name = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'. +dbtablegateway_invalid_table_info = Table must be a string or an instanceof TDbTableInfo. + +directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} does not refer to a valid directory. +cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList. + +soapservice_configfile_invalid = TSoapService.ConfigFile '{0}' does not exist. Note, it has to be specified in a namespace format and the file extension must be '.xml'. +soapservice_request_invalid = SOAP server '{0}' not found. +soapservice_serverid_required = element must have 'id' attribute. +soapservice_serverid_duplicated = SOAP server ID '{0}' is duplicated. +soapserver_id_invalid = Invalid SOAP server ID '{0}'. It should not end with '.wsdl'. +soapserver_version_invalid = Invalid SOAP version '{0}'. It must be either '1.1' or '1.2'. + +dbusermanager_userclass_required = TDbUserManager.UserClass is required. +dbusermanager_userclass_invalid = TDbUserManager.UserClass '{0}' is not a valid user class. The class must extend TDbUser. +dbusermanager_connectionid_invalid = TDbUserManager.ConnectionID '{0}' does not point to a valid TDataSourceConfig module. +dbusermanager_connectionid_required = TDbUserManager.ConnectionID is required. + +feedservice_id_required = TFeedService requires 'id' attribute in its feed elements. +feedservice_feedtype_invalid = The class feed '{0}' must implement IFeedContentProvider interface. +feedservice_class_required = TFeedService requires 'class' attribute in its feed elements. +feedservice_feed_unknown = Unknown feed '{0}' requested. + +tabviewcollection_tabview_required = TTabPanel can only accept TTabView as child. +tabpanel_activeviewid_invalid = TTabPanel.ActiveViewID has an invalid ID '{0}'. +tabpanel_activeviewindex_invalid = TTabPanel.ActiveViewIndex has an invalid Index '{0}'. +tabpanel_view_inexistent = TTabPanel cannot find the specified view. + +cachesession_cachemoduleid_required = TCacheHttpSession.CacheModuleID is required. +cachesession_cachemodule_inexistent = TCacheHttpSession.CacheModuleID '{0}' points to a non-existent module. +cachesession_cachemodule_invalid = TCacheHttpSession.CacheModuleID '{0}' points to a module that does not implement ICache interface. + +urlmapping_urlmappingpattern_required = TUrlMapping can only contain TUrlMappingPattern or its child classes. +urlmapping_global_required = TUrlMapping must be configured as a global module. +urlmapping_configfile_inexistent = TUrlMapping.ConfigFile '{0}' is not a file. +urlmapping_configfile_invalid = TUrlMapping.ConfigFile '{0}' must point to an XML file in namespace format. + +urlmappingpattern_serviceparameter_required = TUrlMappingPattern.ServiceParameter is required for pattern '{0}'. diff --git a/gui/baculum/framework/Exceptions/messages/messages.txt b/gui/baculum/framework/Exceptions/messages/messages.txt new file mode 100644 index 0000000000..bcfc7f97e1 --- /dev/null +++ b/gui/baculum/framework/Exceptions/messages/messages.txt @@ -0,0 +1,503 @@ +prado_application_singleton_required = Prado.Application must only be set once. +prado_component_unknown = Unknown component type '{0}'. This may be caused by the following parsing error in the {0} class file: {1} +prado_using_invalid = '{0}' is not a valid namespace to be used. Make sure '.*' is appended if you want to use a namespace referring to a directory. +prado_alias_redefined = Alias '{0}' cannot be redefined. +prado_alias_invalid = Alias '{0}' refers to an invalid path '{1}'. Only existing directories can be aliased. +prado_aliasname_invalid = Alias '{0}' contains invalid character '.'. + +component_property_undefined = Component property '{0}.{1}' is not defined. +component_property_readonly = Component property '{0}.{1}' is read-only. +component_event_undefined = Component event '{0}.{1}' is not defined. +component_method_undefined = Component method '{0}.{1}' is not defined. +component_eventhandler_invalid = Component event '{0}.{1}' is attached with an invalid event handler '{2}'. +component_expression_invalid = Component '{0}' is evaluating an invalid expression '{1}' : {2}. +component_statements_invalid = Component '{0}' is evaluating invalid PHP statements '{1}' : {2}. +component_class_behavior_defined = Component '{0}' already has a class behavior of '{1}'. +component_not_a_behavior = Component '{0}' is being added as behavior is not a TBaseBehavior. +component_no_tcomponent_class_behaviors = TComponent cannot have class behaviors attached due to recursion. +component_no_class_provided_nor_late_binding = Adding or Removing Class Behaviors must have PHP feature Late Static Binding or a class provided as a parameter + +propertyvalue_enumvalue_invalid = Value '{0}' is a not valid enumeration value ({1}). + +list_index_invalid = Index '{0}' is out of range. +list_item_inexistent = The item cannot be found in the list. +list_data_not_iterable = Data must be either an array or an object implementing Traversable interface. +list_readonly = {0} is read-only. + +map_addition_disallowed = The new item cannot be added to the map. +map_item_unremovable = The item cannot be removed from the map. +map_data_not_iterable = Data must be either an array or an object implementing Traversable interface. +map_readonly = {0} is read-only. + +application_includefile_invalid = Unable to find application configuration {0}. Make sure it is in namespace format and the file ends with ".xml" or ".php". +application_basepath_invalid = Application base path '{0}' does not exist or is not a directory. +application_runtimepath_invalid = Application runtime path '{0}' does not exist or is not writable by Web server process. +application_service_invalid = Service '{0}' must implement IService interface. +application_service_unknown = Requested service '{0}' is not defined. +application_unavailable = Application is unavailable at this time. +application_service_unavailable = Service '{0}' is unavailable at this time. +application_moduleid_duplicated = Application module ID '{0}' is not unique. +application_runtimepath_failed = Unable to create runtime path '{0}'. Make sure the parent directory exists and is writable by the Web process. + +appconfig_aliaspath_invalid = Application configuration uses an invalid file path "{1}". +appconfig_alias_invalid = Application configuration element must have an "id" attribute and a "path" attribute. +appconfig_alias_redefined = Application configuration cannot be redefined. +appconfig_using_invalid = Application configuration element must have a "namespace" attribute. +appconfig_moduleid_required = Application configuration element must have an "id" attribute. +appconfig_moduletype_required = Application configuration must have a "class" attribute. +appconfig_serviceid_required = Application configuration element must have an "id" attribute. +appconfig_servicetype_required = Application configuration must have a "class" attribute. +appconfig_parameterid_required = Application configuration element must have an "id" attribute. +appconfig_includefile_required = Application configuration element must have a "file" attribute. +appconfig_paths_invalid = Application configuration cannot contain element <{0}>. +appconfig_modules_invalid = Application configuration cannot contain element <{0}>. +appconfig_services_invalid = Application configuration cannot contain element <{0}>. +appconfig_parameters_invalid = Application configuration cannot contain element <{0}>. +appconfig_tag_invalid = Application configuration cannot contain element <{0}>. + +securitymanager_validationkey_invalid = TSecurityManager.ValidationKey must not be empty. +securitymanager_encryptionkey_invalid = TSecurityManager.EncryptionKey must not be empty. +securitymanager_mcryptextension_required = Mcrypt PHP extension is required in order to use TSecurityManager's encryption feature. +securitymanager_mcryptextension_initfailed = TSecurityManager failed to initialize the mcrypt module. + +uri_format_invalid = '{0}' is not a valid URI. + +httprequest_separator_invalid = THttpRequest.UrlParamSeparator can only contain a single character. +httprequest_urlmanager_inexist = THttpRequest.UrlManager '{0}' does not point to an existing module. +httprequest_urlmanager_invalid = THttpRequest.UrlManager '{0}' must point to a module extending from TUrlManager. + +httpcookiecollection_httpcookie_required = THttpCookieCollection can only accept THttpCookie objects. + +httpresponse_bufferoutput_unchangeable = THttpResponse.BufferOutput cannot be modified after THttpResponse is initialized. +httpresponse_file_inexistent = THttpResponse cannot send file '{0}'. The file does not exist. + +httpsession_sessionid_unchangeable = THttpSession.SessionID cannot be modified after the session is started. +httpsession_sessionname_unchangeable = THttpSession.SessionName cannot be modified after the session is started. +httpsession_sessionname_invalid = THttpSession.SessionName must contain alphanumeric characters only. +httpsession_savepath_unchangeable = THttpSession.SavePath cannot be modified after the session is started. +httpsession_savepath_invalid = THttpSession.SavePath '{0}' is invalid. +httpsession_storage_unchangeable = THttpSession.Storage cannot be modified after the session is started. +httpsession_cookiemode_unchangeable = THttpSession.CookieMode cannot be modified after the session is started. +httpsession_autostart_unchangeable = THttpSession.AutoStart cannot be modified after the session module is initialized. +httpsession_gcprobability_unchangeable = THttpSession.GCProbability cannot be modified after the session is started. +httpsession_gcprobability_invalid = THttpSession.GCProbability must be an integer between 0 and 100. +httpsession_transid_unchangeable = THttpSession.UseTransparentSessionID cannot be modified after the session is started. +httpsession_transid_cookieonly = THttpSession.UseTransparentSessionID cannot be set when THttpSession.CookieMode is set to Only. +httpsession_maxlifetime_unchangeable = THttpSession.Timeout cannot be modified after the session is started. + +assetmanager_basepath_invalid = TAssetManager.BasePath '{0}' is invalid. Make sure it is in namespace form and points to a directory writable by the Web server process. +assetmanager_basepath_unchangeable = TAssetManager.BasePath cannot be modified after the module is initialized. +assetmanager_baseurl_unchangeable = TAssetManager.BaseUrl cannot be modified after the module is initialized. +assetmanager_filepath_invalid = TAssetManager is publishing an invalid file '{0}'. +assetmanager_tarchecksum_invalid = TAssetManager is publishing a tar file with invalid checksum '{0}'. +assetmanager_tarfile_invalid = TAssetManager is publishing an invalid tar file '{0}'. +assetmanager_source_directory_invalid = TAssetManager is copying an invalid directory '{0}'. + +cache_primary_duplicated = At most one primary cache module is allowed. {0} is trying to register as another primary cache. +sqlitecache_extension_required = TSqliteCache requires SQLite PHP extension. +sqlitecache_dbfile_required = TSqliteCache.DbFile is required. +sqlitecache_connection_failed = TSqliteCache database connection failed. {0}. +sqlitecache_table_creation_failed = TSqliteCache failed to create cache database. {0}. +sqlitecache_dbfile_unchangeable = TSqliteCache.DbFile cannot be modified after the module is initialized. +sqlitecache_dbfile_invalid = TSqliteCache.DbFile is invalid. Make sure it is in a proper namespace format. + +memcache_extension_required = TMemCache requires memcache PHP extension. +memcache_connection_failed = TMemCache failed to connect to memcache server {0}:{1}. +memcache_host_unchangeable = TMemCache.Host cannot be modified after the module is initialized. +memcache_port_unchangeable = TMemCache.Port cannot be modified after the module is initialized. + +apccache_extension_required = TAPCCache requires APC PHP extension. +apccache_extension_not_enabled = TAPCCache need apc.enabled = 1 in php.ini in order to work. +apccache_extension_not_enabled_cli = TAPCCache need apc.enable_cli = 1 in php.ini in order to work with PHP from the command line. + +eacceleratorcache_extension_required = TEACache requires eAccellerator PHP extension. + +errorhandler_errortemplatepath_invalid = TErrorHandler.ErrorTemplatePath '{0}' is invalid. Make sure it is in namespace form and points to a valid directory containing error template files. + +pageservice_page_unknown = Page '{0}' Not Found +pageservice_pageclass_unknown = Page class '{0}' is unknown. +pageservice_basepath_invalid = TPageService.BasePath '{0}' is not a valid directory. +pageservice_page_required = Page Name Required +pageservice_defaultpage_unchangeable = TPageService.DefaultPage cannot be modified after the service is initialized. +pageservice_basepath_unchangeable = TPageService.BasePath cannot be modified after the service is initialized. +pageservice_pageclass_invalid = Page class {0} is invalid. It should be TPage or extend from TPage. +pageservice_includefile_invalid = Unable to find page service configuration {0}. Make sure it is in namespace format and the file ends with ".xml" or ".php". + +pageserviceconf_file_invalid = Unable to open page directory configuration file '{0}'. +pageserviceconf_aliaspath_invalid = uses an invalid file path "{1}" in page directory configuration file '{2}'. +pageserviceconf_alias_invalid = element must have an "id" attribute and a "path" attribute in page directory configuration file '{0}'. +pageserviceconf_using_invalid = element must have a "namespace" attribute in page directory configuration file '{0}'. +pageserviceconf_module_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_moduletype_required = must have a "class" attribute in page directory configuration file '{1}'. +pageserviceconf_parameter_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_page_invalid = element must have an "id" attribute in page directory configuration file '{0}'. +pageserviceconf_includefile_required = Page configuration element must have a "file" attribute. + +template_closingtag_unexpected = Unexpected closing tag '{0}' is found. +template_closingtag_expected = Closing tag '{0}' is expected. +template_directive_nonunique = Directive '<%@ ... %>' must appear at the beginning of the template and can appear at most once. +template_comments_forbidden = Template comments are not allowed within property tags. +template_matching_unexpected = Unexpected matching. +template_property_unknown = {0} has no property called '{1}'. +template_event_unknown = {0} has no event called '{1}'. +template_property_readonly = {0} has a read-only property '{1}'. +template_event_forbidden = {0} is a non-control component. No handler can be attached to its event '{1}' in a template. +template_databind_forbidden = {0} is a non-control component. Expressions cannot be bound to its property '{1}'. +template_component_required = '{0}' is not a component. Only components can appear in a template. +template_format_invalid = Invalid template syntax: {0} +template_property_duplicated = Property {0} is configured twice or more. +template_eventhandler_invalid = {0}.{1} can only accept a static string. +template_controlid_invalid = {0}.ID can only accept a static text string. +template_controlskinid_invalid = {0}.SkinID can only accept a static text string. +template_content_unexpected = Unexpected content is encountered when instantiating template: {0}. +template_include_invalid = Invalid template inclusion. Make sure {0} is a valid namespace pointing to an existing template file whose extension is .tpl. +template_tag_unexpected = Initialization for property {0} contains an unknown tag type {1}. + +xmldocument_file_read_failed = TXmlDocument is unable to read file '{0}'. +xmldocument_file_write_failed = TXmlDocument is unable to write file '{0}'. + +xmlelementlist_xmlelement_required = TXmlElementList can only accept TXmlElement objects. + +authorizationrule_action_invalid = TAuthorizationRule.Action can only take 'allow' or 'deny' as the value. +authorizationrule_verb_invalid = TAuthorizationRule.Verb can only take 'get' or 'post' as the value. + +authorizationrulecollection_authorizationrule_required = TAuthorizationRuleCollection can only accept TAuthorizationRule objects. + +usermanager_userfile_invalid = TUserManager.UserFile '{0}' is not a valid file. +usermanager_userfile_unchangeable = TUserManager.UserFile cannot be modified. The user module has been initialized already. + +authmanager_usermanager_required = TAuthManager.UserManager must be assigned a value. +authmanager_usermanager_inexistent = TAuthManager.UserManager '{0}' does not refer to an ID of application module. +authmanager_usermanager_invalid = TAuthManager.UserManager '{0}' does not refer to a valid TUserManager application module. +authmanager_usermanager_unchangeable = TAuthManager.UserManager cannot be modified after the module is initialized. +authmanager_session_required = TAuthManager requires a session application module. + +thememanager_service_unavailable = TThemeManager requires TPageService to be available. This error often occurs when you configure TThemeManager outside of the page service configuration. +thememanager_basepath_invalid = TThemeManager.BasePath '{0}' is not a valid path alias. Make sure you have defined this alias in configuration and it points to a valid directory. +thememanager_basepath_invalid2 = TThemeManager.BasePath '{0}' is not a valid directory. +thememanager_basepath_unchangeable = TThemeManager.BasePath cannot be modified after the module is initialized. + +theme_baseurl_required = TThemeManager.BasePath is required. By default, a directory named 'themes' under the directory containing the application entry script is assumed. +theme_path_inexistent = Theme path '{0}' does not exist. +theme_control_nested = Skin for control type '{0}' in theme '{1}' cannot be within another skin. +theme_skinid_duplicated = SkinID '{0}.{1}' is duplicated in theme '{2}'. +theme_databind_forbidden = Databind cannot be used in theme '{0}' for control skin '{1}.{2}' about property '{3}'. +theme_property_readonly = Skin is being applied to a read-only control property '{0}.{1}'. +theme_property_undefined = Skin is being applied to an inexistent control property '{0}.{1}'. +theme_tag_unexpected = Initialization for property {0} contains an unknown tag type {1}. + +control_object_reregistered = Duplicated object ID '{0}' found. +control_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character. +control_skinid_unchangeable = {0}.SkinID cannot be modified after a skin has been applied to the control or the child controls have been created. +control_enabletheming_unchangeable = {0}.EnableTheming cannot be modified after the child controls have been created. +control_stylesheet_applied = StyleSheet skin has already been applied to {0}. +control_id_nonunique = {0}.ID '{1}' is not unique among all controls under the same naming container. + +templatecontrol_mastercontrol_invalid = Master control must be of type TTemplateControl or a child class. +templatecontrol_mastercontrol_required = Control '{0}' requires a master control since the control uses TContent. +templatecontrol_contentid_duplicated = TContent ID '{0}' is duplicated. +templatecontrol_placeholderid_duplicated= TContentPlaceHolder ID '{0}' is duplicated. +templatecontrol_directive_invalid = {0}.{1} can only accept a static text string through a template directive. +templatecontrol_placeholder_inexistent = TContent '{0}' does not have a matching TContentPlaceHolder. + +page_form_duplicated = A page can contain at most one TForm. Use regular HTML form tags for the rest forms. +page_isvalid_unknown = TPage.IsValid has not been evaluated yet. +page_postbackcontrol_invalid = Unable to determine postback control '{0}'. +page_control_outofform = {0} '{1}' must be enclosed within TForm. +page_head_duplicated = A page can contain at most one THead. +page_head_required = A THead control is needed in page template in order to render CSS and js in the HTML head section. +page_statepersister_invalid = Page state persister must implement IPageStatePersister interface. +page_csmanagerclass_invalid = ClientScriptManager class '{0}' must be an instance of TClientScriptManager. + +csmanager_pradoscript_invalid = Unknown Prado script library name '{0}'. +csmanager_invalid_packages = Unkownn packages '{1}' for javascript packages defined in '{0}'. Valid packages are '{2}'. + +contentplaceholder_id_required = TContentPlaceHolder must have an ID. + +content_id_required = TContent must have an ID. + +controlcollection_control_required = TControlList can only accept strings or TControl objects. + +webcontrol_accesskey_invalid = {0}.AccessKey '{1}' is invalid. It must be a single character only. +webcontrol_style_invalid = {0}.Style must take string value only. + +listcontrol_selection_invalid = {0} has an invalid selection that is set before performing databinding. +listcontrol_selectedindex_invalid = {0}.SelectedIndex has an invalid value {1}. +listcontrol_selectedvalue_invalid = {0}.SelectedValue has an invalid value '{1}'. +listcontrol_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2} +listcontrol_multiselect_unsupported = {0} does not support multiselection. + +label_associatedcontrol_invalid = TLabel.AssociatedControl '{0}' cannot be found. + +hiddenfield_focus_unsupported = THiddenField does not support setting input focus. +hiddenfield_theming_unsupported = THiddenField does not support theming. +hiddenfield_skinid_unsupported = THiddenField does not support control skin. + +panel_defaultbutton_invalid = TPanel.DefaultButton '{0}' does not refer to an existing button control. + +tablestyle_cellpadding_invalid = TTableStyle.CellPadding must take an integer equal to or greater than -1. +tablestyle_cellspacing_invalid = TTableStyle.CellSpacing must take an integer equal to or greater than -1. + +pagestatepersister_pagestate_corrupted = Page state is corrupted. + +sessionpagestatepersister_pagestate_corrupted = Page state is corrupted. +sessionpagestatepersister_historysize_invalid = TSessionPageStatePersister.History must be an integer greater than 0. + +listitemcollection_item_invalid = TListItemCollection can only take strings or TListItem objects. + +dropdownlist_selectedindices_unsupported= TDropDownList.SelectedIndices is read-only. + +bulletedlist_autopostback_unsupported = TBulletedList.AutoPostBack is read-only. +bulletedlist_selectedindex_unsupported = TBulletedList.SelectedIndex is read-only. +bulletedlist_selectedindices_unsupported= TBulletedList.SelectedIndices is read-only. +bulletedlist_selectedvalue_unsupported = TBulletedList.SelectedValue is read-only. + +radiobuttonlist_selectedindices_unsupported = TRadioButtonList.SelectedIndices is read-only. + +logrouter_configfile_invalid = TLogRouter.ConfigFile '{0}' does not exist. +logrouter_routeclass_required = Class attribute is required in configuration. +logrouter_routetype_required = Log route must be an instance of TLogRoute or its derived class. + +filelogroute_logpath_invalid = TFileLogRoute.LogPath '{0}' must be a directory in namespace format and must be writable by the Web server process. +filelogroute_maxfilesize_invalid = TFileLogRoute.MaxFileSize must be greater than 0. +filelogroute_maxlogfiles_invalid = TFileLogRoute.MaxLogFiles must be greater than 0. + +emaillogroute_sentfrom_required = TEmailLogRoute.SentFrom cannot be empty. + +repeatinfo_repeatcolumns_invalid = TRepeatInfo.RepeatColumns must be no less than 0. + +basevalidator_controltovalidate_invalid = {0}.ControlToValidate is empty or contains an invalid control ID path. +basevalidator_validatable_required = {0}.ControlToValidate must point to a control implementing IValidatable interface. +basevalidator_forcontrol_unsupported = {0}.ForControl is not supported. + +comparevalidator_controltocompare_invalid = TCompareValidator.ControlToCompare contains an invalid control ID path. + +listcontrolvalidator_invalid_control = {0}.ControlToValidate contains an invalid TListControl ID path, "{1}" is a {2}. + +repeater_template_required = TRepeater.{0} requires a template instance implementing ITemplate interface. +repeater_itemtype_unknown = Unknown repeater item type {0}. +repeateritemcollection_item_invalid = TRepeaterItemCollection can only accept objects that are instance of TControl or its descendant class. + +datalist_template_required = TDataList.{0} requires a template instance implementing ITemplate interface. +datalistitemcollection_datalistitem_required = TDataListItemCollection can only accept TDataListItem objects. + +datagrid_template_required = TDataGrid.{0} requires a template instance implementing ITemplate interface. +templatecolumn_template_required = TTemplateColumn.{0} requires a template instance implementing ITemplate interface. +datagrid_currentpageindex_invalid = TDataGrid.CurrentPageIndex must be no less than 0. +datagrid_pagesize_invalid = TDataGrid.PageSize must be greater than 0. +datagrid_virtualitemcount_invalid = TDataGrid.VirtualItemCount must be no less than 0. +datagriditemcollection_datagriditem_required = TDataGridItemCollection can only accept TDataGridItem objects. +datagridcolumncollection_datagridcolumn_required = TDataGridColumnCollection can only accept TDataGridColumn objects. +datagridpagerstyle_pagebuttoncount_invalid = TDataGridPagerStyle.PageButtonCount must be greater than 0. + +datafieldaccessor_data_invalid = TDataFieldAccessor is trying to evaluate a field value of an invalid data. Make sure the data is an array, TMap, TList, or object that contains the specified field '{0}'. +datafieldaccessor_datafield_invalid = TDataFieldAccessor is trying to evaluate data value of an unknown field '{0}': {1}. + +tablerowcollection_tablerow_required = TTableRowCollection can only accept TTableRow objects. + +tablecellcollection_tablerow_required = TTableCellCollection can only accept TTableCell objects. + +multiview_view_required = TMultiView can only accept TView as child. +multiview_activeviewindex_invalid = TMultiView.ActiveViewIndex has an invalid index '{0}'. +multiview_view_inexistent = TMultiView cannot find the specified view. +multiview_viewid_invalid = TMultiView cannot find the view '{0}' to switch to. + +viewcollection_view_required = TViewCollection can only accept TView as its element. + +view_visible_readonly = TView.Visible is read-only. Use TView.Active to toggle its visibility. + +wizard_step_invalid = The step to be activated cannot be found in wizard step collection. +wizard_command_invalid = Invalid wizard navigation command '{0}'. + +table_tablesection_outoforder = TTable table sections must be in the order of: Header, Body and Footer. + +completewizardstep_steptype_readonly = TCompleteWizardStep.StepType is read-only. + +wizardstepcollection_wizardstep_required = TWizardStepCollection can only accept objects of TWizardStep or its derived classes. + +texthighlighter_stylesheet_invalid = Unable to find the stylesheet file for TTextHighlighter. + +hotspotcollection_hotspot_required = THotSpotCollection can only accept instance of THotSpot or its derived classes. + +htmlarea_textmode_readonly = THtmlArea.TextMode is read-only. +htmlarea_tarfile_invalid = THtmlArea is unable to locate the TinyMCE tar file. + +parametermodule_parameterfile_unchangeable = TParameterModule.ParameterFile is not changeable because the module is already initialized. +parametermodule_parameterfile_invalid = TParameterModule.ParameterFile '{0}' is invalid. Make sure it is in namespace format and the file extension is '.xml' or '.php'. +parametermodule_parameterid_required = Parameter element must have 'id' attribute. + +datagridcolumn_id_invalid = {0}.ID '{1}' is invalid. Only alphanumeric and underline characters are allowed. The first character must be an alphabetic or underline character. +datagridcolumn_expression_invalid = {0} is evaluating an invalid expression '{1}' : {2} + +outputcache_cachemoduleid_invalid = TOutputCache.CacheModuleID is set with an invalid cache module ID {0}. Either the module does not exist or does not implement ICache interface. +outputcache_duration_invalid = {0}.Duration must be an integer no less than 0. + +stack_data_not_iterable = TStack can only fetch data from an array or a traversable object. +stack_empty = TStack is empty. + +queue_data_not_iterable = TQueue can only fetch data from an array or a traversable object. +queue_empty = TQueue is empty. + +pager_pagebuttoncount_invalid = TPager.PageButtonCount must be an integer no less than 1. +pager_currentpageindex_invalid = TPager.CurrentPageIndex is out of range. +pager_pagecount_invalid = TPager.PageCount cannot be smaller than 0. +pager_controltopaginate_invalid = TPager.ControlToPaginate {0} must be a valid ID path pointing to a TDataBoundControl-derived control. + +databoundcontrol_pagesize_invalid = {0}.PageSize must be an integer no smaller than 1. +databoundcontrol_virtualitemcount_invalid = {0}.VirtualItemCount must be an integer no smaller than 0. +databoundcontrol_currentpageindex_invalid = {0}.CurrentPageIndex is out of range. +databoundcontrol_datasource_invalid = {0}.DataSource is not valid. +databoundcontrol_datasourceid_inexistent = databoundcontrol_datasourceid_inexistent. +databoundcontrol_datasourceid_invalid = databoundcontrol_datasourceid_invalid +databoundcontrol_datamember_invalid = databoundcontrol_datamember_invalid + +clientscript_invalid_file_position = Invalid file position '{1}' for TClientScript control '{0}', must be 'Head', 'Here' or 'Begin'. +clientscript_invalid_package_path = Invalid PackagePath '{0}' for TClientScript control '{1}'. + +tdatepicker_autopostback_unsupported = '{0}' does not support AutoPostBack. +globalization_cache_path_failed = Unable to create translation message cache path '{0}'. Make sure the parent directory exists and is writable by the Web process. +globalization_source_path_failed = Unable to create translation message path '{0}'. Make sure the parent directory exists and is writable by the Web process. +messagesource_connectionid_invalid = MessageSource_Database.source '{0}' does not point to a valid TDataSourceConfig module. +messagesource_connectionid_required = ConnectionID in MessageSource_Database.source is required. + +callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_callback_options = '{1}' is not a valid TCallbackOptions control for Callback control '{0}'. +callback_invalid_clientside_options = Callback ClientSide property must be either a string that is the ID of a TCallbackOptions control or an instance of TCallbackClientSideOptions.======= +callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_handler = Invalid callback handler, control {0} must implement ICallbackEventHandler. +callback_invalid_target = Invalid callback target, no such control with ID {0}. + +callback_interval_be_positive = Interval for TCallbackTimer "{0}" must be strictly greater than zero seconds. +callback_decay_be_not_negative = Decay rate for TCallbackTimer "{0}" must be not negative. + +callback_no_autopostback = Control "{0}" can not enable AutoPostBack. + +xmltransform_xslextension_required = TXmlTransform requires the PHP's XSL extension. +xmltransform_transformpath_invalid = TXmlTransform.TransformPath '{0}' is invalid. +xmltransform_documentpath_invalid = TXmlTransform.DocumentPath '{0}' is invalid. +xmltransform_transform_required = Either TransformContent or TransformPath property must be set for TXmlTransform. + +ttriggeredcallback_invalid_controlid = ControlID property for '{0}' must not be empty. +tactivecustomvalidator_clientfunction_unsupported = {0} does not support client side validator function. + +dbconnection_open_failed = TDbConnection failed to establish DB connection: {0} +dbconnection_connection_inactive = TDbConnection is inactive. +dbconnection_unsupported_driver_charset = Database driver '{0}' doesn't support setting charset. + +dbcommand_prepare_failed = TDbCommand failed to prepare the SQL statement "{1}": {0} +dbcommand_execute_failed = TDbCommand failed to execute the SQL statement "{1}": {0} +dbcommand_query_failed = TDbCommand failed to execute the query SQL "{1}": {0} +dbcommand_column_empty = TDbCommand returned an empty result and could not obtain the scalar. +dbdatareader_rewind_invalid = TDbDataReader is a forward-only stream. It can only be traversed once. +dbtransaction_transaction_inactive = TDbTransaction is inactive. + +dbcommandbuilder_value_must_not_be_null = Property {0} must not be null as defined by column '{2}' in table '{1}'. + +dbcommon_invalid_table_name = Database table '{0}' not found. Error message: {1}. +dbcommon_invalid_identifier_name = Invalid database identifier name '{0}', see {1} for details. +dbtableinfo_invalid_column_name = Invalid column name '{0}' for database table '{1}'. +dbmetadata_invalid_table_view = Invalid table/view name '{0}', or that table/view '{0}' contains no accessible column/field definitions. +dbmetadata_requires_php_version = PHP version {1} or later is required for using {0} database. + +dbtablegateway_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria. +dbtablegateway_no_primary_key_found = Table '{0}' does not contain any primary key fields. +dbtablegateway_missing_pk_values = Missing primary key values in forming IN(key1, key2, ...) for table '{0}'. +dbtablegateway_pk_value_count_mismatch = Composite key value count mismatch in forming IN( (key1, key2, ..), (key3, key4, ..)) for table '{0}'. +dbtablegateway_mismatch_args_exception = TTableGateway finder method '{0}' expects {1} parameters but found only {2} parameters instead. +dbtablegateway_mismatch_column_name = In dynamic __call() method '{0}', no matching columns were found, valid columns for table '{2}' are '{1}'. +dbtablegateway_invalid_table_info = Table must be a string or an instance of TDbTableInfo. + +directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} does not refer to a valid directory. +cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList. + +soapservice_configfile_invalid = TSoapService.ConfigFile '{0}' does not exist. Note, it has to be specified in a namespace format and the file extension must be '.xml' or '.php'. +soapservice_request_invalid = SOAP server '{0}' not found. +soapservice_serverid_required = element must have 'id' attribute. +soapservice_serverid_duplicated = SOAP server ID '{0}' is duplicated. +soapserver_id_invalid = Invalid SOAP server ID '{0}'. It should not end with '.wsdl'. +soapserver_version_invalid = Invalid SOAP version '{0}'. It must be either '1.1' or '1.2'. + +jsonservice_id_required = TJsonService requires 'id' attribute in its JSON elements. +jsonservice_response_type_invalid = JSON class {0} is invalid. It should be TJsonResponse or extend from TJsonResponse. +jsonservice_class_required = TJsonService requires 'class' attribute in its JSON elements. +jsonservice_provider_unknown = Unknown JSON provider '{0}' requested. + +dbusermanager_userclass_required = TDbUserManager.UserClass is required. +dbusermanager_userclass_invalid = TDbUserManager.UserClass '{0}' is not a valid user class. The class must extend TDbUser. +dbusermanager_connectionid_invalid = TDbUserManager.ConnectionID '{0}' does not point to a valid TDataSourceConfig module. +dbusermanager_connectionid_required = TDbUserManager.ConnectionID is required. + +feedservice_id_required = TFeedService requires 'id' attribute in its feed elements. +feedservice_feedtype_invalid = The class feed '{0}' must implement IFeedContentProvider interface. +feedservice_class_required = TFeedService requires 'class' attribute in its feed elements. +feedservice_feed_unknown = Unknown feed '{0}' requested. + +tabviewcollection_tabview_required = TTabPanel can only accept TTabView as child. +tabpanel_activeviewid_invalid = TTabPanel.ActiveViewID has an invalid ID '{0}'. +tabpanel_activeviewindex_invalid = TTabPanel.ActiveViewIndex has an invalid Index '{0}'. +tabpanel_view_inexistent = TTabPanel cannot find the specified view. + +cachesession_cachemoduleid_required = TCacheHttpSession.CacheModuleID is required. +cachesession_cachemodule_inexistent = TCacheHttpSession.CacheModuleID '{0}' points to a non-existent module. +cachesession_cachemodule_invalid = TCacheHttpSession.CacheModuleID '{0}' points to a module that does not implement ICache interface. + +urlmapping_urlmappingpattern_required = TUrlMapping can only contain TUrlMappingPattern or its child classes. +urlmapping_global_required = TUrlMapping must be configured as a global module. +urlmapping_configfile_inexistent = TUrlMapping.ConfigFile '{0}' is not a file. +urlmapping_configfile_invalid = TUrlMapping.ConfigFile '{0}' must point to an XML file in namespace format. + +urlmappingpattern_serviceparameter_required = TUrlMappingPattern.ServiceParameter is required for pattern '{0}'. + +keyboard_forcontrol_required = TKeyboard.ForControl cannot be empty. +keyboard_forcontrol_invalid = TKeyboard.ForControl '{0}' is invalid. + +captcha_tokenimagetheme_invalid = TCaptcha.TokenImageTheme must be an integer between {0} and {1}. +captcha_tokenfontsize_invalid = TCaptcha.TokenFontSize must be an integer between {0} and {1}. +captcha_mintokenlength_invalid = TCaptcha.MinTokenLength must be an integer between {0} and {1}. +captcha_maxtokenlength_invalid = TCaptcha.MaxTokenLength must be an integer between {0} and {1}. +captcha_tokenalphabet_invalid = TCaptcha.TokenAlphabet must be a string consisting of at least 2 characters. +captcha_privatekey_unknown = TCaptcha.PrivateKey is unknown. Please make sure that your assets directory is writable by the Web server process. +captcha_gd2_required = TCaptcha requires PHP GD2 extension. +captcha_imagettftext_required = TCaptcha requires PHP GD2 extension with TrueType font support. +captcha_imagepng_required = TCaptcha requires PHP GD2 extension with PNG image format support. + +captchavalidator_captchacontrol_required = TReCaptchaValidator.CaptchaControl must point to an existing TCaptcha control. +captchavalidator_captchacontrol_inexistent = TReCaptchaValidator.CaptchaControl {0} must point to an existing TCaptcha control. +captchavalidator_captchacontrol_invalid = TReCaptchaValidator.CaptchaControl {0} must point to an existing TCaptcha control. + +recaptcha_privatekey_unknown = TReCaptcha.PrivateKey is unknown. To use reCAPTCHA you must get an API key. +recaptcha_publickey_unknown = TReCaptcha.PublicKey is unknown. To use reCAPTCHA you must get an API key. + +recaptchavalidator_captchacontrol_invalid = TReCaptchaValidator.ControlToValidate must be an instance of TReCaptcha. + +slider_handle_class_invalid = TSlider.HandleClass '{0}' is not a valid user class. The class must extends TSliderHandle. + +cachepagestatepersister_cachemoduleid_invalid = TCachePageStatePersister.CacheModuleID '{0}' does not point to a valid cache module. +cachepagestatepersister_cache_required = TCachePageStatePersister requires a cache module to be loaded. +cachepagestatepersister_timeout_invalid = TCachePageStatePersister.Timeout must be an integer no less than zero. +cachepagestatepersister_pagestate_corrupted = Page state is corrupted. + +conditional_condition_invalid = TConditional.Condition '{0}' is not a valid PHP expression: {1} + +db_cachetable_inexistent = TDbCache cannot find DB table '{0}' to store cached data. + +ar_data_invalid = {0}.copyFrom() can only take an object or array as parameter. +ar_save_invalid = The {0} instance cannot be saved because it is either deleted or in an unknown state. +ar_delete_invalid = The {0} instance cannot be deleted because it is either a new record or a record already deleted. + +datasource_dbconnection_invalid = TDataSourceConfig.DbConnection '{0}' is invalid. Please make sure it points to a valid application module. +distributeddatasource_child_required = {0} requires one '{1}' child element at minimum. +masterslavedbconnection_connection_exists = {0}.{1} connection already exists. +masterslavedbconnection_interface_required = {0}.{1} requires an instance implementing {2} interface. +slavedbconnection_requires_master = {0} requires a {1}. + +response_status_reason_missing = HTTP 1.1 need reason for extended status-codes +response_status_reason_badchars = For HTTP 1.1 header, the token status-reason must not contain token CR or LF + +activefileupload_temppath_invalid = TActiveFileUpload TempPath path '{0}' does not exist or is not writable by Web server process. + +tactivetablecell_control_outoftable = {0} '{1}' must be enclosed within a TTableRow control. +tactivetablecell_control_notincollection = {0} '{1}' no member of the TTableCellCollection of the parent TTableRow control. + +tactivetablerow_control_outoftable = {0} '{1}' must be enclosed within a TTable control. +tactivetablerow_control_notincollection = {0} '{1}' no member of the TTableRowCollection of the parent TTable control. \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error-fr.html b/gui/baculum/framework/Exceptions/templates/error-fr.html new file mode 100644 index 0000000000..c9c1901545 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error-fr.html @@ -0,0 +1,31 @@ + + + +%%ErrorMessage%% + + + +

    Erreur %%StatusCode%%

    +

    %%ErrorMessage%%

    +

    +Cette erreur est survenue en essayant de répondre à votre requête. +

    +

    +Si vous pensez qu'il s'agit d'une erreur inattendue du serveur, merci de contacter l'administrateur +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error-id.html b/gui/baculum/framework/Exceptions/templates/error-id.html new file mode 100644 index 0000000000..608805a095 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error-id.html @@ -0,0 +1,32 @@ + + + + +%%ErrorMessage%% + + + +

    Kesalahan %%StatusCode%%

    +

    %%ErrorMessage%%

    +

    +Kesalahan di atas terjadi ketika server memproses permintaan anda. +

    +

    +Jika anda pikir ini merupakan kesalahan server, silahkan hubungi webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error-zh.html b/gui/baculum/framework/Exceptions/templates/error-zh.html new file mode 100644 index 0000000000..f69d0e4992 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error-zh.html @@ -0,0 +1,32 @@ + + + + +%%ErrorMessage%% + + + +

    错误%%StatusCode%%

    +

    %%ErrorMessage%%

    +

    +服务器在处理您的页面请求时出现了以上错误。 +

    +

    +如果您认为这是服务器的原因,请联系通知系统管理员。 +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error.html b/gui/baculum/framework/Exceptions/templates/error.html new file mode 100644 index 0000000000..e1715f4dd2 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error.html @@ -0,0 +1,32 @@ + + + + +%%ErrorMessage%% + + + +

    Error %%StatusCode%%

    +

    %%ErrorMessage%%

    +

    +The above error happened when the server was processing your request. +

    +

    +If you think this is a server error, please contact the webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error400-en.html b/gui/baculum/framework/Exceptions/templates/error400-en.html new file mode 100644 index 0000000000..629b360d0e --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error400-en.html @@ -0,0 +1,33 @@ + + + + +Bad Request + + + +

    Bad Request

    +

    %%ErrorMessage%%

    +

    +The request could not be understood by the server due to malformed syntax. +Please do not repeat the request without modifications. +

    +

    +If you think this is a server error, please contact the webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error400-id.html b/gui/baculum/framework/Exceptions/templates/error400-id.html new file mode 100644 index 0000000000..9f020108d3 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error400-id.html @@ -0,0 +1,33 @@ + + + + +Bad Request + + + +

    Permintaan Salah

    +

    %%ErrorMessage%%

    +

    +Permintaan tidak dimengerti oleh server karena sintaks tidak benar. +Harap tidak mengulangi permintaan tanpa memodifikasinya. +

    +

    +Jika anda pikir ini merupakan kesalahan server, silahkan hubungi webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error400-zh.html b/gui/baculum/framework/Exceptions/templates/error400-zh.html new file mode 100644 index 0000000000..08f931180a --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error400-zh.html @@ -0,0 +1,32 @@ + + + + +错误请求 + + + +

    错误请求

    +

    %%ErrorMessage%%

    +

    +服务器无法理解您的请求。请勿重复同样的请求。 +

    +

    +如果您确认这是服务器错误,请联系系统管理员。 +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error400.html b/gui/baculum/framework/Exceptions/templates/error400.html new file mode 100644 index 0000000000..629b360d0e --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error400.html @@ -0,0 +1,33 @@ + + + + +Bad Request + + + +

    Bad Request

    +

    %%ErrorMessage%%

    +

    +The request could not be understood by the server due to malformed syntax. +Please do not repeat the request without modifications. +

    +

    +If you think this is a server error, please contact the webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error404-en.html b/gui/baculum/framework/Exceptions/templates/error404-en.html new file mode 100644 index 0000000000..40862e0e01 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error404-en.html @@ -0,0 +1,33 @@ + + + + +Page Not Found + + + +

    %%ErrorMessage%%

    +

    Error 404

    +

    +The requested URL was not found on this server. +If you entered the URL manually please check your spelling and try again. +

    +

    +If you think this is a server error, please contact the webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error404-fr.html b/gui/baculum/framework/Exceptions/templates/error404-fr.html new file mode 100644 index 0000000000..4a417d71ce --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error404-fr.html @@ -0,0 +1,32 @@ + + + +Page Introuvable + + + +

    %%ErrorMessage%%

    +

    Erreur 404

    +

    +L'URL demandée n'a pas été trouvée sur ce serveur. +Si vous avez tapé cette URL manuellement dans la barre d'adresse, merci de vérifier qu'il n'y aucune faute de frappe pour essayer à nouveau. +

    +

    +Si vous pensez qu'il s'agit d'une erreur inattendue du serveur, merci de contacter l'administrateur +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error404-id.html b/gui/baculum/framework/Exceptions/templates/error404-id.html new file mode 100644 index 0000000000..3a24992196 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error404-id.html @@ -0,0 +1,33 @@ + + + + +Halaman Tidak Ditemukan + + + +

    %%ErrorMessage%%

    +

    Kesalahan 404

    +

    +URL yang diminta tidak ditemukan pada server ini. +Jika anda memasukan URL secara manual silahkan periksa ejaan anda dan coba lagi. +

    +

    +Jika anda pikir ini kesalahan server, silahkan hubungi webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error404-zh.html b/gui/baculum/framework/Exceptions/templates/error404-zh.html new file mode 100644 index 0000000000..c834f9b146 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error404-zh.html @@ -0,0 +1,33 @@ + + + + +无法找到页面 + + + +

    %%ErrorMessage%%

    +

    错误代码404

    +

    +服务器无法找到您所请求的页面。 +如果您是手工输入页面地址的,请检查拼写是否正确。 +

    +

    +如果您确认这是服务器错误,请联系系统管理员。 +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error404.html b/gui/baculum/framework/Exceptions/templates/error404.html new file mode 100644 index 0000000000..40862e0e01 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error404.html @@ -0,0 +1,33 @@ + + + + +Page Not Found + + + +

    %%ErrorMessage%%

    +

    Error 404

    +

    +The requested URL was not found on this server. +If you entered the URL manually please check your spelling and try again. +

    +

    +If you think this is a server error, please contact the webmaster. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error500-en.html b/gui/baculum/framework/Exceptions/templates/error500-en.html new file mode 100644 index 0000000000..69408d211a --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error500-en.html @@ -0,0 +1,33 @@ + + + + +Internal Server Error + + + +

    Internal Server Error

    +

    %%ErrorMessage%%

    +

    +An internal error occurred while the Web server was handling your request. +Please contact the webmaster to report this problem. +

    +

    +Thank you. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error500-fr.html b/gui/baculum/framework/Exceptions/templates/error500-fr.html new file mode 100644 index 0000000000..1c4df1ab9d --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error500-fr.html @@ -0,0 +1,32 @@ + + + +Erreur Interne du Serveur + + + +

    Erreur Interne du Serveur

    +

    %%ErrorMessage%%

    +

    +Une erreur interne s'est produite pendant que le serveur web traitait votre requête. +Veuillez contacter l'administrateur pour lui signaler le problème. +

    +

    +Merci de votre compréhension. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error500-id.html b/gui/baculum/framework/Exceptions/templates/error500-id.html new file mode 100644 index 0000000000..592138f041 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error500-id.html @@ -0,0 +1,33 @@ + + + + +Kesalahan Server Internal + + + +

    Kesalahan Server Internal

    +

    %%ErrorMessage%%

    +

    +Kesalahan server internal terjadi saat server Web menangani permintaan anda. +Silahkan hubungi webmaster untuk melaporkan masalah ini. +

    +

    +Thank you. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error500-zh.html b/gui/baculum/framework/Exceptions/templates/error500-zh.html new file mode 100644 index 0000000000..d655601ffc --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error500-zh.html @@ -0,0 +1,32 @@ + + + + +服务器内部错误 + + + +

    服务器内部错误

    +

    %%ErrorMessage%%

    +

    +服务器在处理您的请求时发生了一个内部错误。请向系统管理员汇报这个错误。 +

    +

    +谢谢。 +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error500.html b/gui/baculum/framework/Exceptions/templates/error500.html new file mode 100644 index 0000000000..69408d211a --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error500.html @@ -0,0 +1,33 @@ + + + + +Internal Server Error + + + +

    Internal Server Error

    +

    %%ErrorMessage%%

    +

    +An internal error occurred while the Web server was handling your request. +Please contact the webmaster to report this problem. +

    +

    +Thank you. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error503-en.html b/gui/baculum/framework/Exceptions/templates/error503-en.html new file mode 100644 index 0000000000..66b8ea062f --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error503-en.html @@ -0,0 +1,31 @@ + + + + +Service Unavailable + + + +

    Service Unavailable

    +

    +Our system is currently under maintenance. Please come back later. +

    +

    +Thank you. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error503-fr.html b/gui/baculum/framework/Exceptions/templates/error503-fr.html new file mode 100644 index 0000000000..896652556f --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error503-fr.html @@ -0,0 +1,30 @@ + + + +Service Indisponible + + + +

    Service Indisponible

    +

    +Le service est actuellement en maintenance et indisponible. Merci de réessayer prochainement. +

    +

    +Merci de votre compréhension. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error503-id.html b/gui/baculum/framework/Exceptions/templates/error503-id.html new file mode 100644 index 0000000000..43a52dea1c --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error503-id.html @@ -0,0 +1,31 @@ + + + + +Layanan Tidak Tersedia + + + +

    Layanan Tidak Tersedia

    +

    +Sistem kami saat ini dalam pemeliharaan. Silahkan kembali lagi nanti. +

    +

    +Terima kasih. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error503-zh.html b/gui/baculum/framework/Exceptions/templates/error503-zh.html new file mode 100644 index 0000000000..b7457d685b --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error503-zh.html @@ -0,0 +1,31 @@ + + + + +系统无法提供服务 + + + +

    系统无法提供服务

    +

    +系统维护中,请稍后再来访问。 +

    +

    +谢谢。 +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/error503.html b/gui/baculum/framework/Exceptions/templates/error503.html new file mode 100644 index 0000000000..66b8ea062f --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/error503.html @@ -0,0 +1,31 @@ + + + + +Service Unavailable + + + +

    Service Unavailable

    +

    +Our system is currently under maintenance. Please come back later. +

    +

    +Thank you. +

    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/exception-en.html b/gui/baculum/framework/Exceptions/templates/exception-en.html new file mode 100644 index 0000000000..aed3755da6 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/exception-en.html @@ -0,0 +1,43 @@ + + + + +%%ErrorType%% + + + +

    %%ErrorType%%

    +

    Description

    +

    %%ErrorMessage%%

    +

    Source File

    +

    %%SourceFile%%

    +
    +
    +%%SourceCode%%
    +
    +
    +

    Stack Trace

    +
    +
    +%%StackTrace%%
    +
    +
    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/exception-fr.html b/gui/baculum/framework/Exceptions/templates/exception-fr.html new file mode 100644 index 0000000000..7cb6136177 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/exception-fr.html @@ -0,0 +1,43 @@ + + + +%%ErrorType%% + + + +

    %%ErrorType%%

    +

    Description

    +

    %%ErrorMessage%%

    +

    Fichier Source

    +

    %%SourceFile%%

    +
    +
    +%%SourceCode%%
    +
    +
    +

    Trace de la pile d'ex閏ution:

    +
    +
    +%%StackTrace%%
    +
    +
    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/exception-id.html b/gui/baculum/framework/Exceptions/templates/exception-id.html new file mode 100644 index 0000000000..75961f5697 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/exception-id.html @@ -0,0 +1,43 @@ + + + + +%%ErrorType%% + + + +

    %%ErrorType%%

    +

    Deskripsi

    +

    %%ErrorMessage%%

    +

    File Sumber

    +

    %%SourceFile%%

    +
    +
    +%%SourceCode%%
    +
    +
    +

    Jejak Stack

    +
    +
    +%%StackTrace%%
    +
    +
    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/exception-zh.html b/gui/baculum/framework/Exceptions/templates/exception-zh.html new file mode 100644 index 0000000000..257e8e690d --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/exception-zh.html @@ -0,0 +1,45 @@ + + + + +%%ErrorType%% + + + +

    %%ErrorType%%

    +

    错误信息

    +

    %%ErrorMessage%%

    +

    +

    错误代码文件

    +

    %%SourceFile%%

    +
    +
    +%%SourceCode%%
    +
    +
    +

    错误堆栈信息

    +
    +
    +%%StackTrace%%
    +
    +
    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/exception.html b/gui/baculum/framework/Exceptions/templates/exception.html new file mode 100644 index 0000000000..f9012206ae --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/exception.html @@ -0,0 +1,42 @@ + + + + +%%ErrorType%% + + + +

    %%ErrorType%%

    +

    Description

    +

    %%ErrorMessage%%

    +

    Source File

    +

    %%SourceFile%%

    +
    +
    +%%SourceCode%%
    +
    +
    +

    Stack Trace

    +
    +
    +%%StackTrace%%
    +
    +
    +
    +%%Time%% %%Version%% +
    + + \ No newline at end of file diff --git a/gui/baculum/framework/Exceptions/templates/readme.txt b/gui/baculum/framework/Exceptions/templates/readme.txt new file mode 100644 index 0000000000..635b668859 --- /dev/null +++ b/gui/baculum/framework/Exceptions/templates/readme.txt @@ -0,0 +1,18 @@ +This directory contains templates used to display PRADO exception and error messages to end-users. + +All error template files follow the following naming convention: + + error-.html + +where refers to a HTTP status code used when raising THttpException, and + refers to a valid language such as en, zh, fr. + +The naming convention for exception template files is similar to that of error template files. + + +CAUTION: When saving a template file, please make sure the file is saved using UTF-8 encoding. +On Windows, you may use Notepad.exe to accomplish such saving. + + +Qiang Xue +Jan. 3, 2006 \ No newline at end of file diff --git a/gui/baculum/framework/I18N/TChoiceFormat.php b/gui/baculum/framework/I18N/TChoiceFormat.php new file mode 100644 index 0000000000..ad9a45005a --- /dev/null +++ b/gui/baculum/framework/I18N/TChoiceFormat.php @@ -0,0 +1,110 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TChoiceFormat.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + + /** + * Get the ChoiceFormat class. + */ +Prado::using('System.I18N.core.ChoiceFormat'); +Prado::using('System.I18N.TTranslate'); + +/** + * TChoiceFormat class. + * + * This component performs message/string choice translation. The translation + * source is set in the TGlobalization module. The following example + * demonstrates a simple 2 choice message translation. + * + * [1] One Apple. |[2] Two Apples + * + * + * The Choice has Value "1" (one), thus the translated string + * is "One Apple". If the Value is "2", then it will show + * "Two Apples". + * + * The message/string choices are separated by the pipe "|" followed + * by a set notation of the form + * # [1,2] -- accepts values between 1 and 2, inclusive. + * # (1,2) -- accepts values between 1 and 2, excluding 1 and 2. + * # {1,2,3,4} -- only values defined in the set are accepted. + * # [-Inf,0) -- accepts value greater or equal to negative infinity + * and strictly less than 0 + * Any non-empty combinations of the delimiters of square and round brackets + * are acceptable. + * + * The string choosen for display depends on the Value property. + * The Value is evaluated for each set until the Value is found + * to belong to a particular set. + * + * Properties + * - Value, float, + *
    Gets or sets the Value that determines which string choice to display. + * Since version 3.1.2 the following set notation is also possible. + * + * # {n: n % 10 > 1 && n % 10 < 5} -- matches numbers like 2, 3, 4, 22, 23, 24 + * + * Where set is defined by the expression after n:. In particular, the expression + * accepts the following mathematical/logical operators to form a set of logical conditions + * on the value given by n: + * # < -- less than. + * # <= -- less than equals. + * # > -- greater than. + * # >= -- greater than equals. + * # == -- of equal value. + * # % -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1. + * # - -- minus, negative. + * # + -- addition. + * # & -- conditional AND. + * # && -- condition AND with short circuit. + * # | -- conditional OR. + * # || -- conditional OR with short circuit. + * # ! -- negation. + * + * Additional round brackets can also be used to perform grouping. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 21:38:49 EST 2004 + * @package System.I18N + */ +class TChoiceFormat extends TTranslate +{ + /** + * @return float the numerical value. + */ + public function getValue() + { + return $this->getViewState('Value',''); + } + + /** + * Sets the numerical choice value + * @param float the choice value + */ + public function setValue($value) + { + $this->setViewState('Value',$value,''); + } + + /** + * Display the choosen translated string. + * Overrides the parent method, also calls parent's renderBody to + * translate. + */ + protected function translateText($text, $subs) + { + $text = parent::translateText($text, $subs); + $choice = new ChoiceFormat(); + $value = $this->getValue(); + $string = $choice->format($text, $value); + if($string) + return strtr($string, array('{Value}'=> $value)); + } +} diff --git a/gui/baculum/framework/I18N/TDateFormat.php b/gui/baculum/framework/I18N/TDateFormat.php new file mode 100644 index 0000000000..544e49730c --- /dev/null +++ b/gui/baculum/framework/I18N/TDateFormat.php @@ -0,0 +1,254 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDateFormat.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + +/** + * Get the DateFormat class. + */ +Prado::using('System.I18N.core.DateFormat'); + +/** + * Get the parent control class. + */ +Prado::using('System.I18N.TI18NControl'); + +/** + * To format dates and/or time according to the current locale use + * + * + * + * The date will be formatted according to the current locale (or culture) + * using the format specified by 'Pattern' attribute. + * + * To format date and/or time for a locale (e.g. de_DE) include a Culture + * attribute, for example: + * + * + * + * The date will be formatted according to this format. + * + * If no Pattern was specified then the date will be formatted with the + * default format (both date and time). If no value for the date is specified + * then the current date will be used. E.g.: + * will result in the current date, formatted with default localized pattern. + * + * Namespace: System.I18N + * + * Properties + * - Value, date, + *
    Gets or sets the date to format. The tag content is used as Value + * if the Value property is not specified. + * - Pattern, string, + *
    Gets or sets the formatting pattern. The predefined patterns are + * 'fulldate', 'longdate', 'mediumdate', 'shortdate', 'fulltime', + * 'longtime', 'mediumtime', and 'shorttime'. Custom patterns can specified + * when the Pattern property does not match the predefined patterns. + * - DefaultText, string, + *
    Gets or sets the default text. If Value is not set, DefaultText will be + * shown instead of todays date and time. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Sat Dec 11 15:25:11 EST 2004 + * @package System.I18N + */ +class TDateFormat extends TI18NControl implements IDataRenderer +{ + /** + * Default DateFormat, set to the application culture. + * @var DateFormat + */ + protected static $formatter; + + /** + * A set of pattern presets and their respective formatting shorthand. + * @var array + */ + static private $_patternPresets = array( + 'fulldate'=>'P','full'=>'P', + 'longdate'=>'D','long'=>'d', + 'mediumdate'=>'p','medium'=>'p', + 'shortdate'=>'d','short'=>'d', + 'fulltime'=>'Q', 'longtime'=>'T', + 'mediumtime'=>'q', 'shorttime'=>'t'); + + /** + * Sets the date time formatting pattern. + * @param string format pattern. + */ + public function setPattern($value) + { + $this->setViewState('Pattern',$value,''); + } + + /** + * Gets the date time format pattern. + * @return string format pattern. + */ + public function getPattern() + { + $string = $this->getViewState('Pattern',''); + + $pattern = null; + + //try the subpattern of "date time" presets + $subpatterns = explode(' ',$string,2); + $datetime = array(); + if(count($subpatterns)==2) + { + $datetime[] = $this->getPreset($subpatterns[0]); + $datetime[] = $this->getPreset($subpatterns[1]); + } + + //we have a good subpattern + if(count($datetime) == 2 + && strlen($datetime[0]) == 1 + && strlen($datetime[1]) == 1) + { + $pattern = $datetime; + } + else //no subpattern, try the presets + $pattern = $this->getPreset($string); + + //no presets found, use the string as the pattern + //and let the DateFormat handle it. + if($pattern===null) + $pattern = $string; + if (!is_array($pattern) && strlen($pattern) == 0) + $pattern = null; + return $pattern; + } + + /** + * For a given string, try and find a preset pattern. + * @param string the preset pattern name + * @return string a preset pattern if found, null otherwise. + */ + protected function getPreset($string) + { + $string = strtolower($string); + foreach(self::$_patternPresets as $pattern => $preset) + { + if($string == $pattern) + return $preset; + } + } + + /** + * Get the date-time value for this control. + * @return string date time value. + */ + public function getValue() + { + $value = $this->getViewState('Value',''); + if(empty($value)) + { + $defaultText = $this->getDefaultText(); + if(empty($defaultText)) + return time(); + } + return $value; + } + + /** + * Set the date-time value for this control. + * @param string the date-time value. + */ + public function setValue($value) + { + $this->setViewState('Value',$value,''); + } + + /** + * Get the default text value for this control. + * @return string default text value + */ + public function getDefaultText() + { + return $this->getViewState('DefaultText',''); + } + + /** + * Set the default text value for this control. + * @param string default text value + */ + public function setDefaultText($value) + { + $this->setViewState('DefaultText',$value,''); + } + + /** + * Get the date-time value for this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string date time value. + * @see getValue + * @since 3.1.2 + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Set the date-time value for this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string the date-time value. + * @see setValue + * @since 3.1.2 + */ + public function setData($value) + { + $this->setValue($value); + } + + /** + * Renders the localized version of the date-time value. + * If the culture is not specified, the default application + * culture will be used. + * This method overrides parent's implementation. + */ + protected function getFormattedDate() + { + $value = $this->getValue(); + $defaultText = $this->getDefaultText(); + if(empty($value) && !empty($defaultText)) + return $this->getDefaultText(); + + $app = $this->getApplication()->getGlobalization(); + + //initialized the default class wide formatter + if(self::$formatter===null) + self::$formatter = new DateFormat($app->getCulture()); + + $culture = $this->getCulture(); + + //return the specific cultural formatted date time + if(strlen($culture) && $app->getCulture() !== $culture) + { + $formatter = new DateFormat($culture); + return $formatter->format($value, + $this->getPattern(), + $this->getCharset()); + } + //return the application wide culture formatted date time. + $result = self::$formatter->format($value, + $this->getPattern(), + $this->getCharset()); + return $result; + } + + public function render($writer) + { + $writer->write($this->getFormattedDate()); + } + +} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/TGlobalization.php b/gui/baculum/framework/I18N/TGlobalization.php new file mode 100644 index 0000000000..6638af2267 --- /dev/null +++ b/gui/baculum/framework/I18N/TGlobalization.php @@ -0,0 +1,298 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TGlobalization.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + + +/** + * TGlobalization contains settings for Culture, Charset + * and TranslationConfiguration. + * + * TGlobalization can be subclassed to change how the Culture, Charset + * are determined. See TGlobalizationAutoDetect for example of + * setting the Culture based on browser settings. + * + * @author Wei Zhuo + * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $ + * @package System.I18N + * @since 3.0 + */ +class TGlobalization extends TModule +{ + /** + * Default character set is 'UTF-8'. + * @var string + */ + private $_defaultCharset = 'UTF-8'; + + /** + * Default culture is 'en'. + * @var string + */ + private $_defaultCulture = 'en'; + + /** + * The current charset. + * @var string + */ + private $_charset=null; + + /** + * The current culture. + * @var string + */ + private $_culture=null; + + /** + * Translation source parameters. + * @var TMap + */ + private $_translation; + + /** + * @var boolean whether we should translate the default culture + */ + private $_translateDefaultCulture=true; + + /** + * Initialize the Culture and Charset for this application. + * You should override this method if you want a different way of + * setting the Culture and/or Charset for your application. + * If you override this method, call parent::init($xml) first. + * @param mixed application configuration + */ + public function init($config) + { + if($this->_charset===null) + $this->_charset=$this->getDefaultCharset(); + if($this->_culture===null) + $this->_culture=$this->getDefaultCulture(); + + if($config!==null) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $translation = isset($config['translate'])?$config['translate']:null; + else + { + $t = $config->getElementByTagName('translation'); + $translation = ($t)?$t->getAttributes():null; + } + if($translation) + $this->setTranslationConfiguration($translation); + } + $this->getApplication()->setGlobalization($this); + } + + /** + * @return string default culture + */ + public function getTranslateDefaultCulture() + { + return $this->_translateDefaultCulture; + } + + /** + * @param bool default culture, e.g. en_US for American English + */ + public function setTranslateDefaultCulture($value) + { + $this->_translateDefaultCulture = TPropertyValue::ensureBoolean($value); + } + + /** + * @return string default culture + */ + public function getDefaultCulture() + { + return $this->_defaultCulture; + } + + /** + * @param string default culture, e.g. en_US for American English + */ + public function setDefaultCulture($culture) + { + $this->_defaultCulture = str_replace('-','_',$culture); + } + + /** + * @return string default charset set + */ + public function getDefaultCharset() + { + return $this->_defaultCharset; + } + + /** + * @param string default localization charset, e.g. UTF-8 + */ + public function setDefaultCharset($charset) + { + $this->_defaultCharset = $charset; + } + + /** + * @return string current application culture + */ + public function getCulture() + { + return $this->_culture; + } + + /** + * @param string culture, e.g. en_US for American English + */ + public function setCulture($culture) + { + $this->_culture = str_replace('-','_',$culture); + } + + /** + * @return string localization charset + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * @param string localization charset, e.g. UTF-8 + */ + public function setCharset($charset) + { + $this->_charset = $charset; + } + + /** + * @return TMap translation source configuration. + */ + public function getTranslationConfiguration() + { + return (!$this->_translateDefaultCulture && ($this->getDefaultCulture() == $this->getCulture())) + ? null + : $this->_translation; + } + + /** + * Sets the translation configuration. Example configuration: + * + * $config['type'] = 'XLIFF'; //XLIFF, gettext, Database or MySQL (deprecated) + * $config['source'] = 'Path.to.directory'; // for types XLIFF and gettext + * $config['source'] = 'connectionId'; // for type Database + * $config['source'] = 'mysql://user:pw@host/db'; // for type MySQL (deprecated) + * $config['catalogue'] = 'messages'; //default catalog + * $config['autosave'] = 'true'; //save untranslated message + * $config['cache'] = 'true'; //cache translated message + * $config['marker'] = '@@'; // surround untranslated text with '@@' + * + * Throws exception is source is not found. + * @param TMap|array configuration options + */ + protected function setTranslationConfiguration($config) + { + if($config['type'] == 'XLIFF' || $config['type'] == 'gettext') + { + if($config['source']) + { + $config['source'] = Prado::getPathOfNamespace($config['source']); + if(!is_dir($config['source'])) + { + if(@mkdir($config['source'])===false) + throw new TConfigurationException('globalization_source_path_failed', + $config['source']); + chmod($config['source'], PRADO_CHMOD); //make it deletable + } + } + else + { + throw new TConfigurationException("invalid source dir '{$config['source']}'"); + } + } + if($config['cache']) + { + $config['cache'] = $this->getApplication()->getRunTimePath().'/i18n'; + if(!is_dir($config['cache'])) + { + if(@mkdir($config['cache'])===false) + throw new TConfigurationException('globalization_cache_path_failed', + $config['cache']); + chmod($config['cache'], PRADO_CHMOD); //make it deletable + } + } + $this->_translation = $config; + } + + /** + * @return string current translation catalogue. + */ + public function getTranslationCatalogue() + { + return $this->_translation['catalogue']; + } + + /** + * @param string update the translation catalogue. + */ + public function setTranslationCatalogue($value) + { + $this->_translation['catalogue'] = $value; + } + + /** + * Gets all the variants of a specific culture. If the parameter + * $culture is null, the current culture is used. + * @param string $culture the Culture string + * @return array variants of the culture. + */ + public function getCultureVariants($culture=null) + { + if($culture===null) $culture = $this->getCulture(); + $variants = explode('_', $culture); + $result = array(); + for(; count($variants) > 0; array_pop($variants)) + $result[] = implode('_', $variants); + return $result; + } + + /** + * Returns a list of possible localized files. Example + * + * $files = $app->getLocalizedResource("path/to/Home.page","en_US"); + * + * will return + *
    +	 * array
    +	 *   0 => 'path/to/en_US/Home.page'
    +	 *   1 => 'path/to/en/Home.page'
    +	 *   2 => 'path/to/Home.en_US.page'
    +	 *   3 => 'path/to/Home.en.page'
    +	 *   4 => 'path/to/Home.page'
    +	 * 
    + * Note that you still need to verify the existance of these files. + * @param string filename + * @param string culture string, null to use current culture + * @return array list of possible localized resource files. + */ + public function getLocalizedResource($file,$culture=null) + { + $files = array(); + $variants = $this->getCultureVariants($culture); + $path = pathinfo($file); + foreach($variants as $variant) + $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$variant.DIRECTORY_SEPARATOR.$path['basename']; + $filename = substr($path['basename'],0,strrpos($path['basename'],'.')); + foreach($variants as $variant) + $files[] = $path['dirname'].DIRECTORY_SEPARATOR.$filename.'.'.$variant.'.'.$path['extension']; + $files[] = $file; + return $files; + } + +} + diff --git a/gui/baculum/framework/I18N/TGlobalizationAutoDetect.php b/gui/baculum/framework/I18N/TGlobalizationAutoDetect.php new file mode 100644 index 0000000000..ad78640110 --- /dev/null +++ b/gui/baculum/framework/I18N/TGlobalizationAutoDetect.php @@ -0,0 +1,49 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $ + * @package System.I18N + */ + +/** + * Import the HTTPNeogtiator + */ +Prado::using('System.I18N.core.HTTPNegotiator'); + +/** + * TGlobalizationAutoDetect class will automatically try to resolve the default + * culture using the user browser language settings. + * + * @author Wei Zhuo + * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $ + * @package System.I18N + */ +class TGlobalizationAutoDetect extends TGlobalization +{ + private $_detectedLanguage; + + public function init($xml) + { + parent::init($xml); + + //set the culture according to browser language settings + $http = new HTTPNegotiator(); + $languages = $http->getLanguages(); + if(count($languages) > 0) + { + $this->_detectedLanguage=$languages[0]; + $this->setCulture($languages[0]); + } + } + + public function getDetectedLanguage() + { + return $this->_detectedLanguage; + } +} + diff --git a/gui/baculum/framework/I18N/TI18NControl.php b/gui/baculum/framework/I18N/TI18NControl.php new file mode 100644 index 0000000000..97a7268adf --- /dev/null +++ b/gui/baculum/framework/I18N/TI18NControl.php @@ -0,0 +1,90 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TI18NControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + + +/** + * TI18NControl class. + * + * Base class for I18N components, providing Culture and Charset properties. + * Namespace: System.I18N + * + * Properties + * - Culture, string, + *
    Gets or sets the culture for formatting. If the Culture property + * is not specified. The culture from the Application/Page is used. + * - Charset, string, + *
    Gets or sets the charset for both input and output. + * If the Charset property is not specified. The charset from the + * Application/Page is used. The default is UTF-8. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Sat Dec 11 15:25:11 EST 2004 + * @package System.I18N + */ +class TI18NControl extends TControl +{ + /** + * Gets the charset. + * It is evaluated in the following order: + * 1) application charset, + * 2) the default charset in globalization + * 3) UTF-8 + * @return string charset + */ + public function getCharset() + { + $app = $this->getApplication()->getGlobalization(false); + + //instance charset + $charset = $this->getViewState('Charset',''); + + //fall back to globalization charset + if(empty($charset)) + $charset = ($app===null) ? '' : $app->getCharset(); + + //fall back to default charset + if(empty($charset)) + $charset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset(); + + return $charset; + } + + /** + * Sets the charset for message output + * @param string the charset, e.g. UTF-8 + */ + public function setCharset($value) + { + $this->setViewState('Charset',$value,''); + } + + + /** + * Get the specific culture for this control. + * @param parameter + * @return string culture identifier. + */ + public function getCulture() + { + return $this->getViewState('Culture',''); + } + + /** + * Get the custom culture identifier. + * @param string culture identifier. + */ + public function setCulture($culture) + { + $this->setViewState('Culture',$culture,''); + } +} + diff --git a/gui/baculum/framework/I18N/TNumberFormat.php b/gui/baculum/framework/I18N/TNumberFormat.php new file mode 100644 index 0000000000..27c124f70d --- /dev/null +++ b/gui/baculum/framework/I18N/TNumberFormat.php @@ -0,0 +1,250 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TNumberFormat.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + +/** + * Get the NumberFormat class. + */ +Prado::using('System.I18N.core.NumberFormat'); + +/** + * Get the parent control class. + */ +Prado::using('System.I18N.TI18NControl'); + +/** + * To format numbers in locale sensitive manner use + * + * + * + * + * Numbers can be formatted as currency, percentage, decimal or scientific + * numbers by specifying the Type attribute. The known types are + * "currency", "percentage", "decimal" and "scientific". + * + * If someone from US want to see sales figures from a store in + * Germany (say using the EURO currency), formatted using the german + * currency, you would need to use the attribute Culture="de_DE" to get + * the currency right, e.g. 100,00. The decimal and grouping separator is + * then also from the de_DE locale. This may lead to some confusion because + * people from US know the "," as thousand separator. Therefore a "Currency" + * attribute is available, so that the output from the following example + * results in 100.00. + * + * + * + * + * Namespace: System.I18N + * + * Properties + * - Value, number, + *
    Gets or sets the number to format. The tag content is used as Value + * if the Value property is not specified. + * - Type, string, + *
    Gets or sets the formatting type. The valid types are + * 'decimal', 'currency', 'percentage' and 'scientific'. + * - Currency, string, + *
    Gets or sets the currency symbol for the currency format. + * The default is 'USD' if the Currency property is not specified. + * - Pattern, string, + *
    Gets or sets the custom number formatting pattern. + * - DefaultText, string, + *
    Gets or sets the default text. If Value is not set, DefaultText will be + * shown instead of the default currency Value/Pattern. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Sat Dec 11 17:49:56 EST 2004 + * @package System.I18N + */ +class TNumberFormat extends TI18NControl implements IDataRenderer +{ + /** + * Default NumberFormat, set to the application culture. + * @var NumberFormat + */ + protected static $formatter; + + /** + * Get the number formatting pattern. + * @return string format pattern. + */ + public function getPattern() + { + return $this->getViewState('Pattern',''); + } + + /** + * Set the number format pattern. + * @param string format pattern. + */ + public function setPattern($pattern) + { + $this->setViewState('Pattern',$pattern,''); + } + + /** + * Get the numberic value for this control. + * @return string number + */ + public function getValue() + { + return $this->getViewState('Value',''); + } + + /** + * Set the numberic value for this control. + * @param string the number value + */ + public function setValue($value) + { + $this->setViewState('Value',$value,''); + } + + /** + * Get the default text value for this control. + * @return string default text value + */ + public function getDefaultText() + { + return $this->getViewState('DefaultText',''); + } + + /** + * Set the default text value for this control. + * @param string default text value + */ + public function setDefaultText($value) + { + $this->setViewState('DefaultText',$value,''); + } + + /** + * Get the numberic value for this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string number + * @see getValue + * @since 3.1.2 + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Set the numberic value for this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string the number value + * @see setValue + * @since 3.1.2 + */ + public function setData($value) + { + $this->setValue($value); + } + + /** + * Get the formatting type for this control. + * @return string formatting type. + */ + public function getType() + { + return $this->getViewState('Type','d'); + } + + /** + * Set the formatting type for this control. + * @param string formatting type, either "decimal", "currency","percentage" + * or "scientific" + * @throws TPropertyTypeInvalidException + */ + public function setType($type) + { + $type = strtolower($type); + + switch($type) + { + case 'decimal': + $this->setViewState('Type','d',''); break; + case 'currency': + $this->setViewState('Type','c',''); break; + case 'percentage': + $this->setViewState('Type','p',''); break; + case 'scientific': + $this->setViewState('Type','e',''); break; + default: + throw new TInvalidDataValueException('numberformat_type_invalid',$type); + } + + } + + /** + * @return string 3 letter currency code. Defaults to 'USD'. + */ + public function getCurrency() + { + return $this->getViewState('Currency','USD'); + } + + /** + * Set the 3-letter ISO 4217 code. For example, the code + * "USD" represents the US Dollar and "EUR" represents the Euro currency. + * @param string currency code. + */ + public function setCurrency($currency) + { + $this->setViewState('Currency', $currency,''); + } + + /** + * Formats the localized number, be it currency or decimal, or percentage. + * If the culture is not specified, the default application + * culture will be used. + * @return string formatted number + */ + protected function getFormattedValue() + { + $value = $this->getValue(); + $defaultText = $this->getDefaultText(); + if(empty($value) && !empty($defaultText)) + return $this->getDefaultText(); + + $app = $this->getApplication()->getGlobalization(); + //initialized the default class wide formatter + if(self::$formatter===null) + self::$formatter = new NumberFormat($app->getCulture()); + + $pattern = strlen($this->getPattern()) > 0 + ? $this->getPattern() : $this->getType(); + + $culture = $this->getCulture(); + //return the specific cultural formatted number + if(!empty($culture) && $app->getCulture() != $culture) + { + $formatter = new NumberFormat($culture); + return $formatter->format($this->getValue(),$pattern, + $this->getCurrency(), + $this->getCharset()); + } + + //return the application wide culture formatted number. + return self::$formatter->format($this->getValue(),$pattern, + $this->getCurrency(), + $this->getCharset()); + } + + public function render($writer) + { + $writer->write($this->getFormattedValue()); + } +} + diff --git a/gui/baculum/framework/I18N/TTranslate.php b/gui/baculum/framework/I18N/TTranslate.php new file mode 100644 index 0000000000..3382f9252d --- /dev/null +++ b/gui/baculum/framework/I18N/TTranslate.php @@ -0,0 +1,255 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTranslate.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + +/** + * Get the parent control class. + */ +Prado::using('System.I18N.TI18NControl'); + +/** + * TTranslate class. + * + * This component performs message/string translation. The translation + * source is set in the TGlobalization handler. The following example + * demonstrated a simple message translation. + * + * + * + * + * Depending on the culture set on the page, the phrase "Goodbye" will + * be translated. + * + * The {@link getParameters Parameters} property can be use to add name values pairs for + * substitution. Substrings enclosed with "{" and "}" in the translation message are consider as the + * parameter names during substitution lookup. The following example will substitute the substring + * "{time}" with the value of the parameter attribute "Parameters.time=<%= time() %>. Note that + * the value of the parameter named "time" is evaluated. + * + * > + * The unix-time is "{time}". + * + * + * + * More complex string substitution can be applied using the + * TTranslateParameter component. + * + * Namespace: System.I18N + * + * Properties + * - Text, string, + *
    Gets or sets the string to translate. + * - Catalogue, string, + *
    Gets or sets the catalogue for message translation. The + * default catalogue can be set by the @Page directive. + * - Key, string, + *
    Gets or sets the key used to message look up. + * - Trim, boolean, + *
    Gets or sets an option to trim the contents. + * Default is to trim the contents. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 21:38:49 EST 2004 + * @package System.I18N + */ +class TTranslate extends TI18NControl +{ + /** + * @return string the text to be localized/translated. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text for localization. + * @param string the text for translation. + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Set the key for message lookup. + * @param string key + */ + public function setKey($value) + { + $this->setViewState('Key',$value,''); + } + + /** + * Get the key for message lookup. + * @return string key + */ + public function getKey() + { + return $this->getViewState('Key',''); + } + + /** + * Get the message catalogue. + * @return string catalogue. + */ + public function getCatalogue() + { + return $this->getViewState('Catalogue',''); + } + + /** + * Set the message catalogue. + * @param string catalogue. + */ + public function setCatalogue($value) + { + $this->setViewState('Catalogue',$value,''); + } + + /** + * Set the option to trim the contents. + * @param boolean trim or not. + */ + public function setTrim($value) + { + $this->setViewState('Trim',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Trim the content or not. + * @return boolean trim or not. + */ + public function getTrim() + { + return $this->getViewState('Trim',true); + } + + /** + * Returns the list of custom parameters. + * Custom parameters are name-value pairs that may subsititute translation + * place holders during rendering. + * @return TAttributeCollection the list of custom parameters + */ + public function getParameters() + { + if($parameters=$this->getViewState('Parameters',null)) + return $parameters; + else + { + $parameters=new TAttributeCollection; + $parameters->setCaseSensitive(true); + $this->setViewState('Parameters',$parameters,null); + return $parameters; + } + } + + /** + * @return boolean whether the named parameter exists + */ + public function hasParameter($name) + { + if($parameters=$this->getViewState('Parameters',null)) + return $parameters->contains($name); + else + return false; + } + + /** + * @return string parameter value, null if parameter does not exist + */ + public function getParameter($name) + { + if($parameters=$this->getViewState('Parameters',null)) + return $parameters->itemAt($name); + else + return null; + } + + /** + * @param string parameter name + * @param string value of the parameter + */ + public function setParameter($name,$value) + { + $this->getParameters()->add($name,$value); + } + + /** + * Removes the named parameter. + * @param string the name of the parameter to be removed. + * @return string parameter value removed, null if parameter does not exist. + */ + public function removeParameter($name) + { + if($parameters=$this->getViewState('Parameters',null)) + return $parameters->remove($name); + else + return null; + } + + /** + * renders the translated string. + */ + public function render($writer) + { + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + $subs = array(); + foreach($this->getParameters() as $key => $value) + $subs['{'.$key.'}'] = $value; + foreach($this->getControls() as $control) + { + if($control instanceof TTranslateParameter) + $subs['{'.$control->getKey().'}'] = $control->getParameter(); + elseif($control instanceof TControl) + $control->render($htmlWriter); + elseif(is_string($control)) + $htmlWriter->write($control); + } + + $text = $this->getText(); + if(strlen($text)==0) + $text = $htmlWriter->flush(); + if($this->getTrim()) + $text = trim($text); + + $writer->write($this->translateText($text, $subs)); + } + + /** + * Translates the text with subsititution. + * @param string text for translation + * @param array list of substitutions + * @return string translated text + */ + protected function translateText($text, $subs) + { + $app = $this->getApplication()->getGlobalization(); + + //no translation handler provided + if(($config = $app->getTranslationConfiguration())===null) + return strtr($text, $subs); + + $catalogue = $this->getCatalogue(); + if(empty($catalogue) && isset($config['catalogue'])) + $catalogue = $config['catalogue']; + if (empty($catalogue)) $catalogue='messages'; + Translation::init($catalogue); + + $key = $this->getKey(); + if(!empty($key)) $text = $key; + + //translate it + return Translation::formatter($catalogue)->format($text, + $subs, $catalogue, $this->getCharset()); + } +} + diff --git a/gui/baculum/framework/I18N/TTranslateParameter.php b/gui/baculum/framework/I18N/TTranslateParameter.php new file mode 100644 index 0000000000..b461a36a00 --- /dev/null +++ b/gui/baculum/framework/I18N/TTranslateParameter.php @@ -0,0 +1,119 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTranslateParameter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + +/** + * TTranslateParameter component should be used inside the TTranslate component to + * allow parameter substitution. + * + * For example, the strings "{greeting}" and "{name}" will be replace + * with the values of "Hello" and "World", respectively. + * The substitution string must be enclose with "{" and "}". + * The parameters can be further translated by using TTranslate. + * + * + * {greeting} {name}! + * World + * Hello + * + * + * + * Namespace: System.I18N + * + * Properties + * - Key, string, required. + *
    Gets or sets the string in TTranslate to substitute. + * - Trim, boolean, + *
    Gets or sets an option to trim the contents of the TParam. + * Default is to trim the contents. + * + * @author Xiang Wei Zhuo + * @version v3.0, last update on Friday, 6 January 2006 + * @package System.I18N + */ +class TTranslateParameter extends TControl +{ + /** + * The substitution key. + * @var string + */ + protected $key; + + /** + * To trim or not to trim the contents. + * @var boolean + */ + protected $trim = true; + + + /** + * Get the parameter substitution key. + * @return string substitution key. + */ + public function getKey() + { + if(empty($this->key)) + throw new TException('The Key property must be specified.'); + return $this->key; + } + + /** + * Set the parameter substitution key. + * @param string substitution key. + */ + public function setKey($value) + { + $this->key = $value; + } + + /** + * Set the option to trim the contents. + * @param boolean trim or not. + */ + public function setTrim($value) + { + $this->trim = TPropertyValue::ensureBoolean($value); + } + + /** + * Trim the content or not. + * @return boolean trim or not. + */ + public function getTrim() + { + return $this->trim; + } + + public function getValue() + { + return $this->getViewState('Value', ''); + } + + public function setValue($value) + { + $this->setViewState('Value', $value, ''); + } + + /** + * @return string parameter contents. + */ + public function getParameter() + { + $value = $this->getValue(); + if(strlen($value) > 0) + return $value; + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + $this->renderControl($htmlWriter); + return $this->getTrim() ? + trim($htmlWriter->flush()) : $htmlWriter->flush(); + } +} + diff --git a/gui/baculum/framework/I18N/Translation.php b/gui/baculum/framework/I18N/Translation.php new file mode 100644 index 0000000000..5219e13264 --- /dev/null +++ b/gui/baculum/framework/I18N/Translation.php @@ -0,0 +1,107 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: Translation.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.I18N + */ + +/** + * Get the MessageFormat class. + */ +Prado::using('System.I18N.core.MessageFormat'); + + +/** + * Translation class. + * + * Provides translation using a static MessageFormatter. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Tue Dec 28 11:54:48 EST 2004 + * @package System.I18N + */ +class Translation extends TComponent +{ + /** + * The array of formatters. We define 1 formatter per translation catalog + * This is a class static variable. + * @var array + */ + protected static $formatters=array(); + + /** + * Initialize the TTranslate translation components + */ + public static function init($catalogue='messages') + { + static $saveEventHandlerAttached=false; + + //initialized the default class wide formatter + if(!isset(self::$formatters[$catalogue])) + { + $app = Prado::getApplication()->getGlobalization(); + $config = $app->getTranslationConfiguration(); + $source = MessageSource::factory($config['type'], + $config['source'], + $config['filename']); + + $source->setCulture($app->getCulture()); + + if(TPropertyValue::ensureBoolean($config['cache'])) + $source->setCache(new MessageCache($config['cache'])); + + self::$formatters[$catalogue] = new MessageFormat($source, $app->getCharset()); + + //mark untranslated text + if($ps=$config['marker']) + self::$formatters[$catalogue]->setUntranslatedPS(array($ps,$ps)); + + //save the message on end request + // Do it only once ! + if(!$saveEventHandlerAttached && TPropertyValue::ensureBoolean($config['autosave'])) + { + Prado::getApplication()->attachEventHandler( + 'OnEndRequest', array('Translation', 'saveMessages')); + $saveEventHandlerAttached=true; + } + } + } + + /** + * Get the static formatter from this component. + * @return MessageFormat formattter. + * @see localize() + */ + public static function formatter($catalogue='messages') + { + return self::$formatters[$catalogue]; + } + + /** + * Save untranslated messages to the catalogue. + */ + public static function saveMessages() + { + static $onceonly = true; + + if($onceonly) + { + foreach (self::$formatters as $catalogue=>$formatter) + { + $app = Prado::getApplication()->getGlobalization(); + $config = $app->getTranslationConfiguration(); + if(isset($config['autosave'])) + { + $formatter->getSource()->setCulture($app->getCulture()); + $formatter->getSource()->save($catalogue); + } + } + $onceonly = false; + } + } +} diff --git a/gui/baculum/framework/I18N/core/ChoiceFormat.php b/gui/baculum/framework/I18N/core/ChoiceFormat.php new file mode 100644 index 0000000000..f5db61dbff --- /dev/null +++ b/gui/baculum/framework/I18N/core/ChoiceFormat.php @@ -0,0 +1,225 @@ + + * @version $Revision: 1.1 $ $Date: 2005/01/11 07:19:39 $ + * @package System.I18N.core + */ + + +/** + * ChoiceFormat class. + * + * ChoiceFormat converts between ranges of numeric values and string + * names for those ranges. + * + * A ChoiceFormat splits the real number line -Inf to +Inf into two or + * more contiguous ranges. Each range is mapped to a string. + * ChoiceFormat is generally used in a MessageFormat for displaying + * grammatically correct plurals such as "There are 2 files." + * + * + * $string = '[0] are no files |[1] is one file |(1,Inf] are {number} files'; + * + * $formatter = new MessageFormat(...); //init for a source + * $translated = $formatter->format($string); + * + * $choice = new ChoiceFormat(); + * echo $choice->format($translated, 0); //shows "are no files" + * + * + * The message/string choices are separated by the pipe "|" followed + * by a set notation of the form + * # [1,2] -- accepts values between 1 and 2, inclusive. + * # (1,2) -- accepts values between 1 and 2, excluding 1 and 2. + * # {1,2,3,4} -- only values defined in the set are accepted. + * # [-Inf,0) -- accepts value greater or equal to negative infinity + * and strictly less than 0 + * Any non-empty combinations of the delimiters of square and round brackets + * are acceptable. + * + * Since version 3.1.2 the following set notation is also possible. + * + * # {n: n % 10 > 1 && n % 10 < 5} -- matches numbers like 2, 3, 4, 22, 23, 24 + * + * Where set is defined by the expression after n:. In particular, the expression + * accepts the following mathematical/logical operators to form a set of logical conditions + * on the value given by n: + * # < -- less than. + * # <= -- less than equals. + * # > -- greater than. + * # >= -- greater than equals. + * # == -- of equal value. + * # % -- modulo, e.g., 1 % 10 equals 1, 11 % 10 equals 1. + * # - -- minus, negative. + * # + -- addition. + * # & -- conditional AND. + * # && -- condition AND with short circuit. + * # | -- conditional OR. + * # || -- conditional OR with short circuit. + * # ! -- negation. + * + * Additional round brackets can also be used to perform grouping. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004 + * @package System.I18N.core + */ +class ChoiceFormat +{ + /** + * The pattern to validate a set notation + * @var string + */ + protected $validate = '/[\(\[\{]|[-Inf\d:\s]+|,|[\+Inf\d\s:\?\-=!><%\|&\(\)]+|[\)\]\}]/ms'; + + /** + * The pattern to parse the formatting string. + * @var string + */ + protected $parse = '/\s*\|?([\(\[\{]([-Inf\d:\s]+,?[\+Inf\d\s:\?\-=!><%\|&\(\)]*)+[\)\]\}])\s*/'; + + /** + * The value for positive infinity. + * @var float + */ + protected $inf; + + + /** + * Constructor. + */ + function __construct() + { + $this->inf = -log(0); + } + + + /** + * Determine if the given number belongs to a given set + * @param float the number to test. + * @param string the set, in set notation. + * @return boolean true if number is in the set, false otherwise. + */ + function isValid($number, $set) + { + $n = preg_match_all($this->validate,$set,$matches,PREG_SET_ORDER); + + if($n < 3) throw new Exception("Invalid set \"{$set}\""); + + if(preg_match('/\{\s*n:([^\}]+)\}/', $set, $def)) + { + return $this->isValidSetNotation($number, $def[1]); + } + + $leftBracket = $matches[0][0]; + $rightBracket = $matches[$n-1][0]; + + $i = 0; + $elements = array(); + foreach($matches as $match) + { + $string = $match[0]; + if($i != 0 && $i != $n-1 && $string !== ',') + { + if($string == '-Inf') + $elements[] = -1*$this->inf; + else if ($string == '+Inf' || $string == 'Inf') + $elements[] = $this->inf; + else + $elements[] = floatval($string); + } + $i++; + } + $total = count($elements); + $number = floatval($number); + + if($leftBracket == '{' && $rightBracket == '}') + return in_array($number, $elements); + + $left = false; + if($leftBracket == '[') + $left = $number >= $elements[0]; + else if ($leftBracket == '(') + $left = $number > $elements[0]; + + $right = false; + if($rightBracket==']') + $right = $number <= $elements[$total-1]; + else if($rightBracket == ')') + $right = $number < $elements[$total-1]; + + if($left && $right) return true; + + return false; + } + + protected function isValidSetNotation($number, $set) + { + $str = '$result = '.str_replace('n', '$number', $set).';'; + try + { + eval($str); + return $result; + } + catch(Exception $e) + { + return false; + } + } + + /** + * Parse a choice string and get a list of sets and a list of strings + * corresponding to the sets. + * @param string the string containing the choices + * @return array array($sets, $strings) + */ + function parse($string) + { + $n = preg_match_all($this->parse,$string,$matches, PREG_OFFSET_CAPTURE); + $sets = array(); + foreach($matches[1] as $match) + $sets[] = $match[0]; + $offset = $matches[0]; + $strings = array(); + for($i = 0; $i < $n; $i++) + { + $len = strlen($offset[$i][0]); + $begin = $i == 0? $len : $offset[$i][1] + $len; + $end = $i == $n-1 ? strlen($string) : $offset[$i+1][1]; + $strings[] = substr($string, $begin, $end - $begin); + } + return array($sets, $strings); + } + + /** + * For the choice string, and a number, find and return the + * string that satisfied the set within the choices. + * @param string the choices string. + * @param float the number to test. + * @return string the choosen string. + */ + public function format($string, $number) + { + list($sets, $strings) = $this->parse($string); + $total = count($sets); + for($i = 0; $i < $total; $i++) + { + if($this->isValid($number, $sets[$i])) + return $strings[$i]; + } + return false; + } +} + diff --git a/gui/baculum/framework/I18N/core/CultureInfo.php b/gui/baculum/framework/I18N/core/CultureInfo.php new file mode 100644 index 0000000000..c22ed5c461 --- /dev/null +++ b/gui/baculum/framework/I18N/core/CultureInfo.php @@ -0,0 +1,632 @@ + + * @version $Id: CultureInfo.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.I18N.core + */ + +/** + * CultureInfo class. + * + * Represents information about a specific culture including the + * names of the culture, the calendar used, as well as access to + * culture-specific objects that provide methods for common operations, + * such as formatting dates, numbers, and currency. + * + * The CultureInfo class holds culture-specific information, such as the + * associated language, sublanguage, country/region, calendar, and cultural + * conventions. This class also provides access to culture-specific + * instances of DateTimeFormatInfo and NumberFormatInfo. These objects + * contain the information required for culture-specific operations, + * such as formatting dates, numbers and currency. + * + * The culture names follow the format "_", + * where is a lowercase two-letter code derived from ISO 639 + * codes. You can find a full list of the ISO-639 codes at + * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt + * + * The is an uppercase two-letter code derived from + * ISO 3166. A copy of ISO-3166 can be found at + * http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html + * + * For example, Australian English is "en_AU". + * + * @author Xiang Wei Zhuo + * @version $Id: CultureInfo.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + * @package System.I18N.core + */ +class CultureInfo +{ + /** + * ICU data filename extension. + * @var string + */ + private $dataFileExt = '.dat'; + + /** + * The ICU data array. + * @var array + */ + private $data = array(); + + /** + * The current culture. + * @var string + */ + private $culture; + + /** + * Directory where the ICU data is stored. + * @var string + */ + private $dataDir; + + /** + * A list of ICU date files loaded. + * @var array + */ + private $dataFiles = array(); + + /** + * The current date time format info. + * @var DateTimeFormatInfo + */ + private $dateTimeFormat; + + /** + * The current number format info. + * @var NumberFormatInfo + */ + private $numberFormat; + + /** + * A list of properties that are accessable/writable. + * @var array + */ + protected $properties = array(); + + /** + * Culture type, all. + * @see getCultures() + * @var int + */ + const ALL = 0; + + /** + * Culture type, neutral. + * @see getCultures() + * @var int + */ + const NEUTRAL = 1; + + /** + * Culture type, specific. + * @see getCultures() + * @var int + */ + const SPECIFIC = 2; + + /** + * Display the culture name. + * @return string the culture name. + * @see getName() + */ + function __toString() + { + return $this->getName(); + } + + + /** + * Allow functions that begins with 'set' to be called directly + * as an attribute/property to retrieve the value. + * @return mixed + */ + function __get($name) + { + $getProperty = 'get'.$name; + if(in_array($getProperty, $this->properties)) + return $this->$getProperty(); + else + throw new Exception('Property '.$name.' does not exists.'); + } + + /** + * Allow functions that begins with 'set' to be called directly + * as an attribute/property to set the value. + */ + function __set($name, $value) + { + $setProperty = 'set'.$name; + if(in_array($setProperty, $this->properties)) + $this->$setProperty($value); + else + throw new Exception('Property '.$name.' can not be set.'); + } + + + /** + * Initializes a new instance of the CultureInfo class based on the + * culture specified by name. E.g. new CultureInfo('en_AU'); + * The culture indentifier must be of the form + * "language_(country/region/variant)". + * @param string a culture name, e.g. "en_AU". + * @return return new CultureInfo. + */ + function __construct($culture='en') + { + $this->properties = get_class_methods($this); + + if(empty($culture)) + $culture = 'en'; + + $this->dataDir = $this->dataDir(); + $this->dataFileExt = $this->fileExt(); + + $this->setCulture($culture); + + $this->loadCultureData('root'); + $this->loadCultureData($culture); + } + + /** + * Get the default directory for the ICU data. + * The default is the "data" directory for this class. + * @return string directory containing the ICU data. + */ + protected static function dataDir() + { + return dirname(__FILE__).'/data/'; + } + + /** + * Get the filename extension for ICU data. Default is ".dat". + * @return string filename extension for ICU data. + */ + protected static function fileExt() + { + return '.dat'; + } + + /** + * Gets the CultureInfo that for this culture string + * @return CultureInfo invariant culture info is "en". + */ + public static function getInstance($culture) + { + static $instances = array(); + if(!isset($instances[$culture])) + $instances[$culture] = new CultureInfo($culture); + return $instances[$culture]; + } + + /** + * Determine if a given culture is valid. Simply checks that the + * culture data exists. + * @param string a culture + * @return boolean true if valid, false otherwise. + */ + public static function validCulture($culture) + { + if(preg_match('/^[_\\w]+$/', $culture)) + return is_file(self::dataDir().$culture.self::fileExt()); + + return false; + } + + /** + * Set the culture for the current instance. The culture indentifier + * must be of the form "_(country/region)". + * @param string culture identifier, e.g. "fr_FR_EURO". + */ + protected function setCulture($culture) + { + if(!empty($culture)) + { + if (!preg_match('/^[_\\w]+$/', $culture)) + throw new Exception('Invalid culture supplied: ' . $culture); + } + + $this->culture = $culture; + } + + /** + * Load the ICU culture data for the specific culture identifier. + * @param string the culture identifier. + */ + protected function loadCultureData($culture) + { + $file_parts = explode('_',$culture); + $current_part = $file_parts[0]; + + $files = array($current_part); + + for($i = 1, $k = count($file_parts); $i < $k; ++$i) + { + $current_part .= '_'.$file_parts[$i]; + $files[] = $current_part; + } + + foreach($files as $file) + { + $filename = $this->dataDir.$file.$this->dataFileExt; + + if(is_file($filename) == false) + throw new Exception('Data file for "'.$file.'" was not found.'); + + if(in_array($filename, $this->dataFiles) === false) + { + array_unshift($this->dataFiles, $file); + + $data = &$this->getData($filename); + $this->data[$file] = &$data; + + if(isset($data['__ALIAS'])) + $this->loadCultureData($data['__ALIAS'][0]); + unset($data); + } + } + } + + /** + * Get the data by unserializing the ICU data from disk. + * The data files are cached in a static variable inside + * this function. + * @param string the ICU data filename + * @return array ICU data + */ + protected function &getData($filename) + { + static $data = array(); + static $files = array(); + + if(!in_array($filename, $files)) + { + $data[$filename] = unserialize(file_get_contents($filename)); + $files[] = $filename; + } + + return $data[$filename]; + } + + /** + * Find the specific ICU data information from the data. + * The path to the specific ICU data is separated with a slash "/". + * E.g. To find the default calendar used by the culture, the path + * "calendar/default" will return the corresponding default calendar. + * Use merge=true to return the ICU including the parent culture. + * E.g. The currency data for a variant, say "en_AU" contains one + * entry, the currency for AUD, the other currency data are stored + * in the "en" data file. Thus to retrieve all the data regarding + * currency for "en_AU", you need to use findInfo("Currencies,true);. + * @param string the data you want to find. + * @param boolean merge the data from its parents. + * @return mixed the specific ICU data. + */ + protected function findInfo($path='/', $merge=false) + { + $result = array(); + foreach($this->dataFiles as $section) + { + $info = $this->searchArray($this->data[$section], $path); + + if($info) + { + if($merge) + $result = array_merge($info,$result); + else + return $info; + } + } + + return $result; + } + + /** + * Search the array for a specific value using a path separated using + * slash "/" separated path. e.g to find $info['hello']['world'], + * the path "hello/world" will return the corresponding value. + * @param array the array for search + * @param string slash "/" separated array path. + * @return mixed the value array using the path + */ + private function searchArray($info, $path='/') + { + $index = explode('/',$path); + + $array = $info; + + for($i = 0, $k = count($index); $i < $k; ++$i) + { + $value = $index[$i]; + if($i < $k-1 && isset($array[$value])) + $array = $array[$value]; + else if ($i == $k-1 && isset($array[$value])) + return $array[$value]; + } + } + + /** + * Gets the culture name in the format + * "_(country/regioncode2)". + * @return string culture name. + */ + function getName() + { + return $this->culture; + } + + /** + * Gets the DateTimeFormatInfo that defines the culturally appropriate + * format of displaying dates and times. + * @return DateTimeFormatInfo date time format information for the culture. + */ + function getDateTimeFormat() + { + if($this->dateTimeFormat === null) + { + $calendar = $this->getCalendar(); + $info = $this->findInfo("calendar/{$calendar}", true); + $this->setDateTimeFormat(new DateTimeFormatInfo($info)); + } + + return $this->dateTimeFormat; + } + + /** + * Set the date time format information. + * @param DateTimeFormatInfo the new date time format info. + */ + function setDateTimeFormat($dateTimeFormat) + { + $this->dateTimeFormat = $dateTimeFormat; + } + + /** + * Gets the default calendar used by the culture, e.g. "gregorian". + * @return string the default calendar. + */ + function getCalendar() + { + $info = $this->findInfo('calendar/default'); + return $info[0]; + } + + /** + * Gets the culture name in the language that the culture is set + * to display. Returns array('Language','Country'); + * 'Country' is omitted if the culture is neutral. + * @return array array with language and country as elements, localized. + */ + function getNativeName() + { + $lang = substr($this->culture,0,2); + $reg = substr($this->culture,3,2); + $language = $this->findInfo("Languages/{$lang}"); + $region = $this->findInfo("Countries/{$reg}"); + if($region) + return $language[0].' ('.$region[0].')'; + else + return $language[0]; + } + + /** + * Gets the culture name in English. + * Returns array('Language','Country'); + * 'Country' is omitted if the culture is neutral. + * @return string language (country), it may locale code string if english name does not exist. + */ + function getEnglishName() + { + $lang = substr($this->culture,0,2); + $reg = substr($this->culture,3,2); + $culture = $this->getInvariantCulture(); + + $language = $culture->findInfo("Languages/{$lang}"); + if(count($language) == 0) + return $this->culture; + + $region = $culture->findInfo("Countries/{$reg}"); + if($region) + return $language[0].' ('.$region[0].')'; + else + return $language[0]; + } + + /** + * Gets the CultureInfo that is culture-independent (invariant). + * Any changes to the invariant culture affects all other + * instances of the invariant culture. + * The invariant culture is assumed to be "en"; + * @return CultureInfo invariant culture info is "en". + */ + static function getInvariantCulture() + { + static $invariant; + if($invariant === null) + $invariant = new CultureInfo(); + return $invariant; + } + + /** + * Gets a value indicating whether the current CultureInfo + * represents a neutral culture. Returns true if the culture + * only contains two characters. + * @return boolean true if culture is neutral, false otherwise. + */ + function getIsNeutralCulture() + { + return strlen($this->culture) == 2; + } + + /** + * Gets the NumberFormatInfo that defines the culturally appropriate + * format of displaying numbers, currency, and percentage. + * @return NumberFormatInfo the number format info for current culture. + */ + function getNumberFormat() + { + if($this->numberFormat === null) + { + $elements = $this->findInfo('NumberElements'); + $patterns = $this->findInfo('NumberPatterns'); + $currencies = $this->getCurrencies(); + $data = array( 'NumberElements'=>$elements, + 'NumberPatterns'=>$patterns, + 'Currencies' => $currencies); + + $this->setNumberFormat(new NumberFormatInfo($data)); + } + return $this->numberFormat; + } + + /** + * Set the number format information. + * @param NumberFormatInfo the new number format info. + */ + function setNumberFormat($numberFormat) + { + $this->numberFormat = $numberFormat; + } + + /** + * Gets the CultureInfo that represents the parent culture of the + * current CultureInfo + * @return CultureInfo parent culture information. + */ + function getParent() + { + if(strlen($this->culture) == 2) + return $this->getInvariantCulture(); + + $lang = substr($this->culture,0,2); + return new CultureInfo($lang); + } + + /** + * Gets the list of supported cultures filtered by the specified + * culture type. This is an EXPENSIVE function, it needs to traverse + * a list of ICU files in the data directory. + * This function can be called statically. + * @param int culture type, CultureInfo::ALL, CultureInfo::NEUTRAL + * or CultureInfo::SPECIFIC. + * @return array list of culture information available. + */ + static function getCultures($type=CultureInfo::ALL) + { + $dataDir = CultureInfo::dataDir(); + $dataExt = CultureInfo::fileExt(); + $dir = dir($dataDir); + + $neutral = array(); + $specific = array(); + + while (false !== ($entry = $dir->read())) + { + if(is_file($dataDir.$entry) + && substr($entry,-4) == $dataExt + && $entry != 'root'.$dataExt) + { + $culture = substr($entry,0,-4); + if(strlen($culture) == 2) + $neutral[] = $culture; + else + $specific[] = $culture; + } + } + $dir->close(); + + switch($type) + { + case CultureInfo::ALL : + $all = array_merge($neutral, $specific); + sort($all); + return $all; + break; + case CultureInfo::NEUTRAL : + return $neutral; + break; + case CultureInfo::SPECIFIC : + return $specific; + break; + } + } + + /** + * Simplify a single element array into its own value. + * E.g. array(0 => array('hello'), 1 => 'world'); + * becomes array(0 => 'hello', 1 => 'world'); + * @param array with single elements arrays + * @return array simplified array. + */ + private function simplify($array) + { + for($i = 0, $k = count($array); $i<$k; ++$i) + { + $key = key($array); + if(is_array($array[$key]) + && count($array[$key]) == 1) + $array[$key] = $array[$key][0]; + next($array); + } + return $array; + } + + /** + * Get a list of countries in the language of the localized version. + * @return array a list of localized country names. + */ + function getCountries() + { + return $this->simplify($this->findInfo('Countries',true)); + } + + /** + * Get a list of currencies in the language of the localized version. + * @return array a list of localized currencies. + */ + function getCurrencies() + { + return $this->findInfo('Currencies',true); + } + + /** + * Get a list of languages in the language of the localized version. + * @return array list of localized language names. + */ + function getLanguages() + { + return $this->simplify($this->findInfo('Languages',true)); + } + + /** + * Get a list of scripts in the language of the localized version. + * @return array list of localized script names. + */ + function getScripts() + { + return $this->simplify($this->findInfo('Scripts',true)); + } + + /** + * Get a list of timezones in the language of the localized version. + * @return array list of localized timezones. + */ + function getTimeZones() + { + return $this->simplify($this->findInfo('zoneStrings',true)); + } +} + diff --git a/gui/baculum/framework/I18N/core/DateFormat.php b/gui/baculum/framework/I18N/core/DateFormat.php new file mode 100644 index 0000000000..6cfe1bfc54 --- /dev/null +++ b/gui/baculum/framework/I18N/core/DateFormat.php @@ -0,0 +1,651 @@ + + * @version $Revision: 1.8 $ $Date: 2005/12/15 07:14:49 $ + * @package System.I18N.core + */ + +/** + * Get the DateTimeFormatInfo class. + */ +require_once(dirname(__FILE__).'/DateTimeFormatInfo.php'); + +/** + * Get the encoding utilities + */ +require_once(dirname(__FILE__).'/util.php'); + +/** + * DateFormat class. + * + * The DateFormat class allows you to format dates and times with + * predefined styles in a locale-sensitive manner. Formatting times + * with the DateFormat class is similar to formatting dates. + * + * Formatting dates with the DateFormat class is a two-step process. + * First, you create a formatter with the getDateInstance method. + * Second, you invoke the format method, which returns a string containing + * the formatted date. + * + * DateTime values are formatted using standard or custom patterns stored + * in the properties of a DateTimeFormatInfo. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Sat Dec 04 14:10:49 EST 2004 + * @package System.I18N.core + */ +class DateFormat +{ + /** + * A list of tokens and their function call. + * @var array + */ + protected $tokens = array( + 'G'=>'Era', + 'y'=>'Year', + 'M'=>'Month', + 'd'=>'Day', + 'h'=>'Hour12', + 'H'=>'Hour24', + 'm'=>'Minutes', + 's'=>'Seconds', + 'E'=>'DayInWeek', + 'D'=>'DayInYear', + 'F'=>'DayInMonth', + 'w'=>'WeekInYear', + 'W'=>'WeekInMonth', + 'a'=>'AMPM', + 'k'=>'HourInDay', + 'K'=>'HourInAMPM', + 'z'=>'TimeZone' + ); + + /** + * A list of methods, to be used by the token function calls. + * @var array + */ + protected $methods = array(); + + /** + * The DateTimeFormatInfo, containing culture specific patterns and names. + * @var DateTimeFormatInfo + */ + protected $formatInfo; + + /** + * Initialize a new DateFormat. + * @param mixed either, null, a CultureInfo instance, + * a DateTimeFormatInfo instance, or a locale. + * @return DateFormat instance + */ + function __construct($formatInfo=null) + { + if($formatInfo === null) + $this->formatInfo = DateTimeFormatInfo::getInvariantInfo(); + else if($formatInfo instanceof CultureInfo) + $this->formatInfo = $formatInfo->DateTimeFormat; + else if($formatInfo instanceof DateTimeFormatInfo) + $this->formatInfo = $formatInfo; + else + $this->formatInfo = DateTimeFormatInfo::getInstance($formatInfo); + + $this->methods = get_class_methods($this); + } + + /** + * Format a date according to the pattern. + * @param mixed the time as integer or string in strtotime format. + * @return string formatted date time. + */ + public function format($time, $pattern='F', $charset='UTF-8') + { + if (is_numeric($time)) //assumes unix epoch + $time = floatval($time); + else if(is_string($time)) + $time = @strtotime($time); + + if($pattern === null) + $pattern = 'F'; + + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + + $date = $s->getDate($time); + + $pattern = $this->getPattern($pattern); + + $tokens = $this->getTokens($pattern); + + for($i = 0, $k = count($tokens); $i<$k; ++$i) + { + $pattern = $tokens[$i]; + if($pattern{0} == "'" + && $pattern{strlen($pattern)-1} == "'") + { + $sub = preg_replace('/(^\')|(\'$)/','',$pattern); + $tokens[$i] = str_replace('``````','\'',$sub); + } + else if($pattern == '``````') + { + $tokens[$i] = '\''; + } + else + { + $function = $this->getFunctionName($pattern); + if($function != null) + { + $fName = 'get'.$function; + if(in_array($fName, $this->methods)) + { + $rs = $this->$fName($date, $pattern); + $tokens[$i] = $rs; + } + else + throw new + Exception('function '.$function.' not found.'); + } + } + } + + return I18N_toEncoding(implode('',$tokens), $charset); + } + + /** + * For a particular token, get the corresponding function to call. + * @param string token + * @return mixed the function if good token, null otherwise. + */ + protected function getFunctionName($token) + { + if(isset($this->tokens[$token{0}])) + return $this->tokens[$token{0}]; + } + + /** + * Get the pattern from DateTimeFormatInfo or some predefined patterns. + * If the $pattern parameter is an array of 2 element, it will assume + * that the first element is the date, and second the time + * and try to find an appropriate pattern and apply + * DateTimeFormatInfo::formatDateTime + * See the tutorial documentation for futher details on the patterns. + * @param mixed a pattern. + * @return string a pattern. + * @see DateTimeFormatInfo::formatDateTime() + */ + protected function getPattern($pattern) + { + if(is_array($pattern) && count($pattern) == 2) + { + return $this->formatInfo->formatDateTime( + $this->getPattern($pattern[0]), + $this->getPattern($pattern[1])); + } + + switch($pattern) + { + case 'd': + return $this->formatInfo->ShortDatePattern; + break; + case 'D': + return $this->formatInfo->LongDatePattern; + break; + case 'p': + return $this->formatInfo->MediumDatePattern; + break; + case 'P': + return $this->formatInfo->FullDatePattern; + break; + case 't': + return $this->formatInfo->ShortTimePattern; + break; + case 'T': + return $this->formatInfo->LongTimePattern; + break; + case 'q': + return $this->formatInfo->MediumTimePattern; + break; + case 'Q': + return $this->formatInfo->FullTimePattern; + break; + case 'f': + return $this->formatInfo->formatDateTime( + $this->formatInfo->LongDatePattern, + $this->formatInfo->ShortTimePattern); + break; + case 'F': + return $this->formatInfo->formatDateTime( + $this->formatInfo->LongDatePattern, + $this->formatInfo->LongTimePattern); + break; + case 'g': + return $this->formatInfo->formatDateTime( + $this->formatInfo->ShortDatePattern, + $this->formatInfo->ShortTimePattern); + break; + case 'G': + return $this->formatInfo->formatDateTime( + $this->formatInfo->ShortDatePattern, + $this->formatInfo->LongTimePattern); + break; + case 'M': + case 'm': + return 'MMMM dd'; + break; + case 'R': + case 'r': + return 'EEE, dd MMM yyyy HH:mm:ss'; + break; + case 's': + return 'yyyy-MM-ddTHH:mm:ss'; + break; + case 'u': + return 'yyyy-MM-dd HH:mm:ss z'; + break; + case 'U': + return 'EEEE dd MMMM yyyy HH:mm:ss'; + break; + case 'Y': + case 'y': + return 'yyyy MMMM'; + break; + default : + return $pattern; + } + } + + /** + * Tokenize the pattern. The tokens are delimited by group of + * similar characters, e.g. 'aabb' will form 2 tokens of 'aa' and 'bb'. + * Any substrings, starting and ending with a single quote (') + * will be treated as a single token. + * @param string pattern. + * @return array string tokens in an array. + */ + protected function getTokens($pattern) + { + $char = null; + $tokens = array(); + $token = null; + + $text = false; + $pattern = preg_replace("/''/", '``````', $pattern); + + for($i = 0; $i < strlen($pattern); $i++) + { + if($char==null || $pattern{$i} == $char || $text) + { + $token .= $pattern{$i}; + } + else + { + $tokens[] = str_replace("","'",$token); + $token = $pattern{$i}; + } + + if($pattern{$i} == "'" && $text == false) + $text = true; + else if($text && $pattern{$i} == "'" && $char == "'") + $text = true; + else if($text && $char != "'" && $pattern{$i} == "'") + $text = false; + + $char = $pattern{$i}; + + } + $tokens[] = $token; + return $tokens; + } + + /** + * Get the year. + * "yy" will return the last two digits of year. + * "yyyy" will return the full integer year. + * @param array getdate format. + * @param string a pattern. + * @return string year + */ + protected function getYear($date, $pattern='yyyy') + { + $year = $date['year']; + switch($pattern) + { + case 'yy': + return substr($year,2); + case 'yyyy': + return $year; + default: + throw new Exception('The pattern for year is either "yy" or "yyyy".'); + } + } + + /** + * Get the month. + * "M" will return integer 1 through 12 + * "MM" will return the narrow month name, e.g. "J" + * "MMM" will return the abrreviated month name, e.g. "Jan" + * "MMMM" will return the month name, e.g. "January" + * @param array getdate format. + * @param string a pattern. + * @return string month name + */ + protected function getMonth($date, $pattern='M') + { + $month = $date['mon']; + + switch($pattern) + { + case 'M': + return $month; + case 'MM': + return str_pad($month, 2,'0',STR_PAD_LEFT); + case 'MMM': + return $this->formatInfo->AbbreviatedMonthNames[$month-1]; + break; + case 'MMMM': + return $this->formatInfo->MonthNames[$month-1]; + default: + throw new Exception('The pattern for month '. + 'is "M", "MM", "MMM", or "MMMM".'); + } + } + + /** + * Get the day of the week. + * "E" will return integer 0 (for Sunday) through 6 (for Saturday). + * "EE" will return the narrow day of the week, e.g. "M" + * "EEE" will return the abrreviated day of the week, e.g. "Mon" + * "EEEE" will return the day of the week, e.g. "Monday" + * @param array getdate format. + * @param string a pattern. + * @return string day of the week. + */ + protected function getDayInWeek($date, $pattern='EEEE') + { + $day = $date['wday']; + + switch($pattern) + { + case 'E': + return $day; + break; + case 'EE': + return $this->formatInfo->NarrowDayNames[$day]; + case 'EEE': + return $this->formatInfo->AbbreviatedDayNames[$day]; + break; + case 'EEEE': + return $this->formatInfo->DayNames[$day]; + break; + default: + throw new Exception('The pattern for day of the week '. + 'is "E", "EE", "EEE", or "EEEE".'); + } + } + + /** + * Get the day of the month. + * "d" for non-padding, "dd" will always return 2 characters. + * @param array getdate format. + * @param string a pattern. + * @return string day of the month + */ + protected function getDay($date, $pattern='d') + { + $day = $date['mday']; + + switch($pattern) + { + case 'd': + return $day; + case 'dd': + return str_pad($day, 2,'0',STR_PAD_LEFT); + default: + throw new Exception('The pattern for day of '. + 'the month is "d" or "dd".'); + } + } + + + /** + * Get the era. i.e. in gregorian, year > 0 is AD, else BC. + * @todo How to support multiple Eras?, e.g. Japanese. + * @param array getdate format. + * @param string a pattern. + * @return string era + */ + protected function getEra($date, $pattern='G') + { + + if($pattern != 'G') + throw new Exception('The pattern for era is "G".'); + + $year = $date['year']; + if($year > 0) + return $this->formatInfo->getEra(1); + else + return $this->formatInfo->getEra(0); + } + + /** + * Get the hours in 24 hour format, i.e. [0-23]. + * "H" for non-padding, "HH" will always return 2 characters. + * @param array getdate format. + * @param string a pattern. + * @return string hours in 24 hour format. + */ + protected function getHour24($date, $pattern='H') + { + $hour = $date['hours']; + + switch($pattern) + { + case 'H': + return $hour; + case 'HH': + return str_pad($hour, 2,'0',STR_PAD_LEFT); + default: + throw new Exception('The pattern for 24 hour '. + 'format is "H" or "HH".'); + } + } + + /** + * Get the AM/PM designator, 12 noon is PM, 12 midnight is AM. + * @param array getdate format. + * @param string a pattern. + * @return string AM or PM designator + */ + protected function getAMPM($date, $pattern='a') + { + if($pattern != 'a') + throw new Exception('The pattern for AM/PM marker is "a".'); + + $hour = $date['hours']; + $ampm = (int)($hour/12); + return $this->formatInfo->AMPMMarkers[$ampm]; + } + + /** + * Get the hours in 12 hour format. + * "h" for non-padding, "hh" will always return 2 characters. + * @param array getdate format. + * @param string a pattern. + * @return string hours in 12 hour format. + */ + protected function getHour12($date, $pattern='h') + { + $hour = $date['hours']; + $hour = ($hour==12|$hour==0)?12:($hour)%12; + + switch($pattern) + { + case 'h': + return $hour; + case 'hh': + return str_pad($hour, 2,'0',STR_PAD_LEFT); + default: + throw new Exception('The pattern for 24 hour '. + 'format is "H" or "HH".'); + } + } + + /** + * Get the minutes. + * "m" for non-padding, "mm" will always return 2 characters. + * @param array getdate format. + * @param string a pattern. + * @return string minutes. + */ + protected function getMinutes($date, $pattern='m') + { + $minutes = $date['minutes']; + + switch($pattern) + { + case 'm': + return $minutes; + case 'mm': + return str_pad($minutes, 2,'0',STR_PAD_LEFT); + default: + throw new Exception('The pattern for minutes is "m" or "mm".'); + } + } + + /** + * Get the seconds. + * "s" for non-padding, "ss" will always return 2 characters. + * @param array getdate format. + * @param string a pattern. + * @return string seconds + */ + protected function getSeconds($date, $pattern='s') + { + $seconds = $date['seconds']; + + switch($pattern) + { + case 's': + return $seconds; + case 'ss': + return str_pad($seconds, 2,'0',STR_PAD_LEFT); + default: + throw new Exception('The pattern for seconds is "s" or "ss".'); + } + } + + /** + * Get the timezone from the server machine. + * @todo How to get the timezone for a different region? + * @param array getdate format. + * @param string a pattern. + * @return string time zone + */ + protected function getTimeZone($date, $pattern='z') + { + if($pattern != 'z') + throw new Exception('The pattern for time zone is "z".'); + + return @date('T', @mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year'])); + } + + /** + * Get the day in the year, e.g. [1-366] + * @param array getdate format. + * @param string a pattern. + * @return int hours in AM/PM format. + */ + protected function getDayInYear($date, $pattern='D') + { + if($pattern != 'D') + throw new Exception('The pattern for day in year is "D".'); + + return $date['yday']; + } + + /** + * Get day in the month. + * @param array getdate format. + * @param string a pattern. + * @return int day in month + */ + protected function getDayInMonth($date, $pattern='FF') + { + switch ($pattern) { + case 'F': + return @date('j', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year'])); + break; + case 'FF': + return @date('d', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year'])); + break; + default: + throw new Exception('The pattern for day in month is "F" or "FF".'); + } + } + + /** + * Get the week in the year. + * @param array getdate format. + * @param string a pattern. + * @return int week in year + */ + protected function getWeekInYear($date, $pattern='w') + { + if($pattern != 'w') + throw new Exception('The pattern for week in year is "w".'); + + return @date('W', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year'])); + } + + /** + * Get week in the month. + * @param array getdate format. + * @return int week in month + */ + protected function getWeekInMonth($date, $pattern='W') + { + if($pattern != 'W') + throw new Exception('The pattern for week in month is "W".'); + + return @date('W', @mktime(0, 0, 0, $date['mon'], $date['mday'], $date['year'])) - date('W', mktime(0, 0, 0, $date['mon'], 1, $date['year'])); + } + + /** + * Get the hours [1-24]. + * @param array getdate format. + * @param string a pattern. + * @return int hours [1-24] + */ + protected function getHourInDay($date, $pattern='k') + { + if($pattern != 'k') + throw new Exception('The pattern for hour in day is "k".'); + + return $date['hours']+1; + } + + /** + * Get the hours in AM/PM format, e.g [1-12] + * @param array getdate format. + * @param string a pattern. + * @return int hours in AM/PM format. + */ + protected function getHourInAMPM($date, $pattern='K') + { + if($pattern != 'K') + throw new Exception('The pattern for hour in AM/PM is "K".'); + + return ($date['hours']+1)%12; + } + +} + diff --git a/gui/baculum/framework/I18N/core/DateTimeFormatInfo.php b/gui/baculum/framework/I18N/core/DateTimeFormatInfo.php new file mode 100644 index 0000000000..d4deee1f51 --- /dev/null +++ b/gui/baculum/framework/I18N/core/DateTimeFormatInfo.php @@ -0,0 +1,516 @@ + + * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $ + * @package System.I18N.core + */ + +/** + * Get the CultureInfo class. + */ +require_once(dirname(__FILE__).'/CultureInfo.php'); + + +/** + * Defines how DateTime values are formatted and displayed, depending + * on the culture. + * + * This class contains information, such as date patterns, time patterns, + * and AM/PM designators. + * + * To create a DateTimeFormatInfo for a specific culture, create a + * CultureInfo for that culture and retrieve the CultureInfo.DateTimeFormat + * property. For example: + * + * $culture = new CultureInfo('en_AU'); + * $dtfi = $culture->DateTimeFormat; + * + * + * To create a DateTimeFormatInfo for the invariant culture, use + * + * DateTimeFormatInfo::getInstance($culture=null); + * + * you may pass a CultureInfo parameter $culture to get the DateTimeFormatInfo + * for a specific culture. + * + * DateTime values are formatted using standard or custom patterns stored in + * the properties of a DateTimeFormatInfo. + * + * The standard patterns can be replaced with custom patterns by setting the + * associated properties of DateTimeFormatInfo. + * + * The following table lists the standard format characters for each standard + * pattern and the associated DateTimeFormatInfo property that can be set to + * modify the standard pattern. The format characters are case-sensitive; + * for example, 'g' and 'G' represent slightly different patterns. + * + * + * Format Character Associated Property Example Format Pattern (en-US) + * -------------------------------------------------------------------------- + * d ShortDatePattern MM/dd/yyyy + * D LongDatePattern dddd, dd MMMM yyyy + * F FullDateTimePattern dddd, dd MMMM yyyy HH:mm:ss + * m, M MonthDayPattern MMMM dd + * r, R RFC1123Pattern ddd, dd MMM yyyy HH':'mm':'ss 'GMT' + * s SortableDateTimePattern yyyy'-'MM'-'dd'T'HH':'mm':'ss + * t ShortTimePattern HH:mm + * T LongTimePattern HH:mm:ss + * Y YearMonthPattern yyyy MMMM + * -------------------------------------------------------------------------- + * + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 03 22:30:31 EST 2004 + * @package System.I18N.core + */ +class DateTimeFormatInfo +{ + /** + * ICU date time formatting data. + * @var array + */ + private $data = array(); + + /** + * A list of properties that are accessable/writable. + * @var array + */ + protected $properties = array(); + + /** + * Allow functions that begins with 'set' to be called directly + * as an attribute/property to retrieve the value. + * @return mixed + */ + function __get($name) + { + $getProperty = 'get'.$name; + if(in_array($getProperty, $this->properties)) + return $this->$getProperty(); + else + throw new Exception('Property '.$name.' does not exists.'); + } + + /** + * Allow functions that begins with 'set' to be called directly + * as an attribute/property to set the value. + */ + function __set($name, $value) + { + $setProperty = 'set'.$name; + if(in_array($setProperty, $this->properties)) + $this->$setProperty($value); + else + throw new Exception('Property '.$name.' can not be set.'); + } + + /** + * Initializes a new writable instance of the DateTimeFormatInfo class + * that is dependent on the ICU data for date time formatting + * information. N.B.You should not initialize this class directly + * unless you know what you are doing. Please use use + * DateTimeFormatInfo::getInstance() to create an instance. + * @param array ICU data for date time formatting. + * @see getInstance() + */ + function __construct($data=array()) + { + $this->properties = get_class_methods($this); + + if(empty($data)) + throw new Exception('Please provide the ICU data to initialize.'); + + $this->data = $data; + } + + /** + * Get the internal ICU data for date time formatting. + * @return array ICU date time formatting data. + */ + protected function getData() + { + return $this->data; + } + + /** + * Gets the default DateTimeFormatInfo that is culture-independent + * (invariant). + * @return DateTimeFormatInfo default DateTimeFormatInfo. + */ + static function getInvariantInfo() + { + static $invariant; + if($invariant === null) + { + $culture = CultureInfo::getInvariantCulture(); + $invariant = $culture->getDateTimeFormat(); + } + return $invariant; + } + + /** + * Returns the DateTimeFormatInfo associated with the specified culture. + * @param CultureInfo the culture that gets the DateTimeFormat property. + * @return DateTimeFormatInfo DateTimeFormatInfo for the specified + * culture. + */ + static function getInstance($culture=null) + { + + if ($culture instanceof CultureInfo) + return $culture->getDateTimeFormat(); + else if(is_string($culture)) + { + $cultureInfo = CultureInfo::getInstance($culture); + return $cultureInfo->getDateTimeFormat(); + } + else + { + $cultureInfo = CultureInfo::getInvariantCulture(); + return $cultureInfo->getDateTimeFormat(); + } + } + + /** + * A one-dimensional array of type String containing + * the culture-specific abbreviated names of the days + * of the week. The array for InvariantInfo contains + * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", and "Sat". + * @return array abbreviated day names + */ + function getAbbreviatedDayNames() + { + return $this->data['dayNames']['format']['abbreviated']; + //return $this->data['dayNames/format/abbreviated']; + } + + /** + * Set the abbreviated day names. The value should be + * an array of string starting with Sunday and ends in Saturady. + * For example, + * array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); + * @param array abbreviated day names. + */ + function setAbbreviatedDayNames($value) + { + $this->data['dayNames']['format']['abbreviated'] = $value; + } + + /** + * A one-dimensional array of type String containing + * the culture-specific narrow names of the days + * of the week. The array for InvariantInfo contains + * "S", "M", "T", "W", "T", "F", and "S". + * @return array narrow day names + */ + function getNarrowDayNames() + { + return $this->data['dayNames']['format']['narrow']; + } + + /** + * Set the narrow day names. The value should be + * an array of string starting with Sunday and ends in Saturady. + * For example, + * array("S", "M", "T", "W", "T", "F", "S"); + * @param array narrow day names. + */ + function setNarrowDayNames($value) + { + $this->data['dayNames']['format']['narrow'] = $value; + } + + /** + * A one-dimensional array of type String containing the + * culture-specific full names of the days of the week. + * The array for InvariantInfo contains "Sunday", "Monday", + * "Tuesday", "Wednesday", "Thursday", "Friday", and "Saturday". + * @return array day names + */ + function getDayNames() + { + return $this->data['dayNames']['format']['wide']; + } + + + /** + * Set the day names. The value should be + * an array of string starting with Sunday and ends in Saturady. + * For example, + * array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + * "Friday", "Saturday".); + * @param array day names. + */ + function setDayNames($value) + { + $this->data['dayNames']['format']['wide'] = $value; + } + + /** + * A one-dimensional array of type String containing the + * culture-specific narrow names of the months. The array + * for InvariantInfo contains "J", "F", "M", "A", "M", "J", + * "J", "A", "S", "O", "N", and "D". + * @return array narrow month names. + */ + function getNarrowMonthNames() + { + return $this->data['monthNames']['format']['narrow']; + } + + /** + * Set the narrow month names. The value should be + * an array of string starting with J and ends in D. + * For example, + * array("J","F","M","A","M","J","J","A","S","O","N","D"); + * @param array month names. + */ + function setNarrowMonthNames($value) + { + $this->data['monthNames']['format']['narrow'] = $value; + } + + /** + * A one-dimensional array of type String containing the + * culture-specific abbreviated names of the months. The array + * for InvariantInfo contains "Jan", "Feb", "Mar", "Apr", "May", + * "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", and "Dec". + * Returns wide names if abbreviated names doesn't exist. + * @return array abbreviated month names. + */ + function getAbbreviatedMonthNames() + { + if (isset($this->data['monthNames']['format']['abbreviated'])) + return $this->data['monthNames']['format']['abbreviated']; + else + return $this->data['monthNames']['format']['wide']; + } + + /** + * Set the abbreviated month names. The value should be + * an array of string starting with Jan and ends in Dec. + * For example, + * array("Jan", "Feb", "Mar", "Apr", "May", "Jun", + * "Jul", "Aug", "Sep","Oct","Nov","Dec"); + * @param array month names. + */ + function setAbbreviatedMonthNames($value) + { + $this->data['monthNames']['format']['abbreviated'] = $value; + } + + /** + * A one-dimensional array of type String containing the + * culture-specific full names of the months. The array for + * InvariantInfo contains "January", "February", "March", "April", + * "May", "June", "July", "August", "September", "October", "November", + * and "December" + * @return array month names. + */ + function getMonthNames() + { + return $this->data['monthNames']['format']['wide']; + } + + /** + * Set the month names. The value should be + * an array of string starting with Janurary and ends in December. + * For example, + * array("January", "February", "March", "April", "May", "June", + * "July", "August", "September","October","November","December"); + * @param array month names. + */ + function setMonthNames($value) + { + $this->data['monthNames']['format']['wide'] = $value; + } + + /** + * A string containing the name of the era. + * @param int era The integer representing the era. + * @return string the era name. + */ + function getEra($era) + { + $eraName = $this->data['eras']['abbreviated']; + return $eraName[$era]; + } + + /** + * The string designator for hours that are "ante meridiem" (before noon). + * The default for InvariantInfo is "AM". + * @return string AM designator. + */ + function getAMDesignator() + { + $result = $this->getAMPMMarkers(); + return $result[0]; + } + + /** + * Set the AM Designator. For example, 'AM'. + * @param string AM designator. + */ + function setAMDesignator($value) + { + $markers = $this->getAMPMMarkers(); + $markers[0] = $value; + $this->setAMPMMarkers($markers); + } + + /** + * The string designator for hours that are "post meridiem" (after noon). + * The default for InvariantInfo is "PM". + * @return string PM designator. + */ + function getPMDesignator() + { + $result = $this->getAMPMMarkers(); + return $result[1]; + } + + /** + * Set the PM Designator. For example, 'PM'. + * @param string PM designator. + */ + function setPMDesignator($value) + { + $markers = $this->getAMPMMarkers(); + $markers[1] = $value; + $this->setAMPMMarkers($markers); + } + + /** + * Get the AM and PM markers array. + * Default InvariantInfo for AM and PM is array('AM','PM'); + * @return array AM and PM markers + */ + function getAMPMMarkers() + { + return $this->data['AmPmMarkers']; + } + + /** + * Set the AM and PM markers array. + * For example array('AM','PM'); + * @param array AM and PM markers + */ + function setAMPMMarkers($value) + { + $this->data['AmPmMarkers'] = $value; + } + + /** + * Returns the full time pattern "HH:mm:ss z" (default). + * This is culture sensitive. + * @return string pattern "HH:mm:ss z". + */ + function getFullTimePattern() + { + return $this->data['DateTimePatterns'][0]; + } + + /** + * Returns the long time pattern "HH:mm:ss z" (default). + * This is culture sensitive. + * @return string pattern "HH:mm:ss z". + */ + function getLongTimePattern() + { + return $this->data['DateTimePatterns'][1]; + } + + /** + * Returns the medium time pattern "HH:mm:ss" (default). + * This is culture sensitive. + * @return string pattern "HH:mm:ss". + */ + function getMediumTimePattern() + { + return $this->data['DateTimePatterns'][2]; + } + + /** + * Returns the short time pattern "HH:mm" (default). + * This is culture sensitive. + * @return string pattern "HH:mm". + */ + function getShortTimePattern() + { + return $this->data['DateTimePatterns'][3]; + } + + /** + * Returns the full date pattern "EEEE, yyyy MMMM dd" (default). + * This is culture sensitive. + * @return string pattern "EEEE, yyyy MMMM dd". + */ + function getFullDatePattern() + { + return $this->data['DateTimePatterns'][4]; + } + + /** + * Returns the long date pattern "yyyy MMMM d" (default). + * This is culture sensitive. + * @return string pattern "yyyy MMMM d". + */ + function getLongDatePattern() + { + return $this->data['DateTimePatterns'][5]; + } + + /** + * Returns the medium date pattern "yyyy MMMM d" (default). + * This is culture sensitive. + * @return string pattern "yyyy MMM d". + */ + function getMediumDatePattern() + { + return $this->data['DateTimePatterns'][6]; + } + + /** + * Returns the short date pattern "yy/MM/dd" (default). + * This is culture sensitive. + * @return string pattern "yy/MM/dd". + */ + function getShortDatePattern() + { + return $this->data['DateTimePatterns'][7]; + } + + /** + * Returns the date time order pattern, "{1} {0}" (default). + * This is culture sensitive. + * @return string pattern "{1} {0}". + */ + function getDateTimeOrderPattern() + { + return $this->data['DateTimePatterns'][8]; + } + + /** + * Formats the date and time in a culture sensitive paterrn. + * The default is "Date Time". + * @return string date and time formated + */ + function formatDateTime($date, $time) + { + $pattern = $this->getDateTimeOrderPattern(); + return str_replace(array('{0}','{1}'), array($time, $date), $pattern); + } + +} diff --git a/gui/baculum/framework/I18N/core/Gettext/MO.php b/gui/baculum/framework/I18N/core/Gettext/MO.php new file mode 100644 index 0000000000..3e84ef2ef9 --- /dev/null +++ b/gui/baculum/framework/I18N/core/Gettext/MO.php @@ -0,0 +1,355 @@ + + * @version $Revision: 1.3 $ $Date: 2005/08/27 03:21:12 $ + * @package System.I18N.core + */ + + +// +----------------------------------------------------------------------+ +// | PEAR :: File :: Gettext :: MO | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license, | +// | that is available at http://www.php.net/license/3_0.txt | +// | If you did not receive a copy of the PHP license and are unable | +// | to obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Copyright (c) 2004 Michael Wallner | +// +----------------------------------------------------------------------+ +// +// $Id: MO.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + +/** + * File::Gettext::MO + * + * @author Michael Wallner + * @license PHP License + */ + +require_once dirname(__FILE__).'/TGettext.php'; + +/** + * File_Gettext_MO + * + * GNU MO file reader and writer. + * + * @author Michael Wallner + * @version $Revision: 1.3 $ + * @access public + * @package System.I18N.core + */ +class TGettext_MO extends TGettext +{ + /** + * file handle + * + * @access private + * @var resource + */ + protected $_handle = null; + + /** + * big endianess + * + * Whether to write with big endian byte order. + * + * @access public + * @var bool + */ + protected $writeBigEndian = false; + + /** + * Constructor + * + * @access public + * @return object File_Gettext_MO + * @param string $file path to GNU MO file + */ + function TGettext_MO($file = '') + { + $this->file = $file; + } + + /** + * _read + * + * @access private + * @return mixed + * @param int $bytes + */ + function _read($bytes = 1) + { + if (0 < $bytes = abs($bytes)) { + return fread($this->_handle, $bytes); + } + return null; + } + + /** + * _readInt + * + * @access private + * @return int + * @param bool $bigendian + */ + function _readInt($bigendian = false) + { + //unpack returns a reference???? + $unpacked = unpack($bigendian ? 'N' : 'V', $this->_read(4)); + return array_shift($unpacked); + } + + /** + * _writeInt + * + * @access private + * @return int + * @param int $int + */ + function _writeInt($int) + { + return $this->_write(pack($this->writeBigEndian ? 'N' : 'V', (int) $int)); + } + + /** + * _write + * + * @access private + * @return int + * @param string $data + */ + function _write($data) + { + return fwrite($this->_handle, $data); + } + + /** + * _writeStr + * + * @access private + * @return int + * @param string $string + */ + function _writeStr($string) + { + return $this->_write($string . "\0"); + } + + /** + * _readStr + * + * @access private + * @return string + * @param array $params associative array with offset and length + * of the string + */ + function _readStr($params) + { + fseek($this->_handle, $params['offset']); + return $this->_read($params['length']); + } + + /** + * Load MO file + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $file + */ + function load($file = null) + { + if (!isset($file)) { + $file = $this->file; + } + + // open MO file + if (!is_resource($this->_handle = @fopen($file, 'rb'))) { + return false; + } + // lock MO file shared + if (!@flock($this->_handle, LOCK_SH)) { + @fclose($this->_handle); + return false; + } + + // read (part of) magic number from MO file header and define endianess + + //unpack returns a reference???? + $unpacked = unpack('c', $this->_read(4)); + switch ($magic = array_shift($unpacked)) + { + case -34: + $be = false; + break; + + case -107: + $be = true; + break; + + default: + return false; + } + + // check file format revision - we currently only support 0 + if (0 !== ($_rev = $this->_readInt($be))) { + return false; + } + + // count of strings in this file + $count = $this->_readInt($be); + + // offset of hashing table of the msgids + $offset_original = $this->_readInt($be); + // offset of hashing table of the msgstrs + $offset_translat = $this->_readInt($be); + + // move to msgid hash table + fseek($this->_handle, $offset_original); + // read lengths and offsets of msgids + $original = array(); + for ($i = 0; $i < $count; $i++) { + $original[$i] = array( + 'length' => $this->_readInt($be), + 'offset' => $this->_readInt($be) + ); + } + + // move to msgstr hash table + fseek($this->_handle, $offset_translat); + // read lengths and offsets of msgstrs + $translat = array(); + for ($i = 0; $i < $count; $i++) { + $translat[$i] = array( + 'length' => $this->_readInt($be), + 'offset' => $this->_readInt($be) + ); + } + + // read all + for ($i = 0; $i < $count; $i++) { + $this->strings[$this->_readStr($original[$i])] = + $this->_readStr($translat[$i]); + } + + // done + @flock($this->_handle, LOCK_UN); + @fclose($this->_handle); + $this->_handle = null; + + // check for meta info + if (isset($this->strings[''])) { + $this->meta = parent::meta2array($this->strings['']); + unset($this->strings['']); + } + + return true; + } + + /** + * Save MO file + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $file + */ + function save($file = null) + { + if (!isset($file)) { + $file = $this->file; + } + + // open MO file + if (!is_resource($this->_handle = @fopen($file, 'wb'))) { + return false; + } + // lock MO file exclusively + if (!@flock($this->_handle, LOCK_EX)) { + @fclose($this->_handle); + return false; + } + + // write magic number + if ($this->writeBigEndian) { + $this->_write(pack('c*', 0x95, 0x04, 0x12, 0xde)); + } else { + $this->_write(pack('c*', 0xde, 0x12, 0x04, 0x95)); + } + + // write file format revision + $this->_writeInt(0); + + $count = count($this->strings) + ($meta = (count($this->meta) ? 1 : 0)); + // write count of strings + $this->_writeInt($count); + + $offset = 28; + // write offset of orig. strings hash table + $this->_writeInt($offset); + + $offset += ($count * 8); + // write offset transl. strings hash table + $this->_writeInt($offset); + + // write size of hash table (we currently ommit the hash table) + $this->_writeInt(0); + + $offset += ($count * 8); + // write offset of hash table + $this->_writeInt($offset); + + // unshift meta info + if ($this->meta) { + $meta = ''; + foreach ($this->meta as $key => $val) { + $meta .= $key . ': ' . $val . "\n"; + } + $strings = array('' => $meta) + $this->strings; + } else { + $strings = $this->strings; + } + + // write offsets for original strings + foreach (array_keys($strings) as $o) { + $len = strlen($o); + $this->_writeInt($len); + $this->_writeInt($offset); + $offset += $len + 1; + } + + // write offsets for translated strings + foreach ($strings as $t) { + $len = strlen($t); + $this->_writeInt($len); + $this->_writeInt($offset); + $offset += $len + 1; + } + + // write original strings + foreach (array_keys($strings) as $o) { + $this->_writeStr($o); + } + + // write translated strings + foreach ($strings as $t) { + $this->_writeStr($t); + } + + // done + @flock($this->_handle, LOCK_UN); + @fclose($this->_handle); + chmod($file,PRADO_CHMOD); + return true; + } +} diff --git a/gui/baculum/framework/I18N/core/Gettext/PO.php b/gui/baculum/framework/I18N/core/Gettext/PO.php new file mode 100644 index 0000000000..950a5de34c --- /dev/null +++ b/gui/baculum/framework/I18N/core/Gettext/PO.php @@ -0,0 +1,160 @@ + + * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $ + * @package System.I18N.core + */ + +// +----------------------------------------------------------------------+ +// | PEAR :: File :: Gettext :: PO | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license, | +// | that is available at http://www.php.net/license/3_0.txt | +// | If you did not receive a copy of the PHP license and are unable | +// | to obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Copyright (c) 2004 Michael Wallner | +// +----------------------------------------------------------------------+ +// +// $Id: PO.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + +/** + * File::Gettext::PO + * + * @author Michael Wallner + * @license PHP License + */ + +require_once dirname(__FILE__).'/TGettext.php'; + +/** + * File_Gettext_PO + * + * GNU PO file reader and writer. + * + * @author Michael Wallner + * @version $Revision: 1.2 $ + * @access public + * @package System.I18N.core + */ +class TGettext_PO extends TGettext +{ + /** + * Constructor + * + * @access public + * @return object File_Gettext_PO + * @param string path to GNU PO file + */ + function TGettext_PO($file = '') + { + $this->file = $file; + } + + /** + * Load PO file + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $file + */ + function load($file = null) + { + if (!isset($file)) { + $file = $this->file; + } + + // load file + if (!$contents = @file($file)) { + return false; + } + $contents = implode('', $contents); + + // match all msgid/msgstr entries + $matched = preg_match_all( + '/(msgid\s+("([^"]|\\\\")*?"\s*)+)\s+' . + '(msgstr\s+("([^"]|\\\\")*?"\s*)+)/', + $contents, $matches + ); + unset($contents); + + if (!$matched) { + return false; + } + + // get all msgids and msgtrs + for ($i = 0; $i < $matched; $i++) { + $msgid = preg_replace( + '/\s*msgid\s*"(.*)"\s*/s', '\\1', $matches[1][$i]); + $msgstr= preg_replace( + '/\s*msgstr\s*"(.*)"\s*/s', '\\1', $matches[4][$i]); + $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr); + } + + // check for meta info + if (isset($this->strings[''])) { + $this->meta = parent::meta2array($this->strings['']); + unset($this->strings['']); + } + + return true; + } + + /** + * Save PO file + * + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $file + */ + function save($file = null) + { + if (!isset($file)) { + $file = $this->file; + } + + // open PO file + if (!is_resource($fh = @fopen($file, 'w'))) { + return false; + } + + // lock PO file exclusively + if (!flock($fh, LOCK_EX)) { + fclose($fh); + return false; + } + // write meta info + if (count($this->meta)) { + $meta = 'msgid ""' . "\nmsgstr " . '""' . "\n"; + foreach ($this->meta as $k => $v) { + $meta .= '"' . $k . ': ' . $v . '\n"' . "\n"; + } + fwrite($fh, $meta . "\n"); + } + // write strings + foreach ($this->strings as $o => $t) { + fwrite($fh, + 'msgid "' . parent::prepare($o, true) . '"' . "\n" . + 'msgstr "' . parent::prepare($t, true) . '"' . "\n\n" + ); + } + + //done + @flock($fh, LOCK_UN); + @fclose($fh); + chmod($file,PRADO_CHMOD); + return true; + } +} diff --git a/gui/baculum/framework/I18N/core/Gettext/TGettext.php b/gui/baculum/framework/I18N/core/Gettext/TGettext.php new file mode 100644 index 0000000000..abf323927f --- /dev/null +++ b/gui/baculum/framework/I18N/core/Gettext/TGettext.php @@ -0,0 +1,286 @@ + + * @version $Revision: 1.4 $ $Date: 2005/01/09 23:36:23 $ + * @package System.I18N.core + */ + +// +----------------------------------------------------------------------+ +// | PEAR :: File :: Gettext | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license, | +// | that is available at http://www.php.net/license/3_0.txt | +// | If you did not receive a copy of the PHP license and are unable | +// | to obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Copyright (c) 2004 Michael Wallner | +// +----------------------------------------------------------------------+ +// +// $Id: TGettext.php 3187 2012-07-12 11:21:01Z ctrlaltca $ + +/** + * File::Gettext + * + * @author Michael Wallner + * @license PHP License + */ + +/** + * Use PHPs builtin error messages + */ +//ini_set('track_errors', true); + +/** + * File_Gettext + * + * GNU gettext file reader and writer. + * + * ################################################################# + * # All protected members of this class are public in its childs. # + * ################################################################# + * + * @author Michael Wallner + * @version $Revision: 1.4 $ + * @access public + * @package System.I18N.core + */ +class TGettext +{ + /** + * strings + * + * associative array with all [msgid => msgstr] entries + * + * @access protected + * @var array + */ + protected $strings = array(); + + /** + * meta + * + * associative array containing meta + * information like project name or content type + * + * @access protected + * @var array + */ + protected $meta = array(); + + /** + * file path + * + * @access protected + * @var string + */ + protected $file = ''; + + /** + * Factory + * + * @static + * @access public + * @return object Returns File_Gettext_PO or File_Gettext_MO on success + * or PEAR_Error on failure. + * @param string $format MO or PO + * @param string $file path to GNU gettext file + */ + function factory($format, $file = '') + { + $format = strToUpper($format); + $filename = dirname(__FILE__).'/'.$format.'.php'; + if(is_file($filename) == false) + throw new Exception ("Class file $file not found"); + + include_once $filename; + $class = 'TGettext_' . $format; + + return new $class($file); + } + + /** + * poFile2moFile + * + * That's a simple fake of the 'msgfmt' console command. It reads the + * contents of a GNU PO file and saves them to a GNU MO file. + * + * @static + * @access public + * @return mixed Returns true on success or PEAR_Error on failure. + * @param string $pofile path to GNU PO file + * @param string $mofile path to GNU MO file + */ + function poFile2moFile($pofile, $mofile) + { + if (!is_file($pofile)) { + throw new Exception("File $pofile doesn't exist."); + } + + include_once dirname(__FILE__).'/PO.php'; + + $PO = new TGettext_PO($pofile); + if (true !== ($e = $PO->load())) { + return $e; + } + + $MO = $PO->toMO(); + if (true !== ($e = $MO->save($mofile))) { + return $e; + } + unset($PO, $MO); + + return true; + } + + /** + * prepare + * + * @static + * @access protected + * @return string + * @param string $string + * @param bool $reverse + */ + function prepare($string, $reverse = false) + { + if ($reverse) { + $smap = array('"', "\n", "\t", "\r"); + $rmap = array('\"', '\\n"' . "\n" . '"', '\\t', '\\r'); + return (string) str_replace($smap, $rmap, $string); + } else { + $string = preg_replace('/"\s+"/', '', $string); + $smap = array('\\n', '\\r', '\\t', '\"'); + $rmap = array("\n", "\r", "\t", '"'); + return (string) str_replace($smap, $rmap, $string); + } + } + + /** + * meta2array + * + * @static + * @access public + * @return array + * @param string $meta + */ + function meta2array($meta) + { + $array = array(); + foreach (explode("\n", $meta) as $info) { + if ($info = trim($info)) { + list($key, $value) = explode(':', $info, 2); + $array[trim($key)] = trim($value); + } + } + return $array; + } + + /** + * toArray + * + * Returns meta info and strings as an array of a structure like that: + * + * array( + * 'meta' => array( + * 'Content-Type' => 'text/plain; charset=iso-8859-1', + * 'Last-Translator' => 'Michael Wallner ', + * 'PO-Revision-Date' => '2004-07-21 17:03+0200', + * 'Language-Team' => 'German ', + * ), + * 'strings' => array( + * 'All rights reserved' => 'Alle Rechte vorbehalten', + * 'Welcome' => 'Willkommen', + * // ... + * ) + * ) + * + * + * @see fromArray() + * @access protected + * @return array + */ + function toArray() + { + return array('meta' => $this->meta, 'strings' => $this->strings); + } + + /** + * fromArray + * + * Assigns meta info and strings from an array of a structure like that: + * + * array( + * 'meta' => array( + * 'Content-Type' => 'text/plain; charset=iso-8859-1', + * 'Last-Translator' => 'Michael Wallner ', + * 'PO-Revision-Date' => date('Y-m-d H:iO'), + * 'Language-Team' => 'German ', + * ), + * 'strings' => array( + * 'All rights reserved' => 'Alle Rechte vorbehalten', + * 'Welcome' => 'Willkommen', + * // ... + * ) + * ) + * + * + * @see toArray() + * @access protected + * @return bool + * @param array $array + */ + function fromArray($array) + { + if (!array_key_exists('strings', $array)) { + if (count($array) != 2) { + return false; + } else { + list($this->meta, $this->strings) = $array; + } + } else { + $this->meta = @$array['meta']; + $this->strings = @$array['strings']; + } + return true; + } + + /** + * toMO + * + * @access protected + * @return object File_Gettext_MO + */ + function toMO() + { + include_once dirname(__FILE__).'/MO.php'; + $MO = new TGettext_MO; + $MO->fromArray($this->toArray()); + return $MO; + } + + /** + * toPO + * + * @access protected + * @return object File_Gettext_PO + */ + function toPO() + { + include_once dirname(__FILE__).'/PO.php'; + $PO = new TGettext_PO; + $PO->fromArray($this->toArray()); + return $PO; + } +} diff --git a/gui/baculum/framework/I18N/core/HTTPNegotiator.php b/gui/baculum/framework/I18N/core/HTTPNegotiator.php new file mode 100644 index 0000000000..26b532b8f5 --- /dev/null +++ b/gui/baculum/framework/I18N/core/HTTPNegotiator.php @@ -0,0 +1,129 @@ + + * @version $Revision: 1.2 $ $Date: 2005/01/05 03:15:14 $ + * @package System.I18N.core + */ + +/** + * Include the CultureInfo class. + */ +require_once(dirname(__FILE__).'/CultureInfo.php'); + +/** + * HTTPNegotiator class. + * + * Get the language and charset information from the client browser. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 16:01:35 EST 2004 + * @package System.I18N.core + */ +class HTTPNegotiator +{ + /** + * A list of languages accepted by the browser. + * @var array + */ + protected $languages; + + /** + * A list of charsets accepted by the browser + * @var array + */ + protected $charsets; + + /** + * Get a list of languages acceptable by the client browser + * @return array languages ordered in the user browser preferences. + */ + function getLanguages() + { + if($this->languages !== null) { + return $this->languages; + } + + $this->languages = array(); + + if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + return $this->languages; + + //$basedir = CultureInfo::dataDir(); + //$ext = CultureInfo::fileExt(); + $info = new CultureInfo(); + + foreach(explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $lang) + { + // Cut off any q-value that might come after a semi-colon + if ($pos = strpos($lang, ';')) + $lang = trim(substr($lang, 0, $pos)); + + if (strstr($lang, '-')) + { + $codes = explode('-',$lang); + if($codes[0] == 'i') + { + // Language not listed in ISO 639 that are not variants + // of any listed language, which can be registerd with the + // i-prefix, such as i-cherokee + if(count($codes)>1) + $lang = $codes[1]; + } + else + { + for($i = 0, $k = count($codes); $i<$k; ++$i) + { + if($i == 0) + $lang = strtolower($codes[0]); + else + $lang .= '_'.strtoupper($codes[$i]); + } + } + } + + + + if($info->validCulture($lang)) + $this->languages[] = $lang; + } + + return $this->languages; + } + + /** + * Get a list of charsets acceptable by the client browser. + * @return array list of charsets in preferable order. + */ + function getCharsets() + { + if($this->charsets !== null) { + return $this->charsets; + } + + $this->charsets = array(); + + if (!isset($_SERVER['HTTP_ACCEPT_CHARSET'])) + return $this->charsets; + + foreach (explode(',', $_SERVER['HTTP_ACCEPT_CHARSET']) as $charset) + { + if (!empty($charset)) + $this->charsets[] = preg_replace('/;.*/', '', $charset); + } + + return $this->charsets; + } +} + diff --git a/gui/baculum/framework/I18N/core/IMessageSource.php b/gui/baculum/framework/I18N/core/IMessageSource.php new file mode 100644 index 0000000000..f8263b9732 --- /dev/null +++ b/gui/baculum/framework/I18N/core/IMessageSource.php @@ -0,0 +1,122 @@ + + * @version $Revision: 1.3 $ $Date: 2005/01/09 22:15:32 $ + * @package System.I18N.core + */ + +/** + * IMessageSource interface. + * + * All messages source used by MessageFormat must be of IMessageSource. + * It defines a set of operations to add and retrive messages from the + * message source. In addition, message source can load a particular + * catalogue. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 17:40:19 EST 2004 + * @package System.I18N.core + */ +interface IMessageSource +{ + /** + * Load the translation table for this particular catalogue. + * The translation should be loaded in the following order. + * # [1] call getCatalogeList($catalogue) to get a list of + * variants for for the specified $catalogue. + * # [2] for each of the variants, call getSource($variant) + * to get the resource, could be a file or catalogue ID. + * # [3] verify that this resource is valid by calling isValidSource($source) + * # [4] try to get the messages from the cache + * # [5] if a cache miss, call load($source) to load the message array + * # [6] store the messages to cache. + * # [7] continue with the foreach loop, e.g. goto [2]. + * + * @param string a catalogue to load + * @return boolean true if loaded, false otherwise. + */ + function load($catalogue = 'messages'); + + /** + * Get the translation table. This includes all the loaded sections. + * It must return a 2 level array of translation strings. + * # "catalogue+variant" the catalogue and its variants. + * # "source string" translation keys, and its translations. + * + * array('catalogue+variant' => + * array('source string' => 'target string', ...) + * ...), + * ...); + * + * + * @return array 2 level array translation table. + */ + function read(); + + /** + * Save the list of untranslated blocks to the translation source. + * If the translation was not found, you should add those + * strings to the translation source via the append() method. + * @param string the catalogue to add to + * @return boolean true if saved successfuly, false otherwise. + */ + function save($catalogue='messages'); + + /** + * Add a untranslated message to the source. Need to call save() + * to save the messages to source. + * @param string message to add + * @return void + */ + function append($message); + + /** + * Delete a particular message from the specified catalogue. + * @param string the source message to delete. + * @param string the catalogue to delete from. + * @return boolean true if deleted, false otherwise. + */ + function delete($message, $catalogue='messages'); + + /** + * Update the translation. + * @param string the source string. + * @param string the new translation string. + * @param string comments + * @param string the catalogue of the translation. + * @return boolean true if translation was updated, false otherwise. + */ + function update($text, $target, $comments, $catalogue='messages'); + + /** + * Returns a list of catalogue as key and all it variants as value. + * @return array list of catalogues + */ + function catalogues(); + + /** + * Set the culture for this particular message source. + * @param string the Culture name. + */ + function setCulture($culture); + + /** + * Get the culture identifier for the source. + * @return string culture identifier. + */ + function getCulture(); + +} + diff --git a/gui/baculum/framework/I18N/core/MessageCache.php b/gui/baculum/framework/I18N/core/MessageCache.php new file mode 100644 index 0000000000..3bd21704bb --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageCache.php @@ -0,0 +1,170 @@ + $cacheDir, + 'lifeTime' => $this->getLifeTime(), + 'automaticSerialization' => true + ); + + $this->cache = new TCache_Lite($options); + } + + /** + * Get the cache life time. + * @return int Cache life time. + */ + public function getLifeTime() + { + return $this->lifetime; + } + + /** + * Set the cache life time. + * @param int $time Cache life time. + */ + public function setLifeTime($time) + { + $this->lifetime = (int)$time; + } + + /** + * Get the cache file ID based section and locale. + * @param string $catalogue The translation section. + * @param string $culture The translation locale, e.g. "en_AU". + */ + protected function getID($catalogue, $culture) + { + return $catalogue.':'.$culture; + } + + /** + * Get the cache file GROUP based section and locale. + * @param string $catalogue The translation section. + * @param string $culture The translation locale, e.g. "en_AU". + */ + protected function getGroup($catalogue, $culture) + { + return $catalogue.':'.get_class($this); + } + + /** + * Get the data from the cache. + * @param string $catalogue The translation section. + * @param string $culture The translation locale, e.g. "en_AU". + * @param string $filename If the source is a file, this file's modified + * time is newer than the cache's modified time, no cache hit. + * @return mixed Boolean FALSE if no cache hit. Otherwise, translation + * table data for the specified section and locale. + */ + public function get($catalogue, $culture, $lastmodified=0) + { + $ID = $this->getID($catalogue, $culture); + $group = $this->getGroup($catalogue, $culture); + + $this->cache->_setFileName($ID, $group); + + $cache = $this->cache->getCacheFile(); + + if(is_file($cache) == false) + return false; + + + $lastmodified = (int)$lastmodified; + + if($lastmodified <= 0 || $lastmodified > filemtime($cache)) + return false; + + //echo '@@ Cache hit: "'.$ID.'" : "'.$group.'"'; + //echo "
    \n"; + + return $this->cache->get($ID, $group); + } + + /** + * Save the data to cache for the specified section and locale. + * @param array $data The data to save. + * @param string $catalogue The translation section. + * @param string $culture The translation locale, e.g. "en_AU". + */ + public function save($data, $catalogue, $culture) + { + $ID = $this->getID($catalogue, $culture); + $group = $this->getGroup($catalogue, $culture); + + //echo '## Cache save: "'.$ID.'" : "'.$group.'"'; + //echo "
    \n"; + + return $this->cache->save($data, $ID, $group); + } + + /** + * Clean up the cache for the specified section and locale. + * @param string $catalogue The translation section. + * @param string $culture The translation locale, e.g. "en_AU". + */ + public function clean($catalogue, $culture) + { + $group = $this->getGroup($catalogue, $culture); + $this->cache->clean($group); + } + + /** + * Flush the cache. Deletes all the cache files. + */ + public function clear() + { + $this->cache->clean(); + } + +} + diff --git a/gui/baculum/framework/I18N/core/MessageFormat.php b/gui/baculum/framework/I18N/core/MessageFormat.php new file mode 100644 index 0000000000..fd0d445d43 --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageFormat.php @@ -0,0 +1,255 @@ + + * @version $Revision: 1.5 $ $Date: 2005/08/27 03:21:12 $ + * @package System.I18N.core + */ + +/** + * Get the MessageSource classes. + */ +require_once(dirname(__FILE__).'/MessageSource.php'); + +/** + * Get the encoding utilities + */ +require_once(dirname(__FILE__).'/util.php'); + +/** + * MessageFormat class. + * + * Format a message, that is, for a particular message find the + * translated message. The following is an example using + * a SQLite database to store the translation message. + * Create a new message format instance and echo "Hello" + * in simplified Chinese. This assumes that the world "Hello" + * is translated in the database. + * + * + * $source = MessageSource::factory('SQLite', 'sqlite://messages.db'); + * $source->setCulture('zh_CN'); + * $source->setCache(new MessageCache('./tmp')); + * + * $formatter = new MessageFormat($source); + * + * echo $formatter->format('Hello'); + * + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 20:46:16 EST 2004 + * @package System.I18N.core + */ +class MessageFormat +{ + /** + * The message source. + * @var MessageSource + */ + protected $source; + + /** + * A list of loaded message catalogues. + * @var array + */ + protected $catagloues = array(); + + /** + * The translation messages. + * @var array + */ + protected $messages = array(); + + /** + * A list of untranslated messages. + * @var array + */ + protected $untranslated = array(); + + /** + * The prefix and suffix to append to untranslated messages. + * @var array + */ + protected $postscript = array('',''); + + /** + * Set the default catalogue. + * @var string + */ + public $Catalogue; + + /** + * Output encoding charset + * @var string + */ + protected $charset = 'UTF-8'; + + /** + * Constructor. + * Create a new instance of MessageFormat using the messages + * from the supplied message source. + * @param MessageSource the source of translation messages. + * @param string charset for the message output. + */ + function __construct(IMessageSource $source, $charset='UTF-8') + { + $this->source = $source; + $this->setCharset($charset); + } + + /** + * Sets the charset for message output. + * @param string charset, default is UTF-8 + */ + public function setCharset($charset) + { + $this->charset = $charset; + } + + /** + * Gets the charset for message output. Default is UTF-8. + * @return string charset, default UTF-8 + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Load the message from a particular catalogue. A listed + * loaded catalogues is kept to prevent reload of the same + * catalogue. The load catalogue messages are stored + * in the $this->message array. + * @param string message catalogue to load. + */ + protected function loadCatalogue($catalogue) + { + if(in_array($catalogue,$this->catagloues)) + return; + + if($this->source->load($catalogue)) + { + $this->messages[$catalogue] = $this->source->read(); + $this->catagloues[] = $catalogue; + } + } + + /** + * Format the string. That is, for a particular string find + * the corresponding translation. Variable subsitution is performed + * for the $args parameter. A different catalogue can be specified + * using the $catalogue parameter. + * The output charset is determined by $this->getCharset(); + * @param string the string to translate. + * @param array a list of string to substitute. + * @param string get the translation from a particular message + * @param string charset, the input AND output charset + * catalogue. + * @return string translated string. + */ + public function format($string,$args=array(), $catalogue=null, $charset=null) + { + if(empty($charset)) $charset = $this->getCharset(); + + //force args as UTF-8 + foreach($args as $k => $v) + $args[$k] = I18N_toUTF8($v, $charset); + $s = $this->formatString(I18N_toUTF8($string, $charset),$args,$catalogue); + return I18N_toEncoding($s, $charset); + } + + /** + * Do string translation. + * @param string the string to translate. + * @param array a list of string to substitute. + * @param string get the translation from a particular message + * catalogue. + * @return string translated string. + */ + protected function formatString($string, $args=array(), $catalogue=null) + { + if(empty($catalogue)) + { + if(empty($this->Catalogue)) + $catalogue = 'messages'; + else + $catalogue = $this->Catalogue; + } + + $this->loadCatalogue($catalogue); + + if(empty($args)) + $args = array(); + + foreach($this->messages[$catalogue] as $variant) + { + // foreach of the translation units + foreach($variant as $source => $result) + { + // we found it, so return the target translation + if($source == $string) + { + //check if it contains only strings. + if(is_string($result)) + $target = $result; + else + { + $target = $result[0]; + } + //found, but untranslated + if(empty($target)) + { + return $this->postscript[0]. + strtr($string, $args). + $this->postscript[1]; + } + else + return strtr($target, $args); + } + } + } + + // well we did not find the translation string. + $this->source->append($string); + + return $this->postscript[0]. + strtr($string, $args). + $this->postscript[1]; + } + + /** + * Get the message source. + * @return MessageSource + */ + function getSource() + { + return $this->source; + } + + /** + * Set the prefix and suffix to append to untranslated messages. + * e.g. $postscript=array('[T]','[/T]'); will output + * "[T]Hello[/T]" if the translation for "Hello" can not be determined. + * @param array first element is the prefix, second element the suffix. + */ + function setUntranslatedPS($postscript) + { + if(is_array($postscript) && count($postscript)>=2) + { + $this->postscript[0] = $postscript[0]; + $this->postscript[1] = $postscript[1]; + } + } +} + diff --git a/gui/baculum/framework/I18N/core/MessageSource.php b/gui/baculum/framework/I18N/core/MessageSource.php new file mode 100644 index 0000000000..7d5d6ed3f9 --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageSource.php @@ -0,0 +1,335 @@ + + * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 $ + * @package System.I18N.core + */ + + /** + * Get the IMessageSource interface. + */ +require_once(dirname(__FILE__).'/IMessageSource.php'); + +/** + * Get the MessageCache class file. + */ +require_once(dirname(__FILE__).'/MessageCache.php'); + +/** + * Abstract MessageSource class. + * + * The base class for all MessageSources. Message sources must be instantiated + * using the factory method. The default valid sources are + * + * # XLIFF -- using XML XLIFF format to store the translation messages. + * # gettext -- Translated messages are stored in the gettext format. + * # Database -- Use an existing TDbConnection to store the messages. + * # SQLite -- (Deprecated) Store the translation messages in a SQLite database. + * # MySQL -- (Deprecated) Using a MySQL database to store the messages. + * + * A custom message source can be instantiated by specifying the filename + * parameter to point to the custom class file. E.g. + * + * $resource = '...'; //custom message source resource + * $classfile = '../MessageSource_MySource.php'; //custom message source + * $source = MessageSource::factory('MySource', $resource, $classfile); + * + * + * If you are writting your own message sources, pay attention to the + * loadCatalogue method. It details how the resources are loaded and cached. + * See also the existing message source types as examples. + * + * The following example instantiates a Database message source, set the culture, + * set the cache handler, and use the source in a message formatter. + * The messages are stored using an existing connection. The source parameter + * for the factory method must contain a valid ConnectionID. + * + * // db1 must be already configured + * $source = MessageSource::factory('Database', 'db1'); + * + * //set the culture and cache, store the cache in the /tmp directory. + * $source->setCulture('en_AU')l + * $source->setCache(new MessageCache('/tmp')); + * + * $formatter = new MessageFormat($source); + * + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 19:55:49 EST 2004 + * @package System.I18N.core + */ +abstract class MessageSource implements IMessageSource +{ + /** + * The culture name for this message source. + * @var string + */ + protected $culture; + + /** + * Array of translation messages. + * @var array + */ + protected $messages = array(); + + /** + * The source of message translations. + * @var string + */ + protected $source; + + /** + * The translation cache. + * @var MessageCache + */ + protected $cache; + + protected $untranslated = array(); + + /** + * Private constructor. MessageSource must be initialized using + * the factory method. + */ + private function __construct() + { + //throw new Exception('Please use the factory method to instantiate.'); + } + + /** + * Factory method to instantiate a new MessageSource depending on the + * source type. The allowed source types are 'XLIFF', 'gettext' and + * 'Database'. The source parameter depends on the source type. + * For 'gettext' and 'XLIFF', 'source' should point to the directory + * where the messages are stored. + * For 'Database', 'source' must be a valid connection id. + * If one of the deprecated types 'MySQL' or 'SQLite' is used, + * 'source' must contain a valid DSN. + * + * Custom message source are possible by supplying the a filename parameter + * in the factory method. + * + * @param string the message source type. + * @param string the location of the resource or the ConnectionID. + * @param string the filename of the custom message source. + * @return MessageSource a new message source of the specified type. + * @throws InvalidMessageSourceTypeException + */ + static function &factory($type, $source='.', $filename='') + { + $types = array('XLIFF','gettext','Database','MySQL','SQLite'); + + if(empty($filename) && !in_array($type, $types)) + throw new Exception('Invalid type "'.$type.'", valid types are '. + implode(', ', $types)); + + $class = 'MessageSource_'.$type; + + if(empty($filename)) + $filename = dirname(__FILE__).'/'.$class.'.php'; + + if(is_file($filename) == false) + throw new Exception("File $filename not found"); + + include_once $filename; + + $obj = new $class($source); + + return $obj; + } + + /** + * Load a particular message catalogue. Use read() to + * to get the array of messages. The catalogue loading sequence + * is as follows + * + * # [1] call getCatalogeList($catalogue) to get a list of + * variants for for the specified $catalogue. + * # [2] for each of the variants, call getSource($variant) + * to get the resource, could be a file or catalogue ID. + * # [3] verify that this resource is valid by calling isValidSource($source) + * # [4] try to get the messages from the cache + * # [5] if a cache miss, call load($source) to load the message array + * # [6] store the messages to cache. + * # [7] continue with the foreach loop, e.g. goto [2]. + * + * @param string a catalogue to load + * @return boolean true if loaded, false otherwise. + * @see read() + */ + function load($catalogue='messages') + { + $variants = $this->getCatalogueList($catalogue); + + $this->messages = array(); + + foreach($variants as $variant) + { + $source = $this->getSource($variant); + + if($this->isValidSource($source) == false) continue; + + $loadData = true; + + if($this->cache) + { + $data = $this->cache->get($variant, + $this->culture, $this->getLastModified($source)); + + if(is_array($data)) + { + $this->messages[$variant] = $data; + $loadData = false; + } + unset($data); + } + if($loadData) + { + $data = &$this->loadData($source); + if(is_array($data)) + { + $this->messages[$variant] = $data; + if($this->cache) + $this->cache->save($data, $variant, $this->culture); + } + unset($data); + } + } + + return true; + } + + /** + * Get the array of messages. + * @param parameter + * @return array translation messages. + */ + public function read() + { + return $this->messages; + } + + /** + * Get the cache handler for this source. + * @return MessageCache cache handler + */ + public function getCache() + { + return $this->cache; + } + + /** + * Set the cache handler for caching the messages. + * @param MessageCache the cache handler. + */ + public function setCache(MessageCache $cache) + { + $this->cache = $cache; + } + + /** + * Add a untranslated message to the source. Need to call save() + * to save the messages to source. + * @param string message to add + */ + public function append($message) + { + if(!in_array($message, $this->untranslated)) + $this->untranslated[] = $message; + } + + /** + * Set the culture for this message source. + * @param string culture name + */ + public function setCulture($culture) + { + $this->culture = $culture; + } + + /** + * Get the culture identifier for the source. + * @return string culture identifier. + */ + public function getCulture() + { + return $this->culture; + } + + /** + * Get the last modified unix-time for this particular catalogue+variant. + * @param string catalogue+variant + * @return int last modified in unix-time format. + */ + protected function getLastModified($source) + { + return 0; + } + + /** + * Load the message for a particular catalogue+variant. + * This methods needs to implemented by subclasses. + * @param string catalogue+variant. + * @return array of translation messages. + */ + protected function &loadData($variant) + { + return array(); + } + + /** + * Get the source, this could be a filename or database ID. + * @param string catalogue+variant + * @return string the resource key + */ + protected function getSource($variant) + { + return $variant; + } + + /** + * Determine if the source is valid. + * @param string catalogue+variant + * @return boolean true if valid, false otherwise. + */ + protected function isValidSource($source) + { + return false; + } + + /** + * Get all the variants of a particular catalogue. + * This method must be implemented by subclasses. + * @param string catalogue name + * @return array list of all variants for this catalogue. + */ + protected function getCatalogueList($catalogue) + { + return array(); + } +} + + +/** + * TMessageSourceIOException thrown when unable to modify message source + * data. + * + * @author Wei Zhuo + * @version $Revision: 1.4 $ $Date: 2005/12/17 06:11:28 ${DATE} ${TIME} $ + * @package System.I18N.core + */ +class TMessageSourceIOException extends TException +{ + +} diff --git a/gui/baculum/framework/I18N/core/MessageSource_Database.php b/gui/baculum/framework/I18N/core/MessageSource_Database.php new file mode 100644 index 0000000000..142c28db54 --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageSource_Database.php @@ -0,0 +1,322 @@ +_connID= (string)$source; + } + + /** + * @return TDbConnection the database connection that may be used to retrieve messages. + */ + public function getDbConnection() + { + if($this->_conn===null) + { + $this->_conn=$this->createDbConnection($this->_connID); + $this->_conn->setActive(true); + } + return $this->_conn; + } + + /** + * Creates the DB connection. + * @param string the module ID for TDataSourceConfig + * @return TDbConnection the created DB connection + * @throws TConfigurationException if module ID is invalid or empty + */ + protected function createDbConnection($connectionID) + { + if($connectionID!=='') + { + $conn=Prado::getApplication()->getModule($connectionID); + if($conn instanceof TDataSourceConfig) + return $conn->getDbConnection(); + else + throw new TConfigurationException('messagesource_connectionid_invalid',$connectionID); + } + else + throw new TConfigurationException('messagesource_connectionid_required'); + } + + /** + * Get an array of messages for a particular catalogue and cultural + * variant. + * @param string the catalogue name + variant + * @return array translation messages. + */ + protected function &loadData($variant) + { + $command=$this->getDBConnection()->createCommand( + 'SELECT t.id, t.source, t.target, t.comments + FROM trans_unit t, catalogue c + WHERE c.cat_id = t.cat_id + AND c.name = :variant + ORDER BY id ASC'); + $command->bindParameter(':variant',$variant,PDO::PARAM_STR); + $dataReader=$command->query(); + + $result = array(); + + foreach ($dataReader as $row) + $result[$row['source']] = array($row['target'],$row['id'],$row['comments']); + + return $result; + } + + /** + * Get the last modified unix-time for this particular catalogue+variant. + * We need to query the database to get the date_modified. + * @param string catalogue+variant + * @return int last modified in unix-time format. + */ + protected function getLastModified($source) + { + $command=$this->getDBConnection()->createCommand( + 'SELECT date_modified FROM catalogue WHERE name = :source'); + $command->bindParameter(':source',$source,PDO::PARAM_STR); + $result=$command->queryScalar(); + return $result ? $result : 0; + } + + + /** + * Check if a particular catalogue+variant exists in the database. + * @param string catalogue+variant + * @return boolean true if the catalogue+variant is in the database, + * false otherwise. + */ + protected function isValidSource($variant) + { + $command=$this->getDBConnection()->createCommand( + 'SELECT COUNT(*) FROM catalogue WHERE name = :variant'); + $command->bindParameter(':variant',$variant,PDO::PARAM_STR); + return $command->queryScalar()==1; + } + + /** + * Get all the variants of a particular catalogue. + * @param string catalogue name + * @return array list of all variants for this catalogue. + */ + protected function getCatalogueList($catalogue) + { + $variants = explode('_',$this->culture); + + $catalogues = array($catalogue); + + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $catalogue.'.'.$variant; + } + } + return array_reverse($catalogues); + } + + /** + * Retrive catalogue details, array($cat_id, $variant, $count). + * @param string catalogue + * @return array catalogue details, array($cat_id, $variant, $count). + */ + private function getCatalogueDetails($catalogue='messages') + { + if(empty($catalogue)) + $catalogue = 'messages'; + + $variant = $catalogue.'.'.$this->culture; + + $command=$this->getDBConnection()->createCommand( + 'SELECT cat_id FROM catalogue WHERE name = :variant'); + $command->bindParameter(':variant',$variant,PDO::PARAM_STR); + $cat_id=$command->queryScalar(); + + if ($cat_id===null) return false; + + $command=$this->getDBConnection()->createCommand( + 'SELECT COUNT(msg_id) FROM trans_unit WHERE cat_id = :catid '); + $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT); + $count=$command->queryScalar(); + + return array($cat_id, $variant, $count); + } + + /** + * Update the catalogue last modified time. + * @return boolean true if updated, false otherwise. + */ + private function updateCatalogueTime($cat_id, $variant) + { + $time = time(); + $command=$this->getDBConnection()->createCommand( + 'UPDATE catalogue SET date_modified = :moddate WHERE cat_id = :catid'); + $command->bindParameter(':moddate',$time,PDO::PARAM_INT); + $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT); + $result=$command->execute(); + + if(!empty($this->cache)) + $this->cache->clean($variant, $this->culture); + + return $result; + } + + /** + * Save the list of untranslated blocks to the translation source. + * If the translation was not found, you should add those + * strings to the translation source via the append() method. + * @param string the catalogue to add to + * @return boolean true if saved successfuly, false otherwise. + */ + function save($catalogue='messages') + { + $messages = $this->untranslated; + + if(count($messages) <= 0) return false; + + $details = $this->getCatalogueDetails($catalogue); + + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + if($cat_id <= 0) return false; + $inserted = 0; + + $time = time(); + + $command=$this->getDBConnection()->createCommand( + 'INSERT INTO trans_unit (cat_id,id,source,date_added) VALUES (:catid,:id,:source,:dateadded)'); + $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT); + $command->bindParameter(':id',$count,PDO::PARAM_INT); + $command->bindParameter(':source',$message,PDO::PARAM_STR); + $command->bindParameter(':dateadded',$time,PDO::PARAM_INT); + foreach($messages as $message) + { + if (empty($message)) continue; + $count++; $inserted++; + $command->execute(); + } + if($inserted > 0) + $this->updateCatalogueTime($cat_id, $variant); + + return $inserted > 0; + } + + /** + * Delete a particular message from the specified catalogue. + * @param string the source message to delete. + * @param string the catalogue to delete from. + * @return boolean true if deleted, false otherwise. + */ + function delete($message, $catalogue='messages') + { + $details = $this->getCatalogueDetails($catalogue); + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + $command=$this->getDBConnection()->createCommand( + 'DELETE FROM trans_unit WHERE cat_id = :catid AND source = :message'); + $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT); + $command->bindParameter(':message',$message,PDO::PARAM_STR); + + return ($command->execute()==1) ? $this->updateCatalogueTime($cat_id, $variant) : false; + + } + + /** + * Update the translation. + * @param string the source string. + * @param string the new translation string. + * @param string comments + * @param string the catalogue of the translation. + * @return boolean true if translation was updated, false otherwise. + */ + function update($text, $target, $comments, $catalogue='messages') + { + $details = $this->getCatalogueDetails($catalogue); + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + $time = time(); + $command=$this->getDBConnection()->createCommand( + 'UPDATE trans_unit SET target = :target, comments = :comments, date_modified = :datemod + WHERE cat_id = :catid AND source = :source'); + $command->bindParameter(':target',$target,PDO::PARAM_STR); + $command->bindParameter(':comments',$comments,PDO::PARAM_STR); + $command->bindParameter(':datemod',$time,PDO::PARAM_INT); + $command->bindParameter(':catid',$cat_id,PDO::PARAM_INT); + $command->bindParameter(':source',$text,PDO::PARAM_STR); + + return ($command->execute()==1) ? $this->updateCatalogueTime($cat_id, $variant) : false; + } + + /** + * Returns a list of catalogue as key and all it variants as value. + * @return array list of catalogues + */ + function catalogues() + { + $command=$this->getDBConnection()->createCommand( 'SELECT name FROM catalogue ORDER BY name'); + $dataReader=$command->query(); + + $result = array(); + + foreach ($dataReader as $row) + { + $details = explode('.',$row[0]); + if(!isset($details[1])) $details[1] = null; + + $result[] = $details; + } + + return $result; + } + +} diff --git a/gui/baculum/framework/I18N/core/MessageSource_MySQL.php b/gui/baculum/framework/I18N/core/MessageSource_MySQL.php new file mode 100644 index 0000000000..743694fa9e --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageSource_MySQL.php @@ -0,0 +1,417 @@ + + * @version $Revision: 1.4 $ $Date: 2005/02/25 09:59:40 $ + * @package System.I18N.core + */ + +/** + * Get the MessageSource class file. + */ +require_once(dirname(__FILE__).'/MessageSource.php'); + +/** + * Get the I18N utility file, contains the DSN parser. + */ +require_once(dirname(__FILE__).'/util.php'); + +/** + * MessageSource_MySQL class. + * + * Retrive the message translation from a MySQL database. + * + * See the MessageSource::factory() method to instantiate this class. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004 + * @package System.I18N.core + */ +class MessageSource_MySQL extends MessageSource +{ + /** + * The datasource string, full DSN to the database. + * @var string + */ + protected $source; + + /** + * The DSN array property, parsed by PEAR's DB DSN parser. + * @var array + */ + protected $dns; + + /** + * A resource link to the database + * @var db + */ + protected $db; + /** + * Constructor. + * Create a new message source using MySQL. + * @param string MySQL datasource, in PEAR's DB DSN format. + * @see MessageSource::factory(); + */ + function __construct($source) + { + $this->source = (string)$source; + $this->dns = parseDSN($this->source); + $this->db = $this->connect(); + } + + /** + * Destructor, close the database connection. + */ + function __destruct() + { + @mysql_close($this->db); + } + + /** + * Connect to the MySQL datasource + * @return resource MySQL connection. + * @throws Exception, connection and database errors. + */ + protected function connect() + { + /*static $conn; + + if($conn!==null) + return $conn; + */ + $dsninfo = $this->dns; + + if (isset($dsninfo['protocol']) && $dsninfo['protocol'] == 'unix') + $dbhost = ':' . $dsninfo['socket']; + else + { + $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost'; + if (!empty($dsninfo['port'])) + $dbhost .= ':' . $dsninfo['port']; + } + $user = $dsninfo['username']; + $pw = $dsninfo['password']; + + $connect_function = 'mysql_connect'; + + if ($dbhost && $user && $pw) + $conn = @$connect_function($dbhost, $user, $pw); + elseif ($dbhost && $user) + $conn = @$connect_function($dbhost, $user); + elseif ($dbhost) + $conn = @$connect_function($dbhost); + else + $conn = false; + + if (empty($conn)) + { + throw new Exception('Error in connecting to '.$dsninfo); + } + + if ($dsninfo['database']) + { + if (!@mysql_select_db($dsninfo['database'], $conn)) + throw new Exception('Error in connecting database, dns:'. + $dsninfo); + } + else + throw new Exception('Please provide a database for message'. + ' translation.'); + return $conn; + } + + /** + * Get the database connection. + * @return db database connection. + */ + public function connection() + { + return $this->db; + } + + /** + * Get an array of messages for a particular catalogue and cultural + * variant. + * @param string the catalogue name + variant + * @return array translation messages. + */ + protected function &loadData($variant) + { + $variant = mysql_real_escape_string($variant); + + $statement = + "SELECT t.id, t.source, t.target, t.comments + FROM trans_unit t, catalogue c + WHERE c.cat_id = t.cat_id + AND c.name = '{$variant}' + ORDER BY id ASC"; + + $rs = mysql_query($statement,$this->db); + + $result = array(); + + while($row = mysql_fetch_array($rs,MYSQL_NUM)) + { + $source = $row[1]; + $result[$source][] = $row[2]; //target + $result[$source][] = $row[0]; //id + $result[$source][] = $row[3]; //comments + } + + return $result; + } + + /** + * Get the last modified unix-time for this particular catalogue+variant. + * We need to query the database to get the date_modified. + * @param string catalogue+variant + * @return int last modified in unix-time format. + */ + protected function getLastModified($source) + { + $source = mysql_real_escape_string($source); + + $rs = mysql_query( + "SELECT date_modified FROM catalogue WHERE name = '{$source}'", + $this->db); + + $result = $rs ? (int)mysql_result($rs,0) : 0; + + return $result; + } + + /** + * Check if a particular catalogue+variant exists in the database. + * @param string catalogue+variant + * @return boolean true if the catalogue+variant is in the database, + * false otherwise. + */ + protected function isValidSource($variant) + { + $variant = mysql_real_escape_string ($variant); + + $rs = mysql_query( + "SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'", + $this->db); + + $row = mysql_fetch_array($rs,MYSQL_NUM); + + $result = $row && $row[0] == '1'; + + return $result; + } + + /** + * Get all the variants of a particular catalogue. + * @param string catalogue name + * @return array list of all variants for this catalogue. + */ + protected function getCatalogueList($catalogue) + { + $variants = explode('_',$this->culture); + + $catalogues = array($catalogue); + + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $catalogue.'.'.$variant; + } + } + return array_reverse($catalogues); + } + + /** + * Retrive catalogue details, array($cat_id, $variant, $count). + * @param string catalogue + * @return array catalogue details, array($cat_id, $variant, $count). + */ + private function getCatalogueDetails($catalogue='messages') + { + if(empty($catalogue)) + $catalogue = 'messages'; + + $variant = $catalogue.'.'.$this->culture; + + $name = mysql_real_escape_string($this->getSource($variant)); + + $rs = mysql_query("SELECT cat_id + FROM catalogue WHERE name = '{$name}'", $this->db); + + if(mysql_num_rows($rs) != 1) + return false; + + $cat_id = (int)mysql_result($rs,0); + + //first get the catalogue ID + $rs = mysql_query( + "SELECT count(msg_id) + FROM trans_unit + WHERE cat_id = {$cat_id}", $this->db); + + $count = (int)mysql_result($rs,0); + + return array($cat_id, $variant, $count); + } + + /** + * Update the catalogue last modified time. + * @return boolean true if updated, false otherwise. + */ + private function updateCatalogueTime($cat_id, $variant) + { + $time = time(); + + $result = mysql_query("UPDATE catalogue + SET date_modified = {$time} + WHERE cat_id = {$cat_id}", $this->db); + + if(!empty($this->cache)) + $this->cache->clean($variant, $this->culture); + + return $result; + } + + /** + * Save the list of untranslated blocks to the translation source. + * If the translation was not found, you should add those + * strings to the translation source via the append() method. + * @param string the catalogue to add to + * @return boolean true if saved successfuly, false otherwise. + */ + function save($catalogue='messages') + { + $messages = $this->untranslated; + + if(count($messages) <= 0) return false; + + $details = $this->getCatalogueDetails($catalogue); + + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + if($cat_id <= 0) return false; + $inserted = 0; + + $time = time(); + + foreach($messages as $message) + { + $count++; $inserted++; + $message = mysql_real_escape_string($message); + $statement = "INSERT INTO trans_unit + (cat_id,id,source,date_added) VALUES + ({$cat_id}, {$count},'{$message}',$time)"; + mysql_query($statement, $this->db); + } + if($inserted > 0) + $this->updateCatalogueTime($cat_id, $variant); + + return $inserted > 0; + } + + /** + * Delete a particular message from the specified catalogue. + * @param string the source message to delete. + * @param string the catalogue to delete from. + * @return boolean true if deleted, false otherwise. + */ + function delete($message, $catalogue='messages') + { + $details = $this->getCatalogueDetails($catalogue); + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + $text = mysql_real_escape_string($message); + + $statement = "DELETE FROM trans_unit WHERE + cat_id = {$cat_id} AND source = '{$message}'"; + $deleted = false; + + mysql_query($statement, $this->db); + + if(mysql_affected_rows($this->db) == 1) + $deleted = $this->updateCatalogueTime($cat_id, $variant); + + return $deleted; + + } + + /** + * Update the translation. + * @param string the source string. + * @param string the new translation string. + * @param string comments + * @param string the catalogue of the translation. + * @return boolean true if translation was updated, false otherwise. + */ + function update($text, $target, $comments, $catalogue='messages') + { + $details = $this->getCatalogueDetails($catalogue); + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + $comments = mysql_real_escape_string($comments); + $target = mysql_real_escape_string($target); + $text = mysql_real_escape_string($text); + + $time = time(); + + $statement = "UPDATE trans_unit SET + target = '{$target}', + comments = '{$comments}', + date_modified = '{$time}' + WHERE cat_id = {$cat_id} + AND source = '{$text}'"; + + $updated = false; + + mysql_query($statement, $this->db); + if(mysql_affected_rows($this->db) == 1) + $updated = $this->updateCatalogueTime($cat_id, $variant); + + return $updated; + } + + /** + * Returns a list of catalogue as key and all it variants as value. + * @return array list of catalogues + */ + function catalogues() + { + $statement = 'SELECT name FROM catalogue ORDER BY name'; + $rs = mysql_query($statement, $this->db); + $result = array(); + while($row = mysql_fetch_array($rs,MYSQL_NUM)) + { + $details = explode('.',$row[0]); + if(!isset($details[1])) $details[1] = null; + + $result[] = $details; + } + return $result; + } + +} + diff --git a/gui/baculum/framework/I18N/core/MessageSource_SQLite.php b/gui/baculum/framework/I18N/core/MessageSource_SQLite.php new file mode 100644 index 0000000000..46b5d865ac --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageSource_SQLite.php @@ -0,0 +1,353 @@ + + * @version $Revision: 1.4 $ $Date: 2005/02/25 09:59:40 $ + * @package System.I18N.core + */ + +/** + * Get the MessageSource class file. + */ +require_once(dirname(__FILE__).'/MessageSource.php'); + +/** + * Get the I18N utility file, contains the DSN parser. + */ +require_once(dirname(__FILE__).'/util.php'); + +/** + * MessageSource_SQLite class. + * + * Retrive the message translation from a SQLite database. + * + * See the MessageSource::factory() method to instantiate this class. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 16:58:58 EST 2004 + * @package System.I18N.core + */ +class MessageSource_SQLite extends MessageSource +{ + /** + * The SQLite datasource, the filename of the database. + * @var string + */ + protected $source; + + /** + * Constructor. + * Create a new message source using SQLite. + * @see MessageSource::factory(); + * @param string SQLite datasource, in PEAR's DB DSN format. + */ + function __construct($source) + { + $dsn = parseDSN((string)$source); + $this->source = $dsn['database']; + } + + /** + * Get an array of messages for a particular catalogue and cultural + * variant. + * @param string the catalogue name + variant + * @return array translation messages. + */ + protected function &loadData($variant) + { + $variant = sqlite_escape_string($variant); + + $statement = + "SELECT t.id, t.source, t.target, t.comments + FROM trans_unit t, catalogue c + WHERE c.cat_id = t.cat_id + AND c.name = '{$variant}' + ORDER BY id ASC"; + + $db = sqlite_open($this->source); + $rs = sqlite_query($statement, $db); + + $result = array(); + + while($row = sqlite_fetch_array($rs,SQLITE_NUM)) + { + $source = $row[1]; + $result[$source][] = $row[2]; //target + $result[$source][] = $row[0]; //id + $result[$source][] = $row[3]; //comments + } + + sqlite_close($db); + + return $result; + } + + /** + * Get the last modified unix-time for this particular catalogue+variant. + * We need to query the database to get the date_modified. + * @param string catalogue+variant + * @return int last modified in unix-time format. + */ + protected function getLastModified($source) + { + $source = sqlite_escape_string($source); + + $db = sqlite_open($this->source); + + $rs = sqlite_query( + "SELECT date_modified FROM catalogue WHERE name = '{$source}'", + $db); + + $result = $rs ? (int)sqlite_fetch_single($rs) : 0; + + sqlite_close($db); + + return $result; + } + + /** + * Check if a particular catalogue+variant exists in the database. + * @param string catalogue+variant + * @return boolean true if the catalogue+variant is in the database, + * false otherwise. + */ + protected function isValidSource($variant) + { + $variant = sqlite_escape_string($variant); + $db = sqlite_open($this->source); + $rs = sqlite_query( + "SELECT COUNT(*) FROM catalogue WHERE name = '{$variant}'", + $db); + $result = $rs && (int)sqlite_fetch_single($rs); + sqlite_close($db); + + return $result; + } + + /** + * Get all the variants of a particular catalogue. + * @param string catalogue name + * @return array list of all variants for this catalogue. + */ + protected function getCatalogueList($catalogue) + { + $variants = explode('_',$this->culture); + + $catalogues = array($catalogue); + + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $catalogue.'.'.$variant; + } + } + return array_reverse($catalogues); + } + + /** + * Retrive catalogue details, array($cat_id, $variant, $count). + * @param string catalogue + * @return array catalogue details, array($cat_id, $variant, $count). + */ + private function getCatalogueDetails($catalogue='messages') + { + if(empty($catalogue)) + $catalogue = 'messages'; + + $variant = $catalogue.'.'.$this->culture; + + $name = sqlite_escape_string($this->getSource($variant)); + + $db = sqlite_open($this->source); + + $rs = sqlite_query("SELECT cat_id + FROM catalogue WHERE name = '{$name}'", $db); + + if(sqlite_num_rows($rs) != 1) + return false; + + $cat_id = (int)sqlite_fetch_single($rs); + + //first get the catalogue ID + $rs = sqlite_query( + "SELECT count(msg_id) + FROM trans_unit + WHERE cat_id = {$cat_id}", $db); + + $count = (int)sqlite_fetch_single($rs); + + sqlite_close($db); + + return array($cat_id, $variant, $count); + } + + /** + * Update the catalogue last modified time. + * @return boolean true if updated, false otherwise. + */ + private function updateCatalogueTime($cat_id, $variant, $db) + { + $time = time(); + + $result = sqlite_query("UPDATE catalogue + SET date_modified = {$time} + WHERE cat_id = {$cat_id}", $db); + + if(!empty($this->cache)) + $this->cache->clean($variant, $this->culture); + + return $result; + } + + /** + * Save the list of untranslated blocks to the translation source. + * If the translation was not found, you should add those + * strings to the translation source via the append() method. + * @param string the catalogue to add to + * @return boolean true if saved successfuly, false otherwise. + */ + function save($catalogue='messages') + { + $messages = $this->untranslated; + + if(count($messages) <= 0) return false; + + $details = $this->getCatalogueDetails($catalogue); + + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + if($cat_id <= 0) return false; + $inserted = 0; + + $db = sqlite_open($this->source); + $time = time(); + + foreach($messages as $message) + { + $message = sqlite_escape_string($message); + $statement = "INSERT INTO trans_unit + (cat_id,id,source,date_added) VALUES + ({$cat_id}, {$count},'{$message}',$time)"; + if(sqlite_query($statement, $db)) + { + $count++; $inserted++; + } + } + if($inserted > 0) + $this->updateCatalogueTime($cat_id, $variant, $db); + + sqlite_close($db); + + return $inserted > 0; + } + + /** + * Update the translation. + * @param string the source string. + * @param string the new translation string. + * @param string comments + * @param string the catalogue of the translation. + * @return boolean true if translation was updated, false otherwise. + */ + function update($text, $target, $comments, $catalogue='messages') + { + $details = $this->getCatalogueDetails($catalogue); + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + $comments = sqlite_escape_string($comments); + $target = sqlite_escape_string($target); + $text = sqlite_escape_string($text); + + $time = time(); + + $db = sqlite_open($this->source); + + $statement = "UPDATE trans_unit SET + target = '{$target}', + comments = '{$comments}', + date_modified = '{$time}' + WHERE cat_id = {$cat_id} + AND source = '{$text}'"; + + $updated = false; + + if(sqlite_query($statement, $db)) + $updated = $this->updateCatalogueTime($cat_id, $variant, $db); + + sqlite_close($db); + + return $updated; + } + + /** + * Delete a particular message from the specified catalogue. + * @param string the source message to delete. + * @param string the catalogue to delete from. + * @return boolean true if deleted, false otherwise. + */ + function delete($message, $catalogue='messages') + { + $details = $this->getCatalogueDetails($catalogue); + if($details) + list($cat_id, $variant, $count) = $details; + else + return false; + + $db = sqlite_open($this->source); + $text = sqlite_escape_string($message); + + $statement = "DELETE FROM trans_unit WHERE + cat_id = {$cat_id} AND source = '{$message}'"; + $deleted = false; + + if(sqlite_query($statement, $db)) + $deleted = $this->updateCatalogueTime($cat_id, $variant, $db); + + sqlite_close($db); + + return $deleted; + } + + /** + * Returns a list of catalogue as key and all it variants as value. + * @return array list of catalogues + */ + function catalogues() + { + $db = sqlite_open($this->source); + $statement = 'SELECT name FROM catalogue ORDER BY name'; + $rs = sqlite_query($statement, $db); + $result = array(); + while($row = sqlite_fetch_array($rs,SQLITE_NUM)) + { + $details = explode('.',$row[0]); + if(!isset($details[1])) $details[1] = null; + + $result[] = $details; + } + sqlite_close($db); + return $result; + } +} + diff --git a/gui/baculum/framework/I18N/core/MessageSource_XLIFF.php b/gui/baculum/framework/I18N/core/MessageSource_XLIFF.php new file mode 100644 index 0000000000..198a129067 --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageSource_XLIFF.php @@ -0,0 +1,529 @@ + + * @version $Revision: 1.8 $ $Date: 2005/12/17 06:11:28 $ + * @package System.I18N.core + */ + +/** + * Get the MessageSource class file. + */ +require_once(dirname(__FILE__).'/MessageSource.php'); + +/** + * MessageSource_XLIFF class. + * + * Using XML XLIFF format as the message source for translation. + * Details and example of XLIFF can be found in the following URLs. + * + * # http://www.opentag.com/xliff.htm + * # http://www-106.ibm.com/developerworks/xml/library/x-localis2/ + * + * See the MessageSource::factory() method to instantiate this class. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004 + * @package System.I18N.core + */ +class MessageSource_XLIFF extends MessageSource +{ + /** + * Message data filename extension. + * @var string + */ + protected $dataExt = '.xml'; + + /** + * Separator between culture name and source. + * @var string + */ + protected $dataSeparator = '.'; + + /** + * Constructor. + * @param string the directory where the messages are stored. + * @see MessageSource::factory(); + */ + function __construct($source) + { + $this->source = (string)$source; + } + + /** + * Load the messages from a XLIFF file. + * @param string XLIFF file. + * @return array of messages. + */ + protected function &loadData($filename) + { + //load it. + if(false === ($XML = simplexml_load_file($filename))) { + return false; + } + + $translationUnit = $XML->xpath('//trans-unit'); + + $translations = array(); + + foreach($translationUnit as $unit) + { + $source = (string)$unit->source; + $translations[$source][] = (string)$unit->target; + $translations[$source][] = (string)$unit['id']; + $translations[$source][] = (string)$unit->note; + } + + return $translations; + } + + /** + * Get the last modified unix-time for this particular catalogue+variant. + * Just use the file modified time. + * @param string catalogue+variant + * @return int last modified in unix-time format. + */ + protected function getLastModified($source) + { + return is_file($source) ? filemtime($source) : 0; + } + + /** + * Get the XLIFF file for a specific message catalogue and cultural + * vairant. + * @param string message catalogue + * @return string full path to the XLIFF file. + */ + protected function getSource($variant) + { + return $this->source.'/'.$variant; + } + + /** + * Determin if the XLIFF file source is valid. + * @param string XLIFF file + * @return boolean true if valid, false otherwise. + */ + protected function isValidSource($source) + { + return is_file($source); + } + + /** + * Get all the variants of a particular catalogue. + * @param string catalogue name + * @return array list of all variants for this catalogue. + */ + protected function getCatalogueList($catalogue) + { + $variants = explode('_',$this->culture); + $source = $catalogue.$this->dataExt; + $catalogues = array($source); + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $catalogue.$this->dataSeparator.$variant.$this->dataExt; + } + } + + $byDir = $this->getCatalogueByDir($catalogue); + $catalogues = array_merge($byDir,array_reverse($catalogues)); + $files = array(); + + foreach($catalogues as $file) + { + $files[] = $file; + $files[] = preg_replace('/\.xml$/', '.xlf', $file); + } + + return $files; + } + + /** + * Traverse through the directory structure to find the catalogues. + * This should only be called by getCatalogueList() + * @param string a particular catalogue. + * @return array a list of catalogues. + * @see getCatalogueList() + */ + private function getCatalogueByDir($catalogue) + { + $variants = explode('_',$this->culture); + $catalogues = array(); + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $variant.'/'.$catalogue.$this->dataExt; + } + } + + return array_reverse($catalogues); + } + + /** + * Returns a list of catalogue and its culture ID. + * E.g. array('messages','en_AU') + * @return array list of catalogues + * @see getCatalogues() + */ + public function catalogues() + { + return $this->getCatalogues(); + } + + /** + * Returns a list of catalogue and its culture ID. This takes care + * of directory structures. + * E.g. array('messages','en_AU') + * @return array list of catalogues + */ + protected function getCatalogues($dir=null,$variant=null) + { + $dir = $dir?$dir:$this->source; + $files = scandir($dir); + $catalogue = array(); + + foreach($files as $file) + { + if(is_dir($dir.'/'.$file) && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file)) { + $catalogue = array_merge( + $catalogue, + $this->getCatalogues($dir.'/'.$file, $file) + ); + } + + $pos = strpos($file,$this->dataExt); + if($pos >0 && substr($file, -1*strlen($this->dataExt)) == $this->dataExt) + { + $name = substr($file,0,$pos); + $dot = strrpos($name,$this->dataSeparator); + $culture = $variant; + $cat = $name; + + if(is_int($dot)) + { + $culture = substr($name, $dot+1, strlen($name)); + $cat = substr($name, 0, $dot); + } + + $details[0] = $cat; + $details[1] = $culture; + $catalogue[] = $details; + } + } + sort($catalogue); + return $catalogue; + } + + /** + * Get the variant for a catalogue depending on the current culture. + * @param string catalogue + * @return string the variant. + * @see save() + * @see update() + * @see delete() + */ + private function getVariants($catalogue='messages') + { + if($catalogue === null) { + $catalogue = 'messages'; + } + + foreach($this->getCatalogueList($catalogue) as $variant) + { + $file = $this->getSource($variant); + if(is_file($file)) { + return array($variant, $file); + } + } + return false; + } + + /** + * Save the list of untranslated blocks to the translation source. + * If the translation was not found, you should add those + * strings to the translation source via the append() method. + * @param string the catalogue to add to + * @return boolean true if saved successfuly, false otherwise. + */ + public function save($catalogue='messages') + { + $messages = $this->untranslated; + if(count($messages) <= 0) { + return false; + } + + $variants = $this->getVariants($catalogue); + + if($variants) { + list($variant, $filename) = $variants; + } else { + list($variant, $filename) = $this->createMessageTemplate($catalogue); + } + + if(is_writable($filename) == false) { + throw new TIOException("Unable to save to file {$filename}, file must be writable."); + } + + //create a new dom, import the existing xml + $dom = new DOMDocument(); + $dom->load($filename); + + //find the body element + $xpath = new DomXPath($dom); + $body = $xpath->query('//body')->item(0); + + $lastNodes = $xpath->query('//trans-unit[last()]'); + if(($last=$lastNodes->item(0))!==null) { + $count = (int)$last->getAttribute('id'); + } else { + $count = 0; + } + + //for each message add it to the XML file using DOM + foreach($messages as $message) + { + $unit = $dom->createElement('trans-unit'); + $unit->setAttribute('id',++$count); + + $source = $dom->createElement('source'); + $source->appendChild($dom->createCDATASection($message)); + + $target = $dom->createElement('target'); + $target->appendChild($dom->createCDATASection('')); + + $unit->appendChild($dom->createTextNode("\n")); + $unit->appendChild($source); + $unit->appendChild($dom->createTextNode("\n")); + $unit->appendChild($target); + $unit->appendChild($dom->createTextNode("\n")); + + $body->appendChild($dom->createTextNode("\n")); + $body->appendChild($unit); + $body->appendChild($dom->createTextNode("\n")); + } + + + $fileNode = $xpath->query('//file')->item(0); + $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z')); + + //save it and clear the cache for this variant + $dom->save($filename); + if(!empty($this->cache)) { + $this->cache->clean($variant, $this->culture); + } + + return true; + } + + /** + * Update the translation. + * @param string the source string. + * @param string the new translation string. + * @param string comments + * @param string the catalogue to save to. + * @return boolean true if translation was updated, false otherwise. + */ + public function update($text, $target, $comments, $catalogue='messages') + { + $variants = $this->getVariants($catalogue); + + if($variants) { + list($variant, $filename) = $variants; + } else { + return false; + } + + if(is_writable($filename) == false) { + throw new TIOException("Unable to update file {$filename}, file must be writable."); + } + + //create a new dom, import the existing xml + $dom = DOMDocument::load($filename); + + //find the body element + $xpath = new DomXPath($dom); + $units = $xpath->query('//trans-unit'); + + //for each of the existin units + foreach($units as $unit) + { + $found = false; + $targetted = false; + $commented = false; + + //in each unit, need to find the source, target and comment nodes + //it will assume that the source is before the target. + foreach($unit->childNodes as $node) + { + //source node + if($node->nodeName == 'source' && $node->firstChild->wholeText == $text) { + $found = true; + } + + //found source, get the target and notes + if($found) + { + //set the new translated string + if($node->nodeName == 'target') + { + $node->nodeValue = $target; + $targetted = true; + } + + //set the notes + if(!empty($comments) && $node->nodeName == 'note') + { + $node->nodeValue = $comments; + $commented = true; + } + } + } + + //append a target + if($found && !$targetted) { + $unit->appendChild($dom->createElement('target',$target)); + } + + //append a note + if($found && !$commented && !empty($comments)) { + $unit->appendChild($dom->createElement('note',$comments)); + } + + //finished searching + if($found) { + break; + } + } + + $fileNode = $xpath->query('//file')->item(0); + $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z')); + + if($dom->save($filename) >0) + { + if(!empty($this->cache)) { + $this->cache->clean($variant, $this->culture); + } + + return true; + } + + return false; + } + + /** + * Delete a particular message from the specified catalogue. + * @param string the source message to delete. + * @param string the catalogue to delete from. + * @return boolean true if deleted, false otherwise. + */ + public function delete($message, $catalogue='messages') + { + $variants = $this->getVariants($catalogue); + if($variants) { + list($variant, $filename) = $variants; + } else { + return false; + } + + if(is_writable($filename) == false) { + throw new TIOException("Unable to modify file {$filename}, file must be writable."); + } + + //create a new dom, import the existing xml + $dom = DOMDocument::load($filename); + + //find the body element + $xpath = new DomXPath($dom); + $units = $xpath->query('//trans-unit'); + + //for each of the existin units + foreach($units as $unit) + { + //in each unit, need to find the source, target and comment nodes + //it will assume that the source is before the target. + foreach($unit->childNodes as $node) + { + //source node + if($node->nodeName == 'source' && $node->firstChild->wholeText == $message) + { + //we found it, remove and save the xml file. + $unit->parentNode->removeChild($unit); + $fileNode = $xpath->query('//file')->item(0); + $fileNode->setAttribute('date', @date('Y-m-d\TH:i:s\Z')); + + if(false !== $dom->save($filename)) { + if(!empty($this->cache)) { + $this->cache->clean($variant, $this->culture); + } + return true; + } + + return false; + } + } + } + + return false; + } + + protected function createMessageTemplate($catalogue) + { + if($catalogue === null) { + $catalogue = 'messages'; + } + + $variants = $this->getCatalogueList($catalogue); + $variant = array_shift($variants); + $file = $this->getSource($variant); + $dir = dirname($file); + + if(!is_dir($dir)) { + @mkdir($dir); + @chmod($dir,PRADO_CHMOD); + } + + if(!is_dir($dir)) { + throw new TException("Unable to create directory $dir"); + } + + file_put_contents($file, $this->getTemplate($catalogue)); + chmod($file, PRADO_CHMOD); + + return array($variant, $file); + } + + protected function getTemplate($catalogue) + { + $date = @date('c'); + $xml = << + + + + + + +EOD; + return $xml; + } +} diff --git a/gui/baculum/framework/I18N/core/MessageSource_gettext.php b/gui/baculum/framework/I18N/core/MessageSource_gettext.php new file mode 100644 index 0000000000..4419c39591 --- /dev/null +++ b/gui/baculum/framework/I18N/core/MessageSource_gettext.php @@ -0,0 +1,456 @@ + + * @version $Revision: 1.7 $ $Date: 2005/12/17 06:11:28 $ + * @package System.I18N.core + */ + +/** + * Get the MessageSource class file. + */ +require_once(dirname(__FILE__).'/MessageSource.php'); + +/** + * Get the Gettext class. + */ +require_once(dirname(__FILE__).'/Gettext/TGettext.php'); + +/** + * MessageSource_gettext class. + * + * Using Gettext MO format as the message source for translation. + * The gettext classes are based on PEAR's gettext MO and PO classes. + * + * See the MessageSource::factory() method to instantiate this class. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 24 16:18:44 EST 2004 + * @package System.I18N.core + */ +class MessageSource_gettext extends MessageSource +{ + /** + * Message data filename extension. + * @var string + */ + protected $dataExt = '.mo'; + + /** + * PO data filename extension + * @var string + */ + protected $poExt = '.po'; + + /** + * Separator between culture name and source. + * @var string + */ + protected $dataSeparator = '.'; + + function __construct($source) + { + $this->source = (string)$source; + } + + + /** + * Load the messages from a MO file. + * @param string MO file. + * @return array of messages. + */ + protected function &loadData($filename) + { + $mo = TGettext::factory('MO',$filename); + $mo->load(); + $result = $mo->toArray(); + + $results = array(); + $count=0; + foreach($result['strings'] as $source => $target) + { + $results[$source][] = $target; //target + $results[$source][] = $count++; //id + $results[$source][] = ''; //comments + } + return $results; + } + + /** + * Determin if the MO file source is valid. + * @param string MO file + * @return boolean true if valid, false otherwise. + */ + protected function isValidSource($filename) + { + return is_file($filename); + } + + /** + * Get the MO file for a specific message catalogue and cultural + * vairant. + * @param string message catalogue + * @return string full path to the MO file. + */ + protected function getSource($variant) + { + return $this->source.'/'.$variant; + } + + /** + * Get the last modified unix-time for this particular catalogue+variant. + * Just use the file modified time. + * @param string catalogue+variant + * @return int last modified in unix-time format. + */ + protected function getLastModified($source) + { + if(is_file($source)) + return filemtime($source); + else + return 0; + } + + /** + * Get all the variants of a particular catalogue. + * @param string catalogue name + * @return array list of all variants for this catalogue. + */ + protected function getCatalogueList($catalogue) + { + $variants = explode('_',$this->culture); + $source = $catalogue.$this->dataExt; + + $catalogues = array($source); + + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $catalogue.$this->dataSeparator. + $variant.$this->dataExt; + } + } + $byDir = $this->getCatalogueByDir($catalogue); + $catalogues = array_merge($byDir,array_reverse($catalogues)); + return $catalogues; + } + + + /** + * Traverse through the directory structure to find the catalogues. + * This should only be called by getCatalogueList() + * @param string a particular catalogue. + * @return array a list of catalogues. + * @see getCatalogueList() + */ + private function getCatalogueByDir($catalogue) + { + $variants = explode('_',$this->culture); + $catalogues = array(); + + $variant = null; + + for($i = 0, $k = count($variants); $i < $k; ++$i) + { + if(isset($variants[$i]{0})) + { + $variant .= ($variant)?'_'.$variants[$i]:$variants[$i]; + $catalogues[] = $variant.'/'.$catalogue.$this->dataExt; + } + } + return array_reverse($catalogues); + } + + /** + * Get the variant for a catalogue depending on the current culture. + * @param string catalogue + * @return string the variant. + * @see save() + * @see update() + * @see delete() + */ + private function getVariants($catalogue='messages') + { + if($catalogue === null) { + $catalogue = 'messages'; + } + + foreach($this->getCatalogueList($catalogue) as $variant) + { + $file = $this->getSource($variant); + $po = $this->getPOFile($file); + if(is_file($file) || is_file($po)) + return array($variant, $file, $po); + } + return false; + } + + private function getPOFile($MOFile) + { + $filebase = substr($MOFile, 0, strlen($MOFile)-strlen($this->dataExt)); + return $filebase.$this->poExt; + } + + /** + * Save the list of untranslated blocks to the translation source. + * If the translation was not found, you should add those + * strings to the translation source via the append() method. + * @param string the catalogue to add to + * @return boolean true if saved successfuly, false otherwise. + */ + function save($catalogue='messages') + { + $messages = $this->untranslated; + + if(count($messages) <= 0) return false; + + $variants = $this->getVariants($catalogue); + + if($variants) + list($variant, $MOFile, $POFile) = $variants; + else + list($variant, $MOFile, $POFile) = $this->createMessageTemplate($catalogue); + + if(is_writable($MOFile) == false) + throw new TIOException("Unable to save to file {$MOFile}, file must be writable."); + if(is_writable($POFile) == false) + throw new TIOException("Unable to save to file {$POFile}, file must be writable."); + + //set the strings as untranslated. + $strings = array(); + foreach($messages as $message) + $strings[$message] = ''; + + //load the PO + $po = TGettext::factory('PO',$POFile); + $po->load(); + $result = $po->toArray(); + + $existing = count($result['strings']); + + //add to strings to the existing message list + $result['strings'] = array_merge($result['strings'],$strings); + + $new = count($result['strings']); + + if($new > $existing) + { + //change the date 2004-12-25 12:26 + $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s'); + + $po->fromArray($result); + $mo = $po->toMO(); + if($po->save() && $mo->save($MOFile)) + { + if(!empty($this->cache)) + $this->cache->clean($variant, $this->culture); + return true; + } + else + return false; + } + return false; + } + + /** + * Delete a particular message from the specified catalogue. + * @param string the source message to delete. + * @param string the catalogue to delete from. + * @return boolean true if deleted, false otherwise. + */ + function delete($message, $catalogue='messages') + { + $variants = $this->getVariants($catalogue); + if($variants) + list($variant, $MOFile, $POFile) = $variants; + else + return false; + + if(is_writable($MOFile) == false) + throw new TIOException("Unable to modify file {$MOFile}, file must be writable."); + if(is_writable($POFile) == false) + throw new TIOException("Unable to modify file {$POFile}, file must be writable."); + + $po = TGettext::factory('PO',$POFile); + $po->load(); + $result = $po->toArray(); + + foreach($result['strings'] as $string => $value) + { + if($string == $message) + { + $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s'); + unset($result['strings'][$string]); + + $po->fromArray($result); + $mo = $po->toMO(); + if($po->save() && $mo->save($MOFile)) + { + if(!empty($this->cache)) + $this->cache->clean($variant, $this->culture); + return true; + } + else + return false; + } + } + + return false; + } + + /** + * Update the translation. + * @param string the source string. + * @param string the new translation string. + * @param string comments + * @param string the catalogue of the translation. + * @return boolean true if translation was updated, false otherwise. + */ + function update($text, $target, $comments, $catalogue='messages') + { + $variants = $this->getVariants($catalogue); + if($variants) + list($variant, $MOFile, $POFile) = $variants; + else + return false; + + if(is_writable($MOFile) == false) + throw new TIOException("Unable to update file {$MOFile}, file must be writable."); + if(is_writable($POFile) == false) + throw new TIOException("Unable to update file {$POFile}, file must be writable."); + + + $po = TGettext::factory('PO',$POFile); + $po->load(); + $result = $po->toArray(); + + foreach($result['strings'] as $string => $value) + { + if($string == $text) + { + $result['strings'][$string] = $target; + $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s'); + + $po->fromArray($result); + $mo = $po->toMO(); + + if($po->save() && $mo->save($MOFile)) + { + if(!empty($this->cache)) + $this->cache->clean($variant, $this->culture); + return true; + } + else + return false; + } + } + + return false; + } + + + /** + * Returns a list of catalogue as key and all it variants as value. + * @return array list of catalogues + */ + function catalogues() + { + return $this->getCatalogues(); + } + + /** + * Returns a list of catalogue and its culture ID. This takes care + * of directory structures. + * E.g. array('messages','en_AU') + * @return array list of catalogues + */ + protected function getCatalogues($dir=null,$variant=null) + { + $dir = $dir?$dir:$this->source; + $files = scandir($dir); + + $catalogue = array(); + + foreach($files as $file) + { + if(is_dir($dir.'/'.$file) + && preg_match('/^[a-z]{2}(_[A-Z]{2,3})?$/',$file)) + { + + $catalogue = array_merge($catalogue, + $this->getCatalogues($dir.'/'.$file, $file)); + } + + $pos = strpos($file,$this->dataExt); + + if($pos >0 + && substr($file,-1*strlen($this->dataExt)) == $this->dataExt) + { + $name = substr($file,0,$pos); + $dot = strrpos($name,$this->dataSeparator); + $culture = $variant; + $cat = $name; + if(is_int($dot)) + { + $culture = substr($name, $dot+1,strlen($name)); + $cat = substr($name,0,$dot); + } + $details[0] = $cat; + $details[1] = $culture; + + + $catalogue[] = $details; + } + } + sort($catalogue); + + return $catalogue; + } + + protected function createMessageTemplate($catalogue) + { + if($catalogue === null) { + $catalogue = 'messages'; + } + $variants = $this->getCatalogueList($catalogue); + $variant = array_shift($variants); + $mo_file = $this->getSource($variant); + $po_file = $this->getPOFile($mo_file); + + $dir = dirname($mo_file); + if(!is_dir($dir)) + { + @mkdir($dir); + @chmod($dir,PRADO_CHMOD); + } + if(!is_dir($dir)) + throw new TException("Unable to create directory $dir"); + + $po = TGettext::factory('PO',$po_file); + $result['meta']['PO-Revision-Date'] = @date('Y-m-d H:i:s'); + $result['strings'] = array(); + + $po->fromArray($result); + $mo = $po->toMO(); + if($po->save() && $mo->save($mo_file)) + return array($variant, $mo_file, $po_file); + else + throw new TException("Unable to create file $po_file and $mo_file"); + } +} + diff --git a/gui/baculum/framework/I18N/core/NumberFormat.php b/gui/baculum/framework/I18N/core/NumberFormat.php new file mode 100644 index 0000000000..ded647828d --- /dev/null +++ b/gui/baculum/framework/I18N/core/NumberFormat.php @@ -0,0 +1,311 @@ + + * @version $Revision: 1.6 $ $Date: 2005/12/20 09:32:42 $ + * @package System.I18N.core + */ + +/** + * Get the NumberFormatInfo class file. + */ +require_once(dirname(__FILE__).'/NumberFormatInfo.php'); + + +/** + * Get the encoding utilities + */ +require_once(dirname(__FILE__).'/util.php'); + + +/** + * NumberFormat class. + * + * NumberFormat formats decimal numbers in any locale. The decimal + * number is formatted according to a particular pattern. These + * patterns can arise from the NumberFormatInfo object which is + * culturally sensitive. The NumberFormat class can be instantiated in + * many ways. E.g. + * + * + * //create a invariant number formatter. + * $formatter = new NumberFormat(); + * + * //create a number format for the french language locale. + * $fr = new NumberFormat('fr'); + * + * //create a number format base on a NumberFormatInfo instance $numberInfo. + * $format = new NumberFormat($numberInfo); + * + * + * A normal decimal number can also be displayed as a currency + * or as a percentage. For example + * + * $format->format(1234.5); //Decimal number "1234.5" + * $format->format(1234.5,'c'); //Default currency "$1234.50" + * $format->format(0.25, 'p') //Percent "25%" + * + * + * Currency is formated using the localized currency pattern. For example + * to format the number as Japanese Yen: + * + * $ja = new NumberFormat('ja_JP'); + * + * //Japanese currency pattern, and using Japanese Yen symbol + * $ja->format(123.14,'c','JPY'); //�?123 (Yen 123) + * + * For each culture, the symbol for each currency may be different. + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Fri Dec 10 18:10:20 EST 2004 + * @package System.I18N.core + */ +class NumberFormat +{ + + /** + * The DateTimeFormatInfo, containing culture specific patterns and names. + * @var DateTimeFormatInfo + */ + protected $formatInfo; + + /** + * Create a new number format instance. The constructor can be instantiated + * with a string that represent a culture/locale. Similarly, passing + * a CultureInfo or NumberFormatInfo instance will instantiated a instance + * for that particular culture. + * @param mixed either null, a CultureInfo, a NumberFormatInfo, or string + * @return NumberFormat + */ + function __construct($formatInfo=null) + { + if($formatInfo === null) + $this->formatInfo = NumberFormatInfo::getInvariantInfo(); + else if($formatInfo instanceof CultureInfo) + $this->formatInfo = $formatInfo->NumberFormat; + else if($formatInfo instanceof NumberFormatInfo) + $this->formatInfo = $formatInfo; + else + $this->formatInfo = + NumberFormatInfo::getInstance($formatInfo); + } + + /** + * For the number for a certain pattern. The valid patterns are + * 'c', 'd', 'e', 'p' or a custom pattern, such as "#.000" for + * 3 decimal places. + * @param mixed the number to format. + * @param string the format pattern, either, 'c', 'd', 'e', 'p' + * or a custom pattern. E.g. "#.000" will format the number to + * 3 decimal places. + * @param string 3-letter ISO 4217 code. For example, the code + * "USD" represents the US Dollar and "EUR" represents the Euro currency. + * @return string formatted number string + */ + function format($number, $pattern='d', $currency='USD', $charset='UTF-8') + { + $oldLocale=setLocale(LC_NUMERIC, '0'); + setlocale(LC_NUMERIC, 'C'); + + $this->setPattern($pattern); + + if(strtolower($pattern) == 'p') + $number = $number * 100; + + $string = (string)$number; + + $decimal = $this->formatDecimal($string); + $integer = $this->formatInteger(abs($number)); + + if(strlen($decimal)>0) + $result = $integer.$decimal; + else + $result = $integer; + + //get the suffix + if($number >= 0) + $suffix = $this->formatInfo->PositivePattern; + else if($number < 0) + $suffix = $this->formatInfo->NegativePattern; + else + $suffix = array("",""); + + //append and prepend suffix + $result = $suffix[0].$result.$suffix[1]; + + //replace currency sign + $symbol = @$this->formatInfo->getCurrencySymbol($currency); + if($symbol === null) { + $symbol = $currency; + } + + $result = str_replace('¤',$symbol, $result); + + setlocale(LC_NUMERIC, $oldLocale); + + return I18N_toEncoding($result, $charset); + } + + /** + * For the integer, perform groupings and string padding. + * @param string the decimal number in string form. + * @return string formatted integer string with grouping + */ + protected function formatInteger($string) + { + $string = (string)$string; + + $decimalDigits = $this->formatInfo->DecimalDigits; + //if not decimal digits, assume 0 decimal points. + if(is_int($decimalDigits) && $decimalDigits > 0) + $string = (string)round(floatval($string),$decimalDigits); + $dp = strpos($string, '.'); + if(is_int($dp)) + $string = substr($string, 0, $dp); + $integer = ''; + + $digitSize = $this->formatInfo->getDigitSize(); + + $string = str_pad($string, $digitSize, '0',STR_PAD_LEFT); + + $len = strlen($string); + + $groupSeparator = $this->formatInfo->GroupSeparator; + $groupSize = $this->formatInfo->GroupSizes; + + + $firstGroup = true; + $multiGroup = is_int($groupSize[1]); + $count = 0; + + if(is_int($groupSize[0])) + { + //now for the integer groupings + for($i=0; $i<$len; $i++) + { + $char = $string{$len-$i-1}; + + if($multiGroup && $count == 0) + { + if($i != 0 && $i%$groupSize[0] == 0) + { + $integer = $groupSeparator . $integer; + $count++; + } + } + else if($multiGroup && $count >= 1) + { + if($i != 0 && ($i-$groupSize[0])%$groupSize[1] == 0) + { + $integer = $groupSeparator . $integer; + $count++; + } + } + else + { + if($i != 0 && $i%$groupSize[0] == 0) + { + $integer = $groupSeparator . $integer; + $count++; + } + } + + $integer = $char . $integer; + } + } + else + $integer = $string; + + return $integer; + } + + /** + * Format the decimal places. + * @param string the decimal number in string form. + * @return string formatted decimal places. + */ + protected function formatDecimal($string) + { + $dp = strpos($string, '.'); + $decimal = ''; + + $decimalDigits = $this->formatInfo->DecimalDigits; + $decimalSeparator = $this->formatInfo->DecimalSeparator; + + //do the correct rounding here + //$string = round(floatval($string), $decimalDigits); + if(is_int($dp)) + { + if($decimalDigits == -1) + { + $decimal = substr($string, $dp+1); + } + else if(is_int($decimalDigits)) + { + $float = round((float)$string, $decimalDigits); + if(strpos((string)$float, '.') === false) + { + $decimal = str_pad($decimal,$decimalDigits,'0'); + } + else + { + $decimal = substr($float, strpos($float,'.')+1); + if(strlen($decimal)<$decimalDigits) + $decimal = str_pad($decimal,$decimalDigits,'0'); + } + } + else + return $decimal; + + return $decimalSeparator.$decimal; + } + else if ($decimalDigits > 0) + return $decimalSeparator.str_pad($decimal,$decimalDigits,'0'); + + return $decimal; + } + + /** + * Set the pattern to format against. The default patterns + * are retrieved from the NumberFormatInfo instance. + * @param string the requested patterns. + * @return string a number format pattern. + */ + protected function setPattern($pattern) + { + switch($pattern) + { + case 'c': + case 'C': + $this->formatInfo->setPattern(NumberFormatInfo::CURRENCY); + break; + case 'd': + case 'D': + $this->formatInfo->setPattern(NumberFormatInfo::DECIMAL); + break; + case 'e': + case 'E': + $this->formatInfo->setPattern(NumberFormatInfo::SCIENTIFIC); + break; + case 'p': + case 'P': + $this->formatInfo->setPattern(NumberFormatInfo::PERCENTAGE); + break; + default: + $this->formatInfo->setPattern($pattern); + break; + } + } +} + diff --git a/gui/baculum/framework/I18N/core/NumberFormatInfo.php b/gui/baculum/framework/I18N/core/NumberFormatInfo.php new file mode 100644 index 0000000000..2a6667260a --- /dev/null +++ b/gui/baculum/framework/I18N/core/NumberFormatInfo.php @@ -0,0 +1,650 @@ + + * @version $Revision: 1.3 $ $Date: 2005/08/04 05:27:19 $ + * @package System.I18N.core + */ + +/** + * Get the CultureInfo class file. + */ +require_once(dirname(__FILE__).'/CultureInfo.php'); + +/** + * NumberFormatInfo class + * + * Defines how numeric values are formatted and displayed, + * depending on the culture. Numeric values are formatted using + * standard or custom patterns stored in the properties of a + * NumberFormatInfo. + * + * This class contains information, such as currency, decimal + * separators, and other numeric symbols. + * + * To create a NumberFormatInfo for a specific culture, + * create a CultureInfo for that culture and retrieve the + * CultureInfo->NumberFormat property. Or use + * NumberFormatInfo::getInstance($culture). + * To create a NumberFormatInfo for the invariant culture, use the + * InvariantInfo::getInvariantInfo(). + * + * + * @author Xiang Wei Zhuo + * @version v1.0, last update on Sun Dec 05 14:48:26 EST 2004 + * @package System.I18N.core + */ +class NumberFormatInfo +{ + + /** + * ICU number formatting data. + * @var array + */ + private $data = array(); + + /** + * A list of properties that are accessable/writable. + * @var array + */ + protected $properties = array(); + + /** + * The number pattern. + * @var array + */ + protected $pattern = array(); + + const DECIMAL = 0; + const CURRENCY = 1; + const PERCENTAGE = 2; + const SCIENTIFIC = 3; + + /** + * Allow functions that begins with 'set' to be called directly + * as an attribute/property to retrieve the value. + * @return mixed + */ + public function __get($name) + { + $getProperty = 'get'.$name; + if(in_array($getProperty, $this->properties)) + return $this->$getProperty(); + else + throw new Exception('Property '.$name.' does not exists.'); + } + + /** + * Allow functions that begins with 'set' to be called directly + * as an attribute/property to set the value. + */ + public function __set($name, $value) + { + $setProperty = 'set'.$name; + if(in_array($setProperty, $this->properties)) + $this->$setProperty($value); + else + throw new Exception('Property '.$name.' can not be set.'); + } + + /** + * Initializes a new writable instance of the NumberFormatInfo class + * that is dependent on the ICU data for number, decimal, and currency + * formatting information. N.B.You should not initialize this + * class directly unless you know what you are doing. Please use use + * NumberFormatInfo::getInstance() to create an instance. + * @param array ICU data for date time formatting. + * @see getInstance() + */ + public function __construct($data=array(), $type=NumberFormatInfo::DECIMAL) + { + $this->properties = get_class_methods($this); + + if(empty($data)) + throw new Exception('Please provide the ICU data to initialize.'); + + $this->data = $data; + + $this->setPattern($type); + } + + /** + * Set the pattern for a specific number pattern. The validate patterns + * NumberFormatInfo::DECIMAL, NumberFormatInfo::CURRENCY, + * NumberFormatInfo::PERCENTAGE, or NumberFormatInfo::SCIENTIFIC + * @param int pattern type. + */ + public function setPattern($type=NumberFormatInfo::DECIMAL) + { + if(is_int($type)) + $this->pattern = + $this->parsePattern($this->data['NumberPatterns'][$type]); + else + $this->pattern = $this->parsePattern($type); + + $this->pattern['negInfty'] = + $this->data['NumberElements'][6]. + $this->data['NumberElements'][9]; + + $this->pattern['posInfty'] = + $this->data['NumberElements'][11]. + $this->data['NumberElements'][9]; + } + + public function getPattern() + { + return $this->pattern; + } + + /** + * Gets the default NumberFormatInfo that is culture-independent + * (invariant). + * @return NumberFormatInfo default NumberFormatInfo. + */ + public static function getInvariantInfo($type=NumberFormatInfo::DECIMAL) + { + static $invariant; + if($invariant === null) + { + $culture = CultureInfo::getInvariantCulture(); + $invariant = $culture->NumberFormat; + $invariant->setPattern($type); + } + return $invariant; + } + + /** + * Returns the NumberFormatInfo associated with the specified culture. + * @param CultureInfo the culture that gets the NumberFormat property. + * @param int the number formatting type, it should be + * NumberFormatInfo::DECIMAL, NumberFormatInfo::CURRENCY, + * NumberFormatInfo::PERCENTAGE, or NumberFormatInfo::SCIENTIFIC + * @return NumberFormatInfo NumberFormatInfo for the specified + * culture. + * @see getCurrencyInstance(); + * @see getPercentageInstance(); + * @see getScientificInstance(); + */ + public static function getInstance($culture=null, + $type=NumberFormatInfo::DECIMAL) + { + if ($culture instanceof CultureInfo) + { + $formatInfo = $culture->NumberFormat; + $formatInfo->setPattern($type); + return $formatInfo; + } + else if(is_string($culture)) + { + $cultureInfo = new CultureInfo($culture); + $formatInfo = $cultureInfo->NumberFormat; + $formatInfo->setPattern($type); + return $formatInfo; + } + else + { + $cultureInfo = new CultureInfo(); + $formatInfo = $cultureInfo->NumberFormat; + $formatInfo->setPattern($type); + return $formatInfo; + } + } + + /** + * Returns the currency format info associated with the specified culture. + * @param CultureInfo the culture that gets the NumberFormat property. + * @return NumberFormatInfo NumberFormatInfo for the specified + * culture. + */ + public static function getCurrencyInstance($culture=null) + { + return self::getInstance($culture, self::CURRENCY); + } + + /** + * Returns the percentage format info associated with the specified culture. + * @param CultureInfo the culture that gets the NumberFormat property. + * @return NumberFormatInfo NumberFormatInfo for the specified + * culture. + */ + public static function getPercentageInstance($culture=null) + { + return self::getInstance($culture, self::PERCENTAGE); + } + + /** + * Returns the scientific format info associated with the specified culture. + * @param CultureInfo the culture that gets the NumberFormat property. + * @return NumberFormatInfo NumberFormatInfo for the specified + * culture. + */ + public static function getScientificInstance($culture=null) + { + return self::getInstance($culture, self::SCIENTIFIC); + } + + /** + * Parse the given pattern and return a list of known properties. + * @param string a number pattern. + * @return array list of pattern properties. + */ + protected function parsePattern($pattern) + { + $pattern = explode(';',$pattern); + + $negative = null; + if(count($pattern) > 1) + $negative = $pattern[1]; + $pattern = $pattern[0]; + + $comma = ','; + $dot = '.'; + $digit = '0'; + $hash = '#'; + + //find the first group point, and decimal point + $groupPos1 = strrpos($pattern,$comma); + $decimalPos = strrpos($pattern,$dot); + + $groupPos2 = false; + $groupSize1 = false; + $groupSize2 = false; + $decimalPoints = is_int($decimalPos)?-1:false; + + $info['negPref'] = $this->data['NumberElements'][6]; + $info['negPost'] = ''; + + $info['negative'] = $negative; + $info['positive'] = $pattern; + + //find the negative prefix and postfix + if($negative) + { + $prefixPostfix = $this->getPrePostfix($negative); + $info['negPref'] = $prefixPostfix[0]; + $info['negPost'] = $prefixPostfix[1]; + } + + $posfix = $this->getPrePostfix($pattern); + $info['posPref'] = $posfix[0]; + $info['posPost'] = $posfix[1]; + + //var_dump($pattern); + //var_dump($decimalPos); + if(is_int($groupPos1)) + { + //get the second group + $groupPos2 = strrpos(substr($pattern,0,$groupPos1),$comma); + + //get the number of decimal digits + if(is_int($decimalPos)) + { + $groupSize1 = $decimalPos - $groupPos1-1; + + } + else + { + //no decimal point, so traverse from the back + //to find the groupsize 1. + for($i=strlen($pattern)-1; $i>=0; $i--) + { + if($pattern{$i} == $digit || $pattern{$i}==$hash) + { + $groupSize1 = $i - $groupPos1; + break; + } + } + } + + //get the second group size + if(is_int($groupPos2)) + $groupSize2 = $groupPos1 - $groupPos2-1; + } + + if(is_int($decimalPos)) + { + for($i=strlen($pattern)-1; $i>=0; $i--) + { + if($pattern{$i} == $dot) break; + if($pattern{$i} == $digit) + { + $decimalPoints = $i - $decimalPos; + break; + } + } + } + + if(is_int($decimalPos)) + $digitPattern = substr($pattern,0,$decimalPos); + else + $digitPattern = $pattern; + + $digitPattern = preg_replace('/[^0]/','',$digitPattern); + + $info['groupPos1'] = $groupPos1; + $info['groupSize1'] = $groupSize1; + $info['groupPos2'] = $groupPos2; + $info['groupSize2'] = $groupSize2; + $info['decimalPos'] = $decimalPos; + $info['decimalPoints'] = $decimalPoints; + $info['digitSize'] = strlen($digitPattern); + return $info; + } + + /** + * Get the prefix and postfix of a pattern. + * @param string pattern + * @return array of prefix and postfix, array(prefix,postfix). + */ + protected function getPrePostfix($pattern) + { + $regexp = '/[#,\.0]+/'; + $result = preg_split($regexp, $pattern); + return array($result[0],$result[1]); + } + + + /** + * Indicates the number of decimal places. + * @return int number of decimal places. + */ + function getDecimalDigits() + { + return $this->pattern['decimalPoints']; + } + + /** + * Set the number of decimal places. + * @param int number of decimal places. + */ + function setDecimalDigits($value) + { + return $this->pattern['decimalPoints'] = $value; + } + + function getDigitSize() + { + return $this->pattern['digitSize']; + } + + function setDigitSize($value) + { + $this->pattern['digitSize'] = $value; + } + + /** + * Gets the string to use as the decimal separator. + * @return string decimal separator. + */ + function getDecimalSeparator() + { + return $this->data['NumberElements'][0]; + } + + /** + * Set the string to use as the decimal separator. + * @param string the decimal point + */ + function setDecimalSeparator($value) + { + return $this->data['NumberElements'][0] = $value; + } + + /** + * Gets the string that separates groups of digits to the left + * of the decimal in currency values. + * @param parameter + * @return string currency group separator. + */ + function getGroupSeparator() + { + return $this->data['NumberElements'][1]; + } + + /** + * Set the string to use as the group separator. + * @param string the group separator. + */ + function setGroupSeparator($value) + { + return $this->data['NumberElements'][1] = $value; + } + + /** + * Gets the number of digits in each group to the left of the decimal + * There can be two grouping sizes, this fucntion + * returns array(group1, group2), if there is only 1 grouping size, + * group2 will be false. + * @return array grouping size(s). + */ + function getGroupSizes() + { + $group1 = $this->pattern['groupSize1']; + $group2 = $this->pattern['groupSize2']; + + return array($group1, $group2); + } + + /** + * Set the number of digits in each group to the left of the decimal. + * There can be two grouping sizes, the value should + * be an array(group1, group2), if there is only 1 grouping size, + * group2 should be false. + * @param array grouping size(s). + */ + function setGroupSizes($groupSize) + { + $this->pattern['groupSize1'] = $groupSize[0]; + $this->pattern['groupSize2'] = $groupSize[1]; + } + + /** + * Gets the format pattern for negative values. + * The negative pattern is composed of a prefix, and postfix. + * This function returns array(prefix, postfix). + * @return arary negative pattern. + */ + function getNegativePattern() + { + $prefix = $this->pattern['negPref']; + $postfix = $this->pattern['negPost']; + return array($prefix, $postfix); + } + + /** + * Set the format pattern for negative values. + * The negative pattern is composed of a prefix, and postfix in the form + * array(prefix, postfix). + * @param arary negative pattern. + */ + function setNegativePattern($pattern) + { + $this->pattern['negPref'] = $pattern[0]; + $this->pattern['negPost'] = $pattern[1]; + } + + /** + * Gets the format pattern for positive values. + * The positive pattern is composed of a prefix, and postfix. + * This function returns array(prefix, postfix). + * @return arary positive pattern. + */ + function getPositivePattern() + { + $prefix = $this->pattern['posPref']; + $postfix = $this->pattern['posPost']; + return array($prefix, $postfix); + } + + /** + * Set the format pattern for positive values. + * The positive pattern is composed of a prefix, and postfix in the form + * array(prefix, postfix). + * @param arary positive pattern. + */ + function setPositivePattern($pattern) + { + $this->pattern['posPref'] = $pattern[0]; + $this->pattern['posPost'] = $pattern[1]; + } + + /** + * Gets the string to use as the currency symbol. + * @return string currency symbol. + */ + function getCurrencySymbol($currency='USD') + { + if(isset($this->pattern['symbol'])) + return $this->pattern['symbol']; + else + return $this->data['Currencies'][$currency][0]; + } + + + /** + * Set the string to use as the currency symbol. + * @param string currency symbol. + */ + function setCurrencySymbol($symbol) + { + $this->pattern['symbol'] = $symbol; + } + + /** + * Gets the string that represents negative infinity. + * @return string negative infinity. + */ + function getNegativeInfinitySymbol() + { + return $this->pattern['negInfty']; + } + + /** + * Set the string that represents negative infinity. + * @param string negative infinity. + */ + function setNegativeInfinitySymbol($value) + { + $this->pattern['negInfty'] = $value; + } + + /** + * Gets the string that represents positive infinity. + * @return string positive infinity. + */ + function getPositiveInfinitySymbol() + { + return $this->pattern['posInfty']; + } + + /** + * Set the string that represents positive infinity. + * @param string positive infinity. + */ + function setPositiveInfinitySymbol($value) + { + $this->pattern['posInfty'] = $value; + } + + /** + * Gets the string that denotes that the associated number is negative. + * @return string negative sign. + */ + function getNegativeSign() + { + return $this->data['NumberElements'][6]; + } + + /** + * Set the string that denotes that the associated number is negative. + * @param string negative sign. + */ + function setNegativeSign($value) + { + $this->data['NumberElements'][6] = $value; + } + + /** + * Gets the string that denotes that the associated number is positive. + * @return string positive sign. + */ + function getPositiveSign() + { + return $this->data['NumberElements'][11]; + } + + /** + * Set the string that denotes that the associated number is positive. + * @param string positive sign. + */ + function setPositiveSign($value) + { + $this->data['NumberElements'][11] = $value; + } + + /** + * Gets the string that represents the IEEE NaN (not a number) value. + * @return string NaN symbol. + */ + function getNaNSymbol() + { + return $this->data['NumberElements'][10]; + } + + /** + * Set the string that represents the IEEE NaN (not a number) value. + * @param string NaN symbol. + */ + function setNaNSymbol($value) + { + $this->data['NumberElements'][10] = $value; + } + + /** + * Gets the string to use as the percent symbol. + * @return string percent symbol. + */ + function getPercentSymbol() + { + return $this->data['NumberElements'][3]; + } + + /** + * Set the string to use as the percent symbol. + * @param string percent symbol. + */ + function setPercentSymbol($value) + { + $this->data['NumberElements'][3] = $value; + } + + /** + * Gets the string to use as the per mille symbol. + * @return string percent symbol. + */ + function getPerMilleSymbol() + { + return $this->data['NumberElements'][8]; + } + + /** + * Set the string to use as the per mille symbol. + * @param string percent symbol. + */ + function setPerMilleSymbol($value) + { + $this->data['NumberElements'][8] = $value; + } +} + diff --git a/gui/baculum/framework/I18N/core/TCache_Lite.php b/gui/baculum/framework/I18N/core/TCache_Lite.php new file mode 100644 index 0000000000..a0629bd153 --- /dev/null +++ b/gui/baculum/framework/I18N/core/TCache_Lite.php @@ -0,0 +1,628 @@ + + * @version $Revision: 1.3 $ $Date: 2005/10/09 10:24:12 $ + * @package System.I18N.core + */ + +/** +* Fast, light and safe Cache Class +* +* TCache_Lite is a fast, light and safe cache system. It's optimized +* for file containers. It is fast and safe (because it uses file +* locking and/or anti-corruption tests). +* +* There are some examples in the 'docs/examples' file +* Technical choices are described in the 'docs/technical' file +* +* A tutorial is available in english at this url : +* http://www.pearfr.org/index.php/en/article/cache_lite +* (big thanks to Pierre-Alain Joye for the translation) +* +* The same tutorial is also available in french at this url : +* http://www.pearfr.org/index.php/fr/article/cache_lite +* +* Memory Caching is from an original idea of +* Mike BENOIT +* +* @package System.I18N.core +* @version $Id: TCache_Lite.php 3188 2012-07-12 12:13:23Z ctrlaltca $ +* @author Fabien MARTY +* @copyright 1997-2005 The PHP Group +* @license http://www.gnu.org/copyleft/lesser.html GNU LGPL +* @link http://pear.php.net/package/Cache_Lite +*/ +class TCache_Lite +{ + + // --- Private properties --- + + /** + * Directory where to put the cache files + * (make sure to add a trailing slash) + * + * @var string $_cacheDir + */ + protected $_cacheDir = '/tmp/'; + + /** + * Enable / disable caching + * + * (can be very usefull for the debug of cached scripts) + * + * @var boolean $_caching + */ + protected $_caching = true; + + /** + * Cache lifetime (in seconds) + * + * @var int $_lifeTime + */ + protected $_lifeTime = 3600; + + /** + * Enable / disable fileLocking + * + * (can avoid cache corruption under bad circumstances) + * + * @var boolean $_fileLocking + */ + protected $_fileLocking = true; + + /** + * Timestamp of the last valid cache + * + * @var int $_refreshTime + */ + protected $_refreshTime; + + /** + * File name (with path) + * + * @var string $_file + */ + protected $_file; + + /** + * Enable / disable write control (the cache is read just after writing + * to detect corrupt entries) + * + * Enable write control will lightly slow the cache writing but not the + * cache reading. Write control can detect some corrupt cache files but + * maybe it's not a perfect control + * + * @var boolean $_writeControl + */ + protected $_writeControl = true; + + /** + * Enable / disable read control + * + * If enabled, a control key is embeded in cache file and this key is + * compared with the one calculated after the reading. + * + * @var boolean $_writeControl + */ + protected $_readControl = true; + + /** + * Type of read control (only if read control is enabled) + * + * Available values are : + * 'md5' for a md5 hash control (best but slowest) + * 'crc32' for a crc32 hash control (lightly less safe but faster, + * better choice) + * 'strlen' for a length only test (fastest) + * + * @var boolean $_readControlType + */ + protected $_readControlType = 'crc32'; + + /** + * Current cache id + * + * @var string $_id + */ + protected $_id; + + /** + * Current cache group + * + * @var string $_group + */ + protected $_group; + + /** + * Enable / Disable "Memory Caching" + * + * NB : There is no lifetime for memory caching ! + * + * @var boolean $_memoryCaching + */ + protected $_memoryCaching = false; + + /** + * Enable / Disable "Only Memory Caching" + * (be carefull, memory caching is "beta quality") + * + * @var boolean $_onlyMemoryCaching + */ + protected $_onlyMemoryCaching = false; + + /** + * Memory caching array + * + * @var array $_memoryCachingArray + */ + protected $_memoryCachingArray = array(); + + /** + * Memory caching counter + * + * @var int $memoryCachingCounter + */ + protected $_memoryCachingCounter = 0; + + /** + * Memory caching limit + * + * @var int $memoryCachingLimit + */ + protected $_memoryCachingLimit = 1000; + + /** + * File Name protection + * + * if set to true, you can use any cache id or group name + * if set to false, it can be faster but cache ids and group names + * will be used directly in cache file names so be carefull with + * special characters... + * + * @var boolean $fileNameProtection + */ + protected $_fileNameProtection = true; + + /** + * Enable / disable automatic serialization + * + * it can be used to save directly datas which aren't strings + * (but it's slower) + * + * @var boolean $_serialize + */ + protected $_automaticSerialization = false; + + // --- Public methods --- + + /** + * Constructor + * + * $options is an assoc. Available options are : + * $options = array( + * 'cacheDir' => directory where to put the cache files (string), + * 'caching' => enable / disable caching (boolean), + * 'lifeTime' => cache lifetime in seconds (int), + * 'fileLocking' => enable / disable fileLocking (boolean), + * 'writeControl' => enable / disable write control (boolean), + * 'readControl' => enable / disable read control (boolean), + * 'readControlType' => type of read control 'crc32', 'md5', 'strlen', + * 'memoryCaching' => enable / disable memory caching (boolean), + * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), + * 'memoryCachingLimit' => max nbr of records in memory caching (int), + * 'fileNameProtection' => enable / disable file name protection (boolean), + * 'automaticSerialization' => enable / disable serialization (boolean) + * ); + * + * @param array $options options + * @access public + */ + function TCache_Lite($options = array(null)) + { + $availableOptions = array( 'automaticSerialization', + 'fileNameProtection', + 'memoryCaching', + 'onlyMemoryCaching', + 'memoryCachingLimit', + 'cacheDir', + 'caching', + 'lifeTime', + 'fileLocking', + 'writeControl', + 'readControl', + 'readControlType'); + foreach($options as $key => $value) { + if(in_array($key, $availableOptions)) { + $property = '_'.$key; + $this->$property = $value; + } + } + $this->_refreshTime = time() - $this->_lifeTime; + } + + /** + * Test if a cache is available and (if yes) return it + * + * @param string $id cache id + * @param string $group name of the cache group + * @param boolean $doNotTestCacheValidity if set to true, the cache + * validity won't be tested + * @return string data of the cache (or false if no cache available) + * @access public + */ + function get($id, $group = 'default', $doNotTestCacheValidity = false) + { + $this->_id = $id; + $this->_group = $group; + $data = false; + if ($this->_caching) { + $this->_setFileName($id, $group); + if ($this->_memoryCaching) { + if (isset($this->_memoryCachingArray[$this->_file])) { + if ($this->_automaticSerialization) { + return unserialize( + $this->_memoryCachingArray[$this->_file]); + } else { + return $this->_memoryCachingArray[$this->_file]; + } + } else { + if ($this->_onlyMemoryCaching) { + return false; + } + } + } + if ($doNotTestCacheValidity) { + if (file_exists($this->_file)) { + $data = $this->_read(); + } + } else { + if (@filemtime($this->_file) > $this->_refreshTime) { + $data = $this->_read(); + } + } + if (($data) and ($this->_memoryCaching)) { + $this->_memoryCacheAdd($this->_file, $data); + } + if ($this->_automaticSerialization && is_string($data)) { + $data = unserialize($data); + } + return $data; + } + return false; + } + + /** + * Save some data in a cache file + * + * @param string $data data to put in cache (can be another type than strings + * if automaticSerialization is on) + * @param string $id cache id + * @param string $group name of the cache group + * @return boolean true if no problem + * @access public + */ + function save($data, $id = null, $group = 'default') + { + if ($this->_caching) { + if ($this->_automaticSerialization) { + $data = serialize($data); + } + if (isset($id)) { + $this->_setFileName($id, $group); + } + if ($this->_memoryCaching) { + $this->_memoryCacheAdd($this->_file, $data); + if ($this->_onlyMemoryCaching) { + return true; + } + } + if ($this->_writeControl) { + if (!$this->_writeAndControl($data)) { + @touch($this->_file, time() - 2*abs($this->_lifeTime)); + return false; + } else { + return true; + } + } else { + return $this->_write($data); + } + } + return false; + } + + /** + * Remove a cache file + * + * @param string $id cache id + * @param string $group name of the cache group + * @return boolean true if no problem + * @access public + */ + function remove($id, $group = 'default') + { + $this->_setFileName($id, $group); + if (!@unlink($this->_file)) { + $this->raiseError('TCache_Lite : Unable to remove cache !', -3); + return false; + } + return true; + } + + /** + * Clean the cache + * + * if no group is specified all cache files will be destroyed + * else only cache files of the specified group will be destroyed + * + * @param string $group name of the cache group + * @return boolean true if no problem + * @access public + */ + function clean($group = false) + { + if ($this->_fileNameProtection) { + $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; + } else { + $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; + } + if ($this->_memoryCaching) { + while (list($key, $value) = each($this->_memoryCaching)) { + if (strpos($key, $motif, 0)) { + unset($this->_memoryCaching[$key]); + $this->_memoryCachingCounter = + $this->_memoryCachingCounter - 1; + } + } + if ($this->_onlyMemoryCaching) { + return true; + } + } + if (!($dh = opendir($this->_cacheDir))) { + $this->raiseError('TCache_Lite : Unable to open cache directory !'); + return false; + } + while ($file = readdir($dh)) { + if (($file != '.') && ($file != '..')) { + $file = $this->_cacheDir . $file; + if (is_file($file)) { + if (strpos($file, $motif, 0)) { + if (!@unlink($file)) { + $this->raiseError('Cache_Lite : Unable to remove cache !', -3); + return false; + } + } + } + } + } + return true; + } + + /** + * Set a new life time + * + * @param int $newLifeTime new life time (in seconds) + * @access public + */ + function setLifeTime($newLifeTime) + { + $this->_lifeTime = $newLifeTime; + $this->_refreshTime = time() - $newLifeTime; + } + + /** + * + * @access public + */ + function saveMemoryCachingState($id, $group = 'default') + { + if ($this->_caching) { + $array = array( + 'counter' => $this->_memoryCachingCounter, + 'array' => $this->_memoryCachingState + ); + $data = serialize($array); + $this->save($data, $id, $group); + } + } + + /** + * + * @access public + */ + function getMemoryCachingState($id, $group = 'default', + $doNotTestCacheValidity = false) + { + if ($this->_caching) { + if ($data = $this->get($id, $group, $doNotTestCacheValidity)) + { + $array = unserialize($data); + $this->_memoryCachingCounter = $array['counter']; + $this->_memoryCachingArray = $array['array']; + } + } + } + + /** + * Return the cache last modification time + * + * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! + * + * @return int last modification time + */ + function lastModified() { + return filemtime($this->cache->_file); + } + + /** + * Trigger a PEAR error + * + * To improve performances, the PEAR.php file is included dynamically. + * The file is so included only when an error is triggered. So, in most + * cases, the file isn't included and perfs are much better. + * + * @param string $msg error message + * @param int $code error code + * @access public + */ + function raiseError($msg, $code) + { + throw new Exception($msg); + } + + // --- Private methods --- + + /** + * + * @access private + */ + function _memoryCacheAdd($id, $data) + { + $this->_memoryCachingArray[$this->_file] = $data; + if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { + list($key, $value) = each($this->_memoryCachingArray); + unset($this->_memoryCachingArray[$key]); + } else { + $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; + } + } + + /** + * Make a file name (with path) + * + * @param string $id cache id + * @param string $group name of the group + * @access private + */ + function _setFileName($id, $group) + { + if ($this->_fileNameProtection) { + $this->_file = ($this->_cacheDir.'cache_'.md5($group).'_' + .md5($id)); + } else { + $this->_file = $this->_cacheDir.'cache_'.$group.'_'.$id; + } + } + + function getCacheFile() + { + return $this->_file; + } + + /** + * Read the cache file and return the content + * + * @return string content of the cache file + * @access private + */ + function _read() + { + $fp = @fopen($this->_file, "rb"); + if ($this->_fileLocking) @flock($fp, LOCK_SH); + if ($fp) { + // because the filesize can be cached by PHP itself... + clearstatcache(); + $length = @filesize($this->_file); + if(version_compare(PHP_VERSION, '5.3.0', 'lt')) + { + $mqr = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + } + if ($this->_readControl) { + $hashControl = @fread($fp, 32); + $length = $length - 32; + } + $data = @fread($fp, $length); + if(isset($mqr)) + set_magic_quotes_runtime($mqr); + if ($this->_fileLocking) @flock($fp, LOCK_UN); + @fclose($fp); + if ($this->_readControl) { + $hashData = $this->_hash($data, $this->_readControlType); + if ($hashData != $hashControl) { + @touch($this->_file, time() - 2*abs($this->_lifeTime)); + return false; + } + } + return $data; + } + $this->raiseError('Cache_Lite : Unable to read cache !', -2); + return false; + } + + /** + * Write the given data in the cache file + * + * @param string $data data to put in cache + * @return boolean true if ok + * @access private + */ + function _write($data) + { + $fp = @fopen($this->_file, "wb"); + if ($fp) { + if ($this->_fileLocking) @flock($fp, LOCK_EX); + if ($this->_readControl) { + @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); + } + $len = strlen($data); + @fwrite($fp, $data, $len); + if ($this->_fileLocking) @flock($fp, LOCK_UN); + @fclose($fp); + return true; + } + $this->raiseError('Cache_Lite : Unable to write cache !', -1); + return false; + } + + /** + * Write the given data in the cache file and control it just after to avoid + * corrupted cache entries + * + * @param string $data data to put in cache + * @return boolean true if the test is ok + * @access private + */ + function _writeAndControl($data) + { + $this->_write($data); + $dataRead = $this->_read($data); + return ($dataRead==$data); + } + + /** + * Make a control key with the string containing datas + * + * @param string $data data + * @param string $controlType type of control 'md5', 'crc32' or 'strlen' + * @return string control key + * @access private + */ + function _hash($data, $controlType) + { + switch ($controlType) { + case 'md5': + return md5($data); + case 'crc32': + return sprintf('% 32d', crc32($data)); + case 'strlen': + return sprintf('% 32d', strlen($data)); + default: + $this->raiseError('Unknown controlType ! '. + '(available values are only \'md5\', \'crc32\', \'strlen\')', -5); + } + } + +} + diff --git a/gui/baculum/framework/I18N/core/data/af.dat b/gui/baculum/framework/I18N/core/data/af.dat new file mode 100644 index 0000000000..bad75bef78 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/af.dat @@ -0,0 +1 @@ +a:7:{s:9:"Countries";a:130:{s:2:"AE";a:1:{i:0;s:26:"Verenigde Arabiese Emirate";}s:2:"AF";a:1:{i:0;s:10:"Afganistan";}s:2:"AG";a:1:{i:0;s:18:"Antigua en Barbuda";}s:2:"AL";a:1:{i:0;s:8:"Albanië";}s:2:"AM";a:1:{i:0;s:8:"Armenië";}s:2:"AR";a:1:{i:0;s:11:"Argentinië";}s:2:"AT";a:1:{i:0;s:9:"Oostenryk";}s:2:"AU";a:1:{i:0;s:10:"Australië";}s:2:"AZ";a:1:{i:0;s:11:"Aserbeidjan";}s:2:"BA";a:1:{i:0;s:22:"Bosnië en Herzegowina";}s:2:"BD";a:1:{i:0;s:10:"Bangladesj";}s:2:"BE";a:1:{i:0;s:7:"België";}s:2:"BF";a:1:{i:0;s:14:"Boerkina Fasso";}s:2:"BG";a:1:{i:0;s:8:"Bulgarye";}s:2:"BH";a:1:{i:0;s:7:"Bahrein";}s:2:"BN";a:1:{i:0;s:7:"Broenei";}s:2:"BO";a:1:{i:0;s:8:"Bolivië";}s:2:"BR";a:1:{i:0;s:9:"Brasilië";}s:2:"BT";a:1:{i:0;s:7:"Bhoetan";}s:2:"BY";a:1:{i:0;s:11:"Wit-Rusland";}s:2:"CA";a:1:{i:0;s:6:"Kanada";}s:2:"CF";a:1:{i:0;s:29:"Sentraal-Afrikaanse Republiek";}s:2:"CG";a:1:{i:0;s:5:"Kongo";}s:2:"CH";a:1:{i:0;s:11:"Switserland";}s:2:"CI";a:1:{i:0;s:8:"Ivoorkus";}s:2:"CL";a:1:{i:0;s:5:"Chili";}s:2:"CM";a:1:{i:0;s:8:"Kameroen";}s:2:"CN";a:1:{i:0;s:5:"Sjina";}s:2:"CU";a:1:{i:0;s:4:"Kuba";}s:2:"CV";a:1:{i:0;s:10:"Kaap Verde";}s:2:"CY";a:1:{i:0;s:6:"Ciprus";}s:2:"CZ";a:1:{i:0;s:19:"Tjeggiese Republiek";}s:2:"DE";a:1:{i:0;s:9:"Duitsland";}s:2:"DJ";a:1:{i:0;s:8:"Djiboeti";}s:2:"DK";a:1:{i:0;s:9:"Denemarke";}s:2:"DO";a:1:{i:0;s:22:"Dominikaanse Republiek";}s:2:"DZ";a:1:{i:0;s:8:"Algerië";}s:2:"EE";a:1:{i:0;s:7:"Estland";}s:2:"EG";a:1:{i:0;s:6:"Egipte";}s:2:"EH";a:1:{i:0;s:10:"Wes-Sahara";}s:2:"ES";a:1:{i:0;s:6:"Spanje";}s:2:"ET";a:1:{i:0;s:9:"Ethiopië";}s:2:"FJ";a:1:{i:0;s:5:"Fidji";}s:2:"FM";a:1:{i:0;s:11:"Mikronesië";}s:2:"FR";a:1:{i:0;s:8:"Frankryk";}s:2:"GA";a:1:{i:0;s:6:"Gaboen";}s:2:"GB";a:1:{i:0;s:15:"Groot-Brittanje";}s:2:"GE";a:1:{i:0;s:8:"Georgië";}s:2:"GM";a:1:{i:0;s:7:"Gambië";}s:2:"GN";a:1:{i:0;s:6:"Guinee";}s:2:"GQ";a:1:{i:0;s:18:"Ekwatoriaal-Guinee";}s:2:"GR";a:1:{i:0;s:10:"Griekeland";}s:2:"GW";a:1:{i:0;s:13:"Guinee-Bissau";}s:2:"HR";a:1:{i:0;s:8:"Kroasië";}s:2:"HT";a:1:{i:0;s:6:"Haïti";}s:2:"HU";a:1:{i:0;s:8:"Hongarye";}s:2:"ID";a:1:{i:0;s:10:"Indonesië";}s:2:"IE";a:1:{i:0;s:7:"Ierland";}s:2:"IN";a:1:{i:0;s:6:"Indië";}s:2:"IQ";a:1:{i:0;s:4:"Irak";}s:2:"IS";a:1:{i:0;s:6:"Ysland";}s:2:"IT";a:1:{i:0;s:7:"Italië";}s:2:"JM";a:1:{i:0;s:7:"Jamaika";}s:2:"JO";a:1:{i:0;s:9:"Jordanië";}s:2:"KE";a:1:{i:0;s:5:"Kenia";}s:2:"KG";a:1:{i:0;s:9:"Kirgisië";}s:2:"KH";a:1:{i:0;s:8:"Kambodja";}s:2:"KM";a:1:{i:0;s:6:"Comore";}s:2:"KN";a:1:{i:0;s:20:"Saint Kitts en Nevis";}s:2:"KP";a:1:{i:0;s:11:"Noord-Korea";}s:2:"KR";a:1:{i:0;s:10:"Suid-Korea";}s:2:"KW";a:1:{i:0;s:7:"Koeweit";}s:2:"KZ";a:1:{i:0;s:9:"Kasakstan";}s:2:"LB";a:1:{i:0;s:7:"Libanon";}s:2:"LR";a:1:{i:0;s:8:"Liberië";}s:2:"LT";a:1:{i:0;s:6:"Litaue";}s:2:"LU";a:1:{i:0;s:9:"Luxemburg";}s:2:"LV";a:1:{i:0;s:7:"Letland";}s:2:"LY";a:1:{i:0;s:6:"Libië";}s:2:"MA";a:1:{i:0;s:7:"Marokko";}s:2:"MG";a:1:{i:0;s:10:"Madagaskar";}s:2:"MH";a:1:{i:0;s:16:"Marshall-eilande";}s:2:"MK";a:1:{i:0;s:10:"Macedonië";}s:2:"MN";a:1:{i:0;s:9:"Mongolië";}s:2:"MR";a:1:{i:0;s:11:"Mouritanië";}s:2:"MV";a:1:{i:0;s:7:"Maldive";}s:2:"MX";a:1:{i:0;s:7:"Meksiko";}s:2:"MY";a:1:{i:0;s:9:"Maleisië";}s:2:"MZ";a:1:{i:0;s:9:"Mosambiek";}s:2:"NA";a:1:{i:0;s:8:"Namibië";}s:2:"NE";a:1:{i:0;s:8:"Nigerië";}s:2:"NL";a:1:{i:0;s:9:"Nederland";}s:2:"NO";a:1:{i:0;s:8:"Noorweë";}s:2:"NR";a:1:{i:0;s:7:"Naoeroe";}s:2:"NZ";a:1:{i:0;s:12:"Nieu-Seeland";}s:2:"PG";a:1:{i:0;s:18:"Papoea Nieu-Guinee";}s:2:"PH";a:1:{i:0;s:9:"Filippyne";}s:2:"PL";a:1:{i:0;s:4:"Pole";}s:2:"QA";a:1:{i:0;s:5:"Katar";}s:2:"RO";a:1:{i:0;s:9:"Roemenië";}s:2:"RU";a:1:{i:0;s:7:"Rusland";}s:2:"SA";a:1:{i:0;s:14:"Saoedi-Arabië";}s:2:"SB";a:1:{i:0;s:15:"Solomon Eilande";}s:2:"SC";a:1:{i:0;s:9:"Seychelle";}s:2:"SD";a:1:{i:0;s:6:"Soedan";}s:2:"SE";a:1:{i:0;s:5:"Swede";}s:2:"SG";a:1:{i:0;s:9:"Singapoer";}s:2:"SI";a:1:{i:0;s:9:"Slowenië";}s:2:"SK";a:1:{i:0;s:8:"Slowakye";}s:2:"SO";a:1:{i:0;s:8:"Somalië";}s:2:"SP";a:1:{i:0;s:7:"Serwië";}s:2:"ST";a:1:{i:0;s:20:"Sao Tome en Principe";}s:2:"SV";a:1:{i:0;s:8:"Salvador";}s:2:"SY";a:1:{i:0;s:6:"Sirië";}s:2:"TD";a:1:{i:0;s:6:"Tsjaad";}s:2:"TJ";a:1:{i:0;s:11:"Tadjikistan";}s:2:"TM";a:1:{i:0;s:10:"Turkmenië";}s:2:"TN";a:1:{i:0;s:8:"Tunisië";}s:2:"TR";a:1:{i:0;s:6:"Turkye";}s:2:"TT";a:1:{i:0;s:18:"Trinidad en Tobago";}s:2:"TZ";a:1:{i:0;s:9:"Tanzanië";}s:2:"UA";a:1:{i:0;s:8:"Oekraine";}s:2:"US";a:1:{i:0;s:27:"Verenigde State van Amerika";}s:2:"UZ";a:1:{i:0;s:11:"Oesbekistan";}s:2:"VA";a:1:{i:0;s:8:"Vatikaan";}s:2:"VC";a:1:{i:0;s:30:"Saint Vincent en die Grenadine";}s:2:"VN";a:1:{i:0;s:8:"Viëtnam";}s:2:"YE";a:1:{i:0;s:5:"Jemen";}s:2:"ZA";a:1:{i:0;s:11:"Suid-Afrika";}s:2:"ZM";a:1:{i:0;s:7:"Zambië";}}s:10:"Currencies";a:1:{s:3:"ZAR";a:2:{i:0;s:1:"R";i:1;s:4:"Rand";}}s:9:"Languages";a:1:{s:2:"af";a:1:{i:0;s:9:"Afrikaans";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Latn";}s:14:"NumberElements";a:12:{i:0;s:1:",";i:1;s:2:" ";i:2;s:1:";";i:3;s:1:"%";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:4:{s:11:"AmPmMarkers";a:2:{i:0;s:3:"vm.";i:1;s:3:"nm.";}s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:2:"So";i:1;s:2:"Ma";i:2;s:2:"Di";i:3;s:2:"Wo";i:4;s:2:"Do";i:5;s:2:"Vr";i:6;s:2:"Sa";}s:4:"wide";a:7:{i:0;s:6:"Sondag";i:1;s:7:"Maandag";i:2;s:7:"Dinsdag";i:3;s:8:"Woensdag";i:4;s:9:"Donderdag";i:5;s:6:"Vrydag";i:6;s:8:"Saterdag";}}}s:4:"eras";a:1:{s:11:"abbreviated";a:2:{i:0;s:4:"v.C.";i:1;s:4:"n.C.";}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:3:"Jan";i:1;s:3:"Feb";i:2;s:3:"Mar";i:3;s:3:"Apr";i:4;s:3:"Mei";i:5;s:3:"Jun";i:6;s:3:"Jul";i:7;s:3:"Aug";i:8;s:3:"Sep";i:9;s:3:"Okt";i:10;s:3:"Nov";i:11;s:3:"Des";}s:4:"wide";a:12:{i:0;s:8:"Januarie";i:1;s:9:"Februarie";i:2;s:5:"Maart";i:3;s:5:"April";i:4;s:3:"Mei";i:5;s:5:"Junie";i:6;s:5:"Julie";i:7;s:8:"Augustus";i:8;s:9:"September";i:9;s:7:"Oktober";i:10;s:8:"November";i:11;s:8:"Desember";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/af_ZA.dat b/gui/baculum/framework/I18N/core/data/af_ZA.dat new file mode 100644 index 0000000000..fd63a83679 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/af_ZA.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;-#,##0.###";i:1;s:22:"¤#,##0.00;-¤#,##0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:1:{s:16:"DateTimePatterns";a:9:{i:0;s:9:"h:mm:ss a";i:1;s:9:"h:mm:ss a";i:2;s:9:"h:mm:ss a";i:3;s:6:"h:mm a";i:4;s:17:"EEEE dd MMMM yyyy";i:5;s:12:"dd MMMM yyyy";i:6;s:11:"dd MMM yyyy";i:7;s:10:"yyyy/MM/dd";i:8;s:7:"{1} {0}";}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/am.dat b/gui/baculum/framework/I18N/core/data/am.dat new file mode 100644 index 0000000000..914ea0296c --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/am.dat @@ -0,0 +1 @@ +a:5:{s:9:"Countries";a:128:{s:2:"AD";a:1:{i:0;s:12:"አንዶራ";}s:2:"AE";a:1:{i:0;s:44:"የተባበሩት አረብ ኤምሬትስ";}s:2:"AL";a:1:{i:0;s:15:"አልባኒያ";}s:2:"AM";a:1:{i:0;s:15:"አርሜኒያ";}s:2:"AN";a:1:{i:0;s:37:"ኔዘርላንድስ አንቲልስ";}s:2:"AR";a:1:{i:0;s:18:"አርጀንቲና";}s:2:"AT";a:1:{i:0;s:15:"ኦስትሪያ";}s:2:"AU";a:1:{i:0;s:21:"አውስትሬሊያ";}s:2:"AZ";a:1:{i:0;s:18:"አዘርባጃን";}s:2:"BA";a:1:{i:0;s:41:"ቦስኒያ እና ሄርዞጎቪኒያ";}s:2:"BB";a:1:{i:0;s:15:"ባርቤዶስ";}s:2:"BE";a:1:{i:0;s:12:"ቤልጄም";}s:2:"BG";a:1:{i:0;s:15:"ቡልጌሪያ";}s:2:"BH";a:1:{i:0;s:12:"ባህሬን";}s:2:"BM";a:1:{i:0;s:12:"ቤርሙዳ";}s:2:"BO";a:1:{i:0;s:12:"ቦሊቪያ";}s:2:"BR";a:1:{i:0;s:12:"ብራዚል";}s:2:"BT";a:1:{i:0;s:12:"ቡህታን";}s:2:"BY";a:1:{i:0;s:12:"ቤላሩስ";}s:2:"BZ";a:1:{i:0;s:9:"ቤሊዘ";}s:2:"CD";a:1:{i:0;s:9:"ኮንጎ";}s:2:"CF";a:1:{i:0;s:50:"የመካከለኛው አፍሪካ ሪፐብሊክ";}s:2:"CH";a:1:{i:0;s:21:"ስዊዘርላንድ";}s:2:"CL";a:1:{i:0;s:6:"ቺሊ";}s:2:"CM";a:1:{i:0;s:12:"ካሜሩን";}s:2:"CN";a:1:{i:0;s:9:"ቻይና";}s:2:"CO";a:1:{i:0;s:15:"ኮሎምቢያ";}s:2:"CV";a:1:{i:0;s:16:"ኬፕ ቬርዴ";}s:2:"CY";a:1:{i:0;s:15:"ሳይፕረስ";}s:2:"CZ";a:1:{i:0;s:22:"ቼክ ሪፑብሊክ";}s:2:"DE";a:1:{i:0;s:12:"ጀርመን";}s:2:"DK";a:1:{i:0;s:15:"ዴንማርክ";}s:2:"DM";a:1:{i:0;s:12:"ዶሚኒካ";}s:2:"DO";a:1:{i:0;s:28:"ዶሚኒክ ሪፑብሊክ";}s:2:"DZ";a:1:{i:0;s:15:"አልጄሪያ";}s:2:"EC";a:1:{i:0;s:12:"ኢኳዶር";}s:2:"EE";a:1:{i:0;s:15:"ኤስቶኒያ";}s:2:"EG";a:1:{i:0;s:9:"ግብጽ";}s:2:"EH";a:1:{i:0;s:25:"ምዕራባዊ ሳህራ";}s:2:"ER";a:1:{i:0;s:12:"ኤርትራ";}s:2:"ES";a:1:{i:0;s:9:"ስፔን";}s:2:"ET";a:1:{i:0;s:15:"ኢትዮጵያ";}s:2:"FI";a:1:{i:0;s:15:"ፊንላንድ";}s:2:"FJ";a:1:{i:0;s:6:"ፊጂ";}s:2:"FM";a:1:{i:0;s:18:"ሚክሮኔዢያ";}s:2:"GB";a:1:{i:0;s:15:"እንግሊዝ";}s:2:"GE";a:1:{i:0;s:12:"ጆርጂያ";}s:2:"GF";a:1:{i:0;s:31:"የፈረንሳይ ጉዊአና";}s:2:"GM";a:1:{i:0;s:12:"ጋምቢያ";}s:2:"GN";a:1:{i:0;s:6:"ጊኒ";}s:2:"GQ";a:1:{i:0;s:25:"ኢኳቶሪያል ጊኒ";}s:2:"GR";a:1:{i:0;s:9:"ግሪክ";}s:2:"GW";a:1:{i:0;s:9:"ቢሳዎ";}s:2:"GY";a:1:{i:0;s:9:"ጉያና";}s:2:"HR";a:1:{i:0;s:15:"ክሮኤሽያ";}s:2:"HT";a:1:{i:0;s:9:"ሀይቲ";}s:2:"HU";a:1:{i:0;s:12:"ሀንጋሪ";}s:2:"ID";a:1:{i:0;s:18:"ኢንዶኔዢያ";}s:2:"IE";a:1:{i:0;s:18:"አየርላንድ";}s:2:"IL";a:1:{i:0;s:15:"እስራኤል";}s:2:"IN";a:1:{i:0;s:9:"ህንድ";}s:2:"IQ";a:1:{i:0;s:9:"ኢራቅ";}s:2:"IS";a:1:{i:0;s:18:"አይስላንድ";}s:2:"IT";a:1:{i:0;s:12:"ጣሊያን";}s:2:"JM";a:1:{i:0;s:12:"ጃማይካ";}s:2:"JO";a:1:{i:0;s:12:"ጆርዳን";}s:2:"JP";a:1:{i:0;s:9:"ጃፓን";}s:2:"KH";a:1:{i:0;s:15:"ካምቦዲያ";}s:2:"KM";a:1:{i:0;s:12:"ኮሞሮስ";}s:2:"KP";a:1:{i:0;s:19:"ደቡብ ኮሪያ";}s:2:"KR";a:1:{i:0;s:19:"ሰሜን ኮሪያ";}s:2:"KW";a:1:{i:0;s:9:"ክዌት";}s:2:"LB";a:1:{i:0;s:12:"ሊባኖስ";}s:2:"LT";a:1:{i:0;s:15:"ሊቱዌኒያ";}s:2:"LV";a:1:{i:0;s:12:"ላትቪያ";}s:2:"LY";a:1:{i:0;s:9:"ሊቢያ";}s:2:"MA";a:1:{i:0;s:9:"ሞሮኮ";}s:2:"MD";a:1:{i:0;s:12:"ሞልዶቫ";}s:2:"MK";a:1:{i:0;s:15:"ማከዶኒያ";}s:2:"MN";a:1:{i:0;s:15:"ሞንጎሊያ";}s:2:"MR";a:1:{i:0;s:15:"ሞሪቴኒያ";}s:2:"MT";a:1:{i:0;s:9:"ማልታ";}s:2:"MU";a:1:{i:0;s:12:"ማሩሸስ";}s:2:"MX";a:1:{i:0;s:12:"ሜክሲኮ";}s:2:"MY";a:1:{i:0;s:12:"ማሌዢያ";}s:2:"NA";a:1:{i:0;s:12:"ናሚቢያ";}s:2:"NC";a:1:{i:0;s:22:"ኒው ካሌዶኒያ";}s:2:"NG";a:1:{i:0;s:15:"ናይጄሪያ";}s:2:"NL";a:1:{i:0;s:18:"ኔዘርላንድ";}s:2:"NO";a:1:{i:0;s:9:"ኖርዌ";}s:2:"NP";a:1:{i:0;s:9:"ኔፓል";}s:2:"NZ";a:1:{i:0;s:19:"ኒው ዚላንድ";}s:2:"PE";a:1:{i:0;s:6:"ፔሩ";}s:2:"PF";a:1:{i:0;s:34:"የፈረንሳይ ፖሊኔዢያ";}s:2:"PG";a:1:{i:0;s:23:"ፓፑዋ ኒው ጊኒ";}s:2:"PL";a:1:{i:0;s:12:"ፖላንድ";}s:2:"PR";a:1:{i:0;s:16:"ፖርታ ሪኮ";}s:2:"RO";a:1:{i:0;s:12:"ሮሜኒያ";}s:2:"RU";a:1:{i:0;s:9:"ራሺያ";}s:2:"SA";a:1:{i:0;s:21:"ሳውድአረቢያ";}s:2:"SD";a:1:{i:0;s:9:"ሱዳን";}s:2:"SE";a:1:{i:0;s:12:"ስዊድን";}s:2:"SG";a:1:{i:0;s:15:"ሲንጋፖር";}s:2:"SI";a:1:{i:0;s:15:"ስሎቬኒያ";}s:2:"SK";a:1:{i:0;s:15:"ስሎቫኪያ";}s:2:"SN";a:1:{i:0;s:12:"ሴኔጋል";}s:2:"SO";a:1:{i:0;s:9:"ሱማሌ";}s:2:"SP";a:1:{i:0;s:12:"ሰርቢያ";}s:2:"SY";a:1:{i:0;s:9:"ሲሪያ";}s:2:"TD";a:1:{i:0;s:6:"ቻድ";}s:2:"TF";a:1:{i:0;s:44:"የፈረንሳይ ደቡባዊ ግዛቶች";}s:2:"TH";a:1:{i:0;s:15:"ታይላንድ";}s:2:"TJ";a:1:{i:0;s:18:"ታጃኪስታን";}s:2:"TL";a:1:{i:0;s:22:"ምስራቅ ቲሞር";}s:2:"TN";a:1:{i:0;s:12:"ቱኒዚያ";}s:2:"TR";a:1:{i:0;s:9:"ቱርክ";}s:2:"TT";a:1:{i:0;s:32:"ትሪኒዳድ እና ቶባጎ";}s:2:"TZ";a:1:{i:0;s:15:"ታንዛኒያ";}s:2:"UG";a:1:{i:0;s:12:"ዩጋንዳ";}s:2:"US";a:1:{i:0;s:12:"አሜሪካ";}s:2:"UZ";a:1:{i:0;s:21:"ዩዝበኪስታን";}s:2:"VE";a:1:{i:0;s:15:"ቬንዙዌላ";}s:2:"VG";a:1:{i:0;s:44:"የእንግሊዝ ድንግል ደሴቶች";}s:2:"VI";a:1:{i:0;s:41:"የአሜሪካ ቨርጂን ደሴቶች";}s:2:"YE";a:1:{i:0;s:9:"የመን";}s:2:"YU";a:1:{i:0;s:18:"ዩጎዝላቪያ";}s:2:"ZA";a:1:{i:0;s:22:"ደቡብ አፍሪካ";}s:2:"ZM";a:1:{i:0;s:12:"ዛምቢያ";}}s:9:"Languages";a:142:{s:2:"aa";a:1:{i:0;s:12:"አፋርኛ";}s:2:"ab";a:1:{i:0;s:15:"አብሐዚኛ";}s:2:"af";a:1:{i:0;s:21:"አፍሪቃንስኛ";}s:2:"am";a:1:{i:0;s:12:"አማርኛ";}s:2:"ar";a:1:{i:0;s:12:"ዐርቢኛ";}s:2:"as";a:1:{i:0;s:15:"አሳሜዛዊ";}s:2:"ay";a:1:{i:0;s:15:"አያማርኛ";}s:2:"az";a:1:{i:0;s:24:"አዜርባይጃንኛ";}s:2:"ba";a:1:{i:0;s:15:"ባስኪርኛ";}s:2:"be";a:1:{i:0;s:15:"ቤላራሻኛ";}s:2:"bg";a:1:{i:0;s:15:"ቡልጋሪኛ";}s:2:"bh";a:1:{i:0;s:9:"ቢሃሪ";}s:2:"bi";a:1:{i:0;s:15:"ቢስላምኛ";}s:2:"bn";a:1:{i:0;s:15:"በንጋሊኛ";}s:2:"bo";a:1:{i:0;s:15:"ትበትንኛ";}s:2:"br";a:1:{i:0;s:15:"ብሬቶንኛ";}s:3:"byn";a:1:{i:0;s:9:"ብሊን";}s:2:"ca";a:1:{i:0;s:15:"ካታላንኛ";}s:2:"co";a:1:{i:0;s:15:"ኮርሲካኛ";}s:2:"cs";a:1:{i:0;s:9:"ቼክኛ";}s:2:"cy";a:1:{i:0;s:9:"ወልሽ";}s:2:"da";a:1:{i:0;s:9:"ዴኒሽ";}s:2:"de";a:1:{i:0;s:12:"ጀርመን";}s:2:"dz";a:1:{i:0;s:18:"ድዞንግኻኛ";}s:2:"el";a:1:{i:0;s:12:"ግሪክኛ";}s:2:"en";a:1:{i:0;s:18:"እንግሊዝኛ";}s:2:"eo";a:1:{i:0;s:18:"ኤስፐራንቶ";}s:2:"es";a:1:{i:0;s:12:"ስፓኒሽ";}s:2:"et";a:1:{i:0;s:18:"ኤስቶኒአን";}s:2:"eu";a:1:{i:0;s:12:"ባስክኛ";}s:2:"fa";a:1:{i:0;s:15:"ፐርሲያኛ";}s:2:"fi";a:1:{i:0;s:9:"ፊኒሽ";}s:2:"fj";a:1:{i:0;s:9:"ፊጂኛ";}s:2:"fo";a:1:{i:0;s:9:"ፋሮኛ";}s:2:"fr";a:1:{i:0;s:18:"ፈረንሳይኛ";}s:2:"fy";a:1:{i:0;s:12:"ፍሪስኛ";}s:2:"ga";a:1:{i:0;s:12:"አይሪሽ";}s:2:"gd";a:1:{i:0;s:28:"እስኮትስ ጌልክኛ";}s:3:"gez";a:1:{i:0;s:12:"ግዕዝኛ";}s:2:"gl";a:1:{i:0;s:12:"ጋለጋኛ";}s:2:"gn";a:1:{i:0;s:12:"ጓራኒኛ";}s:2:"gu";a:1:{i:0;s:15:"ጉጃርቲኛ";}s:2:"ha";a:1:{i:0;s:12:"ሃውሳኛ";}s:2:"he";a:1:{i:0;s:15:"ዕብራስጥ";}s:2:"hi";a:1:{i:0;s:12:"ሐንድኛ";}s:2:"hr";a:1:{i:0;s:18:"ክሮሽያንኛ";}s:2:"hu";a:1:{i:0;s:15:"ሀንጋሪኛ";}s:2:"hy";a:1:{i:0;s:15:"አርመናዊ";}s:2:"ia";a:1:{i:0;s:21:"ኢንቴርሊንጓ";}s:2:"id";a:1:{i:0;s:18:"እንዶኒሲኛ";}s:2:"ie";a:1:{i:0;s:24:"እንተርሊንግወ";}s:2:"ik";a:1:{i:0;s:18:"እኑፒያቅኛ";}s:2:"is";a:1:{i:0;s:21:"አይስላንድኛ";}s:2:"it";a:1:{i:0;s:15:"ጣሊያንኛ";}s:2:"iu";a:1:{i:0;s:21:"እኑክቲቱትኛ";}s:2:"ja";a:1:{i:0;s:12:"ጃፓንኛ";}s:2:"jv";a:1:{i:0;s:12:"ጃቫንኛ";}s:2:"ka";a:1:{i:0;s:18:"ጊዮርጊያን";}s:2:"kk";a:1:{i:0;s:12:"ካዛክኛ";}s:2:"kl";a:1:{i:0;s:18:"ካላሊሱትኛ";}s:2:"km";a:1:{i:0;s:12:"ክመርኛ";}s:2:"kn";a:1:{i:0;s:12:"ካናዳኛ";}s:2:"ko";a:1:{i:0;s:12:"ኮሪያኛ";}s:2:"ks";a:1:{i:0;s:15:"ካሽሚርኛ";}s:2:"ku";a:1:{i:0;s:15:"ኩርድሽኛ";}s:2:"ky";a:1:{i:0;s:15:"ኪርጊዝኛ";}s:2:"la";a:1:{i:0;s:12:"ላቲንኛ";}s:2:"ln";a:1:{i:0;s:15:"ሊንጋላኛ";}s:2:"lo";a:1:{i:0;s:12:"ላውስኛ";}s:2:"lt";a:1:{i:0;s:18:"ሊቱአኒያን";}s:2:"lv";a:1:{i:0;s:15:"ላትቪያን";}s:2:"mg";a:1:{i:0;s:15:"ማላጋስኛ";}s:2:"mi";a:1:{i:0;s:12:"ማዮሪኛ";}s:2:"mk";a:1:{i:0;s:15:"ማከዶኒኛ";}s:2:"ml";a:1:{i:0;s:18:"ማላያላምኛ";}s:2:"mn";a:1:{i:0;s:18:"ሞንጎላዊኛ";}s:2:"mo";a:1:{i:0;s:18:"ሞልዳቫዊና";}s:2:"mr";a:1:{i:0;s:12:"ማራዚኛ";}s:2:"ms";a:1:{i:0;s:12:"ማላይኛ";}s:2:"mt";a:1:{i:0;s:15:"ማልቲስኛ";}s:2:"my";a:1:{i:0;s:12:"ቡርማኛ";}s:2:"na";a:1:{i:0;s:9:"ናኡሩ";}s:2:"ne";a:1:{i:0;s:12:"ኔፓሊኛ";}s:2:"nl";a:1:{i:0;s:6:"ደች";}s:2:"no";a:1:{i:0;s:18:"ኖርዌጂያን";}s:2:"oc";a:1:{i:0;s:15:"ኦኪታንኛ";}s:2:"om";a:1:{i:0;s:12:"ኦሮምኛ";}s:2:"or";a:1:{i:0;s:12:"ኦሪያኛ";}s:2:"pa";a:1:{i:0;s:15:"ፓንጃቢኛ";}s:2:"pl";a:1:{i:0;s:9:"ፖሊሽ";}s:2:"ps";a:1:{i:0;s:12:"ፑሽቶኛ";}s:2:"pt";a:1:{i:0;s:18:"ፖርቱጋሊኛ";}s:2:"qu";a:1:{i:0;s:9:"ኵቿኛ";}s:2:"rm";a:1:{i:0;s:12:"ሮማንስ";}s:2:"rn";a:1:{i:0;s:12:"ሩንዲኛ";}s:2:"ro";a:1:{i:0;s:15:"ሮማኒያን";}s:2:"ru";a:1:{i:0;s:9:"ራሽኛ";}s:2:"rw";a:1:{i:0;s:24:"ኪንያርዋንድኛ";}s:2:"sa";a:1:{i:0;s:21:"ሳንስክሪትኛ";}s:2:"sd";a:1:{i:0;s:15:"ሲንድሂኛ";}s:2:"sg";a:1:{i:0;s:12:"ሳንጎኛ";}s:2:"si";a:1:{i:0;s:15:"ስንሃልኛ";}s:3:"sid";a:1:{i:0;s:12:"ሲዳምኛ";}s:2:"sk";a:1:{i:0;s:15:"ስሎቫክኛ";}s:2:"sl";a:1:{i:0;s:12:"ስሎቪኛ";}s:2:"sm";a:1:{i:0;s:12:"ሳሞአኛ";}s:2:"sn";a:1:{i:0;s:9:"ሾናኛ";}s:2:"so";a:1:{i:0;s:12:"ሱማልኛ";}s:2:"sq";a:1:{i:0;s:12:"ልቤኒኛ";}s:2:"sr";a:1:{i:0;s:12:"ሰርቢኛ";}s:2:"ss";a:1:{i:0;s:12:"ስዋቲኛ";}s:2:"st";a:1:{i:0;s:9:"ሶዞኛ";}s:2:"su";a:1:{i:0;s:12:"ሱዳንኛ";}s:2:"sv";a:1:{i:0;s:15:"ስዊድንኛ";}s:2:"sw";a:1:{i:0;s:15:"ስዋሂሊኛ";}s:2:"ta";a:1:{i:0;s:12:"ታሚልኛ";}s:2:"te";a:1:{i:0;s:12:"ተሉጉኛ";}s:2:"tg";a:1:{i:0;s:12:"ታጂኪኛ";}s:2:"th";a:1:{i:0;s:9:"ታይኛ";}s:2:"ti";a:1:{i:0;s:12:"ትግርኛ";}s:3:"tig";a:1:{i:0;s:9:"ትግረ";}s:2:"tk";a:1:{i:0;s:18:"ቱርክመንኛ";}s:2:"tl";a:1:{i:0;s:15:"ታጋሎገኛ";}s:2:"tn";a:1:{i:0;s:15:"ጽዋናዊኛ";}s:2:"to";a:1:{i:0;s:9:"ቶንጋ";}s:2:"tr";a:1:{i:0;s:12:"ቱርክኛ";}s:2:"ts";a:1:{i:0;s:12:"ጾንጋኛ";}s:2:"tt";a:1:{i:0;s:12:"ታታርኛ";}s:2:"tw";a:1:{i:0;s:9:"ትዊኛ";}s:2:"ug";a:1:{i:0;s:18:"ኡዊግሁርኛ";}s:2:"uk";a:1:{i:0;s:15:"ዩክረኒኛ";}s:2:"ur";a:1:{i:0;s:12:"ኡርዱኛ";}s:2:"uz";a:1:{i:0;s:15:"ኡዝበክኛ";}s:2:"vi";a:1:{i:0;s:15:"ቪትናምኛ";}s:2:"vo";a:1:{i:0;s:15:"ቮላፑክኛ";}s:2:"wo";a:1:{i:0;s:12:"ዎሎፍኛ";}s:2:"xh";a:1:{i:0;s:9:"ዞሳኛ";}s:2:"yi";a:1:{i:0;s:15:"ይዲሻዊኛ";}s:2:"yo";a:1:{i:0;s:15:"ዮሩባዊኛ";}s:2:"za";a:1:{i:0;s:15:"ዡዋንግኛ";}s:2:"zh";a:1:{i:0;s:12:"ቻይንኛ";}s:2:"zu";a:1:{i:0;s:9:"ዙሉኛ";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Ethi";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:4:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:7;i:1;i:1;}s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:9:"እሑድ";i:1;s:6:"ሰኞ";i:2;s:9:"ማክሰ";i:3;s:9:"ረቡዕ";i:4;s:9:"ሐሙስ";i:5;s:9:"ዓርብ";i:6;s:9:"ቅዳሜ";}s:4:"wide";a:7:{i:0;s:9:"እሑድ";i:1;s:6:"ሰኞ";i:2;s:12:"ማክሰኞ";i:3;s:9:"ረቡዕ";i:4;s:9:"ሐሙስ";i:5;s:9:"ዓርብ";i:6;s:9:"ቅዳሜ";}}}s:4:"eras";a:1:{s:11:"abbreviated";a:2:{i:0;s:7:"ዓ/ዓ";i:1;s:7:"ዓ/ም";}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:9:"ጃንዩ";i:1;s:9:"ፌብሩ";i:2;s:9:"ማርች";i:3;s:9:"ኤፕረ";i:4;s:6:"ሜይ";i:5;s:6:"ጁን";i:6;s:9:"ጁላይ";i:7;s:9:"ኦገስ";i:8;s:9:"ሴፕቴ";i:9;s:9:"ኦክተ";i:10;s:9:"ኖቬም";i:11;s:9:"ዲሴም";}s:4:"wide";a:12:{i:0;s:15:"ጃንዩወሪ";i:1;s:15:"ፌብሩወሪ";i:2;s:9:"ማርች";i:3;s:12:"ኤፕረል";i:4;s:6:"ሜይ";i:5;s:6:"ጁን";i:6;s:9:"ጁላይ";i:7;s:12:"ኦገስት";i:8;s:18:"ሴፕቴምበር";i:9;s:18:"ኦክተውበር";i:10;s:15:"ኖቬምበር";i:11;s:15:"ዲሴምበር";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/am_ET.dat b/gui/baculum/framework/I18N/core/data/am_ET.dat new file mode 100644 index 0000000000..673caa4268 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/am_ET.dat @@ -0,0 +1 @@ +a:4:{s:10:"Currencies";a:2:{s:3:"ETB";a:2:{i:0;s:1:"$";i:1;s:3:"ETB";}s:3:"USD";a:2:{i:0;s:3:"US$";i:1;s:3:"USD";}}s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;-#,##0.###";i:1;s:22:"¤#,##0.00;-¤#,##0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:2:{s:11:"AmPmMarkers";a:2:{i:0;s:9:"ጡዋት";i:1;s:12:"ከሳዓት";}s:16:"DateTimePatterns";a:9:{i:0;s:10:"hh:mm:ss a";i:1;s:10:"hh:mm:ss a";i:2;s:9:"h:mm:ss a";i:3;s:6:"h:mm a";i:4;s:29:"EEEE፣ dd MMMM ቀን yyyy G";i:5;s:12:"dd MMMM yyyy";i:6;s:9:"dd-MMM-yy";i:7;s:8:"dd/MM/yy";i:8;s:7:"{1} {0}";}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar.dat b/gui/baculum/framework/I18N/core/data/ar.dat new file mode 100644 index 0000000000..aa8d5538cd --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar.dat @@ -0,0 +1 @@ +a:12:{s:9:"Countries";a:190:{s:2:"AD";a:1:{i:0;s:12:"اندورا";}s:2:"AE";a:1:{i:0;s:46:"الامارات العربية المتحدة";}s:2:"AF";a:1:{i:0;s:18:"افغانستان";}s:2:"AG";a:1:{i:0;s:29:"انتيغوا وبربودا";}s:2:"AI";a:1:{i:0;s:14:"البانيا";}s:2:"AM";a:1:{i:0;s:14:"ارمينيا";}s:2:"AO";a:1:{i:0;s:12:"انغولا";}s:2:"AR";a:1:{i:0;s:18:"الارجنتين";}s:2:"AT";a:1:{i:0;s:12:"النمسا";}s:2:"AU";a:1:{i:0;s:16:"استراليا";}s:2:"AZ";a:1:{i:0;s:16:"آذربيجان";}s:2:"BA";a:1:{i:0;s:29:"البوسنة والهرسك";}s:2:"BB";a:1:{i:0;s:14:"بربادوس";}s:2:"BD";a:1:{i:0;s:16:"بنغلاديش";}s:2:"BE";a:1:{i:0;s:12:"بلجيكا";}s:2:"BF";a:1:{i:0;s:23:"بوركينا فاسو";}s:2:"BG";a:1:{i:0;s:14:"بلغاريا";}s:2:"BH";a:1:{i:0;s:14:"البحرين";}s:2:"BI";a:1:{i:0;s:14:"بوروندي";}s:2:"BJ";a:1:{i:0;s:8:"بنين";}s:2:"BN";a:1:{i:0;s:12:"بروناي";}s:2:"BO";a:1:{i:0;s:14:"بوليفيا";}s:2:"BR";a:1:{i:0;s:16:"البرازيل";}s:2:"BS";a:1:{i:0;s:14:"البهاما";}s:2:"BT";a:1:{i:0;s:10:"بوتان";}s:2:"BW";a:1:{i:0;s:16:"بوتسوانا";}s:2:"BY";a:1:{i:0;s:25:"روسيا البيضاء";}s:2:"BZ";a:1:{i:0;s:8:"بليز";}s:2:"CA";a:1:{i:0;s:8:"كندا";}s:2:"CF";a:1:{i:0;s:42:"جمهورية افريقيا الوسطى";}s:2:"CG";a:1:{i:0;s:14:"الكونغو";}s:2:"CH";a:1:{i:0;s:12:"سويسرا";}s:2:"CL";a:1:{i:0;s:10:"تشيلي";}s:2:"CM";a:1:{i:0;s:18:"الكاميرون";}s:2:"CN";a:1:{i:0;s:10:"الصين";}s:2:"CO";a:1:{i:0;s:16:"كولومبيا";}s:2:"CR";a:1:{i:0;s:18:"كوستاريكا";}s:2:"CU";a:1:{i:0;s:8:"كوبا";}s:2:"CV";a:1:{i:0;s:23:"الرأس الاخضر";}s:2:"CY";a:1:{i:0;s:8:"قبرص";}s:2:"CZ";a:1:{i:0;s:27:"جمهورية التشيك";}s:2:"DE";a:1:{i:0;s:14:"المانيا";}s:2:"DJ";a:1:{i:0;s:12:"جيبوتي";}s:2:"DK";a:1:{i:0;s:16:"الدانمرك";}s:2:"DM";a:1:{i:0;s:16:"دومينيكا";}s:2:"DO";a:1:{i:0;s:41:"الجمهورية الدومينيكية";}s:2:"DZ";a:1:{i:0;s:14:"الجزائر";}s:2:"EC";a:1:{i:0;s:14:"اكوادور";}s:2:"EE";a:1:{i:0;s:14:"استونيا";}s:2:"EG";a:1:{i:0;s:6:"مصر";}s:2:"EH";a:1:{i:0;s:29:"الصحراء الغربية";}s:2:"ER";a:1:{i:0;s:14:"ارتيريا";}s:2:"ES";a:1:{i:0;s:14:"اسبانيا";}s:2:"ET";a:1:{i:0;s:14:"اثيوبيا";}s:2:"FI";a:1:{i:0;s:12:"فنلندا";}s:2:"FJ";a:1:{i:0;s:8:"فيجي";}s:2:"FM";a:1:{i:0;s:20:"ميكرونيزيا";}s:2:"FR";a:1:{i:0;s:10:"فرنسا";}s:2:"GA";a:1:{i:0;s:10:"غابون";}s:2:"GB";a:1:{i:0;s:29:"المملكة المتحدة";}s:2:"GD";a:1:{i:0;s:14:"غرينادا";}s:2:"GE";a:1:{i:0;s:12:"جورجيا";}s:2:"GH";a:1:{i:0;s:8:"غانا";}s:2:"GM";a:1:{i:0;s:12:"غامبيا";}s:2:"GN";a:1:{i:0;s:10:"غينيا";}s:2:"GQ";a:1:{i:0;s:31:"غينيا الاستوائية";}s:2:"GR";a:1:{i:0;s:14:"اليونان";}s:2:"GT";a:1:{i:0;s:18:"غواتيمالا";}s:2:"GW";a:1:{i:0;s:21:"غينيا بيساو";}s:2:"GY";a:1:{i:0;s:14:"غوايانا";}s:2:"HN";a:1:{i:0;s:14:"هندوراس";}s:2:"HR";a:1:{i:0;s:14:"كرواتيا";}s:2:"HT";a:1:{i:0;s:10:"هايتي";}s:2:"HU";a:1:{i:0;s:14:"هنغاريا";}s:2:"ID";a:1:{i:0;s:18:"اندونيسيا";}s:2:"IE";a:1:{i:0;s:14:"ايرلندا";}s:2:"IL";a:1:{i:0;s:14:"اسرائيل";}s:2:"IN";a:1:{i:0;s:10:"الهند";}s:2:"IQ";a:1:{i:0;s:12:"العراق";}s:2:"IR";a:1:{i:0;s:10:"ايران";}s:2:"IS";a:1:{i:0;s:14:"ايسلندا";}s:2:"IT";a:1:{i:0;s:14:"ايطاليا";}s:2:"JM";a:1:{i:0;s:14:"جامايكا";}s:2:"JO";a:1:{i:0;s:12:"الاردن";}s:2:"JP";a:1:{i:0;s:14:"اليابان";}s:2:"KE";a:1:{i:0;s:10:"كينيا";}s:2:"KG";a:1:{i:0;s:20:"قيرغيزستان";}s:2:"KH";a:1:{i:0;s:14:"كمبوديا";}s:2:"KI";a:1:{i:0;s:16:"كيريباتي";}s:2:"KM";a:1:{i:0;s:17:"جزر القمر";}s:2:"KN";a:1:{i:0;s:30:"سانت كيتس ونيفيس";}s:2:"KP";a:1:{i:0;s:27:"كوريا الشمالية";}s:2:"KR";a:1:{i:0;s:27:"كوريا الجنوبية";}s:2:"KW";a:1:{i:0;s:12:"الكويت";}s:2:"KZ";a:1:{i:0;s:18:"كازاخستان";}s:2:"LA";a:1:{i:0;s:8:"لاوس";}s:2:"LB";a:1:{i:0;s:10:"لبنان";}s:2:"LC";a:1:{i:0;s:19:"سانت لوسيا";}s:2:"LI";a:1:{i:0;s:20:"ليختنشتاين";}s:2:"LK";a:1:{i:0;s:17:"سري لانكا";}s:2:"LR";a:1:{i:0;s:14:"ليبيريا";}s:2:"LS";a:1:{i:0;s:12:"ليسوتو";}s:2:"LT";a:1:{i:0;s:16:"ليتوانيا";}s:2:"LU";a:1:{i:0;s:18:"لوكسومبرغ";}s:2:"LV";a:1:{i:0;s:12:"لاتفيا";}s:2:"LY";a:1:{i:0;s:10:"ليبيا";}s:2:"MA";a:1:{i:0;s:12:"المغرب";}s:2:"MC";a:1:{i:0;s:12:"موناكو";}s:2:"MD";a:1:{i:0;s:14:"مولدوفا";}s:2:"MG";a:1:{i:0;s:12:"مدغشقر";}s:2:"MH";a:1:{i:0;s:23:"جزر المارشال";}s:2:"MK";a:1:{i:0;s:14:"مقدونيا";}s:2:"ML";a:1:{i:0;s:8:"مالي";}s:2:"MM";a:1:{i:0;s:14:"ميانمار";}s:2:"MN";a:1:{i:0;s:14:"منغوليا";}s:2:"MR";a:1:{i:0;s:18:"موريتانيا";}s:2:"MT";a:1:{i:0;s:10:"مالطة";}s:2:"MU";a:1:{i:0;s:14:"موريشوس";}s:2:"MV";a:1:{i:0;s:12:"مالديف";}s:2:"MW";a:1:{i:0;s:10:"ملاوي";}s:2:"MX";a:1:{i:0;s:14:"المكسيك";}s:2:"MY";a:1:{i:0;s:14:"ماليزيا";}s:2:"MZ";a:1:{i:0;s:14:"موزمبيق";}s:2:"NA";a:1:{i:0;s:14:"ناميبيا";}s:2:"NE";a:1:{i:0;s:12:"النيجر";}s:2:"NG";a:1:{i:0;s:14:"نيجيريا";}s:2:"NI";a:1:{i:0;s:18:"نيكاراغوا";}s:2:"NL";a:1:{i:0;s:12:"هولندا";}s:2:"NO";a:1:{i:0;s:14:"النرويج";}s:2:"NP";a:1:{i:0;s:14:"النيبال";}s:2:"NR";a:1:{i:0;s:10:"ناورو";}s:2:"NZ";a:1:{i:0;s:27:"زيلندا الجديدة";}s:2:"OM";a:1:{i:0;s:8:"عمان";}s:2:"PA";a:1:{i:0;s:8:"بنما";}s:2:"PE";a:1:{i:0;s:8:"بيرو";}s:2:"PG";a:1:{i:0;s:36:"بابوا غينيا الجديدة";}s:2:"PH";a:1:{i:0;s:16:"الفيلبين";}s:2:"PK";a:1:{i:0;s:18:"الباكستان";}s:2:"PL";a:1:{i:0;s:12:"بولندا";}s:2:"PT";a:1:{i:0;s:16:"البرتغال";}s:2:"PW";a:1:{i:0;s:10:"بالاو";}s:2:"PY";a:1:{i:0;s:16:"باراغواي";}s:2:"QA";a:1:{i:0;s:6:"قطر";}s:2:"RO";a:1:{i:0;s:14:"رومانيا";}s:2:"RU";a:1:{i:0;s:10:"روسيا";}s:2:"RW";a:1:{i:0;s:12:"رواندا";}s:2:"SA";a:1:{i:0;s:31:"العربية السعودية";}s:2:"SB";a:1:{i:0;s:19:"جزر سليمان";}s:2:"SC";a:1:{i:0;s:8:"سيشل";}s:2:"SD";a:1:{i:0;s:14:"السودان";}s:2:"SE";a:1:{i:0;s:12:"السويد";}s:2:"SG";a:1:{i:0;s:16:"سنغافورة";}s:2:"SI";a:1:{i:0;s:16:"سلوفينيا";}s:2:"SK";a:1:{i:0;s:16:"سلوفاكيا";}s:2:"SL";a:1:{i:0;s:16:"سيراليون";}s:2:"SM";a:1:{i:0;s:19:"سان مارينو";}s:2:"SN";a:1:{i:0;s:14:"السنغال";}s:2:"SO";a:1:{i:0;s:14:"الصومال";}s:2:"SR";a:1:{i:0;s:14:"سورينام";}s:2:"ST";a:1:{i:0;s:34:"سان تومي وبرينسيبي";}s:2:"SV";a:1:{i:0;s:18:"السلفادور";}s:2:"SY";a:1:{i:0;s:10:"سورية";}s:2:"SZ";a:1:{i:0;s:18:"سوازيلاند";}s:2:"TD";a:1:{i:0;s:8:"تشاد";}s:2:"TG";a:1:{i:0;s:8:"توغو";}s:2:"TH";a:1:{i:0;s:12:"تايلند";}s:2:"TJ";a:1:{i:0;s:18:"تاجيكستان";}s:2:"TM";a:1:{i:0;s:20:"تركمانستان";}s:2:"TN";a:1:{i:0;s:8:"تونس";}s:2:"TO";a:1:{i:0;s:10:"تونغا";}s:2:"TR";a:1:{i:0;s:10:"تركيا";}s:2:"TT";a:1:{i:0;s:31:"ترينيداد وتوباغو";}s:2:"TV";a:1:{i:0;s:12:"توفالو";}s:2:"TW";a:1:{i:0;s:12:"تايوان";}s:2:"TZ";a:1:{i:0;s:16:"تانزانيا";}s:2:"UA";a:1:{i:0;s:16:"اوكرانيا";}s:2:"UG";a:1:{i:0;s:12:"اوغندا";}s:2:"US";a:1:{i:0;s:52:"الاولايات المتحدة الامريكية";}s:2:"UY";a:1:{i:0;s:14:"اروغواي";}s:2:"UZ";a:1:{i:0;s:16:"ازبكستان";}s:2:"VA";a:1:{i:0;s:18:"الفاتيكان";}s:2:"VC";a:1:{i:0;s:45:"سانت فنسنت وجزر غرينادين";}s:2:"VE";a:1:{i:0;s:14:"فنزويلا";}s:2:"VN";a:1:{i:0;s:12:"فيتنام";}s:2:"VU";a:1:{i:0;s:14:"فانوآتو";}s:2:"WS";a:1:{i:0;s:10:"ساموا";}s:2:"YE";a:1:{i:0;s:10:"اليمن";}s:2:"ZA";a:1:{i:0;s:23:"جنوب افريقيا";}s:2:"ZM";a:1:{i:0;s:12:"زامبيا";}s:2:"ZW";a:1:{i:0;s:16:"زيمبابوي";}}s:10:"Currencies";a:17:{s:3:"AED";a:2:{i:0;s:9:"د.Ø¥.‏";i:1;s:3:"AED";}s:3:"BHD";a:2:{i:0;s:9:"د.ب.‏";i:1;s:3:"BHD";}s:3:"DZD";a:2:{i:0;s:9:"د.ج.‏";i:1;s:3:"DZD";}s:3:"EGP";a:2:{i:0;s:9:"ج.م.‏";i:1;s:3:"EGP";}s:3:"IQD";a:2:{i:0;s:9:"د.ع.‏";i:1;s:3:"IQD";}s:3:"JOD";a:2:{i:0;s:9:"د.Ø£.‏";i:1;s:3:"JOD";}s:3:"KWD";a:2:{i:0;s:9:"د.ك.‏";i:1;s:3:"KWD";}s:3:"LBP";a:2:{i:0;s:9:"ل.ل.‏";i:1;s:3:"LBP";}s:3:"LYD";a:2:{i:0;s:9:"د.ل.‏";i:1;s:3:"LYD";}s:3:"MAD";a:2:{i:0;s:9:"د.م.‏";i:1;s:3:"MAD";}s:3:"OMR";a:2:{i:0;s:9:"ر.ع.‏";i:1;s:3:"OMR";}s:3:"QAR";a:2:{i:0;s:9:"ر.ق.‏";i:1;s:3:"QAR";}s:3:"SAR";a:2:{i:0;s:9:"ر.س.‏";i:1;s:3:"SAR";}s:3:"SDP";a:2:{i:0;s:9:"ج.س.‏";i:1;s:3:"SDP";}s:3:"SYP";a:2:{i:0;s:9:"ل.س.‏";i:1;s:3:"SYP";}s:3:"TND";a:2:{i:0;s:9:"د.ت.‏";i:1;s:3:"TND";}s:3:"YER";a:2:{i:0;s:9:"ر.ي.‏";i:1;s:3:"YER";}}s:4:"Keys";a:3:{s:8:"calendar";a:1:{i:0;s:10:"تقويم";}s:9:"collation";a:1:{i:0;s:14:"الترتيب";}s:8:"currency";a:1:{i:0;s:14:"العملات";}}s:9:"Languages";a:79:{s:2:"aa";a:1:{i:0;s:16:"الافارية";}s:2:"ab";a:1:{i:0;s:18:"الابخازية";}s:2:"af";a:1:{i:0;s:18:"الافريقية";}s:3:"afa";a:1:{i:0;s:21:"افرو-اسيوية";}s:3:"ang";a:1:{i:0;s:27:"انكليزية، قديم";}s:2:"ar";a:1:{i:0;s:14:"العربية";}s:3:"arc";a:1:{i:0;s:16:"الارامية";}s:2:"az";a:1:{i:0;s:10:"اذرية";}s:3:"bal";a:1:{i:0;s:16:"البلوشية";}s:2:"be";a:1:{i:0;s:22:"البيلوروسية";}s:3:"ber";a:1:{i:0;s:16:"البربرية";}s:2:"bg";a:1:{i:0;s:18:"البلغارية";}s:2:"bn";a:1:{i:0;s:18:"البنغالية";}s:2:"bo";a:1:{i:0;s:14:"التبتية";}s:2:"bs";a:1:{i:0;s:16:"البوسنية";}s:2:"ca";a:1:{i:0;s:24:"الكاتالوينية";}s:2:"ce";a:1:{i:0;s:18:"الشيشانية";}s:3:"cop";a:1:{i:0;s:10:"قبطية";}s:2:"cs";a:1:{i:0;s:16:"التشيكية";}s:2:"cy";a:1:{i:0;s:14:"الولزية";}s:2:"da";a:1:{i:0;s:22:"الدانماركية";}s:2:"de";a:1:{i:0;s:18:"الالمانية";}s:2:"dv";a:1:{i:0;s:20:"المالديفية";}s:2:"dz";a:1:{i:0;s:18:"الزونخاية";}s:3:"egy";a:1:{i:0;s:23:"مصرية، قديمة";}s:2:"el";a:1:{i:0;s:18:"اليونانية";}s:2:"en";a:1:{i:0;s:20:"الانجليزية";}s:2:"eo";a:1:{i:0;s:16:"اسبرانتو";}s:2:"es";a:1:{i:0;s:18:"الاسبانية";}s:2:"et";a:1:{i:0;s:14:"استونية";}s:2:"fa";a:1:{i:0;s:16:"الفارسية";}s:2:"fi";a:1:{i:0;s:18:"الفنلندية";}s:2:"fj";a:1:{i:0;s:14:"الفيجية";}s:2:"fr";a:1:{i:0;s:16:"الفرنسية";}s:2:"gu";a:1:{i:0;s:22:"الغوجاراتية";}s:2:"he";a:1:{i:0;s:14:"العبرية";}s:2:"hi";a:1:{i:0;s:14:"الهندية";}s:2:"hr";a:1:{i:0;s:18:"الكرواتية";}s:2:"ht";a:1:{i:0;s:16:"الهايتية";}s:2:"hu";a:1:{i:0;s:18:"الهنغارية";}s:2:"hy";a:1:{i:0;s:16:"الارمنية";}s:2:"id";a:1:{i:0;s:22:"الاندونيسية";}s:3:"ira";a:1:{i:0;s:18:"الايرانية";}s:2:"it";a:1:{i:0;s:18:"الايطالية";}s:2:"ja";a:1:{i:0;s:18:"اليابانية";}s:2:"km";a:1:{i:0;s:16:"الخميرية";}s:2:"ko";a:1:{i:0;s:14:"الكورية";}s:2:"ks";a:1:{i:0;s:20:"الكاشميرية";}s:2:"ku";a:1:{i:0;s:14:"الكردية";}s:2:"la";a:1:{i:0;s:18:"اللاتينية";}s:2:"lt";a:1:{i:0;s:18:"اللتوانية";}s:2:"lv";a:1:{i:0;s:16:"اللاتفية";}s:2:"mn";a:1:{i:0;s:18:"المنغولية";}s:2:"ms";a:1:{i:0;s:21:"لغة الملايو";}s:2:"mt";a:1:{i:0;s:16:"المالطية";}s:2:"my";a:1:{i:0;s:16:"البورمية";}s:2:"ne";a:1:{i:0;s:18:"النيبالية";}s:2:"nl";a:1:{i:0;s:18:"الهولندية";}s:2:"pa";a:1:{i:0;s:18:"البنجابية";}s:2:"pl";a:1:{i:0;s:18:"البولونية";}s:2:"ps";a:1:{i:0;s:18:"البشتونية";}s:2:"pt";a:1:{i:0;s:20:"البرتغالية";}s:2:"ro";a:1:{i:0;s:18:"الرومانية";}s:2:"ru";a:1:{i:0;s:14:"الروسية";}s:3:"smi";a:1:{i:0;s:27:"اللغات السامية";}s:2:"sq";a:1:{i:0;s:18:"الالبانية";}s:2:"sr";a:1:{i:0;s:14:"الصربية";}s:2:"sv";a:1:{i:0;s:16:"السويدية";}s:2:"sw";a:1:{i:0;s:18:"السواحلية";}s:3:"syr";a:1:{i:0;s:18:"السريانية";}s:2:"th";a:1:{i:0;s:22:"التايلاندية";}s:2:"tl";a:1:{i:0;s:22:"التاغالوغية";}s:2:"tr";a:1:{i:0;s:14:"التركية";}s:2:"tt";a:1:{i:0;s:14:"التترية";}s:2:"ug";a:1:{i:0;s:16:"الاغورية";}s:2:"uk";a:1:{i:0;s:20:"الاوكرانية";}s:2:"ur";a:1:{i:0;s:14:"الاردية";}s:2:"vi";a:1:{i:0;s:20:"الفيتنامية";}s:2:"zh";a:1:{i:0;s:14:"الصينية";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Arab";}s:14:"NumberElements";a:12:{i:0;s:2:"Ù«";i:1;s:2:"Ù¬";i:2;s:1:";";i:3;s:2:"Ùª";i:4;s:2:"Ù ";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;#,##0.###-";i:1;s:24:"¤ #,##0.00;¤ #,##0.00-";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Scripts";a:19:{s:4:"Arab";a:1:{i:0;s:14:"العربية";}s:4:"Armn";a:1:{i:0;s:16:"الارمنية";}s:4:"Beng";a:1:{i:0;s:18:"البنغالية";}s:4:"Brai";a:1:{i:0;s:10:"برايل";}s:4:"Copt";a:1:{i:0;s:14:"القبطية";}s:4:"Cprt";a:1:{i:0;s:16:"القبرصية";}s:4:"Ethi";a:1:{i:0;s:18:"الاثيوبية";}s:4:"Geor";a:1:{i:0;s:16:"الجورجية";}s:4:"Grek";a:1:{i:0;s:18:"اليونانية";}s:4:"Gujr";a:1:{i:0;s:18:"تاغجراتية";}s:4:"Hebr";a:1:{i:0;s:14:"العبرية";}s:4:"Khmr";a:1:{i:0;s:16:"الخميرية";}s:4:"Latn";a:1:{i:0;s:16:"اللاتنية";}s:4:"Mong";a:1:{i:0;s:16:"المغولية";}s:4:"Syrc";a:1:{i:0;s:18:"السريانية";}s:4:"Taml";a:1:{i:0;s:14:"التاميل";}s:4:"Tglg";a:1:{i:0;s:20:"التغالوغية";}s:4:"Thai";a:1:{i:0;s:20:"التايلندية";}s:4:"Tibt";a:1:{i:0;s:14:"التبتية";}}s:5:"Types";a:2:{s:8:"calendar";a:7:{s:8:"buddhist";a:1:{i:0;s:19:"تقويم بوني";}s:7:"chinese";a:1:{i:0;s:19:"تقويم صيني";}s:9:"gregorian";a:1:{i:0;s:23:"تقويم ميلادي";}s:6:"hebrew";a:1:{i:0;s:19:"تقويم عبري";}s:7:"islamic";a:1:{i:0;s:23:"تقويم اسلامي";}s:13:"islamic-civil";a:1:{i:0;s:32:"تقويم اسلامي مدني";}s:8:"japanese";a:1:{i:0;s:23:"تقويم ياباني";}}s:9:"collation";a:3:{s:6:"direct";a:1:{i:0;s:21:"ترتيب مباشر";}s:9:"phonebook";a:1:{i:0;s:32:"ترتيب دليل الهاتف";}s:11:"traditional";a:1:{i:0;s:12:"تقليدي";}}}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:4:{s:8:"buddhist";a:1:{s:4:"eras";a:1:{s:11:"abbreviated";a:1:{i:0;s:27:"التقويم البوذي";}}}s:9:"gregorian";a:7:{s:11:"AmPmMarkers";a:2:{i:0;s:2:"ص";i:1;s:2:"م";}s:26:"DateTimeElements:intvector";a:2:{i:0;i:7;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:11:"z h:mm:ss a";i:1;s:9:"h:mm:ss a";i:2;s:9:"h:mm:ss a";i:3;s:6:"h:mm a";i:4;s:18:"EEEE, d MMMM, yyyy";i:5;s:12:"d MMMM, yyyy";i:6;s:10:"dd/MM/yyyy";i:7;s:8:"d/M/yyyy";i:8;s:7:"{1} {0}";}s:8:"dayNames";a:1:{s:6:"format";a:3:{s:11:"abbreviated";a:7:{i:0;s:2:"Ø­";i:1;s:2:"ن";i:2;s:2:"Ø«";i:3;s:2:"ر";i:4;s:2:"Ø®";i:5;s:2:"ج";i:6;s:2:"س";}s:6:"narrow";a:7:{i:0;s:2:"Ø­";i:1;s:2:"ن";i:2;s:2:"Ø«";i:3;s:2:"ر";i:4;s:2:"Ø®";i:5;s:2:"ج";i:6;s:2:"س";}s:4:"wide";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}s:4:"eras";a:1:{s:11:"abbreviated";a:2:{i:0;s:5:"ق.م";i:1;s:2:"م";}}s:10:"monthNames";a:1:{s:6:"format";a:3:{s:11:"abbreviated";a:12:{i:0;s:10:"يناير";i:1;s:12:"فبراير";i:2;s:8:"مارس";i:3;s:10:"أبريل";i:4;s:8:"مايو";i:5;s:10:"يونيو";i:6;s:10:"يوليو";i:7;s:10:"أغسطس";i:8;s:12:"سبتمبر";i:9;s:12:"أكتوبر";i:10;s:12:"نوفمبر";i:11;s:12:"ديسمبر";}s:6:"narrow";a:12:{i:0;s:2:"ي";i:1;s:2:"ف";i:2;s:2:"م";i:3;s:2:"Ø¢";i:4;s:2:"و";i:5;s:2:"ن";i:6;s:2:"ل";i:7;s:2:"غ";i:8;s:2:"س";i:9;s:2:"ك";i:10;s:2:"ب";i:11;s:2:"د";}s:4:"wide";a:12:{i:0;s:10:"يناير";i:1;s:12:"فبراير";i:2;s:8:"مارس";i:3;s:10:"أبريل";i:4;s:8:"مايو";i:5;s:10:"يونيو";i:6;s:10:"يوليو";i:7;s:10:"أغسطس";i:8;s:12:"سبتمبر";i:9;s:12:"أكتوبر";i:10;s:12:"نوفمبر";i:11;s:12:"ديسمبر";}}}s:17:"weekend:intvector";a:4:{i:0;i:5;i:1;i:0;i:2;i:6;i:3;i:86400000;}}s:7:"islamic";a:2:{s:4:"eras";a:1:{s:11:"abbreviated";a:1:{i:0;s:5:"ه‍";}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:17:"abbreviated:alias";a:1:{i:0;s:42:"ar/calendar/islamic/monthNames/format/wide";}s:4:"wide";a:12:{i:0;s:8:"محرم";i:1;s:6:"صفر";i:2;s:19:"ربيع الأول";i:3;s:19:"ربيع الآخر";i:4;s:23:"جمادى الأولى";i:5;s:23:"جمادى الآخرة";i:6;s:6:"رجب";i:7;s:10:"شعبان";i:8;s:10:"رمضان";i:9;s:8:"شوال";i:10;s:17:"ذو القعدة";i:11;s:15:"ذو الحجة";}}}}s:13:"islamic-civil";a:2:{s:10:"eras:alias";a:1:{i:0;s:24:"ar/calendar/islamic/eras";}s:16:"monthNames:alias";a:1:{i:0;s:30:"ar/calendar/islamic/monthNames";}}}s:17:"localPatternChars";a:1:{i:0;s:24:"GanjkHmsSEDFwWxhKzAeugXZ";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_AE.dat b/gui/baculum/framework/I18N/core/data/ar_AE.dat new file mode 100644 index 0000000000..9d38c95dce --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_AE.dat @@ -0,0 +1 @@ +a:1:{s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_BH.dat b/gui/baculum/framework/I18N/core/data/ar_BH.dat new file mode 100644 index 0000000000..9d38c95dce --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_BH.dat @@ -0,0 +1 @@ +a:1:{s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_DZ.dat b/gui/baculum/framework/I18N/core/data/ar_DZ.dat new file mode 100644 index 0000000000..1c3fdb2e3c --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_DZ.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberElements";a:12:{i:0;s:2:"Ù«";i:1;s:2:"Ù¬";i:2;s:1:";";i:3;s:2:"Ùª";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_EG.dat b/gui/baculum/framework/I18N/core/data/ar_EG.dat new file mode 100644 index 0000000000..ab1883f4a7 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_EG.dat @@ -0,0 +1 @@ +a:2:{s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:1:{s:17:"weekend:intvector";a:4:{i:0;i:6;i:1;i:0;i:2;i:7;i:3;i:86400000;}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_IN.dat b/gui/baculum/framework/I18N/core/data/ar_IN.dat new file mode 100644 index 0000000000..e13234cb80 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_IN.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:28:"##,##,##0.###;-##,##,##0.###";i:1;s:32:"¤ ##,##,##0.00;-¤ ##,##,##0.00";i:2;s:10:"##,##,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:3:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:11:"h:mm:ss a z";i:1;s:11:"h:mm:ss a z";i:2;s:9:"h:mm:ss a";i:3;s:6:"h:mm a";i:4;s:16:"EEEE d MMMM yyyy";i:5;s:11:"d MMMM yyyy";i:6;s:10:"dd-MM-yyyy";i:7;s:6:"d-M-yy";i:8;s:7:"{1} {0}";}s:17:"weekend:intvector";a:4:{i:0;i:1;i:1;i:0;i:2;i:1;i:3;i:86400000;}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_IQ.dat b/gui/baculum/framework/I18N/core/data/ar_IQ.dat new file mode 100644 index 0000000000..929a079813 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_IQ.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;#,##0.###-";i:1;s:26:"¤ #,##0.000;¤ #,##0.000-";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_JO.dat b/gui/baculum/framework/I18N/core/data/ar_JO.dat new file mode 100644 index 0000000000..78b0d6a1ce --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_JO.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;#,##0.###-";i:1;s:26:"¤ #,##0.000;¤ #,##0.000-";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:2:{s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:23:"كانون الثاني";i:1;s:8:"شباط";i:2;s:8:"آذار";i:3;s:10:"نيسان";i:4;s:8:"أيار";i:5;s:12:"حزيران";i:6;s:8:"تموز";i:7;s:4:"آب";i:8;s:10:"أيلول";i:9;s:21:"تشرين الأول";i:10;s:23:"تشرين الثاني";i:11;s:21:"كانون الأول";}s:4:"wide";a:12:{i:0;s:23:"كانون الثاني";i:1;s:8:"شباط";i:2;s:8:"آذار";i:3;s:10:"نيسان";i:4;s:8:"أيار";i:5;s:12:"حزيران";i:6;s:8:"تموز";i:7;s:4:"آب";i:8;s:10:"أيلول";i:9;s:21:"تشرين الأول";i:10;s:23:"تشرين الثاني";i:11;s:21:"كانون الأول";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_KW.dat b/gui/baculum/framework/I18N/core/data/ar_KW.dat new file mode 100644 index 0000000000..929a079813 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_KW.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;#,##0.###-";i:1;s:26:"¤ #,##0.000;¤ #,##0.000-";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_LB.dat b/gui/baculum/framework/I18N/core/data/ar_LB.dat new file mode 100644 index 0000000000..92cba5315b --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_LB.dat @@ -0,0 +1 @@ +a:2:{s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:2:{s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:23:"كانون الثاني";i:1;s:8:"شباط";i:2;s:8:"آذار";i:3;s:10:"نيسان";i:4;s:8:"نوار";i:5;s:12:"حزيران";i:6;s:8:"تموز";i:7;s:4:"آب";i:8;s:10:"أيلول";i:9;s:21:"تشرين الأول";i:10;s:23:"تشرين الثاني";i:11;s:21:"كانون الأول";}s:4:"wide";a:12:{i:0;s:23:"كانون الثاني";i:1;s:8:"شباط";i:2;s:8:"آذار";i:3;s:10:"نيسان";i:4;s:8:"نوار";i:5;s:12:"حزيران";i:6;s:8:"تموز";i:7;s:4:"آب";i:8;s:10:"أيلول";i:9;s:21:"تشرين الأول";i:10;s:23:"تشرين الثاني";i:11;s:21:"كانون الأول";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_LY.dat b/gui/baculum/framework/I18N/core/data/ar_LY.dat new file mode 100644 index 0000000000..929a079813 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_LY.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;#,##0.###-";i:1;s:26:"¤ #,##0.000;¤ #,##0.000-";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_MA.dat b/gui/baculum/framework/I18N/core/data/ar_MA.dat new file mode 100644 index 0000000000..1c3fdb2e3c --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_MA.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberElements";a:12:{i:0;s:2:"Ù«";i:1;s:2:"Ù¬";i:2;s:1:";";i:3;s:2:"Ùª";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_OM.dat b/gui/baculum/framework/I18N/core/data/ar_OM.dat new file mode 100644 index 0000000000..9d38c95dce --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_OM.dat @@ -0,0 +1 @@ +a:1:{s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_QA.dat b/gui/baculum/framework/I18N/core/data/ar_QA.dat new file mode 100644 index 0000000000..17db97d7dd --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_QA.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:18:"###0.###;###0.###-";i:1;s:20:"¤###0.00;-¤###0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:1:{s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_SA.dat b/gui/baculum/framework/I18N/core/data/ar_SA.dat new file mode 100644 index 0000000000..17db97d7dd --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_SA.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:18:"###0.###;###0.###-";i:1;s:20:"¤###0.00;-¤###0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:1:{s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_SD.dat b/gui/baculum/framework/I18N/core/data/ar_SD.dat new file mode 100644 index 0000000000..9d38c95dce --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_SD.dat @@ -0,0 +1 @@ +a:1:{s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_SY.dat b/gui/baculum/framework/I18N/core/data/ar_SY.dat new file mode 100644 index 0000000000..7695ad55d6 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_SY.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:18:"###0.###;###0.###-";i:1;s:20:"¤###0.00;-¤###0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:4:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:5;i:1;i:1;}s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:23:"كانون الثاني";i:1;s:8:"شباط";i:2;s:8:"آذار";i:3;s:10:"نيسان";i:4;s:8:"نوار";i:5;s:12:"حزيران";i:6;s:8:"تموز";i:7;s:4:"آب";i:8;s:10:"أيلول";i:9;s:21:"تشرين الأول";i:10;s:23:"تشرين الثاني";i:11;s:21:"كانون الأول";}s:4:"wide";a:12:{i:0;s:23:"كانون الثاني";i:1;s:8:"شباط";i:2;s:8:"آذار";i:3;s:10:"نيسان";i:4;s:8:"نوار";i:5;s:12:"حزيران";i:6;s:8:"تموز";i:7;s:4:"آب";i:8;s:10:"أيلول";i:9;s:21:"تشرين الأول";i:10;s:23:"تشرين الثاني";i:11;s:21:"كانون الأول";}}}s:17:"weekend:intvector";a:4:{i:0;i:6;i:1;i:0;i:2;i:7;i:3;i:86400000;}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_TN.dat b/gui/baculum/framework/I18N/core/data/ar_TN.dat new file mode 100644 index 0000000000..80c9ef71de --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_TN.dat @@ -0,0 +1 @@ +a:4:{s:14:"NumberElements";a:12:{i:0;s:2:"Ù«";i:1;s:2:"Ù¬";i:2;s:1:";";i:3;s:2:"Ùª";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:14:"NumberPatterns";a:4:{i:0;s:18:"###0.###;###0.###-";i:1;s:20:"¤###0.00;-¤###0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:1:{s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ar_YE.dat b/gui/baculum/framework/I18N/core/data/ar_YE.dat new file mode 100644 index 0000000000..17db97d7dd --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ar_YE.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:18:"###0.###;###0.###-";i:1;s:20:"¤###0.00;-¤###0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:1:{s:8:"dayNames";a:1:{s:6:"format";a:1:{s:11:"abbreviated";a:7:{i:0;s:10:"الأحد";i:1;s:14:"الاثنين";i:2;s:16:"الثلاثاء";i:3;s:16:"الأربعاء";i:4;s:12:"الخميس";i:5;s:12:"الجمعة";i:6;s:10:"السبت";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/be.dat b/gui/baculum/framework/I18N/core/data/be.dat new file mode 100644 index 0000000000..5508ad3904 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/be.dat @@ -0,0 +1 @@ +a:8:{s:9:"Countries";a:1:{s:2:"BY";a:1:{i:0;s:16:"Беларусь";}}s:10:"Currencies";a:1:{s:3:"BYB";a:2:{i:0;s:6:"Руб";i:1;s:3:"BYB";}}s:9:"Languages";a:1:{s:2:"be";a:1:{i:0;s:18:"Беларускі";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Cyrl";}s:14:"NumberElements";a:12:{i:0;s:1:",";i:1;s:2:" ";i:2;s:1:";";i:3;s:1:"%";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:5:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:10:"HH.mm.ss z";i:1;s:10:"HH.mm.ss z";i:2;s:8:"HH.mm.ss";i:3;s:5:"HH.mm";i:4;s:17:"EEEE, d MMMM yyyy";i:5;s:11:"d MMMM yyyy";i:6;s:8:"d.M.yyyy";i:7;s:6:"d.M.yy";i:8;s:7:"{1} {0}";}s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:4:"нд";i:1;s:4:"пн";i:2;s:4:"аў";i:3;s:4:"ср";i:4;s:4:"чц";i:5;s:4:"пт";i:6;s:4:"сб";}s:4:"wide";a:7:{i:0;s:14:"нядзеля";i:1;s:20:"панядзелак";i:2;s:14:"аўторак";i:3;s:12:"серада";i:4;s:12:"чацвер";i:5;s:14:"пятніца";i:6;s:12:"субота";}}}s:4:"eras";a:1:{s:11:"abbreviated";a:2:{i:0;s:11:"да н.е.";i:1;s:6:"н.е.";}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:6:"сту";i:1;s:6:"лют";i:2;s:6:"сак";i:3;s:6:"кра";i:4;s:6:"май";i:5;s:6:"чэр";i:6;s:6:"ліп";i:7;s:6:"жні";i:8;s:6:"вер";i:9;s:6:"кас";i:10;s:6:"ліс";i:11;s:6:"сне";}s:4:"wide";a:12:{i:0;s:16:"студзень";i:1;s:8:"люты";i:2;s:14:"сакавік";i:3;s:16:"красавік";i:4;s:6:"май";i:5;s:14:"чэрвень";i:6;s:12:"ліпень";i:7;s:14:"жнівень";i:8;s:16:"верасень";i:9;s:20:"кастрычнік";i:10;s:16:"лістапад";i:11;s:14:"снежань";}}}}}s:17:"localPatternChars";a:1:{i:0;s:24:"GanjkHmsSEDFwWxhKzAeugXZ";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/be_BY.dat b/gui/baculum/framework/I18N/core/data/be_BY.dat new file mode 100644 index 0000000000..16784a7e97 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/be_BY.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;-#,##0.###";i:1;s:16:"¤#,##0;-¤#,##0";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/bg.dat b/gui/baculum/framework/I18N/core/data/bg.dat new file mode 100644 index 0000000000..c2ccd97465 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/bg.dat @@ -0,0 +1 @@ +a:12:{s:9:"Countries";a:239:{s:2:"AD";a:1:{i:0;s:12:"Андора";}s:2:"AE";a:1:{i:0;s:50:"Обединени Арабски Емирства";}s:2:"AF";a:1:{i:0;s:20:"Афганистан";}s:2:"AG";a:1:{i:0;s:32:"Антигуа и Барбуда";}s:2:"AI";a:1:{i:0;s:14:"Ангуила";}s:2:"AL";a:1:{i:0;s:14:"Албания";}s:2:"AM";a:1:{i:0;s:14:"Армения";}s:2:"AN";a:1:{i:0;s:31:"Холандски Антили";}s:2:"AO";a:1:{i:0;s:12:"Ангола";}s:2:"AQ";a:1:{i:0;s:20:"Антарктика";}s:2:"AR";a:1:{i:0;s:18:"Аржентина";}s:2:"AS";a:1:{i:0;s:33:"Американско Самоа";}s:2:"AT";a:1:{i:0;s:14:"Австрия";}s:2:"AU";a:1:{i:0;s:18:"Австралия";}s:2:"AW";a:1:{i:0;s:10:"Аруба";}s:2:"AZ";a:1:{i:0;s:22:"Азербайджан";}s:2:"BA";a:1:{i:0;s:36:"Босна и Херцеговина";}s:2:"BB";a:1:{i:0;s:16:"Барбадос";}s:2:"BD";a:1:{i:0;s:18:"Бангладеш";}s:2:"BE";a:1:{i:0;s:12:"Белгия";}s:2:"BF";a:1:{i:0;s:23:"Буркина Фасо";}s:2:"BG";a:1:{i:0;s:16:"България";}s:2:"BH";a:1:{i:0;s:14:"Бахрейн";}s:2:"BI";a:1:{i:0;s:14:"Бурунди";}s:2:"BJ";a:1:{i:0;s:10:"Бенин";}s:2:"BM";a:1:{i:0;s:14:"Бермуда";}s:2:"BN";a:1:{i:0;s:31:"Бруней Дарусалам";}s:2:"BO";a:1:{i:0;s:14:"Боливия";}s:2:"BR";a:1:{i:0;s:16:"Бразилия";}s:2:"BS";a:1:{i:0;s:12:"Бахами";}s:2:"BT";a:1:{i:0;s:10:"Бутан";}s:2:"BV";a:1:{i:0;s:21:"Остров Буве";}s:2:"BW";a:1:{i:0;s:16:"Ботсуана";}s:2:"BY";a:1:{i:0;s:14:"Беларус";}s:2:"BZ";a:1:{i:0;s:10:"Белиз";}s:2:"CA";a:1:{i:0;s:12:"Канада";}s:2:"CC";a:1:{i:0;s:48:"Кокосови (Кийлинг) острови";}s:2:"CD";a:1:{i:0;s:54:"Демократична Република Конго";}s:2:"CF";a:1:{i:0;s:57:"Централноафриканска Република";}s:2:"CG";a:1:{i:0;s:10:"Конго";}s:2:"CH";a:1:{i:0;s:18:"Швейцария";}s:2:"CI";a:1:{i:0;s:22:"Кот д’Ивоар";}s:2:"CK";a:1:{i:0;s:21:"Острови Кук";}s:2:"CL";a:1:{i:0;s:8:"Чили";}s:2:"CM";a:1:{i:0;s:14:"Камерун";}s:2:"CN";a:1:{i:0;s:10:"Китай";}s:2:"CO";a:1:{i:0;s:16:"Колумбия";}s:2:"CR";a:1:{i:0;s:19:"Коста Рика";}s:2:"CU";a:1:{i:0;s:8:"Куба";}s:2:"CV";a:1:{i:0;s:19:"Кабо Верде";}s:2:"CX";a:1:{i:0;s:29:"Остров Кристмас";}s:2:"CY";a:1:{i:0;s:10:"Кипър";}s:2:"CZ";a:1:{i:0;s:29:"Чешка Република";}s:2:"DE";a:1:{i:0;s:16:"Германия";}s:2:"DJ";a:1:{i:0;s:14:"Джибути";}s:2:"DK";a:1:{i:0;s:10:"Дания";}s:2:"DM";a:1:{i:0;s:16:"Доминика";}s:2:"DO";a:1:{i:0;s:43:"Доминиканска Република";}s:2:"DZ";a:1:{i:0;s:10:"Алжир";}s:2:"EC";a:1:{i:0;s:14:"Еквадор";}s:2:"EE";a:1:{i:0;s:14:"Естония";}s:2:"EG";a:1:{i:0;s:12:"Египет";}s:2:"EH";a:1:{i:0;s:27:"Западна Сахара";}s:2:"ER";a:1:{i:0;s:14:"Еритрея";}s:2:"ES";a:1:{i:0;s:14:"Испания";}s:2:"ET";a:1:{i:0;s:14:"Етиопия";}s:2:"FI";a:1:{i:0;s:18:"Финландия";}s:2:"FJ";a:1:{i:0;s:10:"Фиджи";}s:2:"FK";a:1:{i:0;s:37:"Фолклендски острови";}s:2:"FM";a:1:{i:0;s:49:"Микронезия, Обединени Щати";}s:2:"FO";a:1:{i:0;s:33:"Фарьорски острови";}s:2:"FR";a:1:{i:0;s:14:"Франция";}s:2:"GA";a:1:{i:0;s:10:"Габон";}s:2:"GB";a:1:{i:0;s:35:"Обединено кралство";}s:2:"GD";a:1:{i:0;s:14:"Гренада";}s:2:"GE";a:1:{i:0;s:12:"Грузия";}s:2:"GF";a:1:{i:0;s:27:"Френска Гвиана";}s:2:"GH";a:1:{i:0;s:8:"Гана";}s:2:"GI";a:1:{i:0;s:18:"Гибралтар";}s:2:"GL";a:1:{i:0;s:20:"Гренландия";}s:2:"GM";a:1:{i:0;s:12:"Гамбия";}s:2:"GN";a:1:{i:0;s:12:"Гвинея";}s:2:"GP";a:1:{i:0;s:18:"Гваделупа";}s:2:"GQ";a:1:{i:0;s:37:"Екваториална Гвинея";}s:2:"GR";a:1:{i:0;s:12:"Гърция";}s:2:"GS";a:1:{i:0;s:73:"Южна Джорджия и Южни Сандвичеви Острови";}s:2:"GT";a:1:{i:0;s:18:"Гватемала";}s:2:"GU";a:1:{i:0;s:8:"Гуам";}s:2:"GW";a:1:{i:0;s:23:"Гвинея-Бисау";}s:2:"GY";a:1:{i:0;s:12:"Гвиана";}s:2:"HK";a:1:{i:0;s:43:"Хонг-Конг О.А.Р. на Китай";}s:2:"HM";a:1:{i:0;s:58:"Остров Хърд и Острови Макдоналд";}s:2:"HN";a:1:{i:0;s:16:"Хондурас";}s:2:"HR";a:1:{i:0;s:18:"Хърватска";}s:2:"HT";a:1:{i:0;s:10:"Хаити";}s:2:"HU";a:1:{i:0;s:14:"Унгария";}s:2:"ID";a:1:{i:0;s:18:"Индонезия";}s:2:"IE";a:1:{i:0;s:16:"Ирландия";}s:2:"IL";a:1:{i:0;s:12:"Израел";}s:2:"IN";a:1:{i:0;s:10:"Индия";}s:2:"IO";a:1:{i:0;s:70:"Британски територии в Индийския океан";}s:2:"IQ";a:1:{i:0;s:8:"Ирак";}s:2:"IR";a:1:{i:0;s:45:"Иран, Ислямска република";}s:2:"IS";a:1:{i:0;s:16:"Исландия";}s:2:"IT";a:1:{i:0;s:12:"Италия";}s:2:"JM";a:1:{i:0;s:12:"Ямайка";}s:2:"JO";a:1:{i:0;s:16:"Йордания";}s:2:"JP";a:1:{i:0;s:12:"Япония";}s:2:"KE";a:1:{i:0;s:10:"Кения";}s:2:"KG";a:1:{i:0;s:20:"Киргизстан";}s:2:"KH";a:1:{i:0;s:16:"Камбоджа";}s:2:"KI";a:1:{i:0;s:16:"Кирибати";}s:2:"KM";a:1:{i:0;s:12:"Комори";}s:2:"KN";a:1:{i:0;s:33:"Сейнт Китс и Невис";}s:2:"KP";a:1:{i:0;s:26:"Корея, Северна";}s:2:"KR";a:1:{i:0;s:20:"Корея, Южна";}s:2:"KW";a:1:{i:0;s:12:"Кувейт";}s:2:"KY";a:1:{i:0;s:33:"Кайманови острови";}s:2:"KZ";a:1:{i:0;s:18:"Казахстан";}s:2:"LA";a:1:{i:0;s:67:"Народна Демократична Република Лаос";}s:2:"LB";a:1:{i:0;s:10:"Ливан";}s:2:"LC";a:1:{i:0;s:21:"Сейнт Лусия";}s:2:"LI";a:1:{i:0;s:20:"Лихтенщайн";}s:2:"LK";a:1:{i:0;s:17:"Шри Ланка";}s:2:"LR";a:1:{i:0;s:14:"Либерия";}s:2:"LS";a:1:{i:0;s:12:"Лесото";}s:2:"LT";a:1:{i:0;s:10:"Литва";}s:2:"LU";a:1:{i:0;s:20:"Люксембург";}s:2:"LV";a:1:{i:0;s:12:"Латвия";}s:2:"LY";a:1:{i:0;s:52:"Либийска Арабска Джамахирия";}s:2:"MA";a:1:{i:0;s:12:"Мароко";}s:2:"MC";a:1:{i:0;s:12:"Монако";}s:2:"MD";a:1:{i:0;s:34:"Молдова, Република";}s:2:"MG";a:1:{i:0;s:20:"Мадагаскар";}s:2:"MH";a:1:{i:0;s:33:"Маршалови Острови";}s:2:"MK";a:1:{i:0;s:38:"Македония, Република";}s:2:"ML";a:1:{i:0;s:8:"Мали";}s:2:"MM";a:1:{i:0;s:14:"Мианмар";}s:2:"MN";a:1:{i:0;s:16:"Монголия";}s:2:"MO";a:1:{i:0;s:36:"Макао О.А.Р. на Китай";}s:2:"MP";a:1:{i:0;s:48:"Северни Мариански Острови";}s:2:"MQ";a:1:{i:0;s:18:"Мартиника";}s:2:"MR";a:1:{i:0;s:20:"Мавритания";}s:2:"MS";a:1:{i:0;s:16:"Монсерат";}s:2:"MT";a:1:{i:0;s:10:"Малта";}s:2:"MU";a:1:{i:0;s:16:"Мавриций";}s:2:"MV";a:1:{i:0;s:14:"Малдиви";}s:2:"MW";a:1:{i:0;s:12:"Малави";}s:2:"MX";a:1:{i:0;s:14:"Мексико";}s:2:"MY";a:1:{i:0;s:16:"Малайзия";}s:2:"MZ";a:1:{i:0;s:16:"Мозамбик";}s:2:"NA";a:1:{i:0;s:14:"Намибия";}s:2:"NC";a:1:{i:0;s:27:"Нова Каледония";}s:2:"NE";a:1:{i:0;s:10:"Нигер";}s:2:"NF";a:1:{i:0;s:27:"Остров Норфолк";}s:2:"NG";a:1:{i:0;s:14:"Нигерия";}s:2:"NI";a:1:{i:0;s:18:"Никарагуа";}s:2:"NL";a:1:{i:0;s:16:"Холандия";}s:2:"NO";a:1:{i:0;s:16:"Норвегия";}s:2:"NP";a:1:{i:0;s:10:"Непал";}s:2:"NR";a:1:{i:0;s:10:"Науру";}s:2:"NU";a:1:{i:0;s:8:"Ниуе";}s:2:"NZ";a:1:{i:0;s:25:"Нова Зеландия";}s:2:"OM";a:1:{i:0;s:8:"Оман";}s:2:"PA";a:1:{i:0;s:12:"Панама";}s:2:"PE";a:1:{i:0;s:8:"Перу";}s:2:"PF";a:1:{i:0;s:33:"Френска Полинезия";}s:2:"PG";a:1:{i:0;s:32:"Папуа Нова Гвинея";}s:2:"PH";a:1:{i:0;s:16:"Филипини";}s:2:"PK";a:1:{i:0;s:16:"Пакистан";}s:2:"PL";a:1:{i:0;s:10:"Полша";}s:2:"PM";a:1:{i:0;s:33:"Сен Пиер и Мигелон";}s:2:"PN";a:1:{i:0;s:16:"Питкайрн";}s:2:"PR";a:1:{i:0;s:21:"Пуерто Рико";}s:2:"PS";a:1:{i:0;s:41:"Палестински територии";}s:2:"PT";a:1:{i:0;s:20:"Португалия";}s:2:"PW";a:1:{i:0;s:10:"Палау";}s:2:"PY";a:1:{i:0;s:16:"Парагвай";}s:2:"QA";a:1:{i:0;s:10:"Катар";}s:2:"RE";a:1:{i:0;s:14:"Реюниън";}s:2:"RO";a:1:{i:0;s:14:"Румъния";}s:2:"RU";a:1:{i:0;s:29:"Руска Федерация";}s:2:"RW";a:1:{i:0;s:12:"Руанда";}s:2:"SA";a:1:{i:0;s:31:"Саудитска Арабия";}s:2:"SB";a:1:{i:0;s:35:"Соломонови Острови";}s:2:"SC";a:1:{i:0;s:14:"Сейшели";}s:2:"SD";a:1:{i:0;s:10:"Судан";}s:2:"SE";a:1:{i:0;s:12:"Швеция";}s:2:"SG";a:1:{i:0;s:16:"Сингапур";}s:2:"SH";a:1:{i:0;s:21:"Света Елена";}s:2:"SI";a:1:{i:0;s:16:"Словения";}s:2:"SJ";a:1:{i:0;s:35:"Свалбард и Ян Майен";}s:2:"SK";a:1:{i:0;s:16:"Словакия";}s:2:"SL";a:1:{i:0;s:21:"Сиера Леоне";}s:2:"SM";a:1:{i:0;s:19:"Сан Марино";}s:2:"SN";a:1:{i:0;s:14:"Сенегал";}s:2:"SO";a:1:{i:0;s:14:"Сомалия";}s:2:"SR";a:1:{i:0;s:14:"Суринам";}s:2:"ST";a:1:{i:0;s:35:"Сао Томе и Принципе";}s:2:"SV";a:1:{i:0;s:21:"Ел Салвадор";}s:2:"SY";a:1:{i:0;s:50:"Сирийска Арабска Република";}s:2:"SZ";a:1:{i:0;s:18:"Суазиленд";}s:2:"TC";a:1:{i:0;s:41:"Острови Туркс и Кайкос";}s:2:"TD";a:1:{i:0;s:6:"Чад";}s:2:"TF";a:1:{i:0;s:42:"Френски Южни Територии";}s:2:"TG";a:1:{i:0;s:8:"Того";}s:2:"TH";a:1:{i:0;s:14:"Тайланд";}s:2:"TJ";a:1:{i:0;s:22:"Таджикистан";}s:2:"TK";a:1:{i:0;s:14:"Токелау";}s:2:"TL";a:1:{i:0;s:25:"Източен Тимор";}s:2:"TM";a:1:{i:0;s:24:"Туркменистан";}s:2:"TN";a:1:{i:0;s:10:"Тунис";}s:2:"TO";a:1:{i:0;s:10:"Тонга";}s:2:"TR";a:1:{i:0;s:12:"Турция";}s:2:"TT";a:1:{i:0;s:32:"Тринидад и Тобаго";}s:2:"TV";a:1:{i:0;s:12:"Тувалу";}s:2:"TW";a:1:{i:0;s:12:"Тайван";}s:2:"TZ";a:1:{i:0;s:16:"Танзания";}s:2:"UA";a:1:{i:0;s:14:"Украйна";}s:2:"UG";a:1:{i:0;s:12:"Уганда";}s:2:"UM";a:1:{i:0;s:36:"САЩ - външни острови";}s:2:"US";a:1:{i:0;s:6:"САЩ";}s:2:"UY";a:1:{i:0;s:14:"Уругвай";}s:2:"UZ";a:1:{i:0;s:20:"Узбекистан";}s:2:"VA";a:1:{i:0;s:61:"Свещено море (Ватиканска държава)";}s:2:"VC";a:1:{i:0;s:47:"Сейнт Винсънт и Гренадини";}s:2:"VE";a:1:{i:0;s:18:"Венецуела";}s:2:"VG";a:1:{i:0;s:54:"Британски Вирджински Острови";}s:2:"VI";a:1:{i:0;s:43:"САЩ, Вирджински Острови";}s:2:"VN";a:1:{i:0;s:14:"Виетнам";}s:2:"VU";a:1:{i:0;s:14:"Вануату";}s:2:"WF";a:1:{i:0;s:26:"Уолис и Футуна";}s:2:"WS";a:1:{i:0;s:10:"Самоа";}s:2:"YE";a:1:{i:0;s:10:"Йемен";}s:2:"YT";a:1:{i:0;s:10:"Мейот";}s:2:"YU";a:1:{i:0;s:18:"Югославия";}s:2:"ZA";a:1:{i:0;s:21:"Южна Африка";}s:2:"ZM";a:1:{i:0;s:12:"Замбия";}s:2:"ZW";a:1:{i:0;s:16:"Зимбабве";}}s:10:"Currencies";a:277:{s:3:"ADD";a:2:{i:0;s:3:"ADD";i:1;s:27:"Андорски динар";}s:3:"ADP";a:2:{i:0;s:3:"ADP";i:1;s:29:"Андорска песета";}s:3:"AED";a:2:{i:0;s:3:"AED";i:1;s:63:"Обединени арабски емирства-дирхам";}s:3:"AFA";a:2:{i:0;s:3:"AFA";i:1;s:49:"Афганистански афган (1927-2002)";}s:3:"AFN";a:2:{i:0;s:2:"Af";i:1;s:37:"Афганистански афган";}s:3:"ALK";a:2:{i:0;s:3:"ALK";i:1;s:35:"Албански лек (1946-1961)";}s:3:"ALL";a:2:{i:0;s:3:"lek";i:1;s:23:"Албански лек";}s:3:"ALV";a:2:{i:0;s:3:"ALV";i:1;s:38:"Албански валутен лек";}s:3:"ALX";a:2:{i:0;s:3:"ALX";i:1;s:52:"Албански конвертируем долар";}s:3:"AMD";a:2:{i:0;s:4:"dram";i:1;s:25:"Арменски драм";}s:3:"ANG";a:2:{i:0;s:5:"NA f.";i:1;s:29:"Антилски гулден";}s:3:"AOA";a:2:{i:0;s:3:"AOA";i:1;s:29:"Анголска кванца";}s:3:"AOK";a:2:{i:0;s:3:"AOK";i:1;s:41:"Анголска кванца (1977-1990)";}s:3:"AON";a:2:{i:0;s:3:"AON";i:1;s:50:"Анголска нова кванца (1990-2000)";}s:3:"AOR";a:2:{i:0;s:3:"AOR";i:1;s:50:"Анголска нова кванца (1995-1999)";}s:3:"AOS";a:2:{i:0;s:3:"AOS";i:1;s:29:"Анголско ескудо";}s:3:"ARP";a:2:{i:0;s:3:"ARP";i:1;s:43:"Аржентинско песо (1983-1985)";}s:3:"ARS";a:2:{i:0;s:4:"Arg$";i:1;s:31:"Аржентинско песо";}s:3:"ATS";a:2:{i:0;s:3:"ATS";i:1;s:33:"Австрийски шилинг";}s:3:"AUD";a:2:{i:0;s:2:"$A";i:1;s:35:"Австралийски долар";}s:3:"AUP";a:2:{i:0;s:3:"AUP";i:1;s:33:"Австралийска лира";}s:3:"AWG";a:2:{i:0;s:3:"AWG";i:1;s:44:"Арубски гилдер - о. Аруба";}s:3:"AZM";a:2:{i:0;s:3:"AZM";i:1;s:39:"Азербайджански манат";}s:3:"BAD";a:2:{i:0;s:3:"BAD";i:1;s:47:"Босна и Херцеговина-динар";}s:3:"BAM";a:2:{i:0;s:2:"KM";i:1;s:56:"Босненска конвертируема марка";}s:3:"BAN";a:2:{i:0;s:3:"BAN";i:1;s:54:"Босна и Херцеговина-нов динар";}s:3:"BBD";a:2:{i:0;s:4:"BDS$";i:1;s:33:"Барбейдоски долар";}s:3:"BDT";a:2:{i:0;s:2:"Tk";i:1;s:31:"Бангладешка така";}s:3:"BEC";a:2:{i:0;s:3:"BEC";i:1;s:56:"Белгийски франк (конвертируем)";}s:3:"BEF";a:2:{i:0;s:2:"BF";i:1;s:29:"Белгийски франк";}s:3:"BEL";a:2:{i:0;s:3:"BEL";i:1;s:48:"Белгийски франк (финансов)";}s:3:"BGL";a:2:{i:0;s:4:"лв";i:1;s:3:"BGL";}s:3:"BGM";a:2:{i:0;s:3:"BGM";i:1;s:56:"Български социалистически лев";}s:3:"BGN";a:2:{i:0;s:5:"лв.";i:1;s:3:"BGN";}s:3:"BGO";a:2:{i:0;s:3:"BGO";i:1;s:37:"Български лев (1879-1952)";}s:3:"BGX";a:2:{i:0;s:3:"BGX";i:1;s:50:"Български конвертируем лев";}s:3:"BHD";a:2:{i:0;s:2:"BD";i:1;s:31:"Бахрейнски динар";}s:3:"BIF";a:2:{i:0;s:3:"Fbu";i:1;s:33:"Бурундийски франк";}s:3:"BMD";a:2:{i:0;s:4:"Ber$";i:1;s:29:"Бермудски долар";}s:3:"BMP";a:2:{i:0;s:3:"BMP";i:1;s:27:"Бермудска лира";}s:3:"BND";a:2:{i:0;s:3:"BND";i:1;s:29:"Брунейски долар";}s:3:"BOB";a:2:{i:0;s:2:"Bs";i:1;s:39:"Боливийско боливиано";}s:3:"BOL";a:2:{i:0;s:3:"BOL";i:1;s:51:"Боливийско боливиано (1863-1962)";}s:3:"BOP";a:2:{i:0;s:3:"BOP";i:1;s:29:"Боливийско песо";}s:3:"BRL";a:2:{i:0;s:2:"R$";i:1;s:27:"Бразилски реал";}s:3:"BSD";a:2:{i:0;s:3:"BSD";i:1;s:27:"Бахамски долар";}s:3:"BSP";a:2:{i:0;s:3:"BSP";i:1;s:25:"Бахамска лира";}s:3:"BTN";a:2:{i:0;s:2:"Nu";i:1;s:33:"Бутански нгултрум";}s:3:"BTR";a:2:{i:0;s:3:"BTR";i:1;s:27:"Бутанска рупия";}s:3:"BUR";a:2:{i:0;s:3:"BUR";i:1;s:29:"Бирманска рупия";}s:3:"BWP";a:2:{i:0;s:3:"BWP";i:1;s:29:"Ботсуанска пула";}s:3:"BYB";a:2:{i:0;s:3:"BYB";i:1;s:50:"Беларуска нова рубла (1994-1999)";}s:3:"BYL";a:2:{i:0;s:3:"BYL";i:1;s:41:"Беларуска рубла (1992-1994)";}s:3:"BYR";a:2:{i:0;s:3:"Rbl";i:1;s:29:"Беларуска рубла";}s:3:"BZD";a:2:{i:0;s:3:"BZ$";i:1;s:31:"Белизийски долар";}s:3:"BZH";a:2:{i:0;s:3:"BZH";i:1;s:50:"Британски хондураски долар";}s:3:"CAD";a:2:{i:0;s:4:"Can$";i:1;s:27:"Канадски долар";}s:3:"CDF";a:2:{i:0;s:3:"CDF";i:1;s:31:"Конгоански франк";}s:3:"CHF";a:2:{i:0;s:3:"SwF";i:1;s:31:"Швейцарски франк";}s:3:"CKD";a:2:{i:0;s:3:"CKD";i:1;s:32:"Острови Кук-долар";}s:3:"CLE";a:2:{i:0;s:3:"CLE";i:1;s:29:"Чилийско ескудо";}s:3:"CLP";a:2:{i:0;s:3:"Ch$";i:1;s:25:"Чилийско песо";}s:3:"CNY";a:2:{i:0;s:1:"Y";i:1;s:40:"Китайски ренминби юан";}s:3:"COP";a:2:{i:0;s:4:"Col$";i:1;s:31:"Колумбийско песо";}s:3:"CRC";a:2:{i:0;s:1:"C";i:1;s:37:"Костарикански колон";}s:3:"CSC";a:2:{i:0;s:3:"CSC";i:1;s:35:"Чехословашка крона";}s:3:"CSK";a:2:{i:0;s:3:"CSK";i:1;s:62:"Чехословашка конвертируема крона";}s:3:"CUP";a:2:{i:0;s:3:"CUP";i:1;s:25:"Кубинско песо";}s:3:"CVE";a:2:{i:0;s:5:"CVEsc";i:1;s:32:"Кабо Верде ескудо";}s:3:"CYP";a:2:{i:0;s:3:"£C";i:1;s:25:"Кипърска лира";}s:3:"CZK";a:2:{i:0;s:3:"CZK";i:1;s:21:"Чешка крона";}s:3:"DEM";a:2:{i:0;s:3:"DEM";i:1;s:29:"Германска марка";}s:3:"DJF";a:2:{i:0;s:2:"DF";i:1;s:29:"Джибутски франк";}s:3:"DKK";a:2:{i:0;s:3:"DKr";i:1;s:23:"Датска крона";}s:3:"DOP";a:2:{i:0;s:3:"RD$";i:1;s:33:"Доминиканско песо";}s:3:"DZD";a:2:{i:0;s:2:"DA";i:1;s:27:"Алжирски динар";}s:3:"DZF";a:2:{i:0;s:3:"DZF";i:1;s:34:"Алжирски нов франк";}s:3:"ECS";a:2:{i:0;s:3:"ECS";i:1;s:31:"Еквадорско сукре";}s:3:"EEK";a:2:{i:0;s:3:"EEK";i:1;s:27:"Естонска крона";}s:3:"EGP";a:2:{i:0;s:3:"EGP";i:1;s:27:"Египетска лира";}s:3:"ERN";a:2:{i:0;s:3:"ERN";i:1;s:31:"Еритрейска накфа";}s:3:"ESP";a:2:{i:0;s:3:"ESP";i:1;s:29:"Испанска песета";}s:3:"ETB";a:2:{i:0;s:2:"Br";i:1;s:23:"Етиопски бир";}s:3:"ETD";a:2:{i:0;s:3:"ETD";i:1;s:27:"Етиопски долар";}s:3:"EUR";a:2:{i:0;s:3:"€";i:1;s:8:"Евро";}s:3:"FIM";a:2:{i:0;s:3:"FIM";i:1;s:31:"Финландска марка";}s:3:"FIN";a:2:{i:0;s:3:"FIN";i:1;s:43:"Финландска марка (1860-1962)";}s:3:"FJD";a:2:{i:0;s:2:"F$";i:1;s:23:"Фиджи - долар";}s:3:"FJP";a:2:{i:0;s:3:"FJP";i:1;s:21:"Фиджи - лира";}s:3:"FKP";a:2:{i:0;s:3:"FKP";i:1;s:31:"Фолкландска лира";}s:3:"FRF";a:2:{i:0;s:3:"FRF";i:1;s:25:"Френски франк";}s:3:"GAF";a:2:{i:0;s:3:"GAF";i:1;s:27:"Габон - CFA франк";}s:3:"GBP";a:2:{i:0;s:2:"£";i:1;s:27:"Британска лира";}s:3:"GEL";a:2:{i:0;s:4:"lari";i:1;s:27:"Грузински лари";}s:3:"GHC";a:2:{i:0;s:3:"GHC";i:1;s:25:"Ганайски седи";}s:3:"GHO";a:2:{i:0;s:3:"GHO";i:1;s:36:"Ганайски стари седи";}s:3:"GHP";a:2:{i:0;s:3:"GHP";i:1;s:25:"Ганайска лира";}s:3:"GIP";a:2:{i:0;s:3:"GIP";i:1;s:33:"Гибралтарска лира";}s:3:"GLK";a:2:{i:0;s:3:"GLK";i:1;s:33:"Гренландска крона";}s:3:"GMD";a:2:{i:0;s:3:"GMD";i:1;s:31:"Гамбийски даласи";}s:3:"GMP";a:2:{i:0;s:3:"GMP";i:1;s:27:"Гамбийска лира";}s:3:"GNF";a:2:{i:0;s:2:"GF";i:1;s:29:"Гвинейски франк";}s:3:"GNI";a:2:{i:0;s:3:"GNI";i:1;s:41:"Гвинейски франк (1960-1972)";}s:3:"GPF";a:2:{i:0;s:3:"GPF";i:1;s:33:"Гваделупски франк";}s:3:"GRD";a:2:{i:0;s:3:"GRD";i:1;s:25:"Гръцка драхма";}s:3:"GRN";a:2:{i:0;s:3:"GRN";i:1;s:34:"Гръцка нова драхма";}s:3:"GTQ";a:2:{i:0;s:1:"Q";i:1;s:37:"Гватемалски кветзал";}s:3:"GWP";a:2:{i:0;s:3:"GWP";i:1;s:32:"Гвинея-Бисау песо";}s:3:"GYD";a:2:{i:0;s:2:"G$";i:1;s:25:"Гаянски долар";}s:3:"HKD";a:2:{i:0;s:3:"HK$";i:1;s:33:"Хонгконгски долар";}s:3:"HNL";a:2:{i:0;s:1:"L";i:1;s:35:"Хондураска лемпира";}s:3:"HRD";a:2:{i:0;s:3:"HRD";i:1;s:29:"Хърватски динар";}s:3:"HRK";a:2:{i:0;s:3:"HRK";i:1;s:27:"Хърватска куна";}s:3:"HTG";a:2:{i:0;s:3:"HTG";i:1;s:23:"Хаитски гурд";}s:3:"HUF";a:2:{i:0;s:2:"Ft";i:1;s:29:"Унгарски форинт";}s:3:"IDN";a:2:{i:0;s:3:"IDN";i:1;s:44:"Индонезийска нова рупия";}s:3:"IDR";a:2:{i:0;s:2:"Rp";i:1;s:35:"Индонезийска рупия";}s:3:"IEP";a:2:{i:0;s:4:"IR£";i:1;s:27:"Ирландска лира";}s:3:"ILL";a:2:{i:0;s:3:"ILL";i:1;s:29:"Израелски шекел";}s:3:"ILP";a:2:{i:0;s:3:"ILP";i:1;s:27:"Израелска лира";}s:3:"ILS";a:2:{i:0;s:3:"ILS";i:1;s:36:"Израелски нов шекел";}s:3:"INR";a:2:{i:0;s:3:"INR";i:1;s:27:"Индийска рупия";}s:3:"IQD";a:2:{i:0;s:2:"ID";i:1;s:25:"Иракски динар";}s:3:"IRR";a:2:{i:0;s:2:"RI";i:1;s:23:"Ирански риал";}s:3:"ISK";a:2:{i:0;s:3:"ISK";i:1;s:29:"Исландска крона";}s:3:"ITL";a:2:{i:0;s:3:"₤";i:1;s:29:"Италианска лира";}s:3:"JMD";a:2:{i:0;s:2:"J$";i:1;s:25:"Ямайски долар";}s:3:"JMP";a:2:{i:0;s:3:"JMP";i:1;s:23:"Ямайска лира";}s:3:"JOD";a:2:{i:0;s:2:"JD";i:1;s:29:"Йордански динар";}s:3:"JPY";a:2:{i:0;s:2:"Â¥";i:1;s:23:"Японска йена";}s:3:"KES";a:2:{i:0;s:4:"K Sh";i:1;s:29:"Кенийски шилинг";}s:3:"KGS";a:2:{i:0;s:3:"som";i:1;s:31:"Киргистански сом";}s:3:"KHO";a:2:{i:0;s:3:"KHO";i:1;s:42:"Камбоджански стар риел";}s:3:"KHR";a:2:{i:0;s:2:"CR";i:1;s:33:"Камбоджански риел";}s:3:"KMF";a:2:{i:0;s:2:"CF";i:1;s:27:"Коморски франк";}s:3:"KPW";a:2:{i:0;s:3:"KPW";i:1;s:37:"Севернокорейски вон";}s:3:"KRW";a:2:{i:0;s:3:"KRW";i:1;s:15:"КНДР вон";}s:3:"KWD";a:2:{i:0;s:2:"KD";i:1;s:29:"Кувейтски динар";}s:3:"KYD";a:2:{i:0;s:3:"KYD";i:1;s:46:"Кайманови острови - долар";}s:3:"KZR";a:2:{i:0;s:3:"KZR";i:1;s:35:"Казахстанска рубла";}s:3:"KZT";a:2:{i:0;s:1:"T";i:1;s:35:"Казахстанско тенге";}s:3:"LAK";a:2:{i:0;s:3:"LAK";i:1;s:19:"Лаоски кип";}s:3:"LBP";a:2:{i:0;s:2:"LL";i:1;s:25:"Ливанска лира";}s:3:"LIF";a:2:{i:0;s:3:"LIF";i:1;s:37:"Лихтенщайнски франк";}s:3:"LKR";a:2:{i:0;s:5:"SL Re";i:1;s:31:"Шриланкска рупия";}s:3:"LNR";a:2:{i:0;s:3:"LNR";i:1;s:29:"Сейлонска рупия";}s:3:"LRD";a:2:{i:0;s:3:"LRD";i:1;s:31:"Либерийски долар";}s:3:"LSL";a:2:{i:0;s:1:"M";i:1;s:25:"Лесотско лоти";}s:3:"LTL";a:2:{i:0;s:3:"LTL";i:1;s:27:"Литовски литаз";}s:3:"LUF";a:2:{i:0;s:3:"LUF";i:1;s:37:"Люксембургски франк";}s:3:"LVL";a:2:{i:0;s:3:"LVL";i:1;s:25:"Латвийски лат";}s:3:"LVR";a:2:{i:0;s:3:"LVR";i:1;s:29:"Латвийска рубла";}s:3:"LYD";a:2:{i:0;s:2:"LD";i:1;s:27:"Либийски динар";}s:3:"LYP";a:2:{i:0;s:3:"LYP";i:1;s:25:"Либийска лира";}s:3:"MAD";a:2:{i:0;s:3:"MAD";i:1;s:33:"Марокански дирхам";}s:3:"MAF";a:2:{i:0;s:3:"MAF";i:1;s:31:"Марокански франк";}s:3:"MDL";a:2:{i:0;s:3:"MDL";i:1;s:25:"Молдовско леу";}s:3:"MGF";a:2:{i:0;s:3:"MGF";i:1;s:50:"Малгашки франк - Мадагаскар";}s:3:"MHD";a:2:{i:0;s:3:"MHD";i:1;s:44:"Маршалски острови-долар";}s:3:"MKD";a:2:{i:0;s:4:"MDen";i:1;s:31:"Македонски денар";}s:3:"MKN";a:2:{i:0;s:3:"MKN";i:1;s:43:"Македонски денар (1992-1993)";}s:3:"MMK";a:2:{i:0;s:3:"MMK";i:1;s:42:"Миянмарски (Бирма) кият";}s:3:"MMX";a:2:{i:0;s:3:"MMX";i:1;s:56:"Миянмарски конвертируем долар";}s:3:"MNT";a:2:{i:0;s:3:"Tug";i:1;s:31:"Монголски тугрик";}s:3:"MOP";a:2:{i:0;s:3:"MOP";i:1;s:25:"Макао - патака";}s:3:"MQF";a:2:{i:0;s:3:"MQF";i:1;s:31:"Мартиника - франк";}s:3:"MRO";a:2:{i:0;s:2:"UM";i:1;s:31:"Мавританска огия";}s:3:"MTL";a:2:{i:0;s:2:"Lm";i:1;s:27:"Малтийска лира";}s:3:"MUR";a:2:{i:0;s:3:"MUR";i:1;s:33:"Маврицийска рупия";}s:3:"MVP";a:2:{i:0;s:3:"MVP";i:1;s:46:"Малдивска рупия - Малдиви";}s:3:"MVR";a:2:{i:0;s:3:"MVR";i:1;s:29:"Малдивска руфия";}s:3:"MWK";a:2:{i:0;s:2:"MK";i:1;s:31:"Малавийска квача";}s:3:"MWP";a:2:{i:0;s:3:"MWP";i:1;s:29:"Малавийска лира";}s:3:"MXN";a:2:{i:0;s:4:"MEX$";i:1;s:40:"Мексиканско ново песо";}s:3:"MXP";a:2:{i:0;s:3:"MXP";i:1;s:60:"Мексиканско сребърно песо (1861-1992)";}s:3:"MYR";a:2:{i:0;s:2:"RM";i:1;s:35:"Малайзийски рингит";}s:3:"MZE";a:2:{i:0;s:3:"MZE";i:1;s:35:"Мозамбикско ескудо";}s:3:"MZM";a:2:{i:0;s:2:"Mt";i:1;s:37:"Мозамбикски метикал";}s:3:"NAD";a:2:{i:0;s:2:"N$";i:1;s:31:"Намибийски долар";}s:3:"NGN";a:2:{i:0;s:3:"NGN";i:1;s:31:"Нигерийска найра";}s:3:"NGP";a:2:{i:0;s:3:"NGP";i:1;s:29:"Нигерийска лира";}s:3:"NIC";a:2:{i:0;s:3:"NIC";i:1;s:41:"Никарагуанска кордоба";}s:3:"NIG";a:2:{i:0;s:3:"NIG";i:1;s:54:"Никарагуанска златна кордоба";}s:3:"NLG";a:2:{i:0;s:3:"NLG";i:1;s:31:"Холандски гулден";}s:3:"NOK";a:2:{i:0;s:3:"NKr";i:1;s:27:"Норвежка крона";}s:3:"NPR";a:2:{i:0;s:3:"Nrs";i:1;s:27:"Непалска рупия";}s:3:"NZD";a:2:{i:0;s:3:"$NZ";i:1;s:37:"Новозеландски долар";}s:3:"NZP";a:2:{i:0;s:3:"NZP";i:1;s:35:"Новозеландска лира";}s:3:"OMR";a:2:{i:0;s:2:"RO";i:1;s:23:"Омански риал";}s:3:"PAB";a:2:{i:0;s:3:"PAB";i:1;s:29:"Панамски балбоа";}s:3:"PEN";a:2:{i:0;s:3:"PEN";i:1;s:32:"Перуански нов сол";}s:3:"PES";a:2:{i:0;s:3:"PES";i:1;s:25:"Перуански сол";}s:3:"PGK";a:2:{i:0;s:3:"PGK";i:1;s:46:"Папуа-новогвинейска кина";}s:3:"PHP";a:2:{i:0;s:3:"PHP";i:1;s:29:"Филипинско песо";}s:3:"PKR";a:2:{i:0;s:3:"Pra";i:1;s:33:"Пакистанска рупия";}s:3:"PLN";a:2:{i:0;s:2:"Zl";i:1;s:23:"Полска злота";}s:3:"PLX";a:2:{i:0;s:3:"PLX";i:1;s:48:"Полски конвентируем долар";}s:3:"PLZ";a:2:{i:0;s:3:"PLZ";i:1;s:35:"Полска злота (1950-1995)";}s:3:"PSP";a:2:{i:0;s:3:"PSP";i:1;s:31:"Палестинска лира";}s:3:"PTE";a:2:{i:0;s:3:"PTE";i:1;s:35:"Португалско ескудо";}s:3:"PYG";a:2:{i:0;s:3:"PYG";i:1;s:37:"Парагвайско гуарани";}s:3:"QAR";a:2:{i:0;s:2:"QR";i:1;s:25:"Катарски риал";}s:3:"ROL";a:2:{i:0;s:3:"leu";i:1;s:23:"Румънска лея";}s:3:"RON";a:2:{i:0;s:3:"RON";i:1;s:32:"Румънска нова лея";}s:3:"RUB";a:2:{i:0;s:3:"RUB";i:1;s:21:"Руска рубла";}s:3:"RUR";a:2:{i:0;s:3:"RUR";i:1;s:33:"Руска рубла (1991-1998)";}s:3:"RWF";a:2:{i:0;s:3:"RWF";i:1;s:27:"Руандски франк";}s:3:"SAR";a:2:{i:0;s:3:"SRl";i:1;s:41:"Саудитскоарабски риал";}s:3:"SBD";a:2:{i:0;s:3:"SI$";i:1;s:48:"Соломонови острови - долар";}s:3:"SCR";a:2:{i:0;s:2:"SR";i:1;s:29:"Сейшелска рупия";}s:3:"SDD";a:2:{i:0;s:3:"SDD";i:1;s:27:"Судански динар";}s:3:"SDP";a:2:{i:0;s:3:"SDP";i:1;s:25:"Суданска лира";}s:3:"SEK";a:2:{i:0;s:3:"SKr";i:1;s:25:"Шведска крона";}s:3:"SGD";a:2:{i:0;s:2:"S$";i:1;s:33:"Сингапурски долар";}s:3:"SHP";a:2:{i:0;s:3:"SHP";i:1;s:30:"Света Елена лира";}s:3:"SIT";a:2:{i:0;s:3:"SIT";i:1;s:29:"Словенски толар";}s:3:"SKK";a:2:{i:0;s:2:"Sk";i:1;s:27:"Словашка крона";}s:3:"SLL";a:2:{i:0;s:3:"SLL";i:1;s:35:"Сиералеонско леоне";}s:3:"SML";a:2:{i:0;s:3:"SML";i:1;s:30:"Сан Марино - лира";}s:3:"SOS";a:2:{i:0;s:7:"So. Sh.";i:1;s:33:"Сомалийски шилинг";}s:3:"SRG";a:2:{i:0;s:2:"Sf";i:1;s:33:"Суринамски гилдер";}s:3:"SSP";a:2:{i:0;s:3:"SSP";i:1;s:29:"Шотландска лира";}s:3:"STD";a:2:{i:0;s:2:"Db";i:1;s:48:"Сао Томе и Принсипи - добра";}s:3:"STE";a:2:{i:0;s:3:"STE";i:1;s:50:"Сао Томе и Принсипи - ескудо";}s:3:"SUN";a:2:{i:0;s:3:"SUN";i:1;s:36:"Съветска нова рубла";}s:3:"SUR";a:2:{i:0;s:3:"SUR";i:1;s:27:"Съветска рубла";}s:3:"SVC";a:2:{i:0;s:3:"SVC";i:1;s:33:"Салвадорски колон";}s:3:"SYP";a:2:{i:0;s:2:"LS";i:1;s:25:"Сирийска лира";}s:3:"SZL";a:2:{i:0;s:1:"E";i:1;s:43:"Свазилендски лилангени";}s:3:"THB";a:2:{i:0;s:3:"THB";i:1;s:27:"Тайландски бат";}s:3:"TJR";a:2:{i:0;s:3:"TJR";i:1;s:39:"Таджикистанска рубла";}s:3:"TJS";a:2:{i:0;s:3:"TJS";i:1;s:41:"Таджикистански сомони";}s:3:"TMM";a:2:{i:0;s:3:"TMM";i:1;s:41:"Туркменистански манат";}s:3:"TND";a:2:{i:0;s:3:"TND";i:1;s:31:"Тунизийски динар";}s:3:"TOP";a:2:{i:0;s:2:"T$";i:1;s:26:"Тонга - па анга";}s:3:"TPE";a:2:{i:0;s:3:"TPE";i:1;s:29:"Тиморско ескудо";}s:3:"TPP";a:2:{i:0;s:3:"TPP";i:1;s:29:"Тиморска патака";}s:3:"TRL";a:2:{i:0;s:2:"TL";i:1;s:21:"Турска лира";}s:3:"TTD";a:2:{i:0;s:3:"TT$";i:1;s:45:"Тринидат и Тобаго - долар";}s:3:"TTO";a:2:{i:0;s:3:"TTO";i:1;s:54:"Тринидат и Тобаго - стар долар";}s:3:"TWD";a:2:{i:0;s:3:"NT$";i:1;s:29:"Тайвански долар";}s:3:"TZS";a:2:{i:0;s:4:"T Sh";i:1;s:35:"Танзанийски шилинг";}s:3:"UAH";a:2:{i:0;s:3:"UAH";i:1;s:31:"Украинска хривня";}s:3:"UAK";a:2:{i:0;s:3:"UAK";i:1;s:39:"Украински карбованец";}s:3:"UGS";a:2:{i:0;s:3:"UGS";i:1;s:45:"Угандийски шилинг (1966-1987)";}s:3:"UGX";a:2:{i:0;s:4:"U Sh";i:1;s:40:"Угандийски нов шилинг";}s:3:"USD";a:2:{i:0;s:3:"US$";i:1;s:17:"САЩ долар";}s:3:"UYP";a:2:{i:0;s:3:"UYP";i:1;s:41:"Уругвайско песо (1975-1993)";}s:3:"UYU";a:2:{i:0;s:3:"Ur$";i:1;s:29:"Уругвайско песо";}s:3:"UZS";a:2:{i:0;s:3:"UZS";i:1;s:33:"Узбекистански сум";}s:3:"VAL";a:2:{i:0;s:3:"VAL";i:1;s:27:"Ватикана - лира";}s:3:"VEB";a:2:{i:0;s:2:"Be";i:1;s:37:"Венесуелски боливар";}s:3:"VND";a:2:{i:0;s:3:"VND";i:1;s:29:"Виетнамски донг";}s:3:"VNN";a:2:{i:0;s:3:"VNN";i:1;s:36:"Виетнамски нов донг";}s:3:"VNS";a:2:{i:0;s:3:"VNS";i:1;s:50:"Виетнамски национален донг";}s:3:"VUV";a:2:{i:0;s:2:"VT";i:1;s:25:"Вануату - вату";}s:3:"WSP";a:2:{i:0;s:3:"WSP";i:1;s:21:"Самоа - лира";}s:3:"WST";a:2:{i:0;s:3:"WST";i:1;s:21:"Самоа - тала";}s:3:"XAF";a:2:{i:0;s:3:"XAF";i:1;s:42:"Буркина Фасо - CFA - франк";}s:3:"XAU";a:2:{i:0;s:3:"XAU";i:1;s:10:"Злато";}s:3:"XCD";a:2:{i:0;s:3:"EC$";i:1;s:58:"Източнокарибски долар - Антигуа";}s:3:"XEU";a:2:{i:0;s:3:"XEU";i:1;s:18:"Еку на ЕИО";}s:3:"XFO";a:2:{i:0;s:3:"XFO";i:1;s:38:"Френски златен франк";}s:3:"XOF";a:2:{i:0;s:3:"XOF";i:1;s:27:"Бенин - CFA франк";}s:3:"XPF";a:2:{i:0;s:4:"CFPF";i:1;s:49:"Френскополинезийски франк";}s:3:"YDD";a:2:{i:0;s:3:"YDD";i:1;s:27:"Йеменски динар";}s:3:"YER";a:2:{i:0;s:3:"YRl";i:1;s:25:"Йеменски риал";}s:3:"YUM";a:2:{i:0;s:3:"YUM";i:1;s:31:"Югославски динар";}s:3:"YUN";a:2:{i:0;s:3:"YUN";i:1;s:56:"Югославски конвертируем динар";}s:3:"ZAL";a:2:{i:0;s:3:"ZAL";i:1;s:56:"Южноафрикански ранд (финансов)";}s:3:"ZAP";a:2:{i:0;s:3:"ZAP";i:1;s:37:"Южноафриканска лира";}s:3:"ZAR";a:2:{i:0;s:1:"R";i:1;s:37:"Южноафрикански ранд";}s:3:"ZMK";a:2:{i:0;s:3:"ZMK";i:1;s:29:"Замбийска квача";}s:3:"ZMP";a:2:{i:0;s:3:"ZMP";i:1;s:27:"Замбийска лира";}s:3:"ZRN";a:2:{i:0;s:3:"ZRN";i:1;s:34:"Заирско ново зайре";}s:3:"ZRZ";a:2:{i:0;s:3:"ZRZ";i:1;s:25:"Заирско зайре";}s:3:"ZWD";a:2:{i:0;s:2:"Z$";i:1;s:35:"Зимбабвийски долар";}}s:4:"Keys";a:3:{s:8:"calendar";a:1:{i:0;s:16:"Календар";}s:9:"collation";a:1:{i:0;s:18:"Сортиране";}s:8:"currency";a:1:{i:0;s:12:"Валути";}}s:9:"Languages";a:178:{s:2:"ab";a:1:{i:0;s:18:"Абкхазски";}s:2:"af";a:1:{i:0;s:16:"Африканс";}s:3:"afa";a:1:{i:0;s:38:"Афро-азиатски (други)";}s:3:"afh";a:1:{i:0;s:16:"Африхили";}s:3:"ale";a:1:{i:0;s:16:"Алеутски";}s:2:"am";a:1:{i:0;s:16:"Амхарски";}s:3:"ang";a:1:{i:0;s:42:"Староанглийски (ca.450-1100)";}s:3:"apa";a:1:{i:0;s:30:"Езици на апахите";}s:2:"ar";a:1:{i:0;s:14:"Арабски";}s:3:"art";a:1:{i:0;s:31:"Изкуствен (други)";}s:3:"aus";a:1:{i:0;s:35:"Австралийски езици";}s:2:"av";a:1:{i:0;s:14:"Аварски";}s:2:"ay";a:1:{i:0;s:12:"Аймара";}s:2:"az";a:1:{i:0;s:28:"Азербайджански";}s:2:"ba";a:1:{i:0;s:18:"Башкирски";}s:3:"bat";a:1:{i:0;s:37:"Прибалтийски (други)";}s:2:"be";a:1:{i:0;s:18:"Беларуски";}s:3:"ber";a:1:{i:0;s:18:"берберски";}s:2:"bg";a:1:{i:0;s:18:"Български";}s:2:"bn";a:1:{i:0;s:18:"Бенгалски";}s:3:"bnt";a:1:{i:0;s:10:"Банту";}s:2:"bo";a:1:{i:0;s:16:"Тибетски";}s:2:"br";a:1:{i:0;s:18:"Бретонски";}s:2:"bs";a:1:{i:0;s:18:"Босненски";}s:2:"ca";a:1:{i:0;s:20:"Каталонски";}s:3:"cai";a:1:{i:0;s:72:"Централноамерикански индиански (други)";}s:3:"car";a:1:{i:0;s:16:"Карибски";}s:3:"cau";a:1:{i:0;s:31:"Кавказски (други)";}s:2:"ce";a:1:{i:0;s:16:"Чеченски";}s:3:"cel";a:1:{i:0;s:27:"Келтски (други)";}s:3:"chr";a:1:{i:0;s:12:"чероки";}s:3:"chy";a:1:{i:0;s:16:"чейенски";}s:2:"co";a:1:{i:0;s:22:"Корсикански";}s:3:"cpe";a:1:{i:0;s:54:"Креолски, от английски (други)";}s:3:"cpf";a:1:{i:0;s:50:"Креолски, от френски (други)";}s:3:"cpp";a:1:{i:0;s:58:"Креолски, от португалски (други)";}s:3:"crp";a:1:{i:0;s:29:"Креолски (други)";}s:2:"cs";a:1:{i:0;s:10:"Чешки";}s:2:"cu";a:1:{i:0;s:35:"Църковно славянски";}s:2:"cy";a:1:{i:0;s:12:"Уелски";}s:2:"da";a:1:{i:0;s:12:"Датски";}s:2:"de";a:1:{i:0;s:12:"Немски";}s:3:"den";a:1:{i:0;s:18:"Славянски";}s:3:"dum";a:1:{i:0;s:58:"Холандски, средновековен (1050-1350)";}s:2:"dv";a:1:{i:0;s:12:"Дивехи";}s:3:"egy";a:1:{i:0;s:33:"Египетски (древен)";}s:2:"el";a:1:{i:0;s:12:"Гръцки";}s:2:"en";a:1:{i:0;s:18:"Английски";}s:3:"enm";a:1:{i:0;s:30:"Английски (1100-1500)";}s:2:"eo";a:1:{i:0;s:18:"Есперанто";}s:2:"es";a:1:{i:0;s:16:"Испански";}s:2:"et";a:1:{i:0;s:16:"Естонски";}s:2:"eu";a:1:{i:0;s:10:"Баски";}s:2:"fa";a:1:{i:0;s:18:"Персийски";}s:2:"fi";a:1:{i:0;s:12:"Фински";}s:3:"fiu";a:1:{i:0;s:34:"Угрофинска (други)";}s:2:"fr";a:1:{i:0;s:14:"Френски";}s:3:"frm";a:1:{i:0;s:26:"Френски (1400-1600)";}s:3:"fro";a:1:{i:0;s:25:"Френски (842-1400)";}s:2:"ga";a:1:{i:0;s:18:"Ирландски";}s:2:"gd";a:1:{i:0;s:33:"Шотландски галски";}s:3:"gem";a:1:{i:0;s:31:"Германски (други)";}s:3:"gmh";a:1:{i:0;s:24:"Немски (1050-1500)";}s:3:"goh";a:1:{i:0;s:23:"Немски (750-1050)";}s:3:"got";a:1:{i:0;s:18:"Готически";}s:3:"grc";a:1:{i:0;s:36:"Древногръцки (до 1453)";}s:2:"gu";a:1:{i:0;s:18:"Гуджарати";}s:3:"haw";a:1:{i:0;s:16:"Хавайски";}s:2:"he";a:1:{i:0;s:10:"Иврит";}s:2:"hi";a:1:{i:0;s:10:"Хинди";}s:3:"hit";a:1:{i:0;s:12:"Хитски";}s:2:"hr";a:1:{i:0;s:18:"Хърватски";}s:2:"ht";a:1:{i:0;s:18:"Хаитянски";}s:2:"hu";a:1:{i:0;s:16:"Унгарски";}s:2:"hy";a:1:{i:0;s:16:"Арменски";}s:2:"id";a:1:{i:0;s:24:"Индонезийски";}s:3:"inc";a:1:{i:0;s:29:"Индийски (други)";}s:3:"ine";a:1:{i:0;s:42:"Индо-европейски (други)";}s:2:"io";a:1:{i:0;s:6:"Идо";}s:3:"ira";a:1:{i:0;s:14:"Ирански";}s:2:"is";a:1:{i:0;s:18:"Исландски";}s:2:"it";a:1:{i:0;s:20:"Италиански";}s:2:"ja";a:1:{i:0;s:14:"Японски";}s:3:"jpr";a:1:{i:0;s:35:"еврейско-персийски";}s:3:"jrb";a:1:{i:0;s:31:"еврейско-арабски";}s:2:"jv";a:1:{i:0;s:14:"Явански";}s:2:"ka";a:1:{i:0;s:18:"Грузински";}s:2:"kg";a:1:{i:0;s:20:"Конгоански";}s:2:"ki";a:1:{i:0;s:12:"кикуйу";}s:2:"kk";a:1:{i:0;s:16:"Казахски";}s:2:"km";a:1:{i:0;s:16:"Кхмерски";}s:2:"ko";a:1:{i:0;s:16:"Корейски";}s:2:"ks";a:1:{i:0;s:18:"Кашмирски";}s:2:"ku";a:1:{i:0;s:14:"Кюрдски";}s:2:"ky";a:1:{i:0;s:18:"Киргизски";}s:2:"la";a:1:{i:0;s:16:"Латински";}s:2:"lb";a:1:{i:0;s:26:"Люксембургски";}s:2:"lo";a:1:{i:0;s:12:"Лаоски";}s:2:"lt";a:1:{i:0;s:16:"Литовски";}s:2:"lv";a:1:{i:0;s:18:"Латвийски";}s:3:"mas";a:1:{i:0;s:16:"масайски";}s:2:"mg";a:1:{i:0;s:16:"Малгашки";}s:3:"mga";a:1:{i:0;s:29:"Ирландски (900-1200)";}s:2:"mi";a:1:{i:0;s:14:"Маорски";}s:3:"mis";a:1:{i:0;s:21:"Други езици";}s:2:"mk";a:1:{i:0;s:20:"Македонски";}s:2:"ml";a:1:{i:0;s:16:"Малайски";}s:2:"mn";a:1:{i:0;s:18:"Монголски";}s:2:"mo";a:1:{i:0;s:18:"Молдовски";}s:2:"ms";a:1:{i:0;s:16:"Малайски";}s:2:"mt";a:1:{i:0;s:18:"Малтийски";}s:2:"my";a:1:{i:0;s:18:"Бирмански";}s:3:"nai";a:1:{i:0;s:68:"Северноамерикански индиански (други)";}s:3:"nap";a:1:{i:0;s:26:"Неаполитански";}s:2:"ne";a:1:{i:0;s:16:"Непалски";}s:2:"nl";a:1:{i:0;s:18:"Холандски";}s:2:"no";a:1:{i:0;s:16:"Норвежки";}s:3:"nub";a:1:{i:0;s:27:"Нубийски езици";}s:2:"ny";a:1:{i:0;s:16:"Чинянджа";}s:2:"os";a:1:{i:0;s:14:"Осетски";}s:3:"ota";a:1:{i:0;s:44:"Турски, отомански (1500-1928)";}s:3:"oto";a:1:{i:0;s:33:"Старотурски езици";}s:2:"pa";a:1:{i:0;s:20:"Пенджабски";}s:3:"paa";a:1:{i:0;s:29:"Папуаски (други)";}s:3:"peo";a:1:{i:0;s:50:"Староперсийски (600-400 пр.н.е.)";}s:3:"phi";a:1:{i:0;s:33:"Филипински (други)";}s:3:"phn";a:1:{i:0;s:20:"Финикийски";}s:2:"pl";a:1:{i:0;s:12:"Полски";}s:3:"pro";a:1:{i:0;s:34:"Провансалски (to 1500)";}s:2:"ps";a:1:{i:0;s:8:"Пущу";}s:2:"pt";a:1:{i:0;s:22:"Португалски";}s:2:"qu";a:1:{i:0;s:10:"Кечуа";}s:3:"raj";a:1:{i:0;s:24:"Раджастански";}s:2:"rm";a:1:{i:0;s:24:"Реторомански";}s:2:"rn";a:1:{i:0;s:10:"Рунди";}s:2:"ro";a:1:{i:0;s:16:"Румънски";}s:3:"roa";a:1:{i:0;s:29:"Романски (други)";}s:2:"ru";a:1:{i:0;s:10:"Руски";}s:2:"rw";a:1:{i:0;s:20:"Киняруанда";}s:2:"sa";a:1:{i:0;s:24:"Санкскритски";}s:3:"sah";a:1:{i:0;s:14:"Якутски";}s:3:"sai";a:1:{i:0;s:62:"Южноамерикански индиански (други)";}s:2:"sc";a:1:{i:0;s:18:"Сардински";}s:3:"sem";a:1:{i:0;s:29:"Семитски (други)";}s:2:"sg";a:1:{i:0;s:10:"Санго";}s:3:"sga";a:1:{i:0;s:39:"Староирландски (до 900)";}s:2:"sh";a:1:{i:0;s:28:"Сърбохърватски";}s:2:"si";a:1:{i:0;s:18:"Синхалски";}s:2:"sk";a:1:{i:0;s:16:"Словашки";}s:2:"sl";a:1:{i:0;s:18:"Словенски";}s:3:"sla";a:1:{i:0;s:31:"Славянски (други)";}s:2:"sm";a:1:{i:0;s:18:"Самоански";}s:2:"so";a:1:{i:0;s:20:"Сомалийски";}s:2:"sq";a:1:{i:0;s:16:"Албански";}s:2:"sr";a:1:{i:0;s:14:"Сръбски";}s:2:"ss";a:1:{i:0;s:10:"Суази";}s:2:"st";a:1:{i:0;s:12:"Сесуто";}s:3:"sux";a:1:{i:0;s:16:"Шумерски";}s:2:"sv";a:1:{i:0;s:14:"Шведски";}s:2:"sw";a:1:{i:0;s:14:"Суахили";}s:3:"syr";a:1:{i:0;s:16:"Сирийски";}s:2:"ta";a:1:{i:0;s:16:"Тамилски";}s:3:"tai";a:1:{i:0;s:33:"Тайландски (други)";}s:2:"te";a:1:{i:0;s:12:"Телугу";}s:2:"tg";a:1:{i:0;s:18:"Таджикски";}s:2:"th";a:1:{i:0;s:6:"Таи";}s:2:"tk";a:1:{i:0;s:20:"Туркменски";}s:2:"tr";a:1:{i:0;s:12:"Турски";}s:2:"tt";a:1:{i:0;s:16:"Татарски";}s:3:"tut";a:1:{i:0;s:29:"Алтайски (други)";}s:2:"ty";a:1:{i:0;s:18:"Таитянски";}s:2:"uk";a:1:{i:0;s:18:"Украински";}s:3:"und";a:1:{i:0;s:22:"Неопределен";}s:2:"ur";a:1:{i:0;s:8:"Урду";}s:2:"uz";a:1:{i:0;s:16:"Узбекски";}s:2:"vi";a:1:{i:0;s:20:"Виетнамски";}s:2:"zh";a:1:{i:0;s:16:"Китайски";}s:2:"zu";a:1:{i:0;s:14:"Зулуски";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Cyrl";}s:14:"NumberElements";a:12:{i:0;s:1:",";i:1;s:2:" ";i:2;s:1:";";i:3;s:1:"%";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:7:"Scripts";a:31:{s:4:"Arab";a:1:{i:0;s:14:"Арабска";}s:4:"Armn";a:1:{i:0;s:16:"Арменска";}s:4:"Beng";a:1:{i:0;s:18:"Бенгалска";}s:4:"Brai";a:1:{i:0;s:16:"Брайлова";}s:4:"Cher";a:1:{i:0;s:12:"Чероки";}s:4:"Copt";a:1:{i:0;s:14:"Коптска";}s:4:"Cyrl";a:1:{i:0;s:16:"Кирилица";}s:4:"Deva";a:1:{i:0;s:20:"Деванагари";}s:4:"Ethi";a:1:{i:0;s:16:"Етиопска";}s:4:"Geor";a:1:{i:0;s:18:"Грузинска";}s:4:"Goth";a:1:{i:0;s:18:"Готическа";}s:4:"Grek";a:1:{i:0;s:12:"Гръцка";}s:4:"Gujr";a:1:{i:0;s:18:"Гуджарати";}s:4:"Hang";a:1:{i:0;s:16:"Корейска";}s:4:"Hani";a:1:{i:0;s:16:"Китайска";}s:4:"Hans";a:1:{i:0;s:33:"Опростен китайски";}s:4:"Hant";a:1:{i:0;s:39:"Традиционен китайски";}s:4:"Hebr";a:1:{i:0;s:10:"Иврит";}s:4:"Hira";a:1:{i:0;s:31:"Японски хирагана";}s:4:"Kana";a:1:{i:0;s:31:"Японски катакана";}s:4:"Khmr";a:1:{i:0;s:16:"Кхмерска";}s:4:"Laoo";a:1:{i:0;s:12:"Лаоска";}s:4:"Latn";a:1:{i:0;s:16:"Латинска";}s:4:"Mong";a:1:{i:0;s:18:"Монголска";}s:4:"Mymr";a:1:{i:0;s:18:"Бирманска";}s:4:"Runr";a:1:{i:0;s:18:"Руническа";}s:4:"Taml";a:1:{i:0;s:16:"Тамилска";}s:4:"Telu";a:1:{i:0;s:12:"Телугу";}s:4:"Thai";a:1:{i:0;s:6:"Таи";}s:4:"Tibt";a:1:{i:0;s:16:"Тибетска";}s:4:"Zyyy";a:1:{i:0;s:8:"Обща";}}s:5:"Types";a:2:{s:8:"calendar";a:7:{s:8:"buddhist";a:1:{i:0;s:33:"Будистки календар";}s:7:"chinese";a:1:{i:0;s:33:"Китайски календар";}s:9:"gregorian";a:1:{i:0;s:41:"Григориански календар";}s:6:"hebrew";a:1:{i:0;s:33:"Еврейски календар";}s:7:"islamic";a:1:{i:0;s:33:"Ислямски календар";}s:13:"islamic-civil";a:1:{i:0;s:48:"Ислямски цивилен календар";}s:8:"japanese";a:1:{i:0;s:31:"Японски календар";}}s:9:"collation";a:5:{s:6:"direct";a:1:{i:0;s:16:"Директно";}s:9:"phonebook";a:1:{i:0;s:21:"Азбучен ред";}s:6:"pinyin";a:1:{i:0;s:25:"Сортиране Pinyin";}s:6:"stroke";a:1:{i:0;s:32:"Сортиране по щрих";}s:11:"traditional";a:1:{i:0;s:22:"Традиционно";}}}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:5:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:10:"HH:mm:ss z";i:1;s:8:"HH:mm:ss";i:2;s:8:"HH:mm:ss";i:3;s:5:"HH:mm";i:4;s:18:"dd MMMM yyyy, EEEE";i:5;s:12:"dd MMMM yyyy";i:6;s:10:"dd.MM.yyyy";i:7;s:8:"dd.MM.yy";i:8;s:7:"{1} {0}";}s:8:"dayNames";a:1:{s:6:"format";a:3:{s:11:"abbreviated";a:7:{i:0;s:7:"нед.";i:1;s:7:"пон.";i:2;s:5:"вт.";i:3;s:5:"ср.";i:4;s:9:"четв.";i:5;s:7:"пет.";i:6;s:7:"съб.";}s:6:"narrow";a:7:{i:0;s:2:"н";i:1;s:2:"п";i:2;s:2:"в";i:3;s:2:"с";i:4;s:2:"ч";i:5;s:2:"п";i:6;s:2:"с";}s:4:"wide";a:7:{i:0;s:12:"неделя";i:1;s:20:"понеделник";i:2;s:14:"вторник";i:3;s:10:"сряда";i:4;s:18:"четвъртък";i:5;s:10:"петък";i:6;s:12:"събота";}}}s:4:"eras";a:1:{s:11:"abbreviated";a:2:{i:0;s:11:"пр.н.е.";i:1;s:6:"н.е.";}}s:10:"monthNames";a:1:{s:6:"format";a:3:{s:11:"abbreviated";a:12:{i:0;s:5:"ян.";i:1;s:7:"фев.";i:2;s:8:"март";i:3;s:7:"апр.";i:4;s:6:"май";i:5;s:6:"юни";i:6;s:6:"юли";i:7;s:7:"авг.";i:8;s:7:"сеп.";i:9;s:7:"окт.";i:10;s:9:"ноем.";i:11;s:7:"дек.";}s:6:"narrow";a:12:{i:0;s:2:"я";i:1;s:2:"ф";i:2;s:2:"м";i:3;s:2:"а";i:4;s:2:"м";i:5;s:2:"ю";i:6;s:2:"ю";i:7;s:2:"а";i:8;s:2:"с";i:9;s:2:"о";i:10;s:2:"н";i:11;s:2:"д";}s:4:"wide";a:12:{i:0;s:12:"януари";i:1;s:16:"февруари";i:2;s:8:"март";i:3;s:10:"април";i:4;s:6:"май";i:5;s:6:"юни";i:6;s:6:"юли";i:7;s:12:"август";i:8;s:18:"септември";i:9;s:16:"октомври";i:10;s:14:"ноември";i:11;s:16:"декември";}}}}}s:17:"localPatternChars";a:1:{i:0;s:24:"GanjkHmsSEDFwWxhKzAeugXZ";}s:11:"zoneStrings";a:17:{i:0;a:6:{i:0;s:19:"America/Los_Angeles";i:1;s:46:"Тихоокеанска часова зона";i:2;s:3:"PST";i:3;s:57:"Тихоокеанска лятна часова зона";i:4;s:3:"PDT";i:5;s:21:"Лос Анжелис";}i:1;a:6:{i:0;s:14:"America/Denver";i:1;s:63:"Американска планинска часова зона";i:2;s:3:"MST";i:3;s:74:"Американска планинска лятна часова зона";i:4;s:3:"MDT";i:5;s:12:"Денвър";}i:2;a:6:{i:0;s:15:"America/Phoenix";i:1;s:63:"Американска планинска часова зона";i:2;s:3:"MST";i:3;s:63:"Американска планинска часова зона";i:4;s:3:"MST";i:5;s:12:"Финикс";}i:3;a:6:{i:0;s:15:"America/Chicago";i:1;s:63:"Американска централна часова зона";i:2;s:3:"CST";i:3;s:74:"Американска централна лятна часова зона";i:4;s:3:"CDT";i:5;s:12:"Чикаго";}i:4;a:6:{i:0;s:16:"America/New_York";i:1;s:59:"Американска източна часова зона";i:2;s:3:"EST";i:3;s:70:"Американска източна лятна часова зона";i:4;s:3:"EDT";i:5;s:13:"Ню Йорк";}i:5;a:6:{i:0;s:20:"America/Indianapolis";i:1;s:59:"Американска източна часова зона";i:2;s:3:"EST";i:3;s:59:"Американска източна часова зона";i:4;s:3:"EST";i:5;s:24:"Индианополис";}i:6;a:6:{i:0;s:16:"Pacific/Honolulu";i:1;s:32:"Часова зона Хавай";i:2;s:3:"HST";i:3;s:43:"Лятна часова зона Хавай";i:4;s:3:"HST";i:5;s:16:"Хонолулу";}i:7;a:6:{i:0;s:17:"America/Anchorage";i:1;s:34:"Часова зона Аляска";i:2;s:3:"AST";i:3;s:45:"Лятна часова зона Аляска";i:4;s:3:"ADT";i:5;s:9:"Anchorage";}i:8;a:6:{i:0;s:15:"America/Halifax";i:1;s:46:"Атлантическа часова зона";i:2;s:3:"AST";i:3;s:57:"Атлантическа лятна часова зона";i:4;s:3:"ADT";i:5;s:16:"Халифакс";}i:9;a:6:{i:0;s:16:"America/St_Johns";i:1;s:44:"Часова зона Нюфаундленд";i:2;s:3:"CNT";i:3;s:55:"Лятна часова зона Нюфаундленд";i:4;s:3:"CDT";i:5;s:23:"Сейнт Джоунс";}i:10;a:6:{i:0;s:12:"Europe/Paris";i:1;s:60:"Централноевропейска часова зона";i:2;s:3:"CET";i:3;s:69:"Централноевропеска лятна часова зона";i:4;s:4:"CEST";i:5;s:10:"Париж";}i:11;a:6:{i:0;s:7:"Etc/GMT";i:1;s:36:"Часова зона Гринуич";i:2;s:3:"GMT";i:3;s:36:"Часова зона Гринуич";i:4;s:3:"GMT";i:5;s:12:"Лондон";}i:12;a:6:{i:0;s:17:"Africa/Casablanca";i:1;s:36:"Часова зона Гринуич";i:2;s:3:"GMT";i:3;s:36:"Часова зона Гринуич";i:4;s:3:"GMT";i:5;s:20:"Казабланка";}i:13;a:6:{i:0;s:14:"Asia/Jerusalem";i:1;s:34:"Часова зона Израел";i:2;s:3:"IST";i:3;s:45:"Лятна часова зона Израел";i:4;s:3:"IDT";i:5;s:18:"Йерусалим";}i:14;a:6:{i:0;s:10:"Asia/Tokyo";i:1;s:36:"Японска часова зона";i:2;s:3:"JST";i:3;s:36:"Японска часова зона";i:4;s:3:"JST";i:5;s:10:"Токио";}i:15;a:6:{i:0;s:16:"Europe/Bucharest";i:1;s:56:"Източноевропейска часова зона";i:2;s:3:"EET";i:3;s:67:"Източноевропейска лятна часова зона";i:4;s:4:"EEST";i:5;s:14:"Букурещ";}i:16;a:6:{i:0;s:13:"Asia/Shanghai";i:1;s:38:"Китайска часова зона";i:2;s:3:"CTT";i:3;s:38:"Китайска часова зона";i:4;s:3:"CDT";i:5;s:12:"Шанхай";}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/bg_BG.dat b/gui/baculum/framework/I18N/core/data/bg_BG.dat new file mode 100644 index 0000000000..6ecaa2817a --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/bg_BG.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;-#,##0.###";i:1;s:24:"#,##0.00 ¤;-#,##0.00 ¤";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/bn.dat b/gui/baculum/framework/I18N/core/data/bn.dat new file mode 100644 index 0000000000..bbb4660695 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/bn.dat @@ -0,0 +1 @@ +a:6:{s:9:"Countries";a:107:{s:2:"AF";a:1:{i:0;s:33:"আফগানিস্তান";}s:2:"AL";a:1:{i:0;s:30:"আলব্যানিয়া";}s:2:"AM";a:1:{i:0;s:27:"আর্মেনিয়া";}s:2:"AR";a:1:{i:0;s:36:"আর্জেণ্টাইনা";}s:2:"AT";a:1:{i:0;s:27:"অস্ট্রিয়া";}s:2:"AU";a:1:{i:0;s:33:"অস্ট্রেলিয়া";}s:2:"AZ";a:1:{i:0;s:30:"আজারবাইজান";}s:2:"BD";a:1:{i:0;s:24:"বাংলাদেশ";}s:2:"BE";a:1:{i:0;s:24:"বেল্জিয়ম";}s:2:"BG";a:1:{i:0;s:27:"বুলগেরিয়া";}s:2:"BO";a:1:{i:0;s:24:"বোলিভিয়া";}s:2:"BR";a:1:{i:0;s:21:"ব্রাজিল";}s:2:"BT";a:1:{i:0;s:15:"ভুটান";}s:2:"BY";a:1:{i:0;s:30:"বেলোরুশিয়া";}s:2:"CG";a:1:{i:0;s:15:"কঙ্গো";}s:2:"CH";a:1:{i:0;s:30:"সুইজর্লণ্ড";}s:2:"CL";a:1:{i:0;s:12:"চিলি";}s:2:"CN";a:1:{i:0;s:9:"চীন";}s:2:"CO";a:1:{i:0;s:30:"কোলোম্বিয়া";}s:2:"CR";a:1:{i:0;s:30:"কোস্টারিকা";}s:2:"CU";a:1:{i:0;s:15:"কিউবা";}s:2:"CZ";a:1:{i:0;s:18:"চেকিয়া";}s:2:"DE";a:1:{i:0;s:24:"জার্মানি";}s:2:"DK";a:1:{i:0;s:27:"ডেন্মার্ক";}s:2:"DZ";a:1:{i:0;s:24:"এলজিরিয়া";}s:2:"EC";a:1:{i:0;s:21:"ইকোয়াডর";}s:2:"EE";a:1:{i:0;s:27:"এস্তোনিয়া";}s:2:"EG";a:1:{i:0;s:12:"মিশর";}s:2:"EH";a:1:{i:0;s:40:"পশ্চিমী সাহারা";}s:2:"ES";a:1:{i:0;s:15:"স্পেন";}s:2:"ET";a:1:{i:0;s:24:"ইফিওপিয়া";}s:2:"FI";a:1:{i:0;s:33:"ফিন্ল্যাণ্ড";}s:2:"FR";a:1:{i:0;s:21:"ফ্রান্স";}s:2:"GB";a:1:{i:0;s:30:"গ্রেটবৃটেন";}s:2:"GE";a:1:{i:0;s:21:"জর্জিয়া";}s:2:"GH";a:1:{i:0;s:12:"গানা";}s:2:"GR";a:1:{i:0;s:18:"গ্রীস্";}s:2:"GT";a:1:{i:0;s:30:"গোয়াটিমালা";}s:2:"GY";a:1:{i:0;s:18:"গিয়ানা";}s:2:"HN";a:1:{i:0;s:24:"হণ্ডুরাস";}s:2:"HU";a:1:{i:0;s:24:"হাঙ্গেরি";}s:2:"ID";a:1:{i:0;s:33:"ইন্দোনেশিয়া";}s:2:"IE";a:1:{i:0;s:27:"আয়ার্লণ্ড";}s:2:"IL";a:1:{i:0;s:24:"ইস্রায়েল";}s:2:"IN";a:1:{i:0;s:12:"ভারত";}s:2:"IQ";a:1:{i:0;s:12:"ইরাক";}s:2:"IR";a:1:{i:0;s:12:"ইরান";}s:2:"IS";a:1:{i:0;s:21:"আইসলণ্ড";}s:2:"IT";a:1:{i:0;s:15:"ইতালী";}s:2:"JM";a:1:{i:0;s:24:"জ্যামেকা";}s:2:"JO";a:1:{i:0;s:15:"জর্ডন";}s:2:"JP";a:1:{i:0;s:15:"জাপান";}s:2:"KE";a:1:{i:0;s:18:"কেনিয়া";}s:2:"KG";a:1:{i:0;s:30:"কির্গিজিয়া";}s:2:"KH";a:1:{i:0;s:21:"কাম্বোজ";}s:2:"KR";a:1:{i:0;s:37:"দক্ষিণ কোরিয়া";}s:2:"KZ";a:1:{i:0;s:30:"কাজাকস্থান";}s:2:"LA";a:1:{i:0;s:12:"লাওস";}s:2:"LB";a:1:{i:0;s:18:"লেবানন";}s:2:"LK";a:1:{i:0;s:27:"শ্রীলঙ্কা";}s:2:"LR";a:1:{i:0;s:27:"লাইবিরিয়া";}s:2:"LT";a:1:{i:0;s:18:"লিত্ভা";}s:2:"LU";a:1:{i:0;s:36:"লাক্সেমবার্গ";}s:2:"LV";a:1:{i:0;s:24:"লাত্ভিয়া";}s:2:"LY";a:1:{i:0;s:18:"লিবিয়া";}s:2:"MA";a:1:{i:0;s:21:"মোরক্কো";}s:2:"MD";a:1:{i:0;s:30:"মোল্দাভিয়া";}s:2:"MG";a:1:{i:0;s:33:"মাদাগাস্কার";}s:2:"MN";a:1:{i:0;s:27:"মঙ্গোলিয়া";}s:2:"MX";a:1:{i:0;s:21:"মক্সিকো";}s:2:"MY";a:1:{i:0;s:30:"মাল্যাশিয়া";}s:2:"NI";a:1:{i:0;s:30:"নিকারাগোয়া";}s:2:"NL";a:1:{i:0;s:15:"হলণ্ড";}s:2:"NO";a:1:{i:0;s:15:"নরওয়ে";}s:2:"NP";a:1:{i:0;s:15:"নেপাল";}s:2:"NZ";a:1:{i:0;s:28:"নিউ জিলণ্ড";}s:2:"PA";a:1:{i:0;s:18:"পানামা";}s:2:"PE";a:1:{i:0;s:12:"পিরু";}s:2:"PH";a:1:{i:0;s:24:"ফিলিপাইন";}s:2:"PK";a:1:{i:0;s:27:"পাকিস্তান";}s:2:"PL";a:1:{i:0;s:27:"পোল্যাণ্ড";}s:2:"PT";a:1:{i:0;s:24:"পর্তুগাল";}s:2:"PY";a:1:{i:0;s:30:"প্যারাগোয়ে";}s:2:"RO";a:1:{i:0;s:24:"রুমানিয়া";}s:2:"RU";a:1:{i:0;s:18:"রাশিয়া";}s:2:"SA";a:1:{i:0;s:25:"সাউদি আরব";}s:2:"SD";a:1:{i:0;s:15:"সুদান";}s:2:"SE";a:1:{i:0;s:18:"সুইডেন";}s:2:"SG";a:1:{i:0;s:27:"সিঙ্গাপুর";}s:2:"SK";a:1:{i:0;s:30:"শ্লোভাকিয়া";}s:2:"SO";a:1:{i:0;s:18:"সোমালি";}s:2:"SV";a:1:{i:0;s:21:"সালভেডর";}s:2:"SY";a:1:{i:0;s:18:"সিরিয়া";}s:2:"TH";a:1:{i:0;s:9:"থাই";}s:2:"TJ";a:1:{i:0;s:30:"তাজিকস্থান";}s:2:"TM";a:1:{i:0;s:33:"তুর্কমেনিয়া";}s:2:"TN";a:1:{i:0;s:21:"টিউনিস্";}s:2:"TR";a:1:{i:0;s:18:"তুরস্ক";}s:2:"TW";a:1:{i:0;s:21:"তাইওয়ান";}s:2:"UA";a:1:{i:0;s:24:"ইউক্রেইন";}s:2:"US";a:1:{i:0;s:58:"মার্কিন যুক্তরাষ্ট্র";}s:2:"UY";a:1:{i:0;s:21:"উরুগোয়ে";}s:2:"UZ";a:1:{i:0;s:36:"উজ্বেকিস্থান";}s:2:"VE";a:1:{i:0;s:30:"ভেনেজুয়েলা";}s:2:"VN";a:1:{i:0;s:24:"ভিয়েতনাম";}s:2:"YE";a:1:{i:0;s:12:"ইমেন";}s:2:"ZA";a:1:{i:0;s:40:"দক্ষিণ আফ্রিকা";}}s:10:"Currencies";a:2:{s:3:"BDT";a:2:{i:0;s:3:"৳";i:1;s:3:"BDT";}s:3:"INR";a:2:{i:0;s:12:"টাকা";i:1;s:3:"INR";}}s:9:"Languages";a:1:{s:2:"bn";a:1:{i:0;s:15:"বাংলা";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Beng";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:3:{s:11:"AmPmMarkers";a:2:{i:0;s:27:"পূর্বাহ্ণ";i:1;s:21:"অপরাহ্ণ";}s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:9:"রবি";i:1;s:9:"সোম";i:2;s:15:"মঙ্গল";i:3;s:9:"বুধ";i:4;s:24:"বৃহস্পতি";i:5;s:15:"শুক্র";i:6;s:9:"শনি";}s:4:"wide";a:7:{i:0;s:18:"রবিবার";i:1;s:18:"সোমবার";i:2;s:24:"মঙ্গলবার";i:3;s:18:"বুধবার";i:4;s:33:"বৃহষ্পতিবার";i:5;s:24:"শুক্রবার";i:6;s:18:"শনিবার";}}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:24:"জানুয়ারী";i:1;s:30:"ফেব্রুয়ারী";i:2;s:15:"মার্চ";i:3;s:18:"এপ্রিল";i:4;s:6:"মে";i:5;s:9:"জুন";i:6;s:15:"জুলাই";i:7;s:15:"আগস্ট";i:8;s:30:"সেপ্টেম্বর";i:9;s:21:"অক্টোবর";i:10;s:21:"নভেম্বর";i:11;s:24:"ডিসেম্বর";}s:4:"wide";a:12:{i:0;s:24:"জানুয়ারী";i:1;s:30:"ফেব্রুয়ারী";i:2;s:15:"মার্চ";i:3;s:18:"এপ্রিল";i:4;s:6:"মে";i:5;s:9:"জুন";i:6;s:15:"জুলাই";i:7;s:15:"আগস্ট";i:8;s:30:"সেপ্টেম্বর";i:9;s:21:"অক্টোবর";i:10;s:21:"নভেম্বর";i:11;s:24:"ডিসেম্বর";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/bn_IN.dat b/gui/baculum/framework/I18N/core/data/bn_IN.dat new file mode 100644 index 0000000000..e13234cb80 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/bn_IN.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:28:"##,##,##0.###;-##,##,##0.###";i:1;s:32:"¤ ##,##,##0.00;-¤ ##,##,##0.00";i:2;s:10:"##,##,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:3:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:11:"h:mm:ss a z";i:1;s:11:"h:mm:ss a z";i:2;s:9:"h:mm:ss a";i:3;s:6:"h:mm a";i:4;s:16:"EEEE d MMMM yyyy";i:5;s:11:"d MMMM yyyy";i:6;s:10:"dd-MM-yyyy";i:7;s:6:"d-M-yy";i:8;s:7:"{1} {0}";}s:17:"weekend:intvector";a:4:{i:0;i:1;i:1;i:0;i:2;i:1;i:3;i:86400000;}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ca.dat b/gui/baculum/framework/I18N/core/data/ca.dat new file mode 100644 index 0000000000..df2a2d9a87 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ca.dat @@ -0,0 +1 @@ +a:8:{s:9:"Countries";a:196:{s:2:"AD";a:1:{i:0;s:7:"Andorra";}s:2:"AE";a:1:{i:0;s:25:"Unió dels Emirats Àrabs";}s:2:"AF";a:1:{i:0;s:10:"Afganistan";}s:2:"AI";a:1:{i:0;s:8:"Anguilla";}s:2:"AL";a:1:{i:0;s:8:"Albània";}s:2:"AM";a:1:{i:0;s:8:"Armènia";}s:2:"AN";a:1:{i:0;s:19:"Antilles Holandeses";}s:2:"AO";a:1:{i:0;s:6:"Angola";}s:2:"AR";a:1:{i:0;s:9:"Argentina";}s:2:"AT";a:1:{i:0;s:8:"Âustria";}s:2:"AU";a:1:{i:0;s:10:"Austràlia";}s:2:"AW";a:1:{i:0;s:5:"Aruba";}s:2:"AZ";a:1:{i:0;s:11:"Azerbaidjan";}s:2:"BA";a:1:{i:0;s:21:"Bòsnia i Hercegovina";}s:2:"BB";a:1:{i:0;s:8:"Barbados";}s:2:"BD";a:1:{i:0;s:11:"Bangla Desh";}s:2:"BE";a:1:{i:0;s:8:"Bèlgica";}s:2:"BF";a:1:{i:0;s:12:"Burkina Faso";}s:2:"BG";a:1:{i:0;s:9:"Bulgària";}s:2:"BH";a:1:{i:0;s:7:"Bahrain";}s:2:"BI";a:1:{i:0;s:7:"Burundi";}s:2:"BJ";a:1:{i:0;s:5:"Benin";}s:2:"BM";a:1:{i:0;s:8:"Bermudes";}s:2:"BN";a:1:{i:0;s:6:"Brunei";}s:2:"BO";a:1:{i:0;s:8:"Bolívia";}s:2:"BR";a:1:{i:0;s:6:"Brasil";}s:2:"BS";a:1:{i:0;s:7:"Bahames";}s:2:"BT";a:1:{i:0;s:6:"Bhutan";}s:2:"BW";a:1:{i:0;s:8:"Botswana";}s:2:"BY";a:1:{i:0;s:12:"Bielorússia";}s:2:"BZ";a:1:{i:0;s:6:"Belize";}s:2:"CA";a:1:{i:0;s:7:"Canadà";}s:2:"CF";a:1:{i:0;s:24:"República Centrafricana";}s:2:"CG";a:1:{i:0;s:5:"Congo";}s:2:"CH";a:1:{i:0;s:11:"Switzerland";}s:2:"CI";a:1:{i:0;s:15:"Costa d’Ivori";}s:2:"CL";a:1:{i:0;s:4:"Xile";}s:2:"CM";a:1:{i:0;s:7:"Camerun";}s:2:"CN";a:1:{i:0;s:4:"Xina";}s:2:"CO";a:1:{i:0;s:9:"Colòmbia";}s:2:"CR";a:1:{i:0;s:10:"Costa Rica";}s:2:"CU";a:1:{i:0;s:4:"Cuba";}s:2:"CV";a:1:{i:0;s:8:"Cap Verd";}s:2:"CY";a:1:{i:0;s:5:"Xipre";}s:2:"CZ";a:1:{i:0;s:16:"República Txeca";}s:2:"DE";a:1:{i:0;s:8:"Alemanya";}s:2:"DJ";a:1:{i:0;s:8:"Djibouti";}s:2:"DK";a:1:{i:0;s:9:"Dinamarca";}s:2:"DM";a:1:{i:0;s:8:"Dominica";}s:2:"DO";a:1:{i:0;s:21:"República Dominicana";}s:2:"DZ";a:1:{i:0;s:8:"Algèria";}s:2:"EC";a:1:{i:0;s:7:"Equador";}s:2:"EE";a:1:{i:0;s:8:"Estònia";}s:2:"EG";a:1:{i:0;s:6:"Egipte";}s:2:"EH";a:1:{i:0;s:18:"Sàhara Occidental";}s:2:"ER";a:1:{i:0;s:7:"Eritrea";}s:2:"ES";a:1:{i:0;s:7:"Espanya";}s:2:"ET";a:1:{i:0;s:8:"Etiòpia";}s:2:"FI";a:1:{i:0;s:10:"Finlàndia";}s:2:"FJ";a:1:{i:0;s:4:"Fiji";}s:2:"FM";a:1:{i:0;s:11:"Micronèsia";}s:2:"FR";a:1:{i:0;s:7:"França";}s:2:"GA";a:1:{i:0;s:5:"Gabon";}s:2:"GB";a:1:{i:0;s:10:"Regne Unit";}s:2:"GE";a:1:{i:0;s:8:"Geòrgia";}s:2:"GF";a:1:{i:0;s:16:"Guaiana Francesa";}s:2:"GH";a:1:{i:0;s:5:"Ghana";}s:2:"GM";a:1:{i:0;s:7:"Gàmbia";}s:2:"GN";a:1:{i:0;s:6:"Guinea";}s:2:"GP";a:1:{i:0;s:10:"Guadeloupe";}s:2:"GQ";a:1:{i:0;s:17:"Guinea Equatorial";}s:2:"GR";a:1:{i:0;s:7:"Grècia";}s:2:"GT";a:1:{i:0;s:9:"Guatemala";}s:2:"GW";a:1:{i:0;s:13:"Guinea Bissau";}s:2:"GY";a:1:{i:0;s:6:"Guyana";}s:2:"HN";a:1:{i:0;s:8:"Hondures";}s:2:"HR";a:1:{i:0;s:8:"Croàcia";}s:2:"HT";a:1:{i:0;s:6:"Haití";}s:2:"HU";a:1:{i:0;s:7:"Hongria";}s:2:"ID";a:1:{i:0;s:10:"Indonèsia";}s:2:"IE";a:1:{i:0;s:7:"Irlanda";}s:2:"IL";a:1:{i:0;s:6:"Israel";}s:2:"IN";a:1:{i:0;s:6:"Índia";}s:2:"IQ";a:1:{i:0;s:4:"Iraq";}s:2:"IR";a:1:{i:0;s:4:"Iran";}s:2:"IS";a:1:{i:0;s:9:"Islàndia";}s:2:"IT";a:1:{i:0;s:7:"Itàlia";}s:2:"JM";a:1:{i:0;s:7:"Jamaica";}s:2:"JO";a:1:{i:0;s:9:"Jordània";}s:2:"JP";a:1:{i:0;s:5:"Japó";}s:2:"KE";a:1:{i:0;s:5:"Kenya";}s:2:"KG";a:1:{i:0;s:11:"Kirgizistan";}s:2:"KH";a:1:{i:0;s:8:"Cambodja";}s:2:"KI";a:1:{i:0;s:8:"Kiribati";}s:2:"KM";a:1:{i:0;s:7:"Comores";}s:2:"KP";a:1:{i:0;s:14:"Corea del Nord";}s:2:"KR";a:1:{i:0;s:13:"Corea del Sud";}s:2:"KW";a:1:{i:0;s:6:"Kuwait";}s:2:"KZ";a:1:{i:0;s:10:"Kazakhstan";}s:2:"LA";a:1:{i:0;s:4:"Laos";}s:2:"LB";a:1:{i:0;s:6:"Líban";}s:2:"LI";a:1:{i:0;s:13:"Liechtenstein";}s:2:"LK";a:1:{i:0;s:9:"Sri Lanka";}s:2:"LR";a:1:{i:0;s:8:"Libèria";}s:2:"LS";a:1:{i:0;s:7:"Lesotho";}s:2:"LT";a:1:{i:0;s:9:"Lituània";}s:2:"LU";a:1:{i:0;s:9:"Luxemburg";}s:2:"LV";a:1:{i:0;s:8:"Letònia";}s:2:"LY";a:1:{i:0;s:6:"Líbia";}s:2:"MA";a:1:{i:0;s:6:"Marroc";}s:2:"MC";a:1:{i:0;s:7:"Mònaco";}s:2:"MD";a:1:{i:0;s:9:"Moldàvia";}s:2:"MG";a:1:{i:0;s:10:"Madagascar";}s:2:"MK";a:1:{i:0;s:10:"Macedònia";}s:2:"ML";a:1:{i:0;s:4:"Mali";}s:2:"MM";a:1:{i:0;s:7:"Myanmar";}s:2:"MN";a:1:{i:0;s:9:"Mongòlia";}s:2:"MQ";a:1:{i:0;s:9:"Martinica";}s:2:"MR";a:1:{i:0;s:11:"Mauritània";}s:2:"MS";a:1:{i:0;s:10:"Montserrat";}s:2:"MT";a:1:{i:0;s:5:"Malta";}s:2:"MU";a:1:{i:0;s:7:"Maurici";}s:2:"MX";a:1:{i:0;s:6:"Mèxic";}s:2:"MY";a:1:{i:0;s:9:"Malàisia";}s:2:"MZ";a:1:{i:0;s:9:"Moçambic";}s:2:"NA";a:1:{i:0;s:8:"Namíbia";}s:2:"NC";a:1:{i:0;s:15:"Nova Caledònia";}s:2:"NE";a:1:{i:0;s:6:"Níger";}s:2:"NG";a:1:{i:0;s:8:"Nigèria";}s:2:"NI";a:1:{i:0;s:9:"Nicaragua";}s:2:"NL";a:1:{i:0;s:14:"Països Baixos";}s:2:"NO";a:1:{i:0;s:7:"Noruega";}s:2:"NP";a:1:{i:0;s:5:"Nepal";}s:2:"NU";a:1:{i:0;s:4:"Niue";}s:2:"NZ";a:1:{i:0;s:12:"Nova Zelanda";}s:2:"OM";a:1:{i:0;s:4:"Oman";}s:2:"PA";a:1:{i:0;s:7:"Panamà";}s:2:"PE";a:1:{i:0;s:5:"Perú";}s:2:"PF";a:1:{i:0;s:19:"Polinèsia Francesa";}s:2:"PG";a:1:{i:0;s:17:"Papua Nova Guinea";}s:2:"PH";a:1:{i:0;s:9:"Filipines";}s:2:"PK";a:1:{i:0;s:8:"Pakistan";}s:2:"PL";a:1:{i:0;s:8:"Polònia";}s:2:"PR";a:1:{i:0;s:11:"Puerto Rico";}s:2:"PT";a:1:{i:0;s:8:"Portugal";}s:2:"PY";a:1:{i:0;s:8:"Paraguai";}s:2:"QA";a:1:{i:0;s:5:"Qatar";}s:2:"RO";a:1:{i:0;s:7:"Romania";}s:2:"RU";a:1:{i:0;s:7:"Rússia";}s:2:"RW";a:1:{i:0;s:6:"Rwanda";}s:2:"SA";a:1:{i:0;s:14:"Aràbia Saudí";}s:2:"SC";a:1:{i:0;s:10:"Seychelles";}s:2:"SD";a:1:{i:0;s:5:"Sudan";}s:2:"SE";a:1:{i:0;s:7:"Suècia";}s:2:"SG";a:1:{i:0;s:8:"Singapur";}s:2:"SI";a:1:{i:0;s:10:"Eslovènia";}s:2:"SK";a:1:{i:0;s:11:"Eslovàquia";}s:2:"SL";a:1:{i:0;s:12:"Sierra Leone";}s:2:"SN";a:1:{i:0;s:7:"Senegal";}s:2:"SO";a:1:{i:0;s:8:"Somàlia";}s:2:"SP";a:1:{i:0;s:7:"Sèrbia";}s:2:"SR";a:1:{i:0;s:7:"Surinam";}s:2:"SV";a:1:{i:0;s:11:"El Salvador";}s:2:"SY";a:1:{i:0;s:6:"Síria";}s:2:"SZ";a:1:{i:0;s:12:"Swazilàndia";}s:2:"TD";a:1:{i:0;s:4:"Txad";}s:2:"TF";a:1:{i:0;s:32:"Territoris Meridionals Francesos";}s:2:"TG";a:1:{i:0;s:4:"Togo";}s:2:"TH";a:1:{i:0;s:10:"Tailàndia";}s:2:"TJ";a:1:{i:0;s:11:"Tadjikistan";}s:2:"TK";a:1:{i:0;s:7:"Tokelau";}s:2:"TL";a:1:{i:0;s:5:"Timor";}s:2:"TM";a:1:{i:0;s:12:"Turkmenistan";}s:2:"TN";a:1:{i:0;s:8:"Tunísia";}s:2:"TO";a:1:{i:0;s:5:"Tonga";}s:2:"TR";a:1:{i:0;s:7:"Turquia";}s:2:"TT";a:1:{i:0;s:17:"Trinitat i Tobago";}s:2:"TW";a:1:{i:0;s:6:"Taiwan";}s:2:"TZ";a:1:{i:0;s:9:"Tanzània";}s:2:"UA";a:1:{i:0;s:8:"Ucraïna";}s:2:"UG";a:1:{i:0;s:6:"Uganda";}s:2:"US";a:1:{i:0;s:12:"Estats Units";}s:2:"UY";a:1:{i:0;s:7:"Uruguai";}s:2:"UZ";a:1:{i:0;s:10:"Uzbekistan";}s:2:"VA";a:1:{i:0;s:7:"Vaticà";}s:2:"VE";a:1:{i:0;s:10:"Veneçuela";}s:2:"VG";a:1:{i:0;s:25:"Illes Verges Britàniques";}s:2:"VI";a:1:{i:0;s:21:"Illes Verges dels USA";}s:2:"VN";a:1:{i:0;s:7:"Vietnam";}s:2:"VU";a:1:{i:0;s:7:"Vanuatu";}s:2:"YE";a:1:{i:0;s:5:"Iemen";}s:2:"YT";a:1:{i:0;s:7:"Mayotte";}s:2:"YU";a:1:{i:0;s:11:"Iugoslàvia";}s:2:"ZA";a:1:{i:0;s:11:"Sud-àfrica";}s:2:"ZM";a:1:{i:0;s:7:"Zàmbia";}s:2:"ZW";a:1:{i:0;s:8:"Zimbabwe";}}s:10:"Currencies";a:1:{s:3:"ESP";a:3:{i:0;s:3:"₧";i:1;s:3:"ESP";i:2;a:3:{i:0;s:18:"¤ #,##0;-¤ #,##0";i:1;s:1:",";i:2;s:1:".";}}}s:9:"Languages";a:139:{s:2:"aa";a:1:{i:0;s:5:"àfar";}s:2:"ab";a:1:{i:0;s:6:"abkhaz";}s:2:"af";a:1:{i:0;s:9:"afrikaans";}s:2:"am";a:1:{i:0;s:8:"amhàric";}s:2:"ar";a:1:{i:0;s:5:"ârab";}s:2:"as";a:1:{i:0;s:8:"assamès";}s:2:"ay";a:1:{i:0;s:6:"aimara";}s:2:"az";a:1:{i:0;s:6:"àzeri";}s:2:"ba";a:1:{i:0;s:7:"baixkir";}s:2:"be";a:1:{i:0;s:9:"bielorús";}s:2:"bg";a:1:{i:0;s:7:"búlgar";}s:2:"bh";a:1:{i:0;s:6:"bihari";}s:2:"bi";a:1:{i:0;s:7:"bislama";}s:2:"bn";a:1:{i:0;s:8:"bengalí";}s:2:"bo";a:1:{i:0;s:7:"tibetà";}s:2:"br";a:1:{i:0;s:6:"bretó";}s:2:"ca";a:1:{i:0;s:7:"català";}s:2:"co";a:1:{i:0;s:4:"cors";}s:2:"cs";a:1:{i:0;s:4:"txec";}s:2:"cy";a:1:{i:0;s:9:"gal·lès";}s:2:"da";a:1:{i:0;s:6:"danès";}s:2:"de";a:1:{i:0;s:7:"alemany";}s:2:"dz";a:1:{i:0;s:9:"bhutanès";}s:2:"el";a:1:{i:0;s:4:"grec";}s:2:"en";a:1:{i:0;s:7:"anglès";}s:2:"eo";a:1:{i:0;s:9:"esperanto";}s:2:"es";a:1:{i:0;s:8:"espanyol";}s:2:"et";a:1:{i:0;s:8:"estonià";}s:2:"eu";a:1:{i:0;s:4:"basc";}s:2:"fa";a:1:{i:0;s:5:"persa";}s:2:"fi";a:1:{i:0;s:6:"finès";}s:2:"fj";a:1:{i:0;s:6:"fijià";}s:2:"fo";a:1:{i:0;s:7:"feroès";}s:2:"fr";a:1:{i:0;s:8:"francès";}s:2:"fy";a:1:{i:0;s:6:"frisó";}s:2:"ga";a:1:{i:0;s:9:"irlandès";}s:2:"gd";a:1:{i:0;s:8:"escocès";}s:2:"gl";a:1:{i:0;s:6:"gallec";}s:2:"gn";a:1:{i:0;s:8:"guaraní";}s:2:"gu";a:1:{i:0;s:8:"gujarati";}s:2:"ha";a:1:{i:0;s:5:"hausa";}s:2:"he";a:1:{i:0;s:6:"hebreu";}s:2:"hi";a:1:{i:0;s:5:"hindi";}s:2:"hr";a:1:{i:0;s:5:"croat";}s:2:"hu";a:1:{i:0;s:9:"hongarès";}s:2:"hy";a:1:{i:0;s:6:"armeni";}s:2:"ia";a:1:{i:0;s:11:"interlingua";}s:2:"id";a:1:{i:0;s:8:"indonesi";}s:2:"ie";a:1:{i:0;s:11:"interlingue";}s:2:"ik";a:1:{i:0;s:7:"inupiak";}s:2:"is";a:1:{i:0;s:9:"islandès";}s:2:"it";a:1:{i:0;s:7:"italià";}s:2:"iu";a:1:{i:0;s:9:"inuktitut";}s:2:"ja";a:1:{i:0;s:8:"japonès";}s:2:"jv";a:1:{i:0;s:8:"javanès";}s:2:"ka";a:1:{i:0;s:8:"georgià";}s:2:"kk";a:1:{i:0;s:6:"kazakh";}s:2:"kl";a:1:{i:0;s:12:"greenlandès";}s:2:"km";a:1:{i:0;s:9:"cambodjà";}s:2:"kn";a:1:{i:0;s:7:"kannada";}s:2:"ko";a:1:{i:0;s:6:"coreà";}s:2:"ks";a:1:{i:0;s:8:"caixmiri";}s:2:"ku";a:1:{i:0;s:4:"kurd";}s:2:"ky";a:1:{i:0;s:8:"kirguís";}s:2:"la";a:1:{i:0;s:6:"llatí";}s:2:"ln";a:1:{i:0;s:7:"lingala";}s:2:"lo";a:1:{i:0;s:7:"laosià";}s:2:"lt";a:1:{i:0;s:6:"lituà";}s:2:"lv";a:1:{i:0;s:5:"letó";}s:2:"mg";a:1:{i:0;s:7:"malgaix";}s:2:"mi";a:1:{i:0;s:5:"maori";}s:2:"mk";a:1:{i:0;s:8:"macedoni";}s:2:"ml";a:1:{i:0;s:9:"malaialam";}s:2:"mn";a:1:{i:0;s:6:"mongol";}s:2:"mo";a:1:{i:0;s:6:"moldau";}s:2:"mr";a:1:{i:0;s:7:"marathi";}s:2:"ms";a:1:{i:0;s:5:"malai";}s:2:"mt";a:1:{i:0;s:7:"maltès";}s:2:"my";a:1:{i:0;s:6:"birmà";}s:2:"na";a:1:{i:0;s:7:"nauruà";}s:2:"ne";a:1:{i:0;s:8:"nepalès";}s:2:"nl";a:1:{i:0;s:11:"neerlandès";}s:2:"no";a:1:{i:0;s:6:"noruec";}s:2:"oc";a:1:{i:0;s:7:"occità";}s:2:"om";a:1:{i:0;s:12:"oromo (afan)";}s:2:"or";a:1:{i:0;s:5:"oriya";}s:2:"pa";a:1:{i:0;s:7:"panjabi";}s:2:"pl";a:1:{i:0;s:8:"polonès";}s:2:"ps";a:1:{i:0;s:6:"paixto";}s:2:"pt";a:1:{i:0;s:10:"portuguès";}s:2:"qu";a:1:{i:0;s:8:"quètxua";}s:2:"rm";a:1:{i:0;s:12:"retoromànic";}s:2:"rn";a:1:{i:0;s:7:"kirundi";}s:2:"ro";a:1:{i:0;s:8:"romanès";}s:2:"ru";a:1:{i:0;s:3:"rus";}s:2:"rw";a:1:{i:0;s:11:"kinyarwanda";}s:2:"sa";a:1:{i:0;s:9:"sànscrit";}s:2:"sd";a:1:{i:0;s:6:"sindhi";}s:2:"sg";a:1:{i:0;s:5:"sango";}s:2:"sh";a:1:{i:0;s:11:"serbo-croat";}s:2:"si";a:1:{i:0;s:9:"sinhalès";}s:2:"sk";a:1:{i:0;s:7:"eslovac";}s:2:"sl";a:1:{i:0;s:7:"eslovè";}s:2:"sm";a:1:{i:0;s:6:"samoà";}s:2:"sn";a:1:{i:0;s:5:"shona";}s:2:"so";a:1:{i:0;s:6:"somali";}s:2:"sq";a:1:{i:0;s:8:"albanès";}s:2:"sr";a:1:{i:0;s:5:"serbi";}s:2:"ss";a:1:{i:0;s:7:"siswati";}s:2:"st";a:1:{i:0;s:5:"sotho";}s:2:"su";a:1:{i:0;s:9:"sundanès";}s:2:"sv";a:1:{i:0;s:4:"suec";}s:2:"sw";a:1:{i:0;s:7:"swahili";}s:2:"ta";a:1:{i:0;s:6:"tàmil";}s:2:"te";a:1:{i:0;s:6:"telugu";}s:2:"tg";a:1:{i:0;s:6:"tadjik";}s:2:"th";a:1:{i:0;s:4:"thai";}s:2:"ti";a:1:{i:0;s:8:"tigrinya";}s:2:"tk";a:1:{i:0;s:7:"turcman";}s:2:"tl";a:1:{i:0;s:8:"tagàlog";}s:2:"tn";a:1:{i:0;s:6:"tswana";}s:2:"to";a:1:{i:0;s:5:"tonga";}s:2:"tr";a:1:{i:0;s:4:"turc";}s:2:"ts";a:1:{i:0;s:6:"tsonga";}s:2:"tt";a:1:{i:0;s:6:"tàtar";}s:2:"tw";a:1:{i:0;s:3:"twi";}s:2:"ug";a:1:{i:0;s:5:"uigur";}s:2:"uk";a:1:{i:0;s:10:"ucraïnès";}s:2:"ur";a:1:{i:0;s:5:"urdú";}s:2:"uz";a:1:{i:0;s:5:"uzbek";}s:2:"vi";a:1:{i:0;s:10:"vietnamita";}s:2:"vo";a:1:{i:0;s:7:"volapuk";}s:2:"wo";a:1:{i:0;s:6:"wòlof";}s:2:"xh";a:1:{i:0;s:4:"xosa";}s:2:"yi";a:1:{i:0;s:7:"jiddish";}s:2:"yo";a:1:{i:0;s:6:"ioruba";}s:2:"za";a:1:{i:0;s:6:"zhuang";}s:2:"zh";a:1:{i:0;s:6:"xinés";}s:2:"zu";a:1:{i:0;s:4:"zulu";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Latn";}s:14:"NumberElements";a:12:{i:0;s:1:",";i:1;s:1:".";i:2;s:1:";";i:3;s:1:"%";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:3:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:3:"dg.";i:1;s:3:"dl.";i:2;s:3:"dt.";i:3;s:3:"dc.";i:4;s:3:"dj.";i:5;s:3:"dv.";i:6;s:3:"ds.";}s:4:"wide";a:7:{i:0;s:8:"diumenge";i:1;s:7:"dilluns";i:2;s:7:"dimarts";i:3;s:8:"dimecres";i:4;s:6:"dijous";i:5;s:9:"divendres";i:6;s:8:"dissabte";}}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:4:"gen.";i:1;s:4:"feb.";i:2;s:5:"març";i:3;s:4:"abr.";i:4;s:4:"maig";i:5;s:4:"juny";i:6;s:4:"jul.";i:7;s:3:"ag.";i:8;s:4:"set.";i:9;s:4:"oct.";i:10;s:4:"nov.";i:11;s:4:"des.";}s:4:"wide";a:12:{i:0;s:5:"gener";i:1;s:6:"febrer";i:2;s:5:"març";i:3;s:5:"abril";i:4;s:4:"maig";i:5;s:4:"juny";i:6;s:6:"juliol";i:7;s:5:"agost";i:8;s:8:"setembre";i:9;s:7:"octubre";i:10;s:8:"novembre";i:11;s:8:"desembre";}}}}}s:17:"localPatternChars";a:1:{i:0;s:24:"GuMtkHmsSEDFwWahKzUeygAZ";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/ca_ES.dat b/gui/baculum/framework/I18N/core/data/ca_ES.dat new file mode 100644 index 0000000000..6ecaa2817a --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/ca_ES.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;-#,##0.###";i:1;s:24:"#,##0.00 ¤;-#,##0.00 ¤";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/cs.dat b/gui/baculum/framework/I18N/core/data/cs.dat new file mode 100644 index 0000000000..947762cebe --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/cs.dat @@ -0,0 +1 @@ +a:11:{s:9:"Countries";a:240:{s:2:"AD";a:1:{i:0;s:7:"Andorra";}s:2:"AE";a:1:{i:0;s:26:"Spojené arabské emiráty";}s:2:"AF";a:1:{i:0;s:13:"Afghánistán";}s:2:"AG";a:1:{i:0;s:17:"Antigua a Barbuda";}s:2:"AI";a:1:{i:0;s:7:"Anguila";}s:2:"AL";a:1:{i:0;s:8:"Albánie";}s:2:"AM";a:1:{i:0;s:8:"Arménie";}s:2:"AN";a:1:{i:0;s:18:"Nizozemské Antily";}s:2:"AO";a:1:{i:0;s:6:"Angola";}s:2:"AQ";a:1:{i:0;s:10:"Antarktida";}s:2:"AR";a:1:{i:0;s:9:"Argentina";}s:2:"AS";a:1:{i:0;s:15:"Americká Samoa";}s:2:"AT";a:1:{i:0;s:8:"Rakousko";}s:2:"AU";a:1:{i:0;s:10:"Austrálie";}s:2:"AW";a:1:{i:0;s:5:"Aruba";}s:2:"AZ";a:1:{i:0;s:15:"Ázerbájdžán";}s:2:"BA";a:1:{i:0;s:19:"Bosna a Hercegovina";}s:2:"BB";a:1:{i:0;s:8:"Barbados";}s:2:"BD";a:1:{i:0;s:11:"Bangladéš";}s:2:"BE";a:1:{i:0;s:6:"Belgie";}s:2:"BF";a:1:{i:0;s:12:"Burkina Faso";}s:2:"BG";a:1:{i:0;s:9:"Bulharsko";}s:2:"BH";a:1:{i:0;s:7:"Bahrajn";}s:2:"BI";a:1:{i:0;s:7:"Burundi";}s:2:"BJ";a:1:{i:0;s:5:"Benin";}s:2:"BM";a:1:{i:0;s:7:"Bermudy";}s:2:"BN";a:1:{i:0;s:17:"Brunej Darussalam";}s:2:"BO";a:1:{i:0;s:8:"Bolívie";}s:2:"BR";a:1:{i:0;s:9:"Brazílie";}s:2:"BS";a:1:{i:0;s:6:"Bahamy";}s:2:"BT";a:1:{i:0;s:8:"Bhútán";}s:2:"BV";a:1:{i:0;s:13:"Ostrov Bouvet";}s:2:"BW";a:1:{i:0;s:8:"Botswana";}s:2:"BY";a:1:{i:0;s:10:"Bělorusko";}s:2:"BZ";a:1:{i:0;s:6:"Belize";}s:2:"CA";a:1:{i:0;s:6:"Kanada";}s:2:"CC";a:1:{i:0;s:17:"Kokosové ostrovy";}s:2:"CD";a:1:{i:0;s:30:"Kongo, demokratická republika";}s:2:"CF";a:1:{i:0;s:25:"Středoafrická republika";}s:2:"CG";a:1:{i:0;s:5:"Kongo";}s:2:"CH";a:1:{i:0;s:11:"Å výcarsko";}s:2:"CI";a:1:{i:0;s:20:"Pobřeží slonoviny";}s:2:"CK";a:1:{i:0;s:15:"Cookovy ostrovy";}s:2:"CL";a:1:{i:0;s:5:"Chile";}s:2:"CM";a:1:{i:0;s:7:"Kamerun";}s:2:"CN";a:1:{i:0;s:6:"Čína";}s:2:"CO";a:1:{i:0;s:8:"Kolumbie";}s:2:"CR";a:1:{i:0;s:9:"Kostarika";}s:2:"CU";a:1:{i:0;s:4:"Kuba";}s:2:"CV";a:1:{i:0;s:8:"Kapverdy";}s:2:"CX";a:1:{i:0;s:18:"Vánoční ostrovy";}s:2:"CY";a:1:{i:0;s:4:"Kypr";}s:2:"CZ";a:1:{i:0;s:17:"Česká republika";}s:2:"DE";a:1:{i:0;s:8:"Německo";}s:2:"DJ";a:1:{i:0;s:8:"Džibuti";}s:2:"DK";a:1:{i:0;s:7:"Dánsko";}s:2:"DM";a:1:{i:0;s:8:"Dominika";}s:2:"DO";a:1:{i:0;s:24:"Dominikánská republika";}s:2:"DZ";a:1:{i:0;s:10:"Alžírsko";}s:2:"EC";a:1:{i:0;s:8:"Ekvádor";}s:2:"EE";a:1:{i:0;s:8:"Estonsko";}s:2:"EG";a:1:{i:0;s:5:"Egypt";}s:2:"EH";a:1:{i:0;s:16:"Západní Sahara";}s:2:"ER";a:1:{i:0;s:7:"Eritrea";}s:2:"ES";a:1:{i:0;s:11:"Å panělsko";}s:2:"ET";a:1:{i:0;s:7:"Etiopie";}s:2:"FI";a:1:{i:0;s:6:"Finsko";}s:2:"FJ";a:1:{i:0;s:6:"Fidži";}s:2:"FK";a:1:{i:0;s:20:"Falklandské ostrovy";}s:2:"FM";a:1:{i:0;s:31:"Mikronésie, federativní stát";}s:2:"FO";a:1:{i:0;s:16:"Faerské ostrovy";}s:2:"FR";a:1:{i:0;s:7:"Francie";}s:2:"GA";a:1:{i:0;s:5:"Gabon";}s:2:"GB";a:1:{i:0;s:16:"Velká Británie";}s:2:"GD";a:1:{i:0;s:7:"Grenada";}s:2:"GE";a:1:{i:0;s:6:"Gruzie";}s:2:"GF";a:1:{i:0;s:19:"Francouzská Guyana";}s:2:"GH";a:1:{i:0;s:5:"Ghana";}s:2:"GI";a:1:{i:0;s:9:"Gibraltar";}s:2:"GL";a:1:{i:0;s:8:"Grónsko";}s:2:"GM";a:1:{i:0;s:6:"Gambie";}s:2:"GN";a:1:{i:0;s:6:"Guinea";}s:2:"GP";a:1:{i:0;s:10:"Guadeloupe";}s:2:"GQ";a:1:{i:0;s:18:"Rovníková Guinea";}s:2:"GR";a:1:{i:0;s:6:"Řecko";}s:2:"GS";a:1:{i:0;s:45:"Jižní Georgie a Jižní Sandwichovy ostrovy";}s:2:"GT";a:1:{i:0;s:9:"Guatemala";}s:2:"GU";a:1:{i:0;s:4:"Guam";}s:2:"GW";a:1:{i:0;s:13:"Guinea-Bissau";}s:2:"GY";a:1:{i:0;s:6:"Guyana";}s:2:"HK";a:1:{i:0;s:52:"Hongkong, zvláštní administrativní oblast Číny";}s:2:"HM";a:1:{i:0;s:24:"Ostrovy Heard a McDonald";}s:2:"HN";a:1:{i:0;s:8:"Honduras";}s:2:"HR";a:1:{i:0;s:10:"Chorvatsko";}s:2:"HT";a:1:{i:0;s:5:"Haiti";}s:2:"HU";a:1:{i:0;s:9:"Maďarsko";}s:2:"ID";a:1:{i:0;s:10:"Indonésie";}s:2:"IE";a:1:{i:0;s:5:"Irsko";}s:2:"IL";a:1:{i:0;s:6:"Izrael";}s:2:"IN";a:1:{i:0;s:5:"Indie";}s:2:"IO";a:1:{i:0;s:36:"Britské území v Indickém oceánu";}s:2:"IQ";a:1:{i:0;s:5:"Irák";}s:2:"IR";a:1:{i:0;s:6:"Írán";}s:2:"IS";a:1:{i:0;s:6:"Island";}s:2:"IT";a:1:{i:0;s:7:"Itálie";}s:2:"JM";a:1:{i:0;s:7:"Jamajka";}s:2:"JO";a:1:{i:0;s:10:"Jordánsko";}s:2:"JP";a:1:{i:0;s:8:"Japonsko";}s:2:"KE";a:1:{i:0;s:5:"Keňa";}s:2:"KG";a:1:{i:0;s:11:"Kyrgyzstán";}s:2:"KH";a:1:{i:0;s:9:"Kambodža";}s:2:"KI";a:1:{i:0;s:8:"Kiribati";}s:2:"KM";a:1:{i:0;s:6:"Komory";}s:2:"KN";a:1:{i:0;s:20:"Svatý Kitts a Nevis";}s:2:"KP";a:1:{i:0;s:14:"Severní Korea";}s:2:"KR";a:1:{i:0;s:13:"Jižní Korea";}s:2:"KW";a:1:{i:0;s:6:"Kuvajt";}s:2:"KY";a:1:{i:0;s:18:"Kajmanské ostrovy";}s:2:"KZ";a:1:{i:0;s:11:"Kazachstán";}s:2:"LA";a:1:{i:0;s:36:"Lidově demokratická republika Laos";}s:2:"LB";a:1:{i:0;s:7:"Libanon";}s:2:"LC";a:1:{i:0;s:12:"Svatá Lucie";}s:2:"LI";a:1:{i:0;s:16:"LichtenÅ¡tejnsko";}s:2:"LK";a:1:{i:0;s:10:"Srí Lanka";}s:2:"LR";a:1:{i:0;s:8:"Libérie";}s:2:"LS";a:1:{i:0;s:7:"Lesotho";}s:2:"LT";a:1:{i:0;s:5:"Litva";}s:2:"LU";a:1:{i:0;s:11:"Lucembursko";}s:2:"LV";a:1:{i:0;s:9:"LotyÅ¡sko";}s:2:"LY";a:1:{i:0;s:5:"Libye";}s:2:"MA";a:1:{i:0;s:6:"Maroko";}s:2:"MC";a:1:{i:0;s:6:"Monako";}s:2:"MD";a:1:{i:0;s:20:"Moldavsko, republika";}s:2:"MG";a:1:{i:0;s:10:"Madagaskar";}s:2:"MH";a:1:{i:0;s:19:"Marshallovy ostrovy";}s:2:"MK";a:1:{i:0;s:9:"Macedonia";}s:2:"ML";a:1:{i:0;s:4:"Mali";}s:2:"MM";a:1:{i:0;s:15:"Myanmar (Burma)";}s:2:"MN";a:1:{i:0;s:9:"Mongolsko";}s:2:"MO";a:1:{i:0;s:19:"Macao S.A.R., China";}s:2:"MP";a:1:{i:0;s:16:"Severní Mariany";}s:2:"MQ";a:1:{i:0;s:8:"Martinik";}s:2:"MR";a:1:{i:0;s:11:"Mauritánie";}s:2:"MS";a:1:{i:0;s:10:"Montserrat";}s:2:"MT";a:1:{i:0;s:5:"Malta";}s:2:"MU";a:1:{i:0;s:9:"Mauricius";}s:2:"MV";a:1:{i:0;s:8:"Maladivy";}s:2:"MW";a:1:{i:0;s:6:"Malawi";}s:2:"MX";a:1:{i:0;s:6:"Mexiko";}s:2:"MY";a:1:{i:0;s:8:"Malajsie";}s:2:"MZ";a:1:{i:0;s:8:"Mosambik";}s:2:"NA";a:1:{i:0;s:7:"Namibie";}s:2:"NC";a:1:{i:0;s:15:"Nová Kaledonie";}s:2:"NE";a:1:{i:0;s:5:"Niger";}s:2:"NF";a:1:{i:0;s:7:"Norfolk";}s:2:"NG";a:1:{i:0;s:8:"Nigérie";}s:2:"NI";a:1:{i:0;s:9:"Nikaragua";}s:2:"NL";a:1:{i:0;s:10:"Nizozemsko";}s:2:"NO";a:1:{i:0;s:6:"Norsko";}s:2:"NP";a:1:{i:0;s:6:"Nepál";}s:2:"NR";a:1:{i:0;s:5:"Nauru";}s:2:"NU";a:1:{i:0;s:4:"Niue";}s:2:"NZ";a:1:{i:0;s:13:"Nový Zéland";}s:2:"OM";a:1:{i:0;s:5:"Omán";}s:2:"PA";a:1:{i:0;s:6:"Panama";}s:2:"PE";a:1:{i:0;s:4:"Peru";}s:2:"PF";a:1:{i:0;s:23:"Francouzská Polynésie";}s:2:"PG";a:1:{i:0;s:18:"Papua-Nová Guinea";}s:2:"PH";a:1:{i:0;s:9:"Filipíny";}s:2:"PK";a:1:{i:0;s:10:"Pákistán";}s:2:"PL";a:1:{i:0;s:6:"Polsko";}s:2:"PM";a:1:{i:0;s:24:"Svatý Pierre a Miquelon";}s:2:"PN";a:1:{i:0;s:8:"Pitcairn";}s:2:"PR";a:1:{i:0;s:9:"Portoriko";}s:2:"PS";a:1:{i:0;s:21:"Palestinian Territory";}s:2:"PT";a:1:{i:0;s:11:"Portugalsko";}s:2:"PW";a:1:{i:0;s:5:"Palau";}s:2:"PY";a:1:{i:0;s:8:"Paraguay";}s:2:"QA";a:1:{i:0;s:5:"Katar";}s:2:"RE";a:1:{i:0;s:8:"Réunion";}s:2:"RO";a:1:{i:0;s:8:"Rumunsko";}s:2:"RU";a:1:{i:0;s:5:"Rusko";}s:2:"RW";a:1:{i:0;s:6:"Rwanda";}s:2:"SA";a:1:{i:0;s:17:"Saúdská Arábie";}s:2:"SB";a:1:{i:0;s:20:"Å alamounovy ostrovy";}s:2:"SC";a:1:{i:0;s:8:"Seychely";}s:2:"SD";a:1:{i:0;s:7:"Súdán";}s:2:"SE";a:1:{i:0;s:9:"Å védsko";}s:2:"SG";a:1:{i:0;s:8:"Singapur";}s:2:"SH";a:1:{i:0;s:13:"Svatá Helena";}s:2:"SI";a:1:{i:0;s:9:"Slovinsko";}s:2:"SJ";a:1:{i:0;s:20:"Svalbard a Jan Mayen";}s:2:"SK";a:1:{i:0;s:9:"Slovensko";}s:2:"SL";a:1:{i:0;s:12:"Sierra Leone";}s:2:"SM";a:1:{i:0;s:10:"San Marino";}s:2:"SN";a:1:{i:0;s:7:"Senegal";}s:2:"SO";a:1:{i:0;s:9:"Somálsko";}s:2:"SP";a:1:{i:0;s:6:"Serbia";}s:2:"SR";a:1:{i:0;s:7:"Surinam";}s:2:"ST";a:1:{i:0;s:14:"Svatý Tomáš";}s:2:"SV";a:1:{i:0;s:11:"El Salvador";}s:2:"SY";a:1:{i:0;s:6:"Sýrie";}s:2:"SZ";a:1:{i:0;s:9:"Svazijsko";}s:2:"TC";a:1:{i:0;s:22:"Ostrovy Caicos a Turks";}s:2:"TD";a:1:{i:0;s:4:"Čad";}s:2:"TF";a:1:{i:0;s:30:"Francouzská jižní teritoria";}s:2:"TG";a:1:{i:0;s:4:"Togo";}s:2:"TH";a:1:{i:0;s:7:"Thajsko";}s:2:"TJ";a:1:{i:0;s:14:"Tádžikistán";}s:2:"TK";a:1:{i:0;s:7:"Tokelau";}s:2:"TL";a:1:{i:0;s:16:"Východní Timor";}s:2:"TM";a:1:{i:0;s:13:"Turkmenistán";}s:2:"TN";a:1:{i:0;s:7:"Tunisko";}s:2:"TO";a:1:{i:0;s:5:"Tonga";}s:2:"TR";a:1:{i:0;s:7:"Turecko";}s:2:"TT";a:1:{i:0;s:17:"Trinidad a Tobago";}s:2:"TV";a:1:{i:0;s:6:"Tuvalu";}s:2:"TW";a:1:{i:0;s:9:"Tchaj-wan";}s:2:"TZ";a:1:{i:0;s:8:"Tanzanie";}s:2:"UA";a:1:{i:0;s:8:"Ukrajina";}s:2:"UG";a:1:{i:0;s:6:"Uganda";}s:2:"UM";a:1:{i:0;s:28:"Menší odlehlé ostrovy USA";}s:2:"US";a:1:{i:0;s:15:"Spojené státy";}s:2:"UY";a:1:{i:0;s:7:"Uruguay";}s:2:"UZ";a:1:{i:0;s:11:"Uzbekistán";}s:2:"VA";a:1:{i:0;s:13:"Svatý stolec";}s:2:"VC";a:1:{i:0;s:26:"Svatý Vincent a Grenadiny";}s:2:"VE";a:1:{i:0;s:9:"Venezuela";}s:2:"VG";a:1:{i:0;s:26:"Britské Panenské ostrovy";}s:2:"VI";a:1:{i:0;s:27:"Americké Panenské ostrovy";}s:2:"VN";a:1:{i:0;s:7:"Vietnam";}s:2:"VU";a:1:{i:0;s:7:"Vanuatu";}s:2:"WF";a:1:{i:0;s:15:"Wallis a Futuna";}s:2:"WS";a:1:{i:0;s:5:"Samoa";}s:2:"YE";a:1:{i:0;s:5:"Jemen";}s:2:"YT";a:1:{i:0;s:7:"Mayotte";}s:2:"YU";a:1:{i:0;s:11:"Jugoslávie";}s:2:"ZA";a:1:{i:0;s:14:"Jižní Afrika";}s:2:"ZM";a:1:{i:0;s:6:"Zambie";}s:2:"ZW";a:1:{i:0;s:8:"Zimbabwe";}}s:10:"Currencies";a:360:{s:3:"ADD";a:2:{i:0;s:3:"ADD";i:1;s:16:"Diner andorrský";}s:3:"ADP";a:2:{i:0;s:3:"ADP";i:1;s:17:"Peseta andorrská";}s:3:"AED";a:2:{i:0;s:3:"AED";i:1;s:10:"Dirham SAE";}s:3:"AFA";a:2:{i:0;s:3:"AFA";i:1;s:19:"Afghán (1927-2002)";}s:3:"AFN";a:2:{i:0;s:2:"Af";i:1;s:7:"Afghán";}s:3:"AIF";a:2:{i:0;s:3:"AIF";i:1;s:22:"Affars and Issas Franc";}s:3:"ALK";a:2:{i:0;s:3:"ALK";i:1;s:15:"Lek (1946-1961)";}s:3:"ALL";a:2:{i:0;s:3:"lek";i:1;s:3:"Lek";}s:3:"ALV";a:2:{i:0;s:3:"ALV";i:1;s:19:"Albanian Lek Valute";}s:3:"AMD";a:2:{i:0;s:4:"dram";i:1;s:15:"Dram arménský";}s:3:"ANG";a:2:{i:0;s:5:"NA f.";i:1;s:26:"Zlatý Nizozemských Antil";}s:3:"AOA";a:2:{i:0;s:3:"AOA";i:1;s:6:"Kwanza";}s:3:"AOK";a:2:{i:0;s:3:"AOK";i:1;s:18:"Kwanza (1977-1990)";}s:3:"AON";a:2:{i:0;s:3:"AON";i:1;s:24:"Kwanza nová (1990-2000)";}s:3:"AOR";a:2:{i:0;s:3:"AOR";i:1;s:29:"Kwanza reajustado (1995-1999)";}s:3:"AOS";a:2:{i:0;s:3:"AOS";i:1;s:16:"Escudo angolské";}s:3:"ARA";a:2:{i:0;s:3:"ARA";i:1;s:7:"Austral";}s:3:"ARM";a:2:{i:0;s:3:"ARM";i:1;s:33:"Peso argentinské Moneda Nacional";}s:3:"ARP";a:2:{i:0;s:3:"ARP";i:1;s:29:"Peso argentinské (1983-1985)";}s:3:"ARS";a:2:{i:0;s:4:"Arg$";i:1;s:17:"Peso argentinské";}s:3:"ATS";a:2:{i:0;s:3:"ATS";i:1;s:7:"Å ilink";}s:3:"AUD";a:2:{i:0;s:2:"$A";i:1;s:17:"Dolar australský";}s:3:"AUP";a:2:{i:0;s:3:"AUP";i:1;s:17:"Libra australská";}s:3:"AWG";a:2:{i:0;s:3:"AWG";i:1;s:15:"Zlatý arubský";}s:3:"AZM";a:2:{i:0;s:3:"AZM";i:1;s:24:"Manat ázerbajdžánský";}s:3:"BAD";a:2:{i:0;s:3:"BAD";i:1;s:26:"Dinár Bosny a Hercegoviny";}s:3:"BAM";a:2:{i:0;s:2:"KM";i:1;s:20:"Marka konvertibilní";}s:3:"BAN";a:2:{i:0;s:3:"BAN";i:1;s:32:"Nový Dinár Bosny a Hercegoviny";}s:3:"BBD";a:2:{i:0;s:4:"BDS$";i:1;s:17:"Dolar barbadoský";}s:3:"BDT";a:2:{i:0;s:2:"Tk";i:1;s:4:"Taka";}s:3:"BEC";a:2:{i:0;s:3:"BEC";i:1;s:20:"Frank konvertibilní";}s:3:"BEF";a:2:{i:0;s:2:"BF";i:1;s:15:"Frank belgický";}s:3:"BEL";a:2:{i:0;s:3:"BEL";i:1;s:16:"Frank finanční";}s:3:"BGL";a:2:{i:0;s:3:"lev";i:1;s:3:"Lev";}s:3:"BGM";a:2:{i:0;s:3:"BGM";i:1;s:29:"Lev Bulharský socialistický";}s:3:"BGN";a:2:{i:0;s:3:"BGN";i:1;s:14:"Lev Bulharský";}s:3:"BGO";a:2:{i:0;s:3:"BGO";i:1;s:26:"Lev Bulharský (1879-1952)";}s:3:"BHD";a:2:{i:0;s:2:"BD";i:1;s:18:"Dinár bahrajnský";}s:3:"BIF";a:2:{i:0;s:3:"Fbu";i:1;s:16:"Frank burundský";}s:3:"BMD";a:2:{i:0;s:4:"Ber$";i:1;s:16:"Dolar bermudský";}s:3:"BMP";a:2:{i:0;s:3:"BMP";i:1;s:16:"Libra bermudská";}s:3:"BND";a:2:{i:0;s:3:"BND";i:1;s:16:"Dolar brunejský";}s:3:"BOB";a:2:{i:0;s:2:"Bs";i:1;s:9:"Boliviano";}s:3:"BOL";a:2:{i:0;s:3:"BOL";i:1;s:21:"Boliviano (1863-1962)";}s:3:"BOP";a:2:{i:0;s:3:"BOP";i:1;s:4:"Peso";}s:3:"BOV";a:2:{i:0;s:3:"BOV";i:1;s:5:"Mvdol";}s:3:"BRB";a:2:{i:0;s:3:"BRB";i:1;s:20:"Cruzeiro (1967-1986)";}s:3:"BRC";a:2:{i:0;s:3:"BRC";i:1;s:7:"Cruzado";}s:3:"BRE";a:2:{i:0;s:3:"BRE";i:1;s:20:"Cruzeiro (1990-1993)";}s:3:"BRL";a:2:{i:0;s:2:"R$";i:1;s:15:"Real brazilský";}s:3:"BRN";a:2:{i:0;s:3:"BRN";i:1;s:13:"Cruzado nové";}s:3:"BRR";a:2:{i:0;s:3:"BRR";i:1;s:13:"Cruzeiro real";}s:3:"BRZ";a:2:{i:0;s:3:"BRZ";i:1;s:20:"Cruzeiro (1942-1967)";}s:3:"BSD";a:2:{i:0;s:3:"BSD";i:1;s:15:"Dolar bahamský";}s:3:"BSP";a:2:{i:0;s:3:"BSP";i:1;s:15:"Libra bahamská";}s:3:"BTN";a:2:{i:0;s:2:"Nu";i:1;s:8:"Ngultrum";}s:3:"BTR";a:2:{i:0;s:3:"BTR";i:1;s:18:"Rupie bhútánská";}s:3:"BUK";a:2:{i:0;s:3:"BUK";i:1;s:13:"Kyat barmský";}s:3:"BUR";a:2:{i:0;s:3:"BUR";i:1;s:14:"Rupie barmská";}s:3:"BWP";a:2:{i:0;s:3:"BWP";i:1;s:4:"Pula";}s:3:"BYB";a:2:{i:0;s:3:"BYB";i:1;s:34:"Rubl nový běloruský (1994-1999)";}s:3:"BYL";a:2:{i:0;s:3:"BYL";i:1;s:28:"Rubl běloruský (1992-1994)";}s:3:"BYR";a:2:{i:0;s:3:"Rbl";i:1;s:16:"Rubl běloruský";}s:3:"BZD";a:2:{i:0;s:3:"BZ$";i:1;s:15:"Dolar belizský";}s:3:"BZH";a:2:{i:0;s:3:"BZH";i:1;s:26:"Dolar Britského Hondurasu";}s:3:"CAD";a:2:{i:0;s:4:"Can$";i:1;s:15:"Dolar kanadský";}s:3:"CDF";a:2:{i:0;s:3:"CDF";i:1;s:15:"Frank konžský";}s:3:"CDG";a:2:{i:0;s:3:"CDG";i:1;s:25:"Frank Konžské republiky";}s:3:"CDL";a:2:{i:0;s:3:"CDL";i:1;s:15:"Zaire konžský";}s:3:"CFF";a:2:{i:0;s:3:"CFF";i:1;s:35:"Frank Středoafrické republiky CFA";}s:3:"CHF";a:2:{i:0;s:3:"SwF";i:1;s:18:"Frank Å¡výcarský";}s:3:"CKD";a:2:{i:0;s:3:"CKD";i:1;s:25:"Dolar Cookových ostrovů";}s:3:"CLC";a:2:{i:0;s:3:"CLC";i:1;s:15:"Condor chilský";}s:3:"CLE";a:2:{i:0;s:3:"CLE";i:1;s:15:"Escudo chilské";}s:3:"CLF";a:2:{i:0;s:3:"CLF";i:1;s:19:"Unidades de fomento";}s:3:"CLP";a:2:{i:0;s:3:"Ch$";i:1;s:13:"Peso chilské";}s:3:"CMF";a:2:{i:0;s:3:"CMF";i:1;s:21:"Frank kamerunský CFA";}s:3:"CNP";a:2:{i:0;s:3:"CNP";i:1;s:17:"Juan jen min piao";}s:3:"CNX";a:2:{i:0;s:3:"CNX";i:1;s:19:"Dolar lidové banky";}s:3:"CNY";a:2:{i:0;s:1:"Y";i:1;s:13:"Juan renminbi";}s:3:"COB";a:2:{i:0;s:3:"COB";i:1;s:17:"Peso kolumbijské";}s:3:"COF";a:2:{i:0;s:3:"COF";i:1;s:19:"Krank konžský CFA";}s:3:"COP";a:2:{i:0;s:4:"Col$";i:1;s:17:"Peso kolumbijské";}s:3:"CRC";a:2:{i:0;s:1:"C";i:1;s:18:"Colón kostarický";}s:3:"CSC";a:2:{i:0;s:3:"CSC";i:1;s:23:"Koruna československá";}s:3:"CSK";a:2:{i:0;s:3:"CSK";i:1;s:23:"Koruna československá";}s:3:"CUP";a:2:{i:0;s:3:"CUP";i:1;s:15:"Peso kubánské";}s:3:"CVE";a:2:{i:0;s:5:"CVEsc";i:1;s:18:"Escudo kapverdské";}s:3:"CWG";a:2:{i:0;s:3:"CWG";i:1;s:14:"Zlatý Curacao";}s:3:"CYP";a:2:{i:0;s:2:"£";i:1;s:15:"Libra kyperská";}s:3:"CZK";a:2:{i:0;s:3:"Kč";i:1;s:14:"Koruna česká";}s:3:"DDM";a:2:{i:0;s:3:"DDM";i:1;s:9:"Marka NDR";}s:3:"DEM";a:2:{i:0;s:3:"DEM";i:1;s:15:"Marka německá";}s:3:"DES";a:2:{i:0;s:3:"DES";i:1;s:19:"Sperrmark německá";}s:3:"DJF";a:2:{i:0;s:2:"DF";i:1;s:17:"Frank džibutský";}s:3:"DKK";a:2:{i:0;s:3:"DKr";i:1;s:15:"Koruna dánská";}s:3:"DOP";a:2:{i:0;s:3:"RD$";i:1;s:19:"Peso dominikánské";}s:3:"DZD";a:2:{i:0;s:2:"DA";i:1;s:18:"Dinár alžírský";}s:3:"DZF";a:2:{i:0;s:3:"DZF";i:1;s:23:"Frank nový alžírský";}s:3:"DZG";a:2:{i:0;s:3:"DZG";i:1;s:26:"Frank alžírský germinal";}s:3:"ECS";a:2:{i:0;s:3:"ECS";i:1;s:18:"Sucre ekvádorský";}s:3:"ECV";a:2:{i:0;s:3:"ECV";i:1;s:39:"Ecuador Unidad de Valor Constante (UVC)";}s:3:"EEK";a:2:{i:0;s:3:"EEK";i:1;s:5:"Kroon";}s:3:"EGP";a:2:{i:0;s:3:"EGP";i:1;s:15:"Libra egyptská";}s:3:"ERN";a:2:{i:0;s:3:"ERN";i:1;s:5:"Nakfa";}s:3:"ESP";a:2:{i:0;s:3:"ESP";i:1;s:19:"Peseta Å¡panělská";}s:3:"ETB";a:2:{i:0;s:2:"Br";i:1;s:14:"Birr etiopský";}s:3:"ETD";a:2:{i:0;s:3:"ETD";i:1;s:15:"Dolar etiopský";}s:3:"EUR";a:2:{i:0;s:3:"€";i:1;s:4:"Euro";}s:3:"FIM";a:2:{i:0;s:3:"FIM";i:1;s:6:"Markka";}s:3:"FIN";a:2:{i:0;s:3:"FIN";i:1;s:18:"Markka (1860-1962)";}s:3:"FJD";a:2:{i:0;s:2:"F$";i:1;s:17:"Dolar fidžijský";}s:3:"FJP";a:2:{i:0;s:3:"FJP";i:1;s:17:"Libra fidžijská";}s:3:"FKP";a:2:{i:0;s:3:"FKP";i:1;s:18:"Libra falklandská";}s:3:"FOK";a:2:{i:0;s:3:"FOK";i:1;s:26:"Koruna Faerských ostrovů";}s:3:"FRF";a:2:{i:0;s:3:"FRF";i:1;s:18:"Frank francouzský";}s:3:"FRG";a:2:{i:0;s:3:"FRG";i:1;s:42:"Frank francouzský germinal/Frank poincare";}s:3:"GAF";a:2:{i:0;s:3:"GAF";i:1;s:19:"Frank gabonský CFA";}s:3:"GBP";a:2:{i:0;s:2:"£";i:1;s:17:"Libra Å¡terlinků";}s:3:"GEK";a:2:{i:0;s:3:"GEK";i:1;s:20:"Georgian Kupon Larit";}s:3:"GEL";a:2:{i:0;s:4:"lari";i:1;s:4:"Lari";}s:3:"GHC";a:2:{i:0;s:3:"GHC";i:1;s:4:"Cedi";}s:3:"GHO";a:2:{i:0;s:3:"GHO";i:1;s:14:"Ghana Old Cedi";}s:3:"GHP";a:2:{i:0;s:3:"GHP";i:1;s:14:"Libra ghanská";}s:3:"GHR";a:2:{i:0;s:3:"GHR";i:1;s:19:"Ghana Revalued Cedi";}s:3:"GIP";a:2:{i:0;s:3:"GIP";i:1;s:19:"Libra gibraltarská";}s:3:"GLK";a:2:{i:0;s:3:"GLK";i:1;s:15:"Greenland Krone";}s:3:"GMD";a:2:{i:0;s:3:"GMD";i:1;s:6:"Dalasi";}s:3:"GMP";a:2:{i:0;s:3:"GMP";i:1;s:16:"Libra gambijská";}s:3:"GNF";a:2:{i:0;s:2:"GF";i:1;s:16:"Frank guinejský";}s:3:"GNI";a:2:{i:0;s:3:"GNI";i:1;s:28:"Frank guinejský (1960-1972)";}s:3:"GNS";a:2:{i:0;s:3:"GNS";i:1;s:11:"Guinea Syli";}s:3:"GPF";a:2:{i:0;s:3:"GPF";i:1;s:19:"Frank guadeloupský";}s:3:"GQE";a:2:{i:0;s:3:"GQE";i:1;s:33:"Equatorial Guinea Ekwele Guineana";}s:3:"GQF";a:2:{i:0;s:3:"GQF";i:1;s:24:"Equatorial Guinea Franco";}s:3:"GQP";a:2:{i:0;s:3:"GQP";i:1;s:33:"Equatorial Guinea Peseta Guineana";}s:3:"GRD";a:2:{i:0;s:3:"GRD";i:1;s:7:"Drachma";}s:3:"GRN";a:2:{i:0;s:3:"GRN";i:1;s:21:"Drachma nová řecká";}s:3:"GTQ";a:2:{i:0;s:1:"Q";i:1;s:7:"Quetzal";}s:3:"GUF";a:2:{i:0;s:3:"GUF";i:1;s:26:"French Guyana Franc Guiana";}s:3:"GWE";a:2:{i:0;s:3:"GWE";i:1;s:17:"Escudo guinejské";}s:3:"GWM";a:2:{i:0;s:3:"GWM";i:1;s:26:"Portuguese Guinea Mil Reis";}s:3:"GWP";a:2:{i:0;s:3:"GWP";i:1;s:19:"Peso Guinnea-Bissau";}s:3:"GYD";a:2:{i:0;s:2:"G$";i:1;s:15:"Dolar guyanský";}s:3:"HKD";a:2:{i:0;s:3:"HK$";i:1;s:18:"Dolar hongkongský";}s:3:"HNL";a:2:{i:0;s:1:"L";i:1;s:7:"Lempira";}s:3:"HRD";a:2:{i:0;s:3:"HRD";i:1;s:17:"Dinar chorvatský";}s:3:"HRK";a:2:{i:0;s:3:"HRK";i:1;s:16:"Kuna chorvatská";}s:3:"HTG";a:2:{i:0;s:3:"HTG";i:1;s:6:"Gourde";}s:3:"HUF";a:2:{i:0;s:2:"Ft";i:1;s:6:"Forint";}s:3:"IBP";a:2:{i:0;s:3:"IBP";i:1;s:18:"Libra severoirská";}s:3:"IDG";a:2:{i:0;s:3:"IDG";i:1;s:23:"Indonesian Nica Guilder";}s:3:"IDJ";a:2:{i:0;s:3:"IDJ";i:1;s:22:"Indonesian Java Rupiah";}s:3:"IDN";a:2:{i:0;s:3:"IDN";i:1;s:21:"Indonesian New Rupiah";}s:3:"IDR";a:2:{i:0;s:2:"Rp";i:1;s:18:"Rupie indonézská";}s:3:"IEP";a:2:{i:0;s:4:"IR£";i:1;s:12:"Libra irská";}s:3:"ILL";a:2:{i:0;s:3:"ILL";i:1;s:17:"Å ekel izraelský";}s:3:"ILP";a:2:{i:0;s:3:"ILP";i:1;s:16:"Libra izraelská";}s:3:"ILS";a:2:{i:0;s:3:"ILS";i:1;s:23:"Å ekel nový izraelský";}s:3:"IMP";a:2:{i:0;s:3:"IMP";i:1;s:28:"Libra Å¡terlinků Ostrov Man";}s:3:"INR";a:2:{i:0;s:3:"INR";i:1;s:14:"Rupie indická";}s:3:"IQD";a:2:{i:0;s:2:"ID";i:1;s:15:"Dinár irácký";}s:3:"IRR";a:2:{i:0;s:2:"RI";i:1;s:17:"Rijál íránský";}s:3:"ISK";a:2:{i:0;s:3:"ISK";i:1;s:17:"Koruna islandská";}s:3:"ITL";a:2:{i:0;s:3:"₤";i:1;s:13:"Lira italská";}s:3:"JEP";a:2:{i:0;s:3:"JEP";i:1;s:24:"Libra Å¡terlinků Jersey";}s:3:"JMD";a:2:{i:0;s:2:"J$";i:1;s:15:"Dolar jamajský";}s:3:"JMP";a:2:{i:0;s:3:"JMP";i:1;s:15:"Libra jamajská";}s:3:"JOD";a:2:{i:0;s:2:"JD";i:1;s:18:"Dinár jordánský";}s:3:"JPY";a:2:{i:0;s:2:"Â¥";i:1;s:3:"Jen";}s:3:"KES";a:2:{i:0;s:4:"K Sh";i:1;s:16:"Å ilink keňský";}s:3:"KGS";a:2:{i:0;s:3:"som";i:1;s:3:"Som";}s:3:"KHO";a:2:{i:0;s:3:"KHO";i:1;s:11:"Riel starý";}s:3:"KHR";a:2:{i:0;s:2:"CR";i:1;s:4:"Riel";}s:3:"KID";a:2:{i:0;s:3:"KID";i:1;s:17:"Dolar kiribatský";}s:3:"KMF";a:2:{i:0;s:2:"CF";i:1;s:15:"Frank komorský";}s:3:"KPW";a:2:{i:0;s:3:"KPW";i:1;s:19:"Won severokorejský";}s:3:"KRH";a:2:{i:0;s:3:"KRH";i:1;s:18:"Hwan jihokorejský";}s:3:"KRO";a:2:{i:0;s:3:"KRO";i:1;s:24:"Won starý jihokorejský";}s:3:"KRW";a:2:{i:0;s:3:"KRW";i:1;s:17:"Won jihokorejský";}s:3:"KWD";a:2:{i:0;s:2:"KD";i:1;s:17:"Dinár kuvajtský";}s:3:"KYD";a:2:{i:0;s:3:"KYD";i:1;s:27:"Dolar Kajmanských ostrovů";}s:3:"KZR";a:2:{i:0;s:3:"KZR";i:1;s:15:"Rubl kazaÅ¡ský";}s:3:"KZT";a:2:{i:0;s:1:"T";i:1;s:5:"Tenge";}s:3:"LAK";a:2:{i:0;s:3:"LAK";i:1;s:3:"Kip";}s:3:"LBP";a:2:{i:0;s:2:"LL";i:1;s:17:"Libra libanonská";}s:3:"LIF";a:2:{i:0;s:3:"LIF";i:1;s:23:"Frank lichtenÅ¡tejnský";}s:3:"LKR";a:2:{i:0;s:5:"SL Re";i:1;s:17:"Rupie srílanská";}s:3:"LNR";a:2:{i:0;s:3:"LNR";i:1;s:16:"Rupie cejlonská";}s:3:"LRD";a:2:{i:0;s:3:"LRD";i:1;s:17:"Dolar liberijský";}s:3:"LSL";a:2:{i:0;s:1:"M";i:1;s:4:"Loti";}s:3:"LTL";a:2:{i:0;s:3:"LTL";i:1;s:15:"Litus litevský";}s:3:"LTT";a:2:{i:0;s:3:"LTT";i:1;s:5:"Talon";}s:3:"LUF";a:2:{i:0;s:3:"LUF";i:1;s:18:"Frank lucemburský";}s:3:"LVL";a:2:{i:0;s:3:"LVL";i:1;s:14:"Lat lotyÅ¡ský";}s:3:"LVR";a:2:{i:0;s:3:"LVR";i:1;s:15:"Rubl lotyÅ¡ský";}s:3:"LYD";a:2:{i:0;s:2:"LD";i:1;s:16:"Dinár lybijský";}s:3:"LYP";a:2:{i:0;s:3:"LYP";i:1;s:15:"Libra lybijská";}s:3:"MAD";a:2:{i:0;s:3:"MAD";i:1;s:15:"Dirham marocký";}s:3:"MAF";a:2:{i:0;s:3:"MAF";i:1;s:14:"Frank marocký";}s:3:"MCF";a:2:{i:0;s:3:"MCF";i:1;s:22:"Frank nouveau monacký";}s:3:"MCG";a:2:{i:0;s:3:"MCG";i:1;s:23:"Frank monacký germinal";}s:3:"MDC";a:2:{i:0;s:3:"MDC";i:1;s:18:"Moldovan Leu Cupon";}s:3:"MDL";a:2:{i:0;s:3:"MDL";i:1;s:14:"Leu moldavský";}s:3:"MDR";a:2:{i:0;s:3:"MDR";i:1;s:20:"Moldovan Ruble Cupon";}s:3:"MGA";a:2:{i:0;s:3:"MGA";i:1;s:21:"Ariary madagaskarský";}s:3:"MGF";a:2:{i:0;s:3:"MGF";i:1;s:20:"Frank madagaskarský";}s:3:"MHD";a:2:{i:0;s:3:"MHD";i:1;s:29:"Dolar Marshallových ostrovů";}s:3:"MKD";a:2:{i:0;s:4:"MDen";i:1;s:6:"Denár";}s:3:"MKN";a:2:{i:0;s:3:"MKN";i:1;s:18:"Denár (1992-1993)";}s:3:"MLF";a:2:{i:0;s:3:"MLF";i:1;s:15:"Frank malijský";}s:3:"MMK";a:2:{i:0;s:3:"MMK";i:1;s:4:"Kyat";}s:3:"MNT";a:2:{i:0;s:3:"Tug";i:1;s:6:"Tugrik";}s:3:"MOP";a:2:{i:0;s:3:"MOP";i:1;s:6:"Pataca";}s:3:"MQF";a:2:{i:0;s:3:"MQF";i:1;s:18:"Frank martinikský";}s:3:"MRO";a:2:{i:0;s:2:"UM";i:1;s:7:"Ouguiya";}s:3:"MTL";a:2:{i:0;s:2:"Lm";i:1;s:13:"Lira maltská";}s:3:"MTP";a:2:{i:0;s:3:"MTP";i:1;s:14:"Libra maltská";}s:3:"MUR";a:2:{i:0;s:3:"MUR";i:1;s:18:"Rupie mauricijská";}s:3:"MVP";a:2:{i:0;s:3:"MVP";i:1;s:17:"Rupie maledivská";}s:3:"MVR";a:2:{i:0;s:3:"MVR";i:1;s:7:"Rufiyaa";}s:3:"MWK";a:2:{i:0;s:2:"MK";i:1;s:6:"Kwacha";}s:3:"MWP";a:2:{i:0;s:3:"MWP";i:1;s:17:"Libra malawijská";}s:3:"MXN";a:2:{i:0;s:4:"MEX$";i:1;s:13:"Peso mexické";}s:3:"MXP";a:2:{i:0;s:3:"MXP";i:1;s:37:"Peso stříbrné mexické (1861-1992)";}s:3:"MXV";a:2:{i:0;s:3:"MXV";i:1;s:33:"Mexican Unidad de Inversion (UDI)";}s:3:"MYR";a:2:{i:0;s:2:"RM";i:1;s:21:"Ringgit malajskijský";}s:3:"MZE";a:2:{i:0;s:3:"MZE";i:1;s:16:"Escudo Mosambiku";}s:3:"MZM";a:2:{i:0;s:2:"Mt";i:1;s:7:"Metical";}s:3:"NAD";a:2:{i:0;s:2:"N$";i:1;s:17:"Dolar namibijský";}s:3:"NCF";a:2:{i:0;s:3:"NCF";i:1;s:30:"Frank Nové Kaledonie germinal";}s:3:"NGN";a:2:{i:0;s:3:"NGN";i:1;s:5:"Naira";}s:3:"NGP";a:2:{i:0;s:3:"NGP";i:1;s:17:"Libra nigerijská";}s:3:"NHF";a:2:{i:0;s:3:"NHF";i:1;s:24:"Frank Nových Hebrid CFP";}s:3:"NIC";a:2:{i:0;s:3:"NIC";i:1;s:7:"Cordoba";}s:3:"NIG";a:2:{i:0;s:3:"NIG";i:1;s:14:"Cordoba zlatá";}s:3:"NIO";a:2:{i:0;s:3:"NIO";i:1;s:11:"Cordoba oro";}s:3:"NLG";a:2:{i:0;s:3:"NLG";i:1;s:17:"Zlatý holandský";}s:3:"NOK";a:2:{i:0;s:3:"NKr";i:1;s:14:"Koruna norská";}s:3:"NPR";a:2:{i:0;s:3:"Nrs";i:1;s:16:"Rupie nepálská";}s:3:"NZD";a:2:{i:0;s:3:"$NZ";i:1;s:21:"Dolar novozélandský";}s:3:"NZP";a:2:{i:0;s:3:"NZP";i:1;s:21:"Libra novozélandská";}s:3:"OMR";a:2:{i:0;s:2:"RO";i:1;s:16:"Rijál ománský";}s:3:"OMS";a:2:{i:0;s:3:"OMS";i:1;s:22:"Rijál ománský saidi";}s:3:"PAB";a:2:{i:0;s:3:"PAB";i:1;s:6:"Balboa";}s:3:"PDK";a:2:{i:0;s:3:"PDK";i:1;s:26:"Transdniestria Ruble Kupon";}s:3:"PDN";a:2:{i:0;s:3:"PDN";i:1;s:24:"Transdniestria New Ruble";}s:3:"PDR";a:2:{i:0;s:3:"PDR";i:1;s:20:"Transdniestria Ruble";}s:3:"PEI";a:2:{i:0;s:3:"PEI";i:1;s:4:"Inti";}s:3:"PEN";a:2:{i:0;s:3:"PEN";i:1;s:9:"Nuevo sol";}s:3:"PES";a:2:{i:0;s:3:"PES";i:1;s:3:"Sol";}s:3:"PGK";a:2:{i:0;s:3:"PGK";i:1;s:4:"Kina";}s:3:"PHP";a:2:{i:0;s:3:"PHP";i:1;s:17:"Peso filipínské";}s:3:"PKR";a:2:{i:0;s:3:"Pra";i:1;s:20:"Rupie pákistánská";}s:3:"PLN";a:2:{i:0;s:2:"Zl";i:1;s:6:"Zlotý";}s:3:"PLZ";a:2:{i:0;s:3:"PLZ";i:1;s:18:"Zlotý (1950-1995)";}s:3:"PSP";a:2:{i:0;s:3:"PSP";i:1;s:18:"Libra palestinská";}s:3:"PTC";a:2:{i:0;s:3:"PTC";i:1;s:18:"Conto portugalské";}s:3:"PTE";a:2:{i:0;s:3:"PTE";i:1;s:19:"Escudo portugalské";}s:3:"PYG";a:2:{i:0;s:3:"PYG";i:1;s:7:"Guarani";}s:3:"QAR";a:2:{i:0;s:2:"QR";i:1;s:16:"Rijál katarský";}s:3:"REF";a:2:{i:0;s:3:"REF";i:1;s:13:"Frank Reunion";}s:3:"ROL";a:2:{i:0;s:3:"leu";i:1;s:3:"Lei";}s:3:"RON";a:2:{i:0;s:3:"RON";i:1;s:9:"Lei nový";}s:3:"RUB";a:2:{i:0;s:3:"RUB";i:1;s:11:"Rubl ruský";}s:3:"RUR";a:2:{i:0;s:3:"RUR";i:1;s:23:"Rubl ruský (1991-1998)";}s:3:"RWF";a:2:{i:0;s:3:"RWF";i:1;s:15:"Frank rwandský";}s:3:"SAR";a:2:{i:0;s:3:"SRl";i:1;s:15:"Rijál saudský";}s:3:"SAS";a:2:{i:0;s:3:"SAS";i:1;s:25:"Rijál saudský sovereign";}s:3:"SBD";a:2:{i:0;s:3:"SI$";i:1;s:30:"Dolar Å alamounových ostrovů";}s:3:"SCR";a:2:{i:0;s:2:"SR";i:1;s:17:"Rupie seychelská";}s:3:"SDD";a:2:{i:0;s:3:"SDD";i:1;s:18:"Dinár súdánský";}s:3:"SDP";a:2:{i:0;s:3:"SDP";i:1;s:17:"Libra súdánská";}s:3:"SEK";a:2:{i:0;s:3:"SKr";i:1;s:17:"Koruna Å¡védská";}s:3:"SGD";a:2:{i:0;s:2:"S$";i:1;s:18:"Dolar singapurský";}s:3:"SHP";a:2:{i:0;s:3:"SHP";i:1;s:19:"Libra Svaté Heleny";}s:3:"SIB";a:2:{i:0;s:3:"SIB";i:1;s:19:"Slovenia Tolar Bons";}s:3:"SIT";a:2:{i:0;s:3:"SIT";i:1;s:5:"Tolar";}s:3:"SKK";a:2:{i:0;s:2:"Sk";i:1;s:17:"Koruna slovenská";}s:3:"SML";a:2:{i:0;s:3:"SML";i:1;s:15:"Lira San Marino";}s:3:"SOS";a:2:{i:0;s:7:"So. Sh.";i:1;s:18:"Å ilink somálský";}s:3:"SQS";a:2:{i:0;s:3:"SQS";i:1;s:19:"Somaliland Shilling";}s:3:"SRG";a:2:{i:0;s:2:"Sf";i:1;s:18:"Zlatý surinamský";}s:3:"SSP";a:2:{i:0;s:3:"SSP";i:1;s:14:"Libra skotská";}s:3:"STD";a:2:{i:0;s:2:"Db";i:1;s:5:"Dobra";}s:3:"STE";a:2:{i:0;s:3:"STE";i:1;s:24:"Escudo Svatého Tomáše";}s:3:"SUN";a:2:{i:0;s:3:"SUN";i:1;s:10:"Rubl nový";}s:3:"SUR";a:2:{i:0;s:3:"SUR";i:1;s:4:"Rubl";}s:3:"SVC";a:2:{i:0;s:3:"SVC";i:1;s:18:"Colon salvadorský";}s:3:"SYP";a:2:{i:0;s:2:"LS";i:1;s:13:"Libra syrská";}s:3:"SZL";a:2:{i:0;s:1:"E";i:1;s:9:"Lilangeni";}s:3:"TCC";a:2:{i:0;s:3:"TCC";i:1;s:21:"Koruna Turks a Caicos";}s:3:"TDF";a:2:{i:0;s:3:"TDF";i:1;s:18:"Frank čadský CFA";}s:3:"THB";a:2:{i:0;s:3:"THB";i:1;s:4:"Baht";}s:3:"TJR";a:2:{i:0;s:3:"TJR";i:1;s:16:"Tajikistan Ruble";}s:3:"TJS";a:2:{i:0;s:3:"TJS";i:1;s:6:"Somoni";}s:3:"TMM";a:2:{i:0;s:3:"TMM";i:1;s:5:"Manat";}s:3:"TND";a:2:{i:0;s:3:"TND";i:1;s:15:"Dinár tuniský";}s:3:"TOS";a:2:{i:0;s:3:"TOS";i:1;s:27:"Libra Å¡terlinků tonžská";}s:3:"TPE";a:2:{i:0;s:3:"TPE";i:1;s:16:"Escudo timorské";}s:3:"TPP";a:2:{i:0;s:3:"TPP";i:1;s:16:"Pataca timorská";}s:3:"TRL";a:2:{i:0;s:2:"TL";i:1;s:13:"Lira turecká";}s:3:"TTD";a:2:{i:0;s:3:"TT$";i:1;s:23:"Dolar Trinidad a Tobago";}s:3:"TTO";a:2:{i:0;s:3:"TTO";i:1;s:30:"Dolar starý Trinidad a Tobago";}s:3:"TVD";a:2:{i:0;s:3:"TVD";i:1;s:15:"Dolar tuvalský";}s:3:"TWD";a:2:{i:0;s:3:"NT$";i:1;s:24:"Dolar tchajvanský nový";}s:3:"TZS";a:2:{i:0;s:4:"T Sh";i:1;s:18:"Å ilink tanzanský";}s:3:"UAH";a:2:{i:0;s:3:"UAH";i:1;s:7:"Hřivna";}s:3:"UAK";a:2:{i:0;s:3:"UAK";i:1;s:10:"Karbovanec";}s:3:"UGS";a:2:{i:0;s:3:"UGS";i:1;s:29:"Å ilink ugandský (1966-1987)";}s:3:"UGX";a:2:{i:0;s:4:"U Sh";i:1;s:17:"Å ilink ugandský";}s:3:"USD";a:2:{i:0;s:3:"US$";i:1;s:15:"Dolar americký";}s:3:"USN";a:2:{i:0;s:3:"USN";i:1;s:32:"Dolar americký (příští den)";}s:3:"USS";a:2:{i:0;s:3:"USS";i:1;s:27:"Dolar americký (týž den)";}s:3:"UYF";a:2:{i:0;s:3:"UYF";i:1;s:23:"Peso uruguayské fuerte";}s:3:"UYP";a:2:{i:0;s:3:"UYP";i:1;s:28:"Peso uruguayské (1975-1993)";}s:3:"UYU";a:2:{i:0;s:3:"Ur$";i:1;s:16:"Peso uruguayské";}s:3:"UZC";a:2:{i:0;s:3:"UZC";i:1;s:21:"Uzbekistan Coupon Som";}s:3:"UZS";a:2:{i:0;s:3:"UZS";i:1;s:12:"Sum uzbecký";}s:3:"VAL";a:2:{i:0;s:3:"VAL";i:1;s:14:"Lira Vatikánu";}s:3:"VEB";a:2:{i:0;s:2:"Be";i:1;s:7:"Bolivar";}s:3:"VGD";a:2:{i:0;s:3:"VGD";i:1;s:37:"Dolar Britských Panenských ostrovů";}s:3:"VUV";a:2:{i:0;s:2:"VT";i:1;s:4:"Vatu";}s:3:"WSP";a:2:{i:0;s:3:"WSP";i:1;s:21:"Libra Západní Samoa";}s:3:"WST";a:2:{i:0;s:3:"WST";i:1;s:4:"Tala";}s:3:"XAF";a:2:{i:0;s:3:"XAF";i:1;s:14:"Frank BEAC/CFA";}s:3:"XAM";a:2:{i:0;s:3:"XAM";i:1;s:28:"Asijská peněžní jednotka";}s:3:"XAU";a:2:{i:0;s:3:"XAU";i:1;s:5:"Zlato";}s:3:"XBA";a:2:{i:0;s:3:"XBA";i:1;s:29:"Evropská smíšená jednotka";}s:3:"XBB";a:2:{i:0;s:3:"XBB";i:1;s:29:"Evropská peněžní jednotka";}s:3:"XBC";a:2:{i:0;s:3:"XBC";i:1;s:33:"Evropská jednotka účtu 9 (XBC)";}s:3:"XBD";a:2:{i:0;s:3:"XBD";i:1;s:34:"Evropská jednotka účtu 17 (XBD)";}s:3:"XCD";a:2:{i:0;s:3:"EC$";i:1;s:23:"Dolar východokaribský";}s:3:"XCF";a:2:{i:0;s:3:"XCF";i:1;s:17:"Frank Nouveau CFA";}s:3:"XDR";a:2:{i:0;s:3:"XDR";i:1;s:3:"SDR";}s:3:"XEF";a:2:{i:0;s:3:"XEF";i:1;s:16:"Frank BCEAEC/CFA";}s:3:"XEU";a:2:{i:0;s:3:"XEU";i:1;s:27:"Evropská měnová jednotka";}s:3:"XFO";a:2:{i:0;s:3:"XFO";i:1;s:12:"Frank zlatý";}s:3:"XFU";a:2:{i:0;s:3:"XFU";i:1;s:9:"Frank UIC";}s:3:"XID";a:2:{i:0;s:3:"XID";i:1;s:17:"Dinár islámský";}s:3:"XNF";a:2:{i:0;s:3:"XNF";i:1;s:30:"Frank Francouzských Antil CFA";}s:3:"XOF";a:2:{i:0;s:3:"XOF";i:1;s:15:"Frank BCEAO/CFA";}s:3:"XPF";a:2:{i:0;s:4:"CFPF";i:1;s:9:"Frank CFP";}s:3:"YDD";a:2:{i:0;s:3:"YDD";i:1;s:16:"Dinár jemenský";}s:3:"YEI";a:2:{i:0;s:3:"YEI";i:1;s:22:"Rijál jemenský imadi";}s:3:"YER";a:2:{i:0;s:3:"YRl";i:1;s:16:"Rijál jemenský";}s:3:"YUD";a:2:{i:0;s:3:"YUD";i:1;s:26:"Dinár jugoslávský nový";}s:3:"YUF";a:2:{i:0;s:3:"YUF";i:1;s:33:"Dinár jugoslávský federativní";}s:3:"YUG";a:2:{i:0;s:3:"YUG";i:1;s:25:"Dinár jugoslávský 1994";}s:3:"YUM";a:2:{i:0;s:3:"YUM";i:1;s:20:"Dinár jugoslávský";}s:3:"YUN";a:2:{i:0;s:3:"YUN";i:1;s:20:"Dinár jugoslávský";}s:3:"YUR";a:2:{i:0;s:3:"YUR";i:1;s:33:"Dinár jugoslávský reformovaný";}s:3:"ZAL";a:2:{i:0;s:3:"ZAL";i:1;s:15:"Rand finanční";}s:3:"ZAP";a:2:{i:0;s:3:"ZAP";i:1;s:18:"Libra jihoafrická";}s:3:"ZAR";a:2:{i:0;s:1:"R";i:1;s:4:"Rand";}s:3:"ZMK";a:2:{i:0;s:3:"ZMK";i:1;s:6:"Kwacha";}s:3:"ZMP";a:2:{i:0;s:3:"ZMP";i:1;s:16:"Libra zambijská";}s:3:"ZRN";a:2:{i:0;s:3:"ZRN";i:1;s:11:"Zaire nový";}s:3:"ZRZ";a:2:{i:0;s:3:"ZRZ";i:1;s:5:"Zaire";}s:3:"ZWD";a:2:{i:0;s:2:"Z$";i:1;s:17:"Dolar zimbabwský";}}s:4:"Keys";a:3:{s:8:"calendar";a:1:{i:0;s:10:"Kalendář";}s:9:"collation";a:1:{i:0;s:11:"Třídění";}s:8:"currency";a:1:{i:0;s:5:"Měna";}}s:9:"Languages";a:139:{s:2:"aa";a:1:{i:0;s:10:"AfarÅ¡tina";}s:2:"ab";a:1:{i:0;s:13:"AbcházÅ¡tina";}s:2:"af";a:1:{i:0;s:14:"AfrikánÅ¡tina";}s:2:"am";a:1:{i:0;s:11:"AmharÅ¡tina";}s:2:"ar";a:1:{i:0;s:10:"ArabÅ¡tina";}s:2:"as";a:1:{i:0;s:13:"Assaméština";}s:2:"ay";a:1:{i:0;s:12:"AymárÅ¡tina";}s:2:"az";a:1:{i:0;s:19:"AzerbajdžánÅ¡tina";}s:2:"ba";a:1:{i:0;s:12:"BaskirÅ¡tina";}s:2:"be";a:1:{i:0;s:13:"BěloruÅ¡tina";}s:2:"bg";a:1:{i:0;s:12:"BulharÅ¡tina";}s:2:"bh";a:1:{i:0;s:11:"BiharÅ¡tina";}s:2:"bi";a:1:{i:0;s:13:"BislámÅ¡tina";}s:2:"bn";a:1:{i:0;s:13:"BengálÅ¡tina";}s:2:"bo";a:1:{i:0;s:11:"TibetÅ¡tina";}s:2:"br";a:1:{i:0;s:13:"Bretaňština";}s:2:"ca";a:1:{i:0;s:14:"KatalánÅ¡tina";}s:2:"co";a:1:{i:0;s:11:"Korsičtina";}s:2:"cs";a:1:{i:0;s:9:"ČeÅ¡tina";}s:2:"cy";a:1:{i:0;s:9:"VelÅ¡tina";}s:2:"da";a:1:{i:0;s:10:"DánÅ¡tina";}s:2:"de";a:1:{i:0;s:9:"Němčina";}s:2:"dz";a:1:{i:0;s:14:"BhútánÅ¡tina";}s:2:"el";a:1:{i:0;s:9:"Řečtina";}s:2:"en";a:1:{i:0;s:11:"Angličtina";}s:2:"eo";a:1:{i:0;s:9:"Esperanto";}s:2:"es";a:1:{i:0;s:14:"Å panělÅ¡tina";}s:2:"et";a:1:{i:0;s:11:"EstonÅ¡tina";}s:2:"eu";a:1:{i:0;s:11:"Baskičtina";}s:2:"fa";a:1:{i:0;s:9:"PerÅ¡tina";}s:2:"fi";a:1:{i:0;s:9:"FinÅ¡tina";}s:2:"fj";a:1:{i:0;s:6:"Fidži";}s:2:"fo";a:1:{i:0;s:10:"FaerÅ¡tina";}s:2:"fr";a:1:{i:0;s:14:"FrancouzÅ¡tina";}s:2:"fy";a:1:{i:0;s:10:"Fríština";}s:2:"ga";a:1:{i:0;s:8:"IrÅ¡tina";}s:2:"gd";a:1:{i:0;s:18:"Skotská galÅ¡tina";}s:2:"gl";a:1:{i:0;s:12:"Haličština";}s:2:"gn";a:1:{i:0;s:12:"GuaranÅ¡tina";}s:2:"gu";a:1:{i:0;s:13:"GujaratÅ¡tina";}s:2:"ha";a:1:{i:0;s:5:"Hausa";}s:2:"he";a:1:{i:0;s:12:"HebrejÅ¡tina";}s:2:"hi";a:1:{i:0;s:10:"HindÅ¡tina";}s:2:"hr";a:1:{i:0;s:13:"ChorvatÅ¡tina";}s:2:"hu";a:1:{i:0;s:12:"MaďarÅ¡tina";}s:2:"hy";a:1:{i:0;s:12:"ArménÅ¡tina";}s:2:"ia";a:1:{i:0;s:11:"Interlingua";}s:2:"id";a:1:{i:0;s:13:"Indonéština";}s:2:"ie";a:1:{i:0;s:11:"Interlingue";}s:2:"ik";a:1:{i:0;s:13:"InupiakÅ¡tina";}s:2:"is";a:1:{i:0;s:12:"IslandÅ¡tina";}s:2:"it";a:1:{i:0;s:10:"ItalÅ¡tina";}s:2:"iu";a:1:{i:0;s:15:"InuktitutÅ¡tina";}s:2:"ja";a:1:{i:0;s:11:"JaponÅ¡tina";}s:2:"jv";a:1:{i:0;s:12:"JavánÅ¡tina";}s:2:"ka";a:1:{i:0;s:13:"GruzínÅ¡tina";}s:2:"kk";a:1:{i:0;s:12:"KazachÅ¡tina";}s:2:"kl";a:1:{i:0;s:11:"GrónÅ¡tina";}s:2:"km";a:1:{i:0;s:14:"Kambodžština";}s:2:"kn";a:1:{i:0;s:12:"KannadÅ¡tina";}s:2:"ko";a:1:{i:0;s:11:"KorejÅ¡tina";}s:2:"ks";a:1:{i:0;s:14:"KaÅ¡mírÅ¡tina";}s:2:"ku";a:1:{i:0;s:10:"KurdÅ¡tina";}s:2:"ky";a:1:{i:0;s:12:"KirgizÅ¡tina";}s:2:"la";a:1:{i:0;s:6:"Latina";}s:2:"ln";a:1:{i:0;s:12:"LingalÅ¡tina";}s:2:"lo";a:1:{i:0;s:9:"LaoÅ¡tina";}s:2:"lt";a:1:{i:0;s:11:"LitevÅ¡tina";}s:2:"lv";a:1:{i:0;s:10:"LotyÅ¡tina";}s:2:"mg";a:1:{i:0;s:11:"MalgaÅ¡tina";}s:2:"mi";a:1:{i:0;s:10:"MaorÅ¡tina";}s:2:"mk";a:1:{i:0;s:13:"MakedonÅ¡tina";}s:2:"ml";a:1:{i:0;s:13:"MalabarÅ¡tina";}s:2:"mn";a:1:{i:0;s:12:"MongolÅ¡tina";}s:2:"mo";a:1:{i:0;s:12:"MoldavÅ¡tina";}s:2:"mr";a:1:{i:0;s:7:"Marathi";}s:2:"ms";a:1:{i:0;s:11:"MalajÅ¡tina";}s:2:"mt";a:1:{i:0;s:10:"MaltÅ¡tina";}s:2:"my";a:1:{i:0;s:10:"BarmÅ¡tina";}s:2:"na";a:1:{i:0;s:5:"Nauru";}s:2:"ne";a:1:{i:0;s:12:"NepálÅ¡tina";}s:2:"nl";a:1:{i:0;s:12:"HolandÅ¡tina";}s:2:"no";a:1:{i:0;s:9:"NorÅ¡tina";}s:2:"oc";a:1:{i:0;s:7:"Occitan";}s:2:"om";a:1:{i:0;s:12:"Oromo (Afan)";}s:2:"or";a:1:{i:0;s:5:"Oriya";}s:2:"pa";a:1:{i:0;s:16:"PaňdžábÅ¡tina";}s:2:"pl";a:1:{i:0;s:9:"PolÅ¡tina";}s:2:"ps";a:1:{i:0;s:15:"Pashto (Pushto)";}s:2:"pt";a:1:{i:0;s:14:"PortugalÅ¡tina";}s:2:"qu";a:1:{i:0;s:14:"KečuánÅ¡tina";}s:2:"rm";a:1:{i:0;s:17:"RétorománÅ¡tina";}s:2:"rn";a:1:{i:0;s:7:"Kirundi";}s:2:"ro";a:1:{i:0;s:11:"RumunÅ¡tina";}s:2:"ru";a:1:{i:0;s:8:"RuÅ¡tina";}s:2:"rw";a:1:{i:0;s:16:"KinyarwandÅ¡tina";}s:2:"sa";a:1:{i:0;s:7:"Sanskrt";}s:2:"sd";a:1:{i:0;s:6:"Sindhi";}s:2:"sg";a:1:{i:0;s:6:"Sangho";}s:2:"sh";a:1:{i:0;s:17:"SrbochorvatÅ¡tina";}s:2:"si";a:1:{i:0;s:13:"SinhálÅ¡tina";}s:2:"sk";a:1:{i:0;s:12:"SlovenÅ¡tina";}s:2:"sl";a:1:{i:0;s:12:"SlovinÅ¡tina";}s:2:"sm";a:1:{i:0;s:11:"SamoyÅ¡tina";}s:2:"sn";a:1:{i:0;s:5:"Shona";}s:2:"so";a:1:{i:0;s:12:"SomálÅ¡tina";}s:2:"sq";a:1:{i:0;s:12:"AlbánÅ¡tina";}s:2:"sr";a:1:{i:0;s:9:"SrbÅ¡tina";}s:2:"ss";a:1:{i:0;s:12:"SiswatÅ¡tina";}s:2:"st";a:1:{i:0;s:7:"Sesotho";}s:2:"su";a:1:{i:0;s:12:"SundanÅ¡tina";}s:2:"sv";a:1:{i:0;s:12:"Å védÅ¡tina";}s:2:"sw";a:1:{i:0;s:12:"SvahilÅ¡tina";}s:2:"ta";a:1:{i:0;s:11:"TamilÅ¡tina";}s:2:"te";a:1:{i:0;s:11:"TelugÅ¡tina";}s:2:"tg";a:1:{i:0;s:13:"Tádžičtina";}s:2:"th";a:1:{i:0;s:10:"ThajÅ¡tina";}s:2:"ti";a:1:{i:0;s:14:"TigrinijÅ¡tina";}s:2:"tk";a:1:{i:0;s:13:"TurkmenÅ¡tina";}s:2:"tl";a:1:{i:0;s:7:"Tagalog";}s:2:"tn";a:1:{i:0;s:13:"SetswanÅ¡tina";}s:2:"to";a:1:{i:0;s:5:"Tonga";}s:2:"tr";a:1:{i:0;s:10:"Turečtina";}s:2:"ts";a:1:{i:0;s:6:"Tsonga";}s:2:"tt";a:1:{i:0;s:11:"TatarÅ¡tina";}s:2:"tw";a:1:{i:0;s:3:"Twi";}s:2:"ug";a:1:{i:0;s:12:"UighurÅ¡tina";}s:2:"uk";a:1:{i:0;s:13:"UkrajinÅ¡tina";}s:2:"ur";a:1:{i:0;s:9:"UrdÅ¡tina";}s:2:"uz";a:1:{i:0;s:10:"Uzbečtina";}s:2:"vi";a:1:{i:0;s:13:"VietnamÅ¡tina";}s:2:"vo";a:1:{i:0;s:7:"Volapuk";}s:2:"wo";a:1:{i:0;s:5:"Wolof";}s:2:"xh";a:1:{i:0;s:5:"Xhosa";}s:2:"yi";a:1:{i:0;s:6:"JidiÅ¡";}s:2:"yo";a:1:{i:0;s:6:"Yoruba";}s:2:"za";a:1:{i:0;s:6:"Zhuang";}s:2:"zh";a:1:{i:0;s:11:"ČínÅ¡tina";}s:2:"zu";a:1:{i:0;s:4:"Zulu";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Latn";}s:14:"NumberElements";a:12:{i:0;s:1:",";i:1;s:2:" ";i:2;s:1:";";i:3;s:1:"%";i:4;s:1:"0";i:5;s:1:"#";i:6;s:1:"-";i:7;s:1:"E";i:8;s:3:"‰";i:9;s:3:"∞";i:10;s:3:"�";i:11;s:1:"+";}s:5:"Types";a:1:{s:8:"calendar";a:7:{s:8:"buddhist";a:1:{i:0;s:23:"Budhistický kalendář";}s:7:"chinese";a:1:{i:0;s:20:"Čínský kalendář";}s:9:"gregorian";a:1:{i:0;s:25:"Gregoriánský kalendář";}s:6:"hebrew";a:1:{i:0;s:21:"Hebrejský kalendář";}s:7:"islamic";a:1:{i:0;s:21:"Muslimský kalendář";}s:13:"islamic-civil";a:1:{i:0;s:32:"Muslimský občanský kalendář";}s:8:"japanese";a:1:{i:0;s:20:"Japonský kalendář";}}}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:6:{s:11:"AmPmMarkers";a:2:{i:0;s:4:"dop.";i:1;s:4:"odp.";}s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:9:"H:mm:ss z";i:1;s:9:"H:mm:ss z";i:2;s:7:"H:mm:ss";i:3;s:4:"H:mm";i:4;s:18:"EEEE, d. MMMM yyyy";i:5;s:12:"d. MMMM yyyy";i:6;s:8:"d.M.yyyy";i:7;s:6:"d.M.yy";i:8;s:7:"{1} {0}";}s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:2:"ne";i:1;s:2:"po";i:2;s:3:"út";i:3;s:2:"st";i:4;s:3:"čt";i:5;s:3:"pá";i:6;s:2:"so";}s:4:"wide";a:7:{i:0;s:7:"neděle";i:1;s:9:"pondělí";i:2;s:7:"úterý";i:3;s:7:"středa";i:4;s:8:"čtvrtek";i:5;s:6:"pátek";i:6;s:6:"sobota";}}}s:4:"eras";a:1:{s:11:"abbreviated";a:2:{i:0;s:7:"př.Kr.";i:1;s:6:"po Kr.";}}s:10:"monthNames";a:2:{s:6:"format";a:3:{s:11:"abbreviated";a:12:{i:0;s:2:"1.";i:1;s:2:"2.";i:2;s:2:"3.";i:3;s:2:"4.";i:4;s:2:"5.";i:5;s:2:"6.";i:6;s:2:"7.";i:7;s:2:"8.";i:8;s:2:"9.";i:9;s:3:"10.";i:10;s:3:"11.";i:11;s:3:"12.";}s:6:"narrow";a:12:{i:0;s:1:"l";i:1;s:2:"ú";i:2;s:1:"b";i:3;s:1:"d";i:4;s:1:"k";i:5;s:2:"č";i:6;s:2:"č";i:7;s:1:"s";i:8;s:1:"z";i:9;s:2:"ř";i:10;s:1:"l";i:11;s:1:"p";}s:4:"wide";a:12:{i:0;s:5:"ledna";i:1;s:6:"února";i:2;s:7:"března";i:3;s:5:"dubna";i:4;s:7:"května";i:5;s:7:"června";i:6;s:9:"července";i:7;s:5:"srpna";i:8;s:7:"září";i:9;s:7:"října";i:10;s:9:"listopadu";i:11;s:8:"prosince";}}s:11:"stand-alone";a:3:{s:11:"abbreviated";a:12:{i:0;s:2:"1.";i:1;s:2:"2.";i:2;s:2:"3.";i:3;s:2:"4.";i:4;s:2:"5.";i:5;s:2:"6.";i:6;s:2:"7.";i:7;s:2:"8.";i:8;s:2:"9.";i:9;s:3:"10.";i:10;s:3:"11.";i:11;s:3:"12.";}s:6:"narrow";a:12:{i:0;s:1:"l";i:1;s:2:"ú";i:2;s:1:"b";i:3;s:1:"d";i:4;s:1:"k";i:5;s:2:"č";i:6;s:2:"č";i:7;s:1:"s";i:8;s:1:"z";i:9;s:2:"ř";i:10;s:1:"l";i:11;s:1:"p";}s:4:"wide";a:12:{i:0;s:5:"Leden";i:1;s:5:"Únor";i:2;s:7:"Březen";i:3;s:5:"Duben";i:4;s:7:"Květen";i:5;s:7:"Červen";i:6;s:9:"Červenec";i:7;s:5:"Srpen";i:8;s:7:"Září";i:9;s:7:"Říjen";i:10;s:8:"Listopad";i:11;s:8:"Prosinec";}}}}}s:17:"localPatternChars";a:1:{i:0;s:24:"GuMtkHmsSEDFwWahKzUeygAZ";}s:11:"zoneStrings";a:17:{i:0;a:6:{i:0;s:19:"America/Los_Angeles";i:1;s:27:"Pacifický standardní čas";i:2;s:3:"PST";i:3;s:22:"Pacifický letní čas";i:4;s:3:"PDT";i:5;s:11:"Los Angeles";}i:1;a:6:{i:0;s:14:"America/Denver";i:1;s:24:"Horský standardní čas";i:2;s:3:"MST";i:3;s:19:"Horský letní čas";i:4;s:3:"MDT";i:5;s:6:"Denver";}i:2;a:6:{i:0;s:15:"America/Phoenix";i:1;s:24:"Horský standardní čas";i:2;s:3:"MST";i:3;s:24:"Horský standardní čas";i:4;s:3:"MST";i:5;s:7:"Phoenix";}i:3;a:6:{i:0;s:15:"America/Chicago";i:1;s:28:"Centrální standardní čas";i:2;s:3:"CST";i:3;s:23:"Centrální letní čas";i:4;s:3:"CDT";i:5;s:7:"Chicago";}i:4;a:6:{i:0;s:16:"America/New_York";i:1;s:27:"Východní standardní čas";i:2;s:3:"EST";i:3;s:22:"Východní letní čas";i:4;s:3:"EDT";i:5;s:8:"New York";}i:5;a:6:{i:0;s:20:"America/Indianapolis";i:1;s:27:"Východní standardní čas";i:2;s:3:"EST";i:3;s:27:"Východní standardní čas";i:4;s:3:"EST";i:5;s:12:"Indianapolis";}i:6;a:6:{i:0;s:16:"Pacific/Honolulu";i:1;s:26:"Havajský standardní čas";i:2;s:3:"HST";i:3;s:26:"Havajský standardní čas";i:4;s:3:"HST";i:5;s:8:"Honolulu";}i:7;a:6:{i:0;s:17:"America/Anchorage";i:1;s:27:"AljaÅ¡ský standardní čas";i:2;s:3:"AST";i:3;s:22:"AljaÅ¡ský letní čas";i:4;s:3:"ADT";i:5;s:9:"Anchorage";}i:8;a:6:{i:0;s:15:"America/Halifax";i:1;s:28:"Atlantický standardní čas";i:2;s:3:"AST";i:3;s:23:"Atlantický letní čas";i:4;s:3:"ADT";i:5;s:7:"Halifax";}i:9;a:6:{i:0;s:16:"America/St_Johns";i:1;s:33:"Newfoundlandský standardní čas";i:2;s:3:"CNT";i:3;s:28:"Newfoundlandský letní čas";i:4;s:3:"CDT";i:5;s:9:"St. Johns";}i:10;a:6:{i:0;s:12:"Europe/Paris";i:1;s:33:"Středoevropský standardní čas";i:2;s:3:"CET";i:3;s:28:"Středoevropský letní čas";i:4;s:4:"CEST";i:5;s:8:"Paříž";}i:11;a:6:{i:0;s:7:"Etc/GMT";i:1;s:28:"Greenwichský střední čas";i:2;s:3:"GMT";i:3;s:28:"Greenwichský střední čas";i:4;s:3:"GMT";i:5;s:7:"Londýn";}i:12;a:6:{i:0;s:17:"Africa/Casablanca";i:1;s:28:"Greenwichský střední čas";i:2;s:3:"GMT";i:3;s:28:"Greenwichský střední čas";i:4;s:3:"GMT";i:5;s:10:"Casablanca";}i:13;a:6:{i:0;s:14:"Asia/Jerusalem";i:1;s:27:"Izraelský standardní čas";i:2;s:3:"IST";i:3;s:22:"Izraelský letní čas";i:4;s:3:"IDT";i:5;s:10:"Jeruzalém";}i:14;a:6:{i:0;s:10:"Asia/Tokyo";i:1;s:26:"Japonský standardní čas";i:2;s:3:"JST";i:3;s:26:"Japonský standardní čas";i:4;s:3:"JST";i:5;s:5:"Tokio";}i:15;a:6:{i:0;s:16:"Europe/Bucharest";i:1;s:34:"Východoevropský standardní čas";i:2;s:3:"EET";i:3;s:29:"Východoevropský letní čas";i:4;s:4:"EEST";i:5;s:10:"BukureÅ¡Å¥";}i:16;a:6:{i:0;s:13:"Asia/Shanghai";i:1;s:26:"Čínský standardní čas";i:2;s:3:"CTT";i:3;s:26:"Čínský standardní čas";i:4;s:3:"CDT";i:5;s:8:"Å anghaj";}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/cs_CZ.dat b/gui/baculum/framework/I18N/core/data/cs_CZ.dat new file mode 100644 index 0000000000..f4c2e7ecb2 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/cs_CZ.dat @@ -0,0 +1 @@ +a:2:{s:14:"NumberPatterns";a:4:{i:0;s:18:"#,##0.##;-#,##0.##";i:1;s:24:"#,##0.00 ¤;-#,##0.00 ¤";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/cy.dat b/gui/baculum/framework/I18N/core/data/cy.dat new file mode 100644 index 0000000000..0f7babb704 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/cy.dat @@ -0,0 +1 @@ +a:5:{s:9:"Countries";a:1:{s:2:"GB";a:1:{i:0;s:12:"Prydain Fawr";}}s:9:"Languages";a:1:{s:2:"cy";a:1:{i:0;s:7:"Cymraeg";}}s:12:"LocaleScript";a:1:{i:0;s:4:"Latn";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:2:{s:8:"dayNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:7:{i:0;s:3:"Sul";i:1;s:4:"Llun";i:2;s:3:"Maw";i:3;s:3:"Mer";i:4;s:3:"Iau";i:5;s:4:"Gwen";i:6;s:3:"Sad";}s:4:"wide";a:7:{i:0;s:8:"Dydd Sul";i:1;s:9:"Dydd Llun";i:2;s:11:"Dydd Mawrth";i:3;s:12:"Dydd Mercher";i:4;s:8:"Dydd Iau";i:5;s:11:"Dydd Gwener";i:6;s:11:"Dydd Sadwrn";}}}s:10:"monthNames";a:1:{s:6:"format";a:2:{s:11:"abbreviated";a:12:{i:0;s:3:"Ion";i:1;s:5:"Chwef";i:2;s:6:"Mawrth";i:3;s:6:"Ebrill";i:4;s:3:"Mai";i:5;s:3:"Meh";i:6;s:5:"Gorff";i:7;s:4:"Awst";i:8;s:4:"Medi";i:9;s:3:"Hyd";i:10;s:4:"Tach";i:11;s:4:"Rhag";}s:4:"wide";a:12:{i:0;s:6:"Ionawr";i:1;s:8:"Chwefror";i:2;s:6:"Mawrth";i:3;s:6:"Ebrill";i:4;s:3:"Mai";i:5;s:7:"Mehefin";i:6;s:9:"Gorffenaf";i:7;s:4:"Awst";i:8;s:4:"Medi";i:9;s:6:"Hydref";i:10;s:8:"Tachwedd";i:11;s:7:"Rhagfyr";}}}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/cy_GB.dat b/gui/baculum/framework/I18N/core/data/cy_GB.dat new file mode 100644 index 0000000000..82dfff8879 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/cy_GB.dat @@ -0,0 +1 @@ +a:3:{s:14:"NumberPatterns";a:4:{i:0;s:20:"#,##0.###;-#,##0.###";i:1;s:22:"¤#,##0.00;-¤#,##0.00";i:2;s:6:"#,##0%";i:3;s:3:"#E0";}s:7:"Version";a:1:{i:0;s:3:"1.2";}s:8:"calendar";a:1:{s:9:"gregorian";a:2:{s:26:"DateTimeElements:intvector";a:2:{i:0;i:2;i:1;i:1;}s:16:"DateTimePatterns";a:9:{i:0;s:10:"HH:mm:ss z";i:1;s:10:"HH:mm:ss z";i:2;s:8:"HH:mm:ss";i:3;s:5:"HH:mm";i:4;s:18:"EEEE, dd MMMM yyyy";i:5;s:12:"dd MMMM yyyy";i:6;s:10:"d MMM yyyy";i:7;s:10:"dd/MM/yyyy";i:8;s:7:"{1} {0}";}}}} \ No newline at end of file diff --git a/gui/baculum/framework/I18N/core/data/da.dat b/gui/baculum/framework/I18N/core/data/da.dat new file mode 100644 index 0000000000..ff9b742279 --- /dev/null +++ b/gui/baculum/framework/I18N/core/data/da.dat @@ -0,0 +1 @@ +a:12:{s:9:"Countries";a:240:{s:2:"AD";a:1:{i:0;s:7:"Andorra";}s:2:"AE";a:1:{i:0;s:26:"Forenede Arabiske Emirater";}s:2:"AF";a:1:{i:0;s:11:"Afghanistan";}s:2:"AG";a:1:{i:0;s:18:"Antigua og Barbuda";}s:2:"AI";a:1:{i:0;s:8:"Anguilla";}s:2:"AL";a:1:{i:0;s:8:"Albanien";}s:2:"AM";a:1:{i:0;s:8:"Armenien";}s:2:"AN";a:1:{i:0;s:19:"Hollandske Antiller";}s:2:"AO";a:1:{i:0;s:6:"Angola";}s:2:"AQ";a:1:{i:0;s:9:"Antarktis";}s:2:"AR";a:1:{i:0;s:9:"Argentina";}s:2:"AS";a:1:{i:0;s:16:"Amerikansk Samoa";}s:2:"AT";a:1:{i:0;s:7:"Østrig";}s:2:"AU";a:1:{i:0;s:10:"Australien";}s:2:"AW";a:1:{i:0;s:5:"Aruba";}s:2:"AZ";a:1:{i:0;s:12:"Aserbajdsjan";}s:2:"BA";a:1:{i:0;s:19:"Bosnien-Hercegovina";}s:2:"BB";a:1:{i:0;s:8:"Barbados";}s:2:"BD";a:1:{i:0;s:10:"Bangladesh";}s:2:"BE";a:1:{i:0;s:7:"Belgien";}s:2:"BF";a:1:{i:0;s:12:"Burkina Faso";}s:2:"BG";a:1:{i:0;s:9:"Bulgarien";}s:2:"BH";a:1:{i:0;s:7:"Bahrain";}s:2:"BI";a:1:{i:0;s:7:"Burundi";}s:2:"BJ";a:1:{i:0;s:5:"Benin";}s:2:"BM";a:1:{i:0;s:7:"Bermuda";}s:2:"BN";a:1:{i:0;s:17:"Brunei Darussalam";}s:2:"BO";a:1:{i:0;s:7:"Bolivia";}s:2:"BR";a:1:{i:0;s:9:"Brasilien";}s:2:"BS";a:1:{i:0;s:7:"Bahamas";}s:2:"BT";a:1:{i:0;s:6:"Bhutan";}s:2:"BV";a:1:{i:0;s:8:"Bouvetø";}s:2:"BW";a:1:{i:0;s:8:"Botswana";}s:2:"BY";a:1:{i:0;s:12:"Hviderusland";}s:2:"BZ";a:1:{i:0;s:6:"Belize";}s:2:"CA";a:1:{i:0;s:6:"Canada";}s:2:"CC";a:1:{i:0;s:28:"Cocos-øerne (Keelingøerne)";}s:2:"CD";a:1:{i:0;s:31:"Den Demokratiske Republik Congo";}s:2:"CF";a:1:{i:0;s:26:"Centralafrikanske Republik";}s:2:"CG";a:1:{i:0;s:5:"Congo";}s:2:"CH";a:1:{i:0;s:7:"Schweiz";}s:2:"CI";a:1:{i:0;s:15:"Elfenbenskysten";}s:2:"CK";a:1:{i:0;s:11:"Cook-øerne";}s:2:"CL";a:1:{i:0;s:5:"Chile";}s:2:"CM";a:1:{i:0;s:8:"Cameroun";}s:2:"CN";a:1:{i:0;s:4:"Kina";}s:2:"CO";a:1:{i:0;s:8:"Colombia";}s:2:"CR";a:1:{i:0;s:10:"Costa Rica";}s:2:"CU";a:1:{i:0;s:4:"Cuba";}s:2:"CV";a:1:{i:0;s:9:"Kap Verde";}s:2:"CX";a:1:{i:0;s:8:"Juleøen";}s:2:"CY";a:1:{i:0;s:6:"Cypern";}s:2:"CZ";a:1:{i:0;s:8:"Tjekkiet";}s:2:"DE";a:1:{i:0;s:8:"Tyskland";}s:2:"DJ";a:1:{i:0;s:8:"Djibouti";}s:2:"DK";a:1:{i:0;s:7:"Danmark";}s:2:"DM";a:1:{i:0;s:8:"Dominica";}s:2:"DO";a:1:{i:0;s:25:"Den Dominikanske Republik";}s:2:"DZ";a:1:{i:0;s:8:"Algeriet";}s:2:"EC";a:1:{i:0;s:7:"Ecuador";}s:2:"EE";a:1:{i:0;s:7:"Estland";}s:2:"EG";a:1:{i:0;s:7:"Egypten";}s:2:"EH";a:1:{i:0;s:10:"Vestsahara";}s:2:"ER";a:1:{i:0;s:7:"Eritrea";}s:2:"ES";a:1:{i:0;s:7:"Spanien";}s:2:"ET";a:1:{i:0;s:8:"Etiopien";}s:2:"FI";a:1:{i:0;s:7:"Finland";}s:2:"FJ";a:1:{i:0;s:11:"Fiji-øerne";}s:2:"FK";a:1:{i:0;s:15:"Falklandsøerne";}s:2:"FM";a:1:{i:0;s:28:"Mikronesiens Forenede Stater";}s:2:"FO";a:1:{i:0;s:10:"Færøerne";}s:2:"FR";a:1:{i:0;s:8:"Frankrig";}s:2:"GA";a:1:{i:0;s:5:"Gabon";}s:2:"GB";a:1:{i:0;s:14:"Storbritannien";}s:2:"GD";a:1:{i:0;s:7:"Grenada";}s:2:"GE";a:1:{i:0;s:8:"Georgien";}s:2:"GF";a:1:{i:0;s:13:"Fransk Guyana";}s:2:"GH";a:1:{i:0;s:5:"Ghana";}s:2:"GI";a:1:{i:0;s:9:"Gibraltar";}s:2:"GL";a:1:{i:0;s:9:"Grønland";}s:2:"GM";a:1:{i:0;s:6:"Gambia";}s:2:"GN";a:1:{i:0;s:6:"Guinea";}s:2:"GP";a:1:{i:0;s:10:"Guadeloupe";}s:2:"GQ";a:1:{i:0;s:17:"Ækvatorialguinea";}s:2:"GR";a:1:{i:0;s:11:"Grækenland";}s:2:"GS";a:1:{i:0;s:40:"South Georgia og De Sydlige Sandwichøer";}s:2:"GT";a:1:{i:0;s:9:"Guatemala";}s:2:"GU";a:1:{i:0;s:4:"Guam";}s:2:"GW";a:1:{i:0;s:13:"Guinea-Bissau";}s:2:"GY";a:1:{i:0;s:6:"Guyana";}s:2:"HK";a:1:{i:0;s:12:"SAR Hongkong";}s:2:"HM";a:1:{i:0;s:25:"Heard- og McDonald-øerne";}s:2:"HN";a:1:{i:0;s:8:"Honduras";}s:2:"HR";a:1:{i:0;s:8:"Kroatien";}s:2:"HT";a:1:{i:0;s:5:"Haiti";}s:2:"HU";a:1:{i:0;s:6:"Ungarn";}s:2:"ID";a:1:{i:0;s:10:"Indonesien";}s:2:"IE";a:1:{i:0;s:6:"Irland";}s:2:"IL";a:1:{i:0;s:6:"Israel";}s:2:"IN";a:1:{i:0;s:6:"Indien";}s:2:"IO";a:1:{i:0;s:44:"Det Britiske Territorium i Det Indiske Ocean";}s:2:"IQ";a:1:{i:0;s:4:"Irak";}s:2:"IR";a:1:{i:0;s:4:"Iran";}s:2:"IS";a:1:{i:0;s:6:"Island";}s:2:"IT";a:1:{i:0;s:7:"Italien";}s:2:"JM";a:1:{i:0;s:7:"Jamaica";}s:2:"JO";a:1:{i:0;s:6:"Jordan";}s:2:"JP";a:1:{i:0;s:5:"Japan";}s:2:"KE";a:1:{i:0;s:5:"Kenya";}s:2:"KG";a:1:{i:0;s:11:"Kirgisistan";}s:2:"KH";a:1:{i:0;s:8:"Cambodja";}s:2:"KI";a:1:{i:0;s:8:"Kiribati";}s:2:"KM";a:1:{i:0;s:9:"Comorerne";}s:2:"KN";a:1:{i:0;s:20:"Saint Kitts og Nevis";}s:2:"KP";a:1:{i:0;s:9:"Nordkorea";}s:2:"KR";a:1:{i:0;s:8:"Sydkorea";}s:2:"KW";a:1:{i:0;s:6:"Kuwait";}s:2:"KY";a:1:{i:0;s:12:"Caymanøerne";}s:2:"KZ";a:1:{i:0;s:10:"Kasakhstan";}s:2:"LA";a:1:{i:0;s:4:"Laos";}s:2:"LB";a:1:{i:0;s:7:"Libanon";}s:2:"LC";a:1:{i:0;s:11:"Saint Lucia";}s:2:"LI";a:1:{i:0;s:13:"Liechtenstein";}s:2:"LK";a:1:{i:0;s:9:"Sri Lanka";}s:2:"LR";a:1:{i:0;s:7:"Liberia";}s:2:"LS";a:1:{i:0;s:7:"Lesotho";}s:2:"LT";a:1:{i:0;s:7:"Litauen";}s:2:"LU";a:1:{i:0;s:10:"Luxembourg";}s:2:"LV";a:1:{i:0;s:7:"Letland";}s:2:"LY";a:1:{i:0;s:6:"Libyen";}s:2:"MA";a:1:{i:0;s:7:"Marokko";}s:2:"MC";a:1:{i:0;s:6:"Monaco";}s:2:"MD";a:1:{i:0;s:19:"Republikken Moldova";}s:2:"MG";a:1:{i:0;s:10:"Madagaskar";}s:2:"MH";a:1:{i:0;s:14:"Marshalløerne";}s:2:"MK";a:1:{i:0;s:22:"Republikken Makedonien";}s:2:"ML";a:1:{i:0;s:4:"Mali";}s:2:"MM";a:1:{i:0;s:7:"Myanmar";}s:2:"MN";a:1:{i:0;s:9:"Mongoliet";}s:2:"MO";a:1:{i:0;s:9:"SAR Macao";}s:2:"MP";a:1:{i:0;s:14:"Nordmarianerne";}s:2:"MQ";a:1:{i:0;s:10:"Martinique";}s:2:"MR";a:1:{i:0;s:11:"Mauretanien";}s:2:"MS";a:1:{i:0;s:10:"Montserrat";}s:2:"MT";a:1:{i:0;s:5:"Malta";}s:2:"MU";a:1:{i:0;s:9:"Mauritius";}s:2:"MV";a:1:{i:0;s:10:"Maldiverne";}s:2:"MW";a:1:{i:0;s:6:"Malawi";}s:2:"MX";a:1:{i:0;s:6:"Mexico";}s:2:"MY";a:1:{i:0;s:8:"Malaysia";}s:2:"MZ";a:1:{i:0;s:10:"Mozambique";}s:2:"NA";a:1:{i:0;s:7:"Namibia";}s:2:"NC";a:1:{i:0;s:13:"Ny Caledonien";}s:2:"NE";a:1:{i:0;s:5:"Niger";}s:2:"NF";a:1:{i:0;s:14:"Norfolk Island";}s:2:"NG";a:1:{i:0;s:7:"Nigeria";}s:2:"NI";a:1:{i:0;s:9:"Nicaragua";}s:2:"NL";a:1:{i:0;s:7:"Holland";}s:2:"NO";a:1:{i:0;s:5:"Norge";}s:2:"NP";a:1:{i:0;s:5:"Nepal";}s:2:"NR";a:1:{i:0;s:5:"Nauru";}s:2:"NU";a:1:{i:0;s:4:"Niue";}s:2:"NZ";a:1:{i:0;s:11:"New Zealand";}s:2:"OM";a:1:{i:0;s:4:"Oman";}s:2:"PA";a:1:{i:0;s:6:"Panama";}s:2:"PE";a:1:{i:0;s:4:"Peru";}s:2:"PF";a:1:{i:0;s:17:"Fransk Polynesien";}s:2:"PG";a:1:{i:0;s:15:"Papua Ny Guinea";}s:2:"PH";a:1:{i:0;s:12:"Filippinerne";}s:2:"PK";a:1:{i:0;s:8:"Pakistan";}s:2:"PL";a:1:{i:0;s:5:"Polen";}s:2:"PM";a:1:{i:0;s:24:"Saint Pierre og Miquelon";}s:2:"PN";a:1:{i:0;s:8:"Pitcairn";}s:2:"PR";a:1:{i:0;s:11:"Puerto Rico";}s:2:"PS";a:1:{i:0;s:28:"De palæstinensiske omrÃ¥der";}s:2:"PT";a:1:{i:0;s:8:"Portugal";}s:2:"PW";a:1:{i:0;s:5:"Palau";}s:2:"PY";a:1:{i:0;s:8:"Paraguay";}s:2:"QA";a:1:{i:0;s:5:"Qatar";}s:2:"RE";a:1:{i:0;s:7:"Reunion";}s:2:"RO";a:1:{i:0;s:9:"Rumænien";}s:2:"RU";a:1:{i:0;s:7:"Rusland";}s:2:"RW";a:1:{i:0;s:6:"Rwanda";}s:2:"SA";a:1:{i:0;s:13:"Saudi-Arabien";}s:2:"SB";a:1:{i:0;s:13:"Salomonøerne";}s:2:"SC";a:1:{i:0;s:12:"Seychellerne";}s:2:"SD";a:1:{i:0;s:5:"Sudan";}s:2:"SE";a:1:{i:0;s:7:"Sverige";}s:2:"SG";a:1:{i:0;s:9:"Singapore";}s:2:"SH";a:1:{i:0;s:10:"St. Helena";}s:2:"SI";a:1:{i:0;s:9:"Slovenien";}s:2:"SJ";a:1:{i:0;s:21:"Svalbard og Jan Mayen";}s:2:"SK";a:1:{i:0;s:9:"Slovakiet";}s:2:"SL";a:1:{i:0;s:12:"Sierra Leone";}s:2:"SM";a:1:{i:0;s:10:"San Marino";}s:2:"SN";a:1:{i:0;s:7:"Senegal";}s:2:"SO";a:1:{i:0;s:7:"Somalia";}s:2:"SP";a:1:{i:0;s:7:"Serbien";}s:2:"SR";a:1:{i:0;s:7:"Surinam";}s:2:"ST";a:1:{i:0;s:23:"São Tomé og Príncipe";}s:2:"SV";a:1:{i:0;s:11:"El Salvador";}s:2:"SY";a:1:{i:0;s:6:"Syrien";}s:2:"SZ";a:1:{i:0;s:9:"Swaziland";}s:2:"TC";a:1:{i:0;s:22:"Turks- og Caicosøerne";}s:2:"TD";a:1:{i:0;s:5:"Tchad";}s:2:"TF";a:1:{i:0;s:47:"Franske Besiddelser i Det Sydlige Indiske Ocean";}s:2:"TG";a:1:{i:0;s:4:"Togo";}s:2:"TH";a:1:{i:0;s:8:"Thailand";}s:2:"TJ";a:1:{i:0;s:12:"Tadsjikistan";}s:2:"TK";a:1:{i:0;s:7:"Tokelau";}s:2:"TL";a:1:{i:0;s:11:"Timor-Leste";}s:2:"TM";a:1:{i:0;s:12:"Turkmenistan";}s:2:"TN";a:1:{i:0;s:8:"Tunesien";}s:2:"TO";a:1:{i:0;s:5:"Tonga";}s:2:"TR";a:1:{i:0;s:7:"Tyrkiet";}s:2:"TT";a:1:{i:0;s:18:"Trinidad og Tobago";}s:2:"TV";a:1:{i:0;s:6:"Tuvalu";}s:2:"TW";a:1:{i:0;s:6:"Taiwan";}s:2:"TZ";a:1:{i:0;s:8:"Tanzania";}s:2:"UA";a:1:{i:0;s:7:"Ukraine";}s:2:"UG";a:1:{i:0;s:6:"Uganda";}s:2:"UM";a:1:{i:0;s:38:"De Mindre Amerikanske Oversøiske Øer";}s:2:"US";a:1:{i:0;s:3:"USA";}s:2:"UY";a:1:{i:0;s:7:"Uruguay";}s:2:"UZ";a:1:{i:0;s:10:"Usbekistan";}s:2:"VA";a:1:{i:0;s:13:"Vatikanstaten";}s:2:"VC";a:1:{i:0;s:27:"St. Vincent og Grenadinerne";}s:2:"VE";a:1:{i:0;s:9:"Venezuela";}s:2:"VG";a:1:{i:0;s:22:"De britiske jomfruøer";}s:2:"VI";a:1:{i:0;s:25:"De amerikanske jomfruøer";}s:2:"VN";a:1:{i:0;s:7:"Vietnam";}s:2:"VU";a:1:{i:0;s:7:"Vanuatu";}s:2:"WF";a:1:{i:0;s:22:"Wallis og Futunaøerne";}s:2:"WS";a:1:{i:0;s:5:"Samoa";}s:2:"YE";a:1:{i:0;s:5:"Yemen";}s:2:"YT";a:1:{i:0;s:7:"Mayotte";}s:2:"YU";a:1:{i:0;s:11:"Jugoslavien";}s:2:"ZA";a:1:{i:0;s:9:"Sydafrika";}s:2:"ZM";a:1:{i:0;s:6:"Zambia";}s:2:"ZW";a:1:{i:0;s:8:"Zimbabwe";}}s:10:"Currencies";a:346:{s:3:"ADD";a:2:{i:0;s:3:"ADD";i:1;s:16:"Andorransk diner";}s:3:"ADP";a:2:{i:0;s:3:"ADP";i:1;s:17:"Andorransk peseta";}s:3:"AED";a:2:{i:0;s:3:"AED";i:1;s:40:"Dirham fra de Forenede Arabiske Emirater";}s:3:"AIF";a:2:{i:0;s:3:"AIF";i:1;s:21:"Affars og Issas franc";}s:3:"ALK";a:2:{i:0;s:3:"ALK";i:1;s:23:"Albansk lek (1946-1961)";}s:3:"ALL";a:2:{i:0;s:3:"lek";i:1;s:11:"Albansk lek";}s:3:"ALV";a:2:{i:0;s:3:"ALV";i:1;s:11:"Albansk lek";}s:3:"AMD";a:2:{i:0;s:4:"dram";i:1;s:12:"Armensk dram";}s:3:"ANG";a:2:{i:0;s:5:"NA f.";i:1;s:35:"Gylden fra De Nederlandske Antiller";}s:3:"AOA";a:2:{i:0;s:3:"AOA";i:1;s:16:"Angolansk kwanza";}s:3:"AOK";a:2:{i:0;s:3:"AOK";i:1;s:28:"Angolansk kwanza (1977-1990)";}s:3:"AON";a:2:{i:0;s:3:"AON";i:1;s:31:"Ny angolansk kwanza (1990-2000)";}s:3:"AOR";a:2:{i:0;s:3:"AOR";i:1;s:39:"Angolansk kwanza reajustado (1995-1999)";}s:3:"AOS";a:2:{i:0;s:3:"AOS";i:1;s:16:"Angolansk escudo";}s:3:"ARA";a:2:{i:0;s:3:"ARA";i:1;s:18:"Argentinsk austral";}s:3:"ARM";a:2:{i:0;s:3:"ARM";i:1;s:31:"Argentinsk peso moneda nacional";}s:3:"ARP";a:2:{i:0;s:3:"ARP";i:1;s:27:"Argentinsk peso (1983-1985)";}s:3:"ARS";a:2:{i:0;s:4:"Arg$";i:1;s:15:"Argentinsk peso";}s:3:"ATS";a:2:{i:0;s:3:"ATS";i:1;s:19:"Østrigsk schilling";}s:3:"AUD";a:2:{i:0;s:2:"$A";i:1;s:16:"Australsk dollar";}s:3:"AUP";a:2:{i:0;s:3:"AUP";i:1;s:14:"Australsk pund";}s:3:"AWG";a:2:{i:0;s:3:"AWG";i:1;s:15:"Arubansk gylden";}s:3:"AZM";a:2:{i:0;s:3:"AZM";i:1;s:20:"Aserbajdsjansk manat";}s:3:"BAD";a:2:{i:0;s:3:"BAD";i:1;s:26:"Bosnien-Hercegovinsk dinar";}s:3:"BAM";a:2:{i:0;s:2:"KM";i:1;s:37:"Bosnien-Hercegovinsk konvertibel mark";}s:3:"BAN";a:2:{i:0;s:3:"BAN";i:1;s:29:"Ny bosnien-hercegovinsk dinar";}s:3:"BBD";a:2:{i:0;s:4:"BDS$";i:1;s:16:"Barbadisk dollar";}s:3:"BDT";a:2:{i:0;s:2:"Tk";i:1;s:18:"Bangladeshisk taka";}s:3:"BEC";a:2:{i:0;s:3:"BEC";i:1;s:27:"Belgisk franc (konvertibel)";}s:3:"BEF";a:2:{i:0;s:2:"BF";i:1;s:13:"Belgisk franc";}s:3:"BEL";a:2:{i:0;s:3:"BEL";i:1;s:25:"Belgisk franc (financial)";}s:3:"BGL";a:2:{i:0;s:3:"lev";i:1;s:17:"Bulgarsk hard lev";}s:3:"BGM";a:2:{i:0;s:3:"BGM";i:1;s:22:"Bulgarsk socialist lev";}s:3:"BGN";a:2:{i:0;s:3:"BGN";i:1;s:15:"Ny Bulgarsk lev";}s:3:"BGO";a:2:{i:0;s:3:"BGO";i:1;s:24:"Bulgarsk lev (1879-1952)";}s:3:"BHD";a:2:{i:0;s:2:"BD";i:1;s:15:"Bahrainsk dinar";}s:3:"BIF";a:2:{i:0;s:3:"Fbu";i:1;s:15:"Burundisk franc";}s:3:"BMD";a:2:{i:0;s:4:"Ber$";i:1;s:17:"Bermudansk dollar";}s:3:"BMP";a:2:{i:0;s:3:"BMP";i:1;s:15:"Bermudansk pund";}s:3:"BND";a:2:{i:0;s:3:"BND";i:1;s:15:"Bruneisk dollar";}s:3:"BOP";a:2:{i:0;s:3:"BOP";i:1;s:15:"Boliviansk peso";}s:3:"BOV";a:2:{i:0;s:3:"BOV";i:1;s:16:"Boliviansk mvdol";}s:3:"BRB";a:2:{i:0;s:3:"BRB";i:1;s:37:"Brasiliansk cruzeiro novo (1967-1986)";}s:3:"BRC";a:2:{i:0;s:3:"BRC";i:1;s:19:"Brasiliansk cruzado";}s:3:"BRE";a:2:{i:0;s:3:"BRE";i:1;s:32:"Brasiliansk cruzeiro (1990-1993)";}s:3:"BRL";a:2:{i:0;s:2:"R$";i:1;s:16:"Brasiliansk real";}s:3:"BRN";a:2:{i:0;s:3:"BRN";i:1;s:24:"Brasiliansk cruzado novo";}s:3:"BRR";a:2:{i:0;s:3:"BRR";i:1;s:20:"Brasiliansk cruzeiro";}s:3:"BRZ";a:2:{i:0;s:3:"BRZ";i:1;s:32:"Brasiliansk cruzeiro (1942-1967)";}s:3:"BSD";a:2:{i:0;s:3:"BSD";i:1;s:16:"Bahamansk dollar";}s:3:"BSP";a:2:{i:0;s:3:"BSP";i:1;s:14:"Bahamansk pund";}s:3:"BTN";a:2:{i:0;s:2:"Nu";i:1;s:17:"Bhutansk ngultrum";}s:3:"BTR";a:2:{i:0;s:3:"BTR";i:1;s:14:"Bhutansk rupee";}s:3:"BUK";a:2:{i:0;s:3:"BUK";i:1;s:14:"Burmesisk kyat";}s:3:"BUR";a:2:{i:0;s:3:"BUR";i:1;s:15:"Burmesisk rupee";}s:3:"BWP";a:2:{i:0;s:3:"BWP";i:1;s:14:"Botswansk pula";}s:3:"BYB";a:2:{i:0;s:3:"BYB";i:1;s:33:"Ny hviderussisk rubel (1994-1999)";}s:3:"BYL";a:2:{i:0;s:3:"BYL";i:1;s:30:"Hviderussisk rubel (1992-1994)";}s:3:"BYR";a:2:{i:0;s:3:"Rbl";i:1;s:18:"Hviderussisk rubel";}s:3:"BZD";a:2:{i:0;s:3:"BZ$";i:1;s:15:"Belizisk dollar";}s:3:"BZH";a:2:{i:0;s:3:"BZH";i:1;s:23:"Britisk Honduras dollar";}s:3:"CAD";a:2:{i:0;s:4:"Can$";i:1;s:15:"Canadisk dollar";}s:3:"CDF";a:2:{i:0;s:3:"CDF";i:1;s:27:"Congolesisk franc congolais";}s:3:"CDG";a:2:{i:0;s:3:"CDG";i:1;s:17:"Congolesisk franc";}s:3:"CDL";a:2:{i:0;s:3:"CDL";i:1;s:17:"Congolesisk Zaire";}s:3:"CFF";a:2:{i:0;s:3:"CFF";i:1;s:44:"CFA-franc fra den Centralafrikanske republik";}s:3:"CHF";a:2:{i:0;s:3:"SwF";i:1;s:16:"Schweizisk franc";}s:3:"CKD";a:2:{i:0;s:3:"CKD";i:1;s:21:"Dollar fra Cookøerne";}s:3:"CLC";a:2:{i:0;s:3:"CLC";i:1;s:15:"Chilensk condor";}s:3:"CLE";a:2:{i:0;s:3:"CLE";i:1;s:15:"Chilensk escudo";}s:3:"CLF";a:2:{i:0;s:3:"CLF";i:1;s:28:"Chilensk unidades de fomento";}s:3:"CLP";a:2:{i:0;s:3:"Ch$";i:1;s:13:"Chilensk peso";}s:3:"CMF";a:2:{i:0;s:3:"CMF";i:1;s:20:"Camerounsk CFA-franc";}s:3:"CNP";a:2:{i:0;s:3:"CNP";i:1;s:26:"Kinesisk jen min piao yuan";}s:3:"CNY";a:2:{i:0;s:1:"Y";i:1;s:22:"Kinesisk yuan renminbi";}s:3:"COB";a:2:{i:0;s:3:"COB";i:1;s:21:"Colombiansk papirpeso";}s:3:"COF";a:2:{i:0;s:3:"COF";i:1;s:21:"Congolesisk CFA-franc";}s:3:"COP";a:2:{i:0;s:4:"Col$";i:1;s:16:"Colombiansk peso";}s:3:"CRC";a:2:{i:0;s:1:"C";i:1;s:18:"Costaricansk colon";}s:3:"CSC";a:2:{i:0;s:3:"CSC";i:1;s:22:"Tjekkoslovakisk koruna";}s:3:"CSK";a:2:{i:0;s:3:"CSK";i:1;s:27:"Tjekkoslovakisk hard koruna";}s:3:"CUP";a:2:{i:0;s:3:"CUP";i:1;s:12:"Cubansk peso";}s:3:"CVE";a:2:{i:0;s:5:"CVEsc";i:1;s:17:"Kapverdisk escudo";}s:3:"CWG";a:2:{i:0;s:3:"CWG";i:1;s:16:"Curacaosk gylden";}s:3:"CYP";a:2:{i:0;s:3:"£C";i:1;s:15:"Cypriotisk pund";}s:3:"CZK";a:2:{i:0;s:3:"CZK";i:1;s:15:"Tjekkisk koruna";}s:3:"DDM";a:2:{i:0;s:3:"DDM";i:1;s:13:"Østtysk mark";}s:3:"DEM";a:2:{i:0;s:3:"DEM";i:1;s:9:"Tysk mark";}s:3:"DES";a:2:{i:0;s:3:"DES";i:1;s:14:"Tysk sperrmark";}s:3:"DJF";a:2:{i:0;s:2:"DF";i:1;s:16:"Djiboutisk franc";}s:3:"DKK";a:2:{i:0;s:2:"kr";i:1;s:11:"Dansk krone";}s:3:"DOP";a:2:{i:0;s:3:"RD$";i:1;s:16:"Dominikansk peso";}s:3:"DZD";a:2:{i:0;s:2:"DA";i:1;s:14:"Algerisk dinar";}s:3:"DZF";a:2:{i:0;s:3:"DZF";i:1;s:17:"Ny algerisk franc";}s:3:"DZG";a:2:{i:0;s:3:"DZG";i:1;s:23:"Algerisk franc germinal";}s:3:"ECS";a:2:{i:0;s:3:"ECS";i:1;s:18:"Ecuadoriansk sucre";}s:3:"EEK";a:2:{i:0;s:3:"EEK";i:1;s:12:"Estisk kroon";}s:3:"EGP";a:2:{i:0;s:3:"EGP";i:1;s:13:"Egyptisk pund";}s:3:"ERN";a:2:{i:0;s:3:"ERN";i:1;s:15:"Eritreisk nakfa";}s:3:"ESP";a:2:{i:0;s:3:"ESP";i:1;s:13:"Spansk peseta";}s:3:"ETB";a:2:{i:0;s:2:"Br";i:1;s:13:"Etiopisk birr";}s:3:"ETD";a:2:{i:0;s:3:"ETD";i:1;s:15:"Etiopisk dollar";}s:3:"EUR";a:2:{i:0;s:3:"€";i:1;s:4:"Euro";}s:3:"FIM";a:2:{i:0;s:3:"FIM";i:1;s:10:"Finsk mark";}s:3:"FIN";a:2:{i:0;s:3:"FIN";i:1;s:22:"Finsk mark (1860-1962)";}s:3:"FJD";a:2:{i:0;s:2:"F$";i:1;s:15:"Fijiansk dollar";}s:3:"FJP";a:2:{i:0;s:3:"FJP";i:1;s:13:"Fijiansk pund";}s:3:"FKP";a:2:{i:0;s:3:"FKP";i:1;s:24:"Pund fra Falklandsøerne";}s:3:"FOK";a:2:{i:0;s:3:"FOK";i:1;s:14:"Færøsk krone";}s:3:"FRF";a:2:{i:0;s:3:"FRF";i:1;s:12:"Fransk franc";}s:3:"GAF";a:2:{i:0;s:3:"GAF";i:1;s:20:"Gabonesisk CFA-franc";}s:3:"GBP";a:2:{i:0;s:2:"£";i:1;s:12:"Britisk pund";}s:3:"GEK";a:2:{i:0;s:3:"GEK";i:1;s:20:"Georgisk kupon larit";}s:3:"GEL";a:2:{i:0;s:4:"lari";i:1;s:13:"Georgisk lari";}s:3:"GHC";a:2:{i:0;s:3:"GHC";i:1;s:14:"Ghanesisk cedi";}s:3:"GHO";a:2:{i:0;s:3:"GHO";i:1;s:21:"Gammel ghanesisk cedi";}s:3:"GHP";a:2:{i:0;s:3:"GHP";i:1;s:14:"Ghanesisk pund";}s:3:"GHR";a:2:{i:0;s:3:"GHR";i:1;s:25:"Ghanesisk revalueret cedi";}s:3:"GIP";a:2:{i:0;s:3:"GIP";i:1;s:17:"Gibraltarisk pund";}s:3:"GLK";a:2:{i:0;s:3:"GLK";i:1;s:17:"Grønlandsk krone";}s:3:"GMD";a:2:{i:0;s:3:"GMD";i:1;s:14:"Gambisk dalasi";}s:3:"GMP";a:2:{i:0;s:3:"GMP";i:1;s:12:"Gambisk pund";}s:3:"GNF";a:2:{i:0;s:2:"GF";i:1;s:15:"Guineansk franc";}s:3:"GNI";a:2:{i:0;s:3:"GNI";i:1;s:27:"Guineansk franc (1960-1972)";}s:3:"GNS";a:2:{i:0;s:3:"GNS";i:1;s:14:"Guineansk syli";}s:3:"GPF";a:2:{i:0;s:3:"GPF";i:1;s:17:"Guadeloupsk franc";}s:3:"GQE";a:2:{i:0;s:3:"GQE";i:1;s:35:"Ækvatorialguineask ekwele guineana";}s:3:"GQF";a:2:{i:0;s:3:"GQF";i:1;s:26:"Ækvatorialguineask franco";}s:3:"GQP";a:2:{i:0;s:3:"GQP";i:1;s:35:"Ækvatorialguineask peseta guineana";}s:3:"GRD";a:2:{i:0;s:3:"GRD";i:1;s:14:"Græsk drachma";}s:3:"GRN";a:2:{i:0;s:3:"GRN";i:1;s:17:"Ny græsk drachma";}s:3:"GTQ";a:2:{i:0;s:1:"Q";i:1;s:20:"Guatemalansk quetzal";}s:3:"GUF";a:2:{i:0;s:3:"GUF";i:1;s:27:"Fransk-guyansk franc guiana";}s:3:"GWE";a:2:{i:0;s:3:"GWE";i:1;s:25:"Portugisisk guinea escudo";}s:3:"GWM";a:2:{i:0;s:3:"GWM";i:1;s:27:"Portugisisk guinea mil reis";}s:3:"GWP";a:2:{i:0;s:3:"GWP";i:1;s:14:"Guineansk peso";}s:3:"GYD";a:2:{i:0;s:2:"G$";i:1;s:14:"Guyansk dollar";}s:3:"HNL";a:2:{i:0;s:1:"L";i:1;s:18:"Honduransk lempira";}s:3:"HRD";a:2:{i:0;s:3:"HRD";i:1;s:14:"Kroatisk dinar";}s:3:"HRK";a:2:{i:0;s:3:"HRK";i:1;s:13:"Kroatisk kuna";}s:3:"HTG";a:2:{i:0;s:3:"HTG";i:1;s:14:"Haitisk gourde";}s:3:"HUF";a:2:{i:0;s:2:"Ft";i:1;s:14:"Ungarsk forint";}s:3:"IBP";a:2:{i:0;s:3:"IBP";i:1;s:13:"Nordirsk pund";}s:3:"IDG";a:2:{i:0;s:3:"IDG";i:1;s:23:"Indonesisk nica guilder";}s:3:"IDJ";a:2:{i:0;s:3:"IDJ";i:1;s:22:"Indonesisk java rupiah";}s:3:"IDN";a:2:{i:0;s:3:"IDN";i:1;s:20:"Ny indonesisk rupiah";}s:3:"IDR";a:2:{i:0;s:2:"Rp";i:1;s:17:"Indonesisk pupiah";}s:3:"IEP";a:2:{i:0;s:4:"IR£";i:1;s:9:"Irsk pund";}s:3:"ILL";a:2:{i:0;s:3:"ILL";i:1;s:15:"Israelsk shekel";}s:3:"ILP";a:2:{i:0;s:3:"ILP";i:1;s:13:"Israelsk pund";}s:3:"ILS";a:2:{i:0;s:3:"ILS";i:1;s:18:"Ny israelsk shekel";}s:3:"INR";a:2:{i:0;s:18:"=0#Rs.|1#Re.|1 + * The latest version of PRADO can be obtained from: + * {@link http://prado.sourceforge.net/} + * + * @author Wei Zhuo + * @version $Revision: 1.3 $ $Date: 2005/08/27 03:21:12 $ + * @package System.I18N.core + */ + + + /** + * For a given DSN (database connection string), return some information + * about the DSN. This function comes from PEAR's DB package. + * + * LICENSE: This source file is subject to version 3.0 of the PHP license + * that is available through the world-wide-web at the following URI: + * http://www.php.net/license/3_0.txt. If you did not receive a copy of + * the PHP License and are unable to obtain it through the web, please + * send a note to license@php.net so we can mail you a copy immediately. + * + * @param string DSN format, similar to PEAR's DB + * @return array DSN information. + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @link http://pear.php.net/package/DB + */ + function parseDSN($dsn) + { + if (is_array($dsn)) { + return $dsn; + } + + $parsed = array( + 'phptype' => false, + 'dbsyntax' => false, + 'username' => false, + 'password' => false, + 'protocol' => false, + 'hostspec' => false, + 'port' => false, + 'socket' => false, + 'database' => false + ); + + // Find phptype and dbsyntax + if (($pos = strpos($dsn, '://')) !== false) { + $str = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 3); + } else { + $str = $dsn; + $dsn = null; + } + + // Get phptype and dbsyntax + // $str => phptype(dbsyntax) + if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { + $parsed['phptype'] = $arr[1]; + $parsed['dbsyntax'] = (empty($arr[2])) ? $arr[1] : $arr[2]; + } else { + $parsed['phptype'] = $str; + $parsed['dbsyntax'] = $str; + } + + if (empty($dsn)) { + return $parsed; + } + + // Get (if found): username and password + // $dsn => username:password@protocol+hostspec/database + if (($at = strrpos($dsn,'@')) !== false) { + $str = substr($dsn, 0, $at); + $dsn = substr($dsn, $at + 1); + if (($pos = strpos($str, ':')) !== false) { + $parsed['username'] = rawurldecode(substr($str, 0, $pos)); + $parsed['password'] = rawurldecode(substr($str, $pos + 1)); + } else { + $parsed['username'] = rawurldecode($str); + } + } + + // Find protocol and hostspec + + // $dsn => proto(proto_opts)/database + if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { + $proto = $match[1]; + $proto_opts = (!empty($match[2])) ? $match[2] : false; + $dsn = $match[3]; + + // $dsn => protocol+hostspec/database (old format) + } else { + if (strpos($dsn, '+') !== false) { + list($proto, $dsn) = explode('+', $dsn, 2); + } + if (strpos($dsn, '/') !== false) { + list($proto_opts, $dsn) = explode('/', $dsn, 2); + } else { + $proto_opts = $dsn; + $dsn = null; + } + } + + // process the different protocol options + $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; + $proto_opts = rawurldecode($proto_opts); + if ($parsed['protocol'] == 'tcp') { + if (strpos($proto_opts, ':') !== false) { + list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts); + } else { + $parsed['hostspec'] = $proto_opts; + } + } elseif ($parsed['protocol'] == 'unix') { + $parsed['socket'] = $proto_opts; + } + + // Get dabase if any + // $dsn => database + if (!empty($dsn)) { + // /database + if (($pos = strpos($dsn, '?')) === false) { + $parsed['database'] = $dsn; + // /database?param1=value1¶m2=value2 + } else { + $parsed['database'] = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 1); + if (strpos($dsn, '&') !== false) { + $opts = explode('&', $dsn); + } else { // database?param1=value1 + $opts = array($dsn); + } + foreach ($opts as $opt) { + list($key, $value) = explode('=', $opt); + if (!isset($parsed[$key])) { // don't allow params overwrite + $parsed[$key] = rawurldecode($value); + } + } + } + } + + return $parsed; + } + + + /** + * Convert strings to UTF-8 via iconv. NB, the result may not by UTF-8 + * if the conversion failed. + * @param string string to convert to UTF-8 + * @return string UTF-8 encoded string, original string if iconv failed. + */ + function I18N_toUTF8($string, $from) + { + if($from != 'UTF-8') + { + $s = iconv($from,'UTF-8',$string); //to UTF-8 + return $s !== false ? $s : $string; //it could return false + } + return $string; + } + + /** + * Convert UTF-8 strings to a different encoding. NB. The result + * may not have been encoded if iconv fails. + * @param string the UTF-8 string for conversion + * @return string encoded string. + */ + function I18N_toEncoding($string, $to) + { + if($to != 'UTF-8') + { + $s = iconv('UTF-8', $to, $string); + return $s !== false ? $s : $string; + } + return $string; + } + diff --git a/gui/baculum/framework/I18N/schema/mysql.sql b/gui/baculum/framework/I18N/schema/mysql.sql new file mode 100644 index 0000000000..55b0fd2b3a --- /dev/null +++ b/gui/baculum/framework/I18N/schema/mysql.sql @@ -0,0 +1,58 @@ +# +# Database : `messages` for I18N in PRADO +# + +# -------------------------------------------------------- + +# +# Table structure for table `catalogue` +# + +CREATE TABLE `catalogue` ( + `cat_id` int(11) NOT NULL auto_increment, + `name` varchar(100) NOT NULL default '', + `source_lang` varchar(100) NOT NULL default '', + `target_lang` varchar(100) NOT NULL default '', + `date_created` int(11) NOT NULL default '0', + `date_modified` int(11) NOT NULL default '0', + `author` varchar(255) NOT NULL default '', + PRIMARY KEY (`cat_id`) +) TYPE=MyISAM AUTO_INCREMENT=4 ; + +# +# Dumping data for table `catalogue` +# + +INSERT INTO `catalogue` VALUES (1, 'messages', '', '', 0, 0, ''); +INSERT INTO `catalogue` VALUES (2, 'messages.en', '', '', 0, 0, ''); +INSERT INTO `catalogue` VALUES (3, 'messages.en_AU', '', '', 0, 0, ''); + +# -------------------------------------------------------- + +# +# Table structure for table `trans_unit` +# + +CREATE TABLE `trans_unit` ( + `msg_id` int(11) NOT NULL auto_increment, + `cat_id` int(11) NOT NULL default '1', + `id` varchar(255) NOT NULL default '', + `source` text NOT NULL, + `target` text NOT NULL, + `comments` text NOT NULL, + `date_added` int(11) NOT NULL default '0', + `date_modified` int(11) NOT NULL default '0', + `author` varchar(255) NOT NULL default '', + `translated` tinyint(1) NOT NULL default '0', + PRIMARY KEY (`msg_id`) +) TYPE=MyISAM AUTO_INCREMENT=6 ; + +# +# Dumping data for table `trans_unit` +# + +INSERT INTO `trans_unit` VALUES (1, 1, '1', 'Hello', 'Hello World', '', 0, 0, '', 1); +INSERT INTO `trans_unit` VALUES (2, 2, '', 'Hello', 'Hello :)', '', 0, 0, '', 0); +INSERT INTO `trans_unit` VALUES (3, 1, '1', 'Welcome', 'Welcome', '', 0, 0, '', 0); +INSERT INTO `trans_unit` VALUES (4, 3, '', 'Hello', 'G\'day Mate!', '', 0, 0, '', 0); +INSERT INTO `trans_unit` VALUES (5, 3, '', 'Welcome', 'Welcome Mate!', '', 0, 0, '', 0); \ No newline at end of file diff --git a/gui/baculum/framework/I18N/schema/postgresql.sql b/gui/baculum/framework/I18N/schema/postgresql.sql new file mode 100644 index 0000000000..938126aa26 --- /dev/null +++ b/gui/baculum/framework/I18N/schema/postgresql.sql @@ -0,0 +1,40 @@ + +-- Table structure for table catalogue + +CREATE TABLE catalogue ( + cat_id serial NOT NULL primary key, + name varchar(100) NOT NULL default '', + source_lang varchar(100) NOT NULL default '', + target_lang varchar(100) NOT NULL default '', + date_created int NOT NULL default 0, + date_modified int NOT NULL default 0, + author varchar(255) NOT NULL default '' +); + +-- Dumping data for table catalogue + +INSERT INTO catalogue VALUES (nextval('catalogue_cat_id_seq'), 'messages', '', '', 0, 0, ''); +INSERT INTO catalogue VALUES (nextval('catalogue_cat_id_seq'), 'messages.en', '', '', 0, 0, ''); +INSERT INTO catalogue VALUES (nextval('catalogue_cat_id_seq'), 'messages.en_AU', '', '', 0, 0, ''); + +-- Table structure for table trans_unit + +CREATE TABLE trans_unit ( + msg_id serial NOT NULL primary key, + cat_id int NOT NULL default 1, + id varchar(255) NOT NULL default '', + source text NOT NULL, + target text NOT NULL default '', + comments text NOT NULL default '', + date_added int NOT NULL default 0, + date_modified int NOT NULL default 0, + author varchar(255) NOT NULL default '', + translated smallint NOT NULL default 0 +); + +INSERT INTO trans_unit VALUES (nextval('trans_unit_msg_id_seq'), 1, '1', 'Hello', 'Hello World', '', 0, 0, '', 1); +INSERT INTO trans_unit VALUES (nextval('trans_unit_msg_id_seq'), 2, '', 'Hello', 'Hello :)', '', 0, 0, '', 0); +INSERT INTO trans_unit VALUES (nextval('trans_unit_msg_id_seq'), 1, '1', 'Welcome', 'Welcome', '', 0, 0, '', 0); +INSERT INTO trans_unit VALUES (nextval('trans_unit_msg_id_seq'), 3, '', 'Hello', 'G''day Mate!', '', 0, 0, '', 0); +INSERT INTO trans_unit VALUES (nextval('trans_unit_msg_id_seq'), 3, '', 'Welcome', 'Welcome Mate!', '', 0, 0, '', 0); + diff --git a/gui/baculum/framework/I18N/schema/sqlite.sql b/gui/baculum/framework/I18N/schema/sqlite.sql new file mode 100644 index 0000000000..68d15d4d7b --- /dev/null +++ b/gui/baculum/framework/I18N/schema/sqlite.sql @@ -0,0 +1,49 @@ +# Database: messages.db for I18N in PRADO +# -------------------------------------------------------- + +# +# Table structure for table: catalogue +# +CREATE TABLE catalogue ( + cat_id INTEGER PRIMARY KEY, + name VARCHAR NOT NULL, + source_lang VARCHAR , + target_lang VARCHAR , + date_created INT, + date_modified INT, + author VARCHAR ); + +# +# Dumping data for table: catalogue +# +INSERT INTO catalogue VALUES ('1', 'messages', '', '', '', '', ''); +INSERT INTO catalogue VALUES ('2', 'messages.en', '', '', '', '', ''); +INSERT INTO catalogue VALUES ('3', 'messages.en_AU', '', '', '', '', ''); +# -------------------------------------------------------- + + +# +# Table structure for table: trans_unit +# +CREATE TABLE trans_unit ( + msg_id INTEGER PRIMARY KEY, + cat_id INTEGER NOT NULL DEFAULT '1', + id VARCHAR, + source TEXT, + target TEXT, + comments TEXT, + date_added INT, + date_modified INT, + author VARCHAR, + translated INT(1) NOT NULL DEFAULT '0' ); + +# +# Dumping data for table: trans_unit +# +INSERT INTO trans_unit VALUES ('1', '1', '1', 'Hello', 'Hello World', '', '', '', '', '1'); +INSERT INTO trans_unit VALUES ('2', '2', '', 'Hello', 'Hello :)', '', '', '', '', '0'); +INSERT INTO trans_unit VALUES ('3', '1', '1', 'Welcome', 'Welcome', '', '', '', '', '0'); +INSERT INTO trans_unit VALUES ('4', '3', '', 'Hello', 'G''day Mate!', '', '', '', '', '0'); +INSERT INTO trans_unit VALUES ('5', '3', '', 'Welcome', 'Welcome Mate!', '', '', '', '', '0'); +# -------------------------------------------------------- + diff --git a/gui/baculum/framework/IO/TTarFileExtractor.php b/gui/baculum/framework/IO/TTarFileExtractor.php new file mode 100644 index 0000000000..ca67320da5 --- /dev/null +++ b/gui/baculum/framework/IO/TTarFileExtractor.php @@ -0,0 +1,573 @@ + + * @copyright Copyright © 1997-2003 The PHP Group + * @version $Id: TTarFileExtractor.php 3188 2012-07-12 12:13:23Z ctrlaltca $ + * @package System.IO + */ + +/* vim: set ts=4 sw=4: */ +// +----------------------------------------------------------------------+ +// | PHP Version 4 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available through the world-wide-web at the following url: | +// | http://www.php.net/license/3_0.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Author: Vincent Blavet | +// +----------------------------------------------------------------------+ +// +// $Id: TTarFileExtractor.php 3188 2012-07-12 12:13:23Z ctrlaltca $ + +/** + * TTarFileExtractor class + * + * @author Vincent Blavet + * @version $Id: TTarFileExtractor.php 3188 2012-07-12 12:13:23Z ctrlaltca $ + * @package System.IO + * @since 3.0 + */ +class TTarFileExtractor +{ + /** + * @var string Name of the Tar + */ + private $_tarname=''; + + /** + * @var file descriptor + */ + private $_file=0; + + /** + * @var string Local Tar name of a remote Tar (http:// or ftp://) + */ + private $_temp_tarname=''; + + /** + * Archive_Tar Class constructor. This flavour of the constructor only + * declare a new Archive_Tar object, identifying it by the name of the + * tar file. + * + * @param string $p_tarname The name of the tar archive to create + * @access public + */ + public function __construct($p_tarname) + { + $this->_tarname = $p_tarname; + } + + public function __destruct() + { + $this->_close(); + // ----- Look for a local copy to delete + if ($this->_temp_tarname != '') + @unlink($this->_temp_tarname); + } + + public function extract($p_path='') + { + return $this->extractModify($p_path, ''); + } + + /** + * This method extract all the content of the archive in the directory + * indicated by $p_path. When relevant the memorized path of the + * files/dir can be modified by removing the $p_remove_path path at the + * beginning of the file/dir path. + * While extracting a file, if the directory path does not exists it is + * created. + * While extracting a file, if the file already exists it is replaced + * without looking for last modification date. + * While extracting a file, if the file already exists and is write + * protected, the extraction is aborted. + * While extracting a file, if a directory with the same name already + * exists, the extraction is aborted. + * While extracting a directory, if a file with the same name already + * exists, the extraction is aborted. + * While extracting a file/directory if the destination directory exist + * and is write protected, or does not exist but can not be created, + * the extraction is aborted. + * If after extraction an extracted file does not show the correct + * stored file size, the extraction is aborted. + * When the extraction is aborted, a PEAR error text is set and false + * is returned. However the result can be a partial extraction that may + * need to be manually cleaned. + * + * @param string $p_path The path of the directory where the + * files/dir need to by extracted. + * @param string $p_remove_path Part of the memorized path that can be + * removed if present at the beginning of + * the file/dir path. + * @return boolean true on success, false on error. + * @access public + */ + protected function extractModify($p_path, $p_remove_path) + { + $v_result = true; + $v_list_detail = array(); + + if ($v_result = $this->_openRead()) { + $v_result = $this->_extractList($p_path, $v_list_detail, + "complete", 0, $p_remove_path); + $this->_close(); + } + + return $v_result; + } + + protected function _error($p_message) + { + throw new Exception($p_message); + } + + private function _isArchive($p_filename=null) + { + if ($p_filename == null) { + $p_filename = $this->_tarname; + } + clearstatcache(); + return @is_file($p_filename); + } + + private function _openRead() + { + if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { + + // ----- Look if a local copy need to be done + if ($this->_temp_tarname == '') { + $this->_temp_tarname = uniqid('tar').'.tmp'; + if (!$v_file_from = @fopen($this->_tarname, 'rb')) { + $this->_error('Unable to open in read mode \'' + .$this->_tarname.'\''); + $this->_temp_tarname = ''; + return false; + } + if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { + $this->_error('Unable to open in write mode \'' + .$this->_temp_tarname.'\''); + $this->_temp_tarname = ''; + return false; + } + while ($v_data = @fread($v_file_from, 1024)) + @fwrite($v_file_to, $v_data); + @fclose($v_file_from); + @fclose($v_file_to); + } + + // ----- File to open if the local copy + $v_filename = $this->_temp_tarname; + + } else + // ----- File to open if the normal Tar file + $v_filename = $this->_tarname; + + $this->_file = @fopen($v_filename, "rb"); + + if ($this->_file == 0) { + $this->_error('Unable to open in read mode \''.$v_filename.'\''); + return false; + } + + return true; + } + + private function _close() + { + //if (isset($this->_file)) { + if (is_resource($this->_file)) + { + @fclose($this->_file); + $this->_file = 0; + } + + // ----- Look if a local copy need to be erase + // Note that it might be interesting to keep the url for a time : ToDo + if ($this->_temp_tarname != '') { + @unlink($this->_temp_tarname); + $this->_temp_tarname = ''; + } + + return true; + } + + private function _cleanFile() + { + $this->_close(); + + // ----- Look for a local copy + if ($this->_temp_tarname != '') { + // ----- Remove the local copy but not the remote tarname + @unlink($this->_temp_tarname); + $this->_temp_tarname = ''; + } else { + // ----- Remove the local tarname file + @unlink($this->_tarname); + } + $this->_tarname = ''; + + return true; + } + + private function _readBlock() + { + $v_block = null; + if (is_resource($this->_file)) { + $v_block = @fread($this->_file, 512); + } + return $v_block; + } + + private function _jumpBlock($p_len=null) + { + if (is_resource($this->_file)) { + if ($p_len === null) + $p_len = 1; + + @fseek($this->_file, @ftell($this->_file)+($p_len*512)); + } + return true; + } + + private function _readHeader($v_binary_data, &$v_header) + { + if (strlen($v_binary_data)==0) { + $v_header['filename'] = ''; + return true; + } + + if (strlen($v_binary_data) != 512) { + $v_header['filename'] = ''; + $this->_error('Invalid block size : '.strlen($v_binary_data)); + return false; + } + + // ----- Calculate the checksum + $v_checksum = 0; + // ..... First part of the header + for ($i=0; $i<148; $i++) + $v_checksum+=ord(substr($v_binary_data,$i,1)); + // ..... Ignore the checksum value and replace it by ' ' (space) + for ($i=148; $i<156; $i++) + $v_checksum += ord(' '); + // ..... Last part of the header + for ($i=156; $i<512; $i++) + $v_checksum+=ord(substr($v_binary_data,$i,1)); + + $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" + ."a8checksum/a1typeflag/a100link/a6magic/a2version/" + ."a32uname/a32gname/a8devmajor/a8devminor", + $v_binary_data); + + // ----- Extract the checksum + $v_header['checksum'] = OctDec(trim($v_data['checksum'])); + if ($v_header['checksum'] != $v_checksum) { + $v_header['filename'] = ''; + + // ----- Look for last block (empty block) + if (($v_checksum == 256) && ($v_header['checksum'] == 0)) + return true; + + $this->_error('Invalid checksum for file "'.$v_data['filename'] + .'" : '.$v_checksum.' calculated, ' + .$v_header['checksum'].' expected'); + return false; + } + + // ----- Extract the properties + $v_header['filename'] = trim($v_data['filename']); + $v_header['mode'] = OctDec(trim($v_data['mode'])); + $v_header['uid'] = OctDec(trim($v_data['uid'])); + $v_header['gid'] = OctDec(trim($v_data['gid'])); + $v_header['size'] = OctDec(trim($v_data['size'])); + $v_header['mtime'] = OctDec(trim($v_data['mtime'])); + if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { + $v_header['size'] = 0; + } + return true; + } + + private function _readLongHeader(&$v_header) + { + $v_filename = ''; + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + $v_filename .= $v_content; + } + + // ----- Read the next header + $v_binary_data = $this->_readBlock(); + + if (!$this->_readHeader($v_binary_data, $v_header)) + return false; + + $v_header['filename'] = $v_filename; + + return true; + } + + protected function _extractList($p_path, &$p_list_detail, $p_mode, + $p_file_list, $p_remove_path) + { + $v_result=true; + $v_nb = 0; + $v_extract_all = true; + $v_listing = false; + + $p_path = $this->_translateWinPath($p_path, false); + if ($p_path == '' || (substr($p_path, 0, 1) != '/' + && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { + $p_path = "./".$p_path; + } + $p_remove_path = $this->_translateWinPath($p_remove_path); + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) + $p_remove_path .= '/'; + $p_remove_path_size = strlen($p_remove_path); + + switch ($p_mode) { + case "complete" : + $v_extract_all = true; + $v_listing = false; + break; + case "partial" : + $v_extract_all = false; + $v_listing = false; + break; + case "list" : + $v_extract_all = false; + $v_listing = true; + break; + default : + $this->_error('Invalid extract mode ('.$p_mode.')'); + return false; + } + + clearstatcache(); + + while (strlen($v_binary_data = $this->_readBlock()) != 0) + { + $v_extract_file = false; + $v_extraction_stopped = 0; + + if (!$this->_readHeader($v_binary_data, $v_header)) + return false; + + if ($v_header['filename'] == '') { + continue; + } + + // ----- Look for long filename + if ($v_header['typeflag'] == 'L') { + if (!$this->_readLongHeader($v_header)) + return false; + } + + if ((!$v_extract_all) && (is_array($p_file_list))) { + // ----- By default no unzip if the file is not found + $v_extract_file = false; + + for ($i=0; $i strlen($p_file_list[$i])) + && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) + == $p_file_list[$i])) { + $v_extract_file = true; + break; + } + } + + // ----- It is a file, so compare the file names + elseif ($p_file_list[$i] == $v_header['filename']) { + $v_extract_file = true; + break; + } + } + } else { + $v_extract_file = true; + } + + // ----- Look if this file need to be extracted + if (($v_extract_file) && (!$v_listing)) + { + if (($p_remove_path != '') + && (substr($v_header['filename'], 0, $p_remove_path_size) + == $p_remove_path)) + $v_header['filename'] = substr($v_header['filename'], + $p_remove_path_size); + if (($p_path != './') && ($p_path != '/')) { + while (substr($p_path, -1) == '/') + $p_path = substr($p_path, 0, strlen($p_path)-1); + + if (substr($v_header['filename'], 0, 1) == '/') + $v_header['filename'] = $p_path.$v_header['filename']; + else + $v_header['filename'] = $p_path.'/'.$v_header['filename']; + } + if (file_exists($v_header['filename'])) { + if ( (@is_dir($v_header['filename'])) + && ($v_header['typeflag'] == '')) { + $this->_error('File '.$v_header['filename'] + .' already exists as a directory'); + return false; + } + if ( ($this->_isArchive($v_header['filename'])) + && ($v_header['typeflag'] == "5")) { + $this->_error('Directory '.$v_header['filename'] + .' already exists as a file'); + return false; + } + if (!is_writeable($v_header['filename'])) { + $this->_error('File '.$v_header['filename'] + .' already exists and is write protected'); + return false; + } + if (filemtime($v_header['filename']) > $v_header['mtime']) { + // To be completed : An error or silent no replace ? + } + } + + // ----- Check the directory availability and create it if necessary + elseif (($v_result + = $this->_dirCheck(($v_header['typeflag'] == "5" + ?$v_header['filename'] + :dirname($v_header['filename'])))) != 1) { + $this->_error('Unable to create path for '.$v_header['filename']); + return false; + } + + if ($v_extract_file) { + if ($v_header['typeflag'] == "5") { + if (!@file_exists($v_header['filename'])) { + if (!@mkdir($v_header['filename'], PRADO_CHMOD)) { + $this->_error('Unable to create directory {' + .$v_header['filename'].'}'); + return false; + } + chmod($v_header['filename'], PRADO_CHMOD); + } + } else { + if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { + $this->_error('Error while opening {'.$v_header['filename'] + .'} in write binary mode'); + return false; + } else { + $n = floor($v_header['size']/512); + for ($i=0; $i<$n; $i++) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, 512); + } + if (($v_header['size'] % 512) != 0) { + $v_content = $this->_readBlock(); + fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); + } + + @fclose($v_dest_file); + + // ----- Change the file mode, mtime + @touch($v_header['filename'], $v_header['mtime']); + // To be completed + //chmod($v_header[filename], DecOct($v_header[mode])); + } + + // ----- Check the file size + clearstatcache(); + if (filesize($v_header['filename']) != $v_header['size']) { + $this->_error('Extracted file '.$v_header['filename'] + .' does not have the correct file size \'' + .filesize($v_header['filename']) + .'\' ('.$v_header['size'] + .' expected). Archive may be corrupted.'); + return false; + } + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + } else { + $this->_jumpBlock(ceil(($v_header['size']/512))); + } + + /* TBC : Seems to be unused ... + if ($this->_compress) + $v_end_of_file = @gzeof($this->_file); + else + $v_end_of_file = @feof($this->_file); + */ + + if ($v_listing || $v_extract_file || $v_extraction_stopped) { + // ----- Log extracted files + if (($v_file_dir = dirname($v_header['filename'])) + == $v_header['filename']) + $v_file_dir = ''; + if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) + $v_file_dir = '/'; + + $p_list_detail[$v_nb++] = $v_header; + } + } + + return true; + } + + /** + * Check if a directory exists and create it (including parent + * dirs) if not. + * + * @param string $p_dir directory to check + * + * @return bool true if the directory exists or was created + */ + protected function _dirCheck($p_dir) + { + if ((@is_dir($p_dir)) || ($p_dir == '')) + return true; + + $p_parent_dir = dirname($p_dir); + + if (($p_parent_dir != $p_dir) && + ($p_parent_dir != '') && + (!$this->_dirCheck($p_parent_dir))) + return false; + + if (!@mkdir($p_dir, PRADO_CHMOD)) { + $this->_error("Unable to create directory '$p_dir'"); + return false; + } + chmod($p_dir,PRADO_CHMOD); + + return true; + } + + protected function _translateWinPath($p_path, $p_remove_disk_letter=true) + { + if (substr(PHP_OS, 0, 3) == 'WIN') { + // ----- Look for potential disk letter + if ( ($p_remove_disk_letter) + && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } +} diff --git a/gui/baculum/framework/IO/TTextWriter.php b/gui/baculum/framework/IO/TTextWriter.php new file mode 100644 index 0000000000..f5930a73b9 --- /dev/null +++ b/gui/baculum/framework/IO/TTextWriter.php @@ -0,0 +1,59 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTextWriter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.IO + */ + +/** + * TTextWriter class. + * + * TTextWriter implements a memory-based text writer. + * Content written by TTextWriter are stored in memory + * and can be obtained by calling {@link flush()}. + * + * @author Qiang Xue + * @version $Id: TTextWriter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.IO + * @since 3.0 + */ +class TTextWriter extends TComponent implements ITextWriter +{ + private $_str=''; + + /** + * Flushes the content that has been written. + * @return string the content being flushed + */ + public function flush() + { + $str=$this->_str; + $this->_str=''; + return $str; + } + + /** + * Writes a string. + * @param string string to be written + */ + public function write($str) + { + $this->_str.=$str; + } + + /** + * Writers a string and terminates it with a newline. + * @param string content to be written + * @see write + */ + public function writeLine($str='') + { + $this->write($str."\n"); + } +} + diff --git a/gui/baculum/framework/PradoBase.php b/gui/baculum/framework/PradoBase.php new file mode 100644 index 0000000000..6e884cec74 --- /dev/null +++ b/gui/baculum/framework/PradoBase.php @@ -0,0 +1,632 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: PradoBase.php 3325 2013-09-13 08:19:04Z ctrlaltca $ + * @package System + */ + +/** + * Defines the PRADO framework installation path. + */ +if(!defined('PRADO_DIR')) + define('PRADO_DIR',dirname(__FILE__)); +/** + * Defines the default permission for writable directories and files + */ +if(!defined('PRADO_CHMOD')) + define('PRADO_CHMOD',0777); + +/** + * PradoBase class. + * + * PradoBase implements a few fundamental static methods. + * + * To use the static methods, Use Prado as the class name rather than PradoBase. + * PradoBase is meant to serve as the base class of Prado. The latter might be + * rewritten for customization. + * + * @author Qiang Xue + * @version $Id: PradoBase.php 3325 2013-09-13 08:19:04Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class PradoBase +{ + /** + * File extension for Prado class files. + */ + const CLASS_FILE_EXT='.php'; + /** + * @var array list of path aliases + */ + private static $_aliases=array('System'=>PRADO_DIR); + /** + * @var array list of namespaces currently in use + */ + private static $_usings=array(); + /** + * @var TApplication the application instance + */ + private static $_application=null; + /** + * @var TLogger logger instance + */ + private static $_logger=null; + + /** + * @var array list of class exists checks + */ + protected static $classExists = array(); + + /** + * @return string the version of Prado framework + */ + public static function getVersion() + { + return '3.2.3'; + } + + /** + * Initializes error handlers. + * This method set error and exception handlers to be functions + * defined in this class. + */ + public static function initErrorHandlers() + { + /** + * Sets error handler to be Prado::phpErrorHandler + */ + set_error_handler(array('PradoBase','phpErrorHandler')); + /** + * Sets exception handler to be Prado::exceptionHandler + */ + set_exception_handler(array('PradoBase','exceptionHandler')); + } + + /** + * Class autoload loader. + * This method is provided to be invoked within an __autoload() magic method. + * @param string class name + */ + public static function autoload($className) + { + include_once($className.self::CLASS_FILE_EXT); + if(!class_exists($className,false) && !interface_exists($className,false)) + self::fatalError("Class file for '$className' cannot be found."); + } + + /** + * @param integer the type of "powered logo". Valid values include 0 and 1. + * @return string a string that can be displayed on your Web page showing powered-by-PRADO information + */ + public static function poweredByPrado($logoType=0) + { + $logoName=$logoType==1?'powered2':'powered'; + if(self::$_application!==null) + { + $am=self::$_application->getAssetManager(); + $url=$am->publishFilePath(self::getPathOfNamespace('System.'.$logoName,'.gif')); + } + else + $url='http://www.pradosoft.com/images/'.$logoName.'.gif'; + return 'Powered by PRADO'; + } + + /** + * PHP error handler. + * This method should be registered as PHP error handler using + * {@link set_error_handler}. The method throws an exception that + * contains the error information. + * @param integer the level of the error raised + * @param string the error message + * @param string the filename that the error was raised in + * @param integer the line number the error was raised at + */ + public static function phpErrorHandler($errno,$errstr,$errfile,$errline) + { + if(error_reporting() & $errno) + throw new TPhpErrorException($errno,$errstr,$errfile,$errline); + } + + /** + * Default exception handler. + * This method should be registered as default exception handler using + * {@link set_exception_handler}. The method tries to use the errorhandler + * module of the Prado application to handle the exception. + * If the application or the module does not exist, it simply echoes the + * exception. + * @param Exception exception that is not caught + */ + public static function exceptionHandler($exception) + { + if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null) + { + $errorHandler->handleError(null,$exception); + } + else + { + echo $exception; + } + exit(1); + } + + /** + * Stores the application instance in the class static member. + * This method helps implement a singleton pattern for TApplication. + * Repeated invocation of this method or the application constructor + * will cause the throw of an exception. + * This method should only be used by framework developers. + * @param TApplication the application instance + * @throws TInvalidOperationException if this method is invoked twice or more. + */ + public static function setApplication($application) + { + if(self::$_application!==null && !defined('PRADO_TEST_RUN')) + throw new TInvalidOperationException('prado_application_singleton_required'); + self::$_application=$application; + } + + /** + * @return TApplication the application singleton, null if the singleton has not be created yet. + */ + public static function getApplication() + { + return self::$_application; + } + + /** + * @return string the path of the framework + */ + public static function getFrameworkPath() + { + return PRADO_DIR; + } + + /** + * Serializes a data. + * The original PHP serialize function has a bug that may not serialize + * properly an object. + * @param mixed data to be serialized + * @return string the serialized data + */ + public static function serialize($data) + { + return serialize($data); + } + + /** + * Unserializes a data. + * The original PHP unserialize function has a bug that may not unserialize + * properly an object. + * @param string data to be unserialized + * @return mixed unserialized data, null if unserialize failed + */ + public static function unserialize($str) + { + return unserialize($str); + } + + /** + * Creates a component with the specified type. + * A component type can be either the component class name + * or a namespace referring to the path of the component class file. + * For example, 'TButton', 'System.Web.UI.WebControls.TButton' are both + * valid component type. + * This method can also pass parameters to component constructors. + * All parameters passed to this method except the first one (the component type) + * will be supplied as component constructor parameters. + * @param string component type + * @return TComponent component instance of the specified type + * @throws TInvalidDataValueException if the component type is unknown + */ + public static function createComponent($type) + { + if(!isset(self::$classExists[$type])) + self::$classExists[$type] = class_exists($type, false); + + if( !isset(self::$_usings[$type]) && !self::$classExists[$type]) { + self::using($type); + self::$classExists[$type] = class_exists($type, false); + } + + if( ($pos = strrpos($type, '.')) !== false) + $type = substr($type,$pos+1); + + if(($n=func_num_args())>1) + { + $args = func_get_args(); + switch($n) { + case 2: + return new $type($args[1]); + break; + case 3: + return new $type($args[1], $args[2]); + break; + case 4: + return new $type($args[1], $args[2], $args[3]); + break; + case 5: + return new $type($args[1], $args[2], $args[3], $args[4]); + break; + default: + $s='$args[1]'; + for($i=2;$i<$n;++$i) + $s.=",\$args[$i]"; + eval("\$component=new $type($s);"); + return $component; + break; + } + } + else + return new $type; + } + + /** + * Uses a namespace. + * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file. + * If the namespace corresponds to a directory, the directory will be appended + * to the include path. If the namespace corresponds to a file, it will be included (include_once). + * @param string namespace to be used + * @param boolean whether to check the existence of the class after the class file is included + * @throws TInvalidDataValueException if the namespace is invalid + */ + public static function using($namespace,$checkClassExistence=true) + { + if(isset(self::$_usings[$namespace]) || class_exists($namespace,false)) + return; + if(($pos=strrpos($namespace,'.'))===false) // a class name + { + try + { + include_once($namespace.self::CLASS_FILE_EXT); + } + catch(Exception $e) + { + if($checkClassExistence && !class_exists($namespace,false)) + throw new TInvalidOperationException('prado_component_unknown',$namespace,$e->getMessage()); + else + throw $e; + } + } + else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) + { + $className=substr($namespace,$pos+1); + if($className==='*') // a directory + { + self::$_usings[$namespace]=$path; + set_include_path(get_include_path().PATH_SEPARATOR.$path); + } + else // a file + { + self::$_usings[$namespace]=$path; + if(!$checkClassExistence || !class_exists($className,false)) + { + try + { + include_once($path); + } + catch(Exception $e) + { + if($checkClassExistence && !class_exists($className,false)) + throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage()); + else + throw $e; + } + } + } + } + else + throw new TInvalidDataValueException('prado_using_invalid',$namespace); + } + + /** + * Translates a namespace into a file path. + * The first segment of the namespace is considered as a path alias + * which is replaced with the actual path. The rest segments are + * subdirectory names appended to the aliased path. + * If the namespace ends with an asterisk '*', it represents a directory; + * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty). + * Note, this method does not ensure the existence of the resulting file path. + * @param string namespace + * @param string extension to be appended if the namespace refers to a file + * @return string file path corresponding to the namespace, null if namespace is invalid + */ + public static function getPathOfNamespace($namespace, $ext='') + { + if(self::CLASS_FILE_EXT === $ext || empty($ext)) + { + if(isset(self::$_usings[$namespace])) + return self::$_usings[$namespace]; + + if(isset(self::$_aliases[$namespace])) + return self::$_aliases[$namespace]; + } + + $segs = explode('.',$namespace); + $alias = array_shift($segs); + + if(null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias))) + return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file === '*') ? '' : DIRECTORY_SEPARATOR.$file.$ext); + + return null; + } + + /** + * @param string alias to the path + * @return string the path corresponding to the alias, null if alias not defined. + */ + public static function getPathOfAlias($alias) + { + return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null; + } + + protected static function getPathAliases() + { + return self::$_aliases; + } + + /** + * @param string alias to the path + * @param string the path corresponding to the alias + * @throws TInvalidOperationException if the alias is already defined + * @throws TInvalidDataValueException if the path is not a valid file path + */ + public static function setPathOfAlias($alias,$path) + { + if(isset(self::$_aliases[$alias]) && !defined('PRADO_TEST_RUN')) + throw new TInvalidOperationException('prado_alias_redefined',$alias); + else if(($rp=realpath($path))!==false && is_dir($rp)) + { + if(strpos($alias,'.')===false) + self::$_aliases[$alias]=$rp; + else + throw new TInvalidDataValueException('prado_aliasname_invalid',$alias); + } + else + throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path); + } + + /** + * Fatal error handler. + * This method displays an error message together with the current call stack. + * The application will exit after calling this method. + * @param string error message + */ + public static function fatalError($msg) + { + echo '

    Fatal Error

    '; + echo '

    '.$msg.'

    '; + if(!function_exists('debug_backtrace')) + return; + echo '

    Debug Backtrace

    '; + echo '
    ';
    +		$index=-1;
    +		foreach(debug_backtrace() as $t)
    +		{
    +			$index++;
    +			if($index==0)  // hide the backtrace of this function
    +				continue;
    +			echo '#'.$index.' ';
    +			if(isset($t['file']))
    +				echo basename($t['file']) . ':' . $t['line'];
    +			else
    +				 echo '';
    +			echo ' -- ';
    +			if(isset($t['class']))
    +				echo $t['class'] . $t['type'];
    +			echo $t['function'] . '(';
    +			if(isset($t['args']) && sizeof($t['args']) > 0)
    +			{
    +				$count=0;
    +				foreach($t['args'] as $item)
    +				{
    +					if(is_string($item))
    +					{
    +						$str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
    +						if (strlen($item) > 70)
    +							echo "'". substr($str, 0, 70) . "...'";
    +						else
    +							echo "'" . $str . "'";
    +					}
    +					else if (is_int($item) || is_float($item))
    +						echo $item;
    +					else if (is_object($item))
    +						echo get_class($item);
    +					else if (is_array($item))
    +						echo 'array(' . count($item) . ')';
    +					else if (is_bool($item))
    +						echo $item ? 'true' : 'false';
    +					else if ($item === null)
    +						echo 'NULL';
    +					else if (is_resource($item))
    +						echo get_resource_type($item);
    +					$count++;
    +					if (count($t['args']) > $count)
    +						echo ', ';
    +				}
    +			}
    +			echo ")\n";
    +		}
    +		echo '
    '; + exit(1); + } + + /** + * Returns a list of user preferred languages. + * The languages are returned as an array. Each array element + * represents a single language preference. The languages are ordered + * according to user preferences. The first language is the most preferred. + * @return array list of user preferred languages. + */ + public static function getUserLanguages() + { + static $languages=null; + if($languages===null) + { + if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + $languages[0]='en'; + else + { + $languages=array(); + foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) + { + $array=explode(';q=',trim($language)); + $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0; + } + arsort($languages); + $languages=array_keys($languages); + if(empty($languages)) + $languages[0]='en'; + } + } + return $languages; + } + + /** + * Returns the most preferred language by the client user. + * @return string the most preferred language by the client user, defaults to English. + */ + public static function getPreferredLanguage() + { + static $language=null; + if($language===null) + { + $langs=Prado::getUserLanguages(); + $lang=explode('-',$langs[0]); + if(empty($lang[0]) || !ctype_alpha($lang[0])) + $language='en'; + else + $language=$lang[0]; + } + return $language; + } + + /** + * Writes a log message. + * This method wraps {@link log()} by checking the application mode. + * When the application is in Debug mode, debug backtrace information is appended + * to the message and the message is logged at DEBUG level. + * When the application is in Performance mode, this method does nothing. + * Otherwise, the message is logged at INFO level. + * @param string message to be logged + * @param string category of the message + * @param (string|TControl) control of the message + * @see log, getLogger + */ + public static function trace($msg,$category='Uncategorized',$ctl=null) + { + if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance) + return; + if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug) + { + $trace=debug_backtrace(); + if(isset($trace[0]['file']) && isset($trace[0]['line'])) + $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})"; + $level=TLogger::DEBUG; + } + else + $level=TLogger::INFO; + self::log($msg,$level,$category,$ctl); + } + + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link TLogger::getLogs} + * and may be recorded in different media, such as file, email, database, using + * {@link TLogRouter}. + * @param string message to be logged + * @param integer level of the message. Valid values include + * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, + * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. + * @param string category of the message + * @param (string|TControl) control of the message + */ + public static function log($msg,$level=TLogger::INFO,$category='Uncategorized',$ctl=null) + { + if(self::$_logger===null) + self::$_logger=new TLogger; + self::$_logger->log($msg,$level,$category,$ctl); + } + + /** + * @return TLogger message logger + */ + public static function getLogger() + { + if(self::$_logger===null) + self::$_logger=new TLogger; + return self::$_logger; + } + + /** + * Converts a variable into a string representation. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as PRADO controls. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @param boolean whether to syntax highlight the output. Defaults to false. + * @return string the string representation of the variable + */ + public static function varDump($var,$depth=10,$highlight=false) + { + Prado::using('System.Util.TVarDumper'); + return TVarDumper::dump($var,$depth,$highlight); + } + + /** + * Localize a text to the locale/culture specified in the globalization handler. + * @param string text to be localized. + * @param array a set of parameters to substitute. + * @param string a different catalogue to find the localize text. + * @param string the input AND output charset. + * @return string localized text. + * @see TTranslate::formatter() + * @see TTranslate::init() + */ + public static function localize($text, $parameters=array(), $catalogue=null, $charset=null) + { + Prado::using('System.I18N.Translation'); + $app = Prado::getApplication()->getGlobalization(false); + + $params = array(); + foreach($parameters as $key => $value) + $params['{'.$key.'}'] = $value; + + //no translation handler provided + if($app===null || ($config = $app->getTranslationConfiguration())===null) + return strtr($text, $params); + + if ($catalogue===null) + $catalogue=isset($config['catalogue'])?$config['catalogue']:'messages'; + + Translation::init($catalogue); + + //globalization charset + $appCharset = $app===null ? '' : $app->getCharset(); + + //default charset + $defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset(); + + //fall back + if(empty($charset)) $charset = $appCharset; + if(empty($charset)) $charset = $defaultCharset; + + return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset); + } +} + + +/** + * Includes the classes essential for PradoBase class + */ +PradoBase::using('System.TComponent'); +PradoBase::using('System.Exceptions.TException'); +PradoBase::using('System.Util.TLogger'); diff --git a/gui/baculum/framework/Security/IUserManager.php b/gui/baculum/framework/Security/IUserManager.php new file mode 100644 index 0000000000..390189cb0a --- /dev/null +++ b/gui/baculum/framework/Security/IUserManager.php @@ -0,0 +1,58 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: IUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ + +/** + * IUserManager interface + * + * IUserManager specifies the interface that must be implemented by + * a user manager class if it is to be used together with {@link TAuthManager} + * and {@link TUser}. + * + * @author Qiang Xue + * @version $Id: IUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +interface IUserManager +{ + /** + * @return string name for a guest user. + */ + public function getGuestName(); + /** + * Returns a user instance given the user name. + * @param string user name, null if it is a guest. + * @return TUser the user instance, null if the specified username is not in the user database. + */ + public function getUser($username=null); + /** + * Returns a user instance according to auth data stored in a cookie. + * @param THttpCookie the cookie storing user authentication information + * @return TUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data. + * @since 3.1.1 + */ + public function getUserFromCookie($cookie); + /** + * Saves user auth data into a cookie. + * @param THttpCookie the cookie to receive the user auth data. + * @since 3.1.1 + */ + public function saveUserToCookie($cookie); + /** + * Validates if the username and password are correct. + * @param string user name + * @param string password + * @return boolean true if validation is successful, false otherwise. + */ + public function validateUser($username,$password); +} + diff --git a/gui/baculum/framework/Security/TAuthManager.php b/gui/baculum/framework/Security/TAuthManager.php new file mode 100644 index 0000000000..e30b0bcb43 --- /dev/null +++ b/gui/baculum/framework/Security/TAuthManager.php @@ -0,0 +1,456 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TAuthManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ + +/** + * Using IUserManager interface + */ +Prado::using('System.Security.IUserManager'); + +/** + * TAuthManager class + * + * TAuthManager performs user authentication and authorization for a Prado application. + * TAuthManager works together with a {@link IUserManager} module that can be + * specified via the {@link setUserManager UserManager} property. + * If an authorization fails, TAuthManager will try to redirect the client + * browser to a login page that is specified via the {@link setLoginPage LoginPage}. + * To login or logout a user, call {@link login} or {@link logout}, respectively. + * + * The {@link setAuthExpire AuthExpire} property can be used to define the time + * in seconds after which the authentication should expire. + * {@link setAllowAutoLogin AllowAutoLogin} specifies if the login information + * should be stored in a cookie to perform automatic login. Enabling this + * feature will cause that {@link setAuthExpire AuthExpire} has no effect + * since the user will be logged in again on authentication expiration. + * + * To load TAuthManager, configure it in application configuration as follows, + * + * + * + * @author Qiang Xue + * @version $Id: TAuthManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +class TAuthManager extends TModule +{ + /** + * GET variable name for return url + */ + const RETURN_URL_VAR='ReturnUrl'; + /** + * @var boolean if the module has been initialized + */ + private $_initialized=false; + /** + * @var IUserManager user manager instance + */ + private $_userManager; + /** + * @var string login page + */ + private $_loginPage; + /** + * @var boolean whether authorization should be skipped + */ + private $_skipAuthorization=false; + /** + * @var string the session var name for storing return URL + */ + private $_returnUrlVarName; + /** + * @var boolean whether to allow auto login (using cookie) + */ + private $_allowAutoLogin=false; + /** + * @var string variable name used to store user session or cookie + */ + private $_userKey; + /** + * @var integer authentication expiration time in seconds. Defaults to zero (no expiration) + */ + private $_authExpire=0; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if user manager does not exist or is not IUserManager + */ + public function init($config) + { + if($this->_userManager===null) + throw new TConfigurationException('authmanager_usermanager_required'); + if($this->_returnUrlVarName===null) + $this->_returnUrlVarName=$this->getApplication()->getID().':'.self::RETURN_URL_VAR; + $application=$this->getApplication(); + if(is_string($this->_userManager)) + { + if(($users=$application->getModule($this->_userManager))===null) + throw new TConfigurationException('authmanager_usermanager_inexistent',$this->_userManager); + if(!($users instanceof IUserManager)) + throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager); + $this->_userManager=$users; + } + $application->attachEventHandler('OnAuthentication',array($this,'doAuthentication')); + $application->attachEventHandler('OnEndRequest',array($this,'leave')); + $application->attachEventHandler('OnAuthorization',array($this,'doAuthorization')); + $this->_initialized=true; + } + + /** + * @return IUserManager user manager instance + */ + public function getUserManager() + { + return $this->_userManager; + } + + /** + * @param string|IUserManager the user manager module ID or the user manager object + * @throws TInvalidOperationException if the module has been initialized or the user manager object is not IUserManager + */ + public function setUserManager($provider) + { + if($this->_initialized) + throw new TInvalidOperationException('authmanager_usermanager_unchangeable'); + if(!is_string($provider) && !($provider instanceof IUserManager)) + throw new TConfigurationException('authmanager_usermanager_invalid',$this->_userManager); + $this->_userManager=$provider; + } + + /** + * @return string path of login page should login is required + */ + public function getLoginPage() + { + return $this->_loginPage; + } + + /** + * Sets the login page that the client browser will be redirected to if login is needed. + * Login page should be specified in the format of page path. + * @param string path of login page should login is required + * @see TPageService + */ + public function setLoginPage($pagePath) + { + $this->_loginPage=$pagePath; + } + + /** + * Performs authentication. + * This is the event handler attached to application's Authentication event. + * Do not call this method directly. + * @param mixed sender of the Authentication event + * @param mixed event parameter + */ + public function doAuthentication($sender,$param) + { + $this->onAuthenticate($param); + + $service=$this->getService(); + if(($service instanceof TPageService) && $service->getRequestedPagePath()===$this->getLoginPage()) + $this->_skipAuthorization=true; + } + + /** + * Performs authorization. + * This is the event handler attached to application's Authorization event. + * Do not call this method directly. + * @param mixed sender of the Authorization event + * @param mixed event parameter + */ + public function doAuthorization($sender,$param) + { + if(!$this->_skipAuthorization) + { + $this->onAuthorize($param); + } + } + + /** + * Performs login redirect if authorization fails. + * This is the event handler attached to application's EndRequest event. + * Do not call this method directly. + * @param mixed sender of the event + * @param mixed event parameter + */ + public function leave($sender,$param) + { + $application=$this->getApplication(); + if($application->getResponse()->getStatusCode()===401) + { + $service=$application->getService(); + if($service instanceof TPageService) + { + $returnUrl=$application->getRequest()->getRequestUri(); + $this->setReturnUrl($returnUrl); + $url=$service->constructUrl($this->getLoginPage()); + $application->getResponse()->redirect($url); + } + } + } + + /** + * @return string the name of the session variable storing return URL. It defaults to 'AppID:ReturnUrl' + */ + public function getReturnUrlVarName() + { + return $this->_returnUrlVarName; + } + + /** + * @param string the name of the session variable storing return URL. + */ + public function setReturnUrlVarName($value) + { + $this->_returnUrlVarName=$value; + } + + /** + * @return string URL that the browser should be redirected to when login succeeds. + */ + public function getReturnUrl() + { + return $this->getSession()->itemAt($this->getReturnUrlVarName()); + } + + /** + * Sets the URL that the browser should be redirected to when login succeeds. + * @param string the URL to be redirected to. + */ + public function setReturnUrl($value) + { + $this->getSession()->add($this->getReturnUrlVarName(),$value); + } + + /** + * @return boolean whether to allow remembering login so that the user logs on automatically next time. Defaults to false. + * @since 3.1.1 + */ + public function getAllowAutoLogin() + { + return $this->_allowAutoLogin; + } + + /** + * @param boolean whether to allow remembering login so that the user logs on automatically next time. Users have to enable cookie to make use of this feature. + * @since 3.1.1 + */ + public function setAllowAutoLogin($value) + { + $this->_allowAutoLogin=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer authentication expiration time in seconds. Defaults to zero (no expiration). + * @since 3.1.3 + */ + public function getAuthExpire() + { + return $this->_authExpire; + } + + /** + * @param integer authentication expiration time in seconds. Defaults to zero (no expiration). + * @since 3.1.3 + */ + public function setAuthExpire($value) + { + $this->_authExpire=TPropertyValue::ensureInteger($value); + } + + /** + * Performs the real authentication work. + * An OnAuthenticate event will be raised if there is any handler attached to it. + * If the application already has a non-null user, it will return without further authentication. + * Otherwise, user information will be restored from session data. + * @param mixed parameter to be passed to OnAuthenticate event + * @throws TConfigurationException if session module does not exist. + */ + public function onAuthenticate($param) + { + $application=$this->getApplication(); + + // restoring user info from session + if(($session=$application->getSession())===null) + throw new TConfigurationException('authmanager_session_required'); + $session->open(); + $sessionInfo=$session->itemAt($this->getUserKey()); + $user=$this->_userManager->getUser(null)->loadFromString($sessionInfo); + + // check for authentication expiration + $isAuthExpired = $this->_authExpire>0 && !$user->getIsGuest() && + ($expiretime=$session->itemAt('AuthExpireTime')) && $expiretimegetAllowAutoLogin() && ($user->getIsGuest() || $isAuthExpired)) + { + $cookie=$this->getRequest()->getCookies()->itemAt($this->getUserKey()); + if($cookie instanceof THttpCookie) + { + if(($user2=$this->_userManager->getUserFromCookie($cookie))!==null) + { + $user=$user2; + $this->updateSessionUser($user); + // user is restored from cookie, auth may not expire + $isAuthExpired = false; + } + } + } + + $application->setUser($user); + + // handle authentication expiration or update expiration time + if($isAuthExpired) + $this->onAuthExpire($param); + else + $session->add('AuthExpireTime', time() + $this->_authExpire); + + // event handler gets a chance to do further auth work + if($this->hasEventHandler('OnAuthenticate')) + $this->raiseEvent('OnAuthenticate',$this,$application); + } + + /** + * Performs user logout on authentication expiration. + * An 'OnAuthExpire' event will be raised if there is any handler attached to it. + * @param mixed parameter to be passed to OnAuthExpire event. + */ + public function onAuthExpire($param) + { + $this->logout(); + if($this->hasEventHandler('OnAuthExpire')) + $this->raiseEvent('OnAuthExpire',$this,$param); + } + + /** + * Performs the real authorization work. + * Authorization rules obtained from the application will be used to check + * if a user is allowed. If authorization fails, the response status code + * will be set as 401 and the application terminates. + * @param mixed parameter to be passed to OnAuthorize event + */ + public function onAuthorize($param) + { + $application=$this->getApplication(); + if($this->hasEventHandler('OnAuthorize')) + $this->raiseEvent('OnAuthorize',$this,$application); + if(!$application->getAuthorizationRules()->isUserAllowed($application->getUser(),$application->getRequest()->getRequestType(),$application->getRequest()->getUserHostAddress())) + { + $application->getResponse()->setStatusCode(401); + $application->completeRequest(); + } + } + + /** + * @return string a unique variable name for storing user session/cookie data + * @since 3.1.1 + */ + public function getUserKey() + { + if($this->_userKey===null) + $this->_userKey=$this->generateUserKey(); + return $this->_userKey; + } + + /** + * @return string a key used to store user information in session + * @since 3.1.1 + */ + protected function generateUserKey() + { + return md5($this->getApplication()->getUniqueID().'prado:user'); + } + + /** + * Updates the user data stored in session. + * @param IUser user object + * @throws new TConfigurationException if session module is not loaded. + */ + public function updateSessionUser($user) + { + if(!$user->getIsGuest()) + { + if(($session=$this->getSession())===null) + throw new TConfigurationException('authmanager_session_required'); + else + $session->add($this->getUserKey(),$user->saveToString()); + } + } + + /** + * Switches to a new user. + * This method will logout the current user first and login with a new one (without password.) + * @param string the new username + * @return boolean if the switch is successful + */ + public function switchUser($username) + { + if(($user=$this->_userManager->getUser($username))===null) + return false; + $this->updateSessionUser($user); + $this->getApplication()->setUser($user); + return true; + } + + /** + * Logs in a user with username and password. + * The username and password will be used to validate if login is successful. + * If yes, a user object will be created for the application. + * @param string username + * @param string password + * @param integer number of seconds that automatic login will remain effective. If 0, it means user logs out when session ends. This parameter is added since 3.1.1. + * @return boolean if login is successful + */ + public function login($username,$password,$expire=0) + { + if($this->_userManager->validateUser($username,$password)) + { + if(($user=$this->_userManager->getUser($username))===null) + return false; + $this->updateSessionUser($user); + $this->getApplication()->setUser($user); + + if($expire>0) + { + $cookie=new THttpCookie($this->getUserKey(),''); + $cookie->setExpire(time()+$expire); + $this->_userManager->saveUserToCookie($cookie); + $this->getResponse()->getCookies()->add($cookie); + } + return true; + } + else + return false; + } + + /** + * Logs out a user. + * User session will be destroyed after this method is called. + * @throws TConfigurationException if session module is not loaded. + */ + public function logout() + { + if(($session=$this->getSession())===null) + throw new TConfigurationException('authmanager_session_required'); + $this->getApplication()->getUser()->setIsGuest(true); + $session->destroy(); + if($this->getAllowAutoLogin()) + { + $cookie=new THttpCookie($this->getUserKey(),''); + $this->getResponse()->getCookies()->add($cookie); + } + } +} + diff --git a/gui/baculum/framework/Security/TAuthorizationRule.php b/gui/baculum/framework/Security/TAuthorizationRule.php new file mode 100644 index 0000000000..cae28bfb76 --- /dev/null +++ b/gui/baculum/framework/Security/TAuthorizationRule.php @@ -0,0 +1,296 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TAuthorizationRule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ +/** + * TAuthorizationRule class + * + * TAuthorizationRule represents a single authorization rule. + * A rule is specified by an action (required), a list of users (optional), + * a list of roles (optional), a verb (optional), and a list of IP rules (optional). + * Action can be either 'allow' or 'deny'. + * Guest (anonymous, unauthenticated) users are represented by question mark '?'. + * All users (including guest users) are represented by asterisk '*'. + * Authenticated users are represented by '@'. + * Users/roles are case-insensitive. + * Different users/roles are separated by comma ','. + * Verb can be either 'get' or 'post'. If it is absent, it means both. + * IP rules are separated by comma ',' and can contain wild card in the rules (e.g. '192.132.23.33, 192.122.*.*') + * + * @author Qiang Xue + * @version $Id: TAuthorizationRule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +class TAuthorizationRule extends TComponent +{ + /** + * @var string action, either 'allow' or 'deny' + */ + private $_action; + /** + * @var array list of user IDs + */ + private $_users; + /** + * @var array list of roles + */ + private $_roles; + /** + * @var string verb, may be empty, 'get', or 'post'. + */ + private $_verb; + /** + * @var string IP patterns + */ + private $_ipRules; + /** + * @var boolean if this rule applies to everyone + */ + private $_everyone; + /** + * @var boolean if this rule applies to guest user + */ + private $_guest; + /** + * @var boolean if this rule applies to authenticated users + */ + private $_authenticated; + + /** + * Constructor. + * @param string action, either 'deny' or 'allow' + * @param string a comma separated user list + * @param string a comma separated role list + * @param string verb, can be empty, 'get', or 'post' + * @param string IP rules (separated by comma, can contain wild card *) + */ + public function __construct($action,$users,$roles,$verb='',$ipRules='') + { + $action=strtolower(trim($action)); + if($action==='allow' || $action==='deny') + $this->_action=$action; + else + throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); + $this->_users=array(); + $this->_roles=array(); + $this->_ipRules=array(); + $this->_everyone=false; + $this->_guest=false; + $this->_authenticated=false; + + if(trim($users)==='') + $users='*'; + foreach(explode(',',$users) as $user) + { + if(($user=trim(strtolower($user)))!=='') + { + if($user==='*') + { + $this->_everyone=true; + break; + } + else if($user==='?') + $this->_guest=true; + else if($user==='@') + $this->_authenticated=true; + else + $this->_users[]=$user; + } + } + + if(trim($roles)==='') + $roles='*'; + foreach(explode(',',$roles) as $role) + { + if(($role=trim(strtolower($role)))!=='') + $this->_roles[]=$role; + } + + if(($verb=trim(strtolower($verb)))==='') + $verb='*'; + if($verb==='*' || $verb==='get' || $verb==='post') + $this->_verb=$verb; + else + throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); + + if(trim($ipRules)==='') + $ipRules='*'; + foreach(explode(',',$ipRules) as $ipRule) + { + if(($ipRule=trim($ipRule))!=='') + $this->_ipRules[]=$ipRule; + } + } + + /** + * @return string action, either 'allow' or 'deny' + */ + public function getAction() + { + return $this->_action; + } + + /** + * @return array list of user IDs + */ + public function getUsers() + { + return $this->_users; + } + + /** + * @return array list of roles + */ + public function getRoles() + { + return $this->_roles; + } + + /** + * @return string verb, may be empty, 'get', or 'post'. + */ + public function getVerb() + { + return $this->_verb; + } + + /** + * @return array list of IP rules. + * @since 3.1.1 + */ + public function getIPRules() + { + return $this->_ipRules; + } + + /** + * @return boolean if this rule applies to everyone + */ + public function getGuestApplied() + { + return $this->_guest || $this->_everyone; + } + + /** + * @return boolean if this rule applies to everyone + */ + public function getEveryoneApplied() + { + return $this->_everyone; + } + + /** + * @return boolean if this rule applies to authenticated users + */ + public function getAuthenticatedApplied() + { + return $this->_authenticated || $this->_everyone; + } + + /** + * @param IUser the user object + * @param string the request verb (GET, PUT) + * @param string the request IP address + * @return integer 1 if the user is allowed, -1 if the user is denied, 0 if the rule does not apply to the user + */ + public function isUserAllowed(IUser $user,$verb,$ip) + { + if($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user)) + return ($this->_action==='allow')?1:-1; + else + return 0; + } + + private function isIpMatched($ip) + { + if(empty($this->_ipRules)) + return 1; + foreach($this->_ipRules as $rule) + { + if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && strncmp($ip,$rule,$pos)===0)) + return 1; + } + return 0; + } + + private function isUserMatched($user) + { + return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()),$this->_users)); + } + + private function isRoleMatched($user) + { + foreach($this->_roles as $role) + { + if($role==='*' || $user->isInRole($role)) + return true; + } + return false; + } + + private function isVerbMatched($verb) + { + return ($this->_verb==='*' || strcasecmp($verb,$this->_verb)===0); + } +} + + +/** + * TAuthorizationRuleCollection class. + * TAuthorizationRuleCollection represents a collection of authorization rules {@link TAuthorizationRule}. + * To check if a user is allowed, call {@link isUserAllowed}. + * + * @author Qiang Xue + * @version $Id: TAuthorizationRule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +class TAuthorizationRuleCollection extends TList +{ + /** + * @param IUser the user to be authorized + * @param string verb, can be empty, 'post' or 'get'. + * @param string the request IP address + * @return boolean whether the user is allowed + */ + public function isUserAllowed($user,$verb,$ip) + { + if($user instanceof IUser) + { + $verb=strtolower(trim($verb)); + foreach($this as $rule) + { + if(($decision=$rule->isUserAllowed($user,$verb,$ip))!==0) + return ($decision>0); + } + return true; + } + else + return false; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added TAuthorizationRule object. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TAuthorizationRule object. + */ + public function insertAt($index,$item) + { + if($item instanceof TAuthorizationRule) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('authorizationrulecollection_authorizationrule_required'); + } +} + diff --git a/gui/baculum/framework/Security/TDbUserManager.php b/gui/baculum/framework/Security/TDbUserManager.php new file mode 100644 index 0000000000..aabd6e5c44 --- /dev/null +++ b/gui/baculum/framework/Security/TDbUserManager.php @@ -0,0 +1,320 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDbUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ + +/** + * Using IUserManager interface + */ +Prado::using('System.Security.IUserManager'); +Prado::using('System.Data.TDataSourceConfig'); +Prado::using('System.Security.TUser'); + +/** + * TDbUserManager class + * + * TDbUserManager manages user accounts that are stored in a database. + * TDbUserManager is mainly designed to be used together with {@link TAuthManager} + * which manages how users are authenticated and authorized in a Prado application. + * + * To use TDbUserManager together with TAuthManager, configure them in + * the application configuration like following: + * + * + * + * + * + * + * In the above, {@link setUserClass UserClass} specifies what class will be used + * to create user instance. The class must extend from {@link TDbUser}. + * {@link setConnectionID ConnectionID} refers to the ID of a {@link TDataSourceConfig} module + * which specifies how to establish database connection to retrieve user information. + * + * @author Qiang Xue + * @version $Id: TDbUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.1.0 + */ +class TDbUserManager extends TModule implements IUserManager +{ + private $_connID=''; + private $_conn; + private $_guestName='Guest'; + private $_userClass=''; + private $_userFactory; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + if($this->_userClass==='') + throw new TConfigurationException('dbusermanager_userclass_required'); + $this->_userFactory=Prado::createComponent($this->_userClass,$this); + if(!($this->_userFactory instanceof TDbUser)) + throw new TInvalidDataTypeException('dbusermanager_userclass_invalid',$this->_userClass); + } + + /** + * @return string the user class name in namespace format. Defaults to empty string, meaning not set. + */ + public function getUserClass() + { + return $this->_userClass; + } + + /** + * @param string the user class name in namespace format. The user class must extend from {@link TDbUser}. + */ + public function setUserClass($value) + { + $this->_userClass=$value; + } + + /** + * @return string guest name, defaults to 'Guest' + */ + public function getGuestName() + { + return $this->_guestName; + } + + /** + * @param string name to be used for guest users. + */ + public function setGuestName($value) + { + $this->_guestName=$value; + } + + /** + * Validates if the username and password are correct. + * @param string user name + * @param string password + * @return boolean true if validation is successful, false otherwise. + */ + public function validateUser($username,$password) + { + return $this->_userFactory->validateUser($username,$password); + } + + /** + * Returns a user instance given the user name. + * @param string user name, null if it is a guest. + * @return TUser the user instance, null if the specified username is not in the user database. + */ + public function getUser($username=null) + { + if($username===null) + { + $user=Prado::createComponent($this->_userClass,$this); + $user->setIsGuest(true); + return $user; + } + else + return $this->_userFactory->createUser($username); + } + + /** + * @return string the ID of a TDataSourceConfig module. Defaults to empty string, meaning not set. + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Sets the ID of a TDataSourceConfig module. + * The datasource module will be used to establish the DB connection + * that will be used by the user manager. + * @param string module ID. + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return TDbConnection the database connection that may be used to retrieve user data. + */ + public function getDbConnection() + { + if($this->_conn===null) + { + $this->_conn=$this->createDbConnection($this->_connID); + $this->_conn->setActive(true); + } + return $this->_conn; + } + + /** + * Creates the DB connection. + * @param string the module ID for TDataSourceConfig + * @return TDbConnection the created DB connection + * @throws TConfigurationException if module ID is invalid or empty + */ + protected function createDbConnection($connectionID) + { + if($connectionID!=='') + { + $conn=$this->getApplication()->getModule($connectionID); + if($conn instanceof TDataSourceConfig) + return $conn->getDbConnection(); + else + throw new TConfigurationException('dbusermanager_connectionid_invalid',$connectionID); + } + else + throw new TConfigurationException('dbusermanager_connectionid_required'); + } + + /** + * Returns a user instance according to auth data stored in a cookie. + * @param THttpCookie the cookie storing user authentication information + * @return TDbUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data. + * @since 3.1.1 + */ + public function getUserFromCookie($cookie) + { + return $this->_userFactory->createUserFromCookie($cookie); + } + + /** + * Saves user auth data into a cookie. + * @param THttpCookie the cookie to receive the user auth data. + * @since 3.1.1 + */ + public function saveUserToCookie($cookie) + { + $user=$this->getApplication()->getUser(); + if($user instanceof TDbUser) + $user->saveUserToCookie($cookie); + } +} + + +/** + * TDbUser class + * + * TDbUser is the base user class for using together with {@link TDbUserManager}. + * Two methods are declared and must be implemented in the descendant classes: + * - {@link validateUser()}: validates if username and password are correct entries. + * - {@link createUser()}: creates a new user instance given the username + * + * @author Qiang Xue + * @version $Id: TDbUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.1.0 + */ +abstract class TDbUser extends TUser +{ + private $_connection; + + /** + * Returns a database connection that may be used to retrieve data from database. + * + * @return TDbConnection database connection that may be used to retrieve data from database + */ + public function getDbConnection() + { + if($this->_connection===null) + { + $userManager=$this->getManager(); + if($userManager instanceof TDbUserManager) + { + $connection=$userManager->getDbConnection(); + if($connection instanceof TDbConnection) + { + $connection->setActive(true); + $this->_connection=$connection; + } + } + if($this->_connection===null) + throw new TConfigurationException('dbuser_dbconnection_invalid'); + } + return $this->_connection; + } + + /** + * Validates if username and password are correct entries. + * Usually, this is accomplished by checking if the user database + * contains this (username, password) pair. + * You may use {@link getDbConnection DbConnection} to deal with database. + * @param string username (case-sensitive) + * @param string password + * @return boolean whether the validation succeeds + */ + abstract public function validateUser($username,$password); + + /** + * Creates a new user instance given the username. + * This method usually needs to retrieve necessary user information + * (e.g. role, name, rank, etc.) from the user database according to + * the specified username. The newly created user instance should be + * initialized with these information. + * + * If the username is invalid (not found in the user database), null + * should be returned. + * + * You may use {@link getDbConnection DbConnection} to deal with database. + * + * @param string username (case-sensitive) + * @return TDbUser the newly created and initialized user instance + */ + abstract public function createUser($username); + + /** + * Creates a new user instance given the cookie containing auth data. + * + * This method is invoked when {@link TAuthManager::setAllowAutoLogin AllowAutoLogin} is set true. + * The default implementation simply returns null, meaning no user instance can be created + * from the given cookie. + * + * If you want to support automatic login (remember login), you should override this method. + * Typically, you obtain the username and a unique token from the cookie's value. + * You then verify the token is valid and use the username to create a user instance. + * + * @param THttpCookie the cookie storing user authentication information + * @return TDbUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data. + * @see saveUserToCookie + * @since 3.1.1 + */ + public function createUserFromCookie($cookie) + { + return null; + } + + /** + * Saves necessary auth data into a cookie. + * This method is invoked when {@link TAuthManager::setAllowAutoLogin AllowAutoLogin} is set true. + * The default implementation does nothing, meaning auth data is not stored in the cookie + * (and thus automatic login is not supported.) + * + * If you want to support automatic login (remember login), you should override this method. + * Typically, you generate a unique token according to the current login information + * and save it together with the username in the cookie's value. + * You should avoid revealing the password in the generated token. + * + * @param THttpCookie the cookie to store the user auth information + * @see createUserFromCookie + * @since 3.1.1 + */ + public function saveUserToCookie($cookie) + { + } +} + diff --git a/gui/baculum/framework/Security/TSecurityManager.php b/gui/baculum/framework/Security/TSecurityManager.php new file mode 100644 index 0000000000..b5267db1e5 --- /dev/null +++ b/gui/baculum/framework/Security/TSecurityManager.php @@ -0,0 +1,365 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSecurityManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ + +/** + * TSecurityManager class + * + * TSecurityManager provides private keys, hashing and encryption + * functionalities that may be used by other PRADO components, + * such as viewstate persister, cookies. + * + * TSecurityManager is mainly used to protect data from being tampered + * and viewed. It can generate HMAC and encrypt the data. + * The private key used to generate HMAC is set by {@link setValidationKey ValidationKey}. + * The key used to encrypt data is specified by {@link setEncryptionKey EncryptionKey}. + * If the above keys are not explicitly set, random keys will be generated + * and used. + * + * To prefix data with an HMAC, call {@link hashData()}. + * To validate if data is tampered, call {@link validateData()}, which will + * return the real data if it is not tampered. + * The algorithm used to generated HMAC is specified by {@link setValidation Validation}. + * + * To encrypt and decrypt data, call {@link encrypt()} and {@link decrypt()} + * respectively. The encryption algorithm can be set by {@link setEncryption Encryption}. + * + * Note, to use encryption, the PHP Mcrypt extension must be loaded. + * + * @author Qiang Xue + * @version $Id: TSecurityManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +class TSecurityManager extends TModule +{ + const STATE_VALIDATION_KEY = 'prado:securitymanager:validationkey'; + const STATE_ENCRYPTION_KEY = 'prado:securitymanager:encryptionkey'; + + private $_validationKey = null; + private $_encryptionKey = null; + private $_hashAlgorithm = 'sha1'; + private $_cryptAlgorithm = 'rijndael-256'; + private $_mbstring; + + /** + * Initializes the module. + * The security module is registered with the application. + * @param TXmlElement initial module configuration + */ + public function init($config) + { + $this->_mbstring=extension_loaded('mbstring'); + $this->getApplication()->setSecurityManager($this); + } + + /** + * Generates a random key. + */ + protected function generateRandomKey() + { + return sprintf('%08x%08x%08x%08x',mt_rand(),mt_rand(),mt_rand(),mt_rand()); + } + + /** + * @return string the private key used to generate HMAC. + * If the key is not explicitly set, a random one is generated and returned. + */ + public function getValidationKey() + { + if(null === $this->_validationKey) { + if(null === ($this->_validationKey = $this->getApplication()->getGlobalState(self::STATE_VALIDATION_KEY))) { + $this->_validationKey = $this->generateRandomKey(); + $this->getApplication()->setGlobalState(self::STATE_VALIDATION_KEY, $this->_validationKey, null, true); + } + } + return $this->_validationKey; + } + + /** + * @param string the key used to generate HMAC + * @throws TInvalidDataValueException if the key is empty + */ + public function setValidationKey($value) + { + if('' === $value) + throw new TInvalidDataValueException('securitymanager_validationkey_invalid'); + + $this->_validationKey = $value; + } + + /** + * @return string the private key used to encrypt/decrypt data. + * If the key is not explicitly set, a random one is generated and returned. + */ + public function getEncryptionKey() + { + if(null === $this->_encryptionKey) { + if(null === ($this->_encryptionKey = $this->getApplication()->getGlobalState(self::STATE_ENCRYPTION_KEY))) { + $this->_encryptionKey = $this->generateRandomKey(); + $this->getApplication()->setGlobalState(self::STATE_ENCRYPTION_KEY, $this->_encryptionKey, null, true); + } + } + return $this->_encryptionKey; + } + + /** + * @param string the key used to encrypt/decrypt data. + * @throws TInvalidDataValueException if the key is empty + */ + public function setEncryptionKey($value) + { + if('' === $value) + throw new TInvalidDataValueException('securitymanager_encryptionkey_invalid'); + + $this->_encryptionKey = $value; + } + + /** + * This method has been deprecated since version 3.2.1. + * Please use {@link getHashAlgorithm()} instead. + * @return string hashing algorithm used to generate HMAC. Defaults to 'sha1'. + */ + public function getValidation() + { + return $this->_hashAlgorithm; + } + + /** + * @return string hashing algorithm used to generate HMAC. Defaults to 'sha1'. + */ + public function getHashAlgorithm() + { + return $this->_hashAlgorithm; + } + + /** + * This method has been deprecated since version 3.2.1. + * Please use {@link setHashAlgorithm()} instead. + * @param TSecurityManagerValidationMode hashing algorithm used to generate HMAC. + */ + public function setValidation($value) + { + $this->_hashAlgorithm = TPropertyValue::ensureEnum($value, 'TSecurityManagerValidationMode'); + } + + /** + * @param string hashing algorithm used to generate HMAC. + */ + public function setHashAlgorithm($value) + { + $this->_hashAlgorithm = TPropertyValue::ensureString($value); + } + + /** + * This method has been deprecated since version 3.2.1. + * Please use {@link getCryptAlgorithm()} instead. + * @return string the algorithm used to encrypt/decrypt data. + */ + public function getEncryption() + { + if(is_string($this->_cryptAlgorithm)) + return $this->_cryptAlgorithm; + // fake the pre-3.2.1 answer + return "3DES"; + } + + /** + * This method has been deprecated since version 3.2.1. + * Please use {@link setCryptAlgorithm()} instead. + * @param string cipther name + */ + public function setEncryption($value) + { + $this->_cryptAlgorithm = $value; + } + + /** + * @return mixed the algorithm used to encrypt/decrypt data. Defaults to the string 'rijndael-256'. + */ + public function getCryptAlgorithm() + { + return $this->_cryptAlgorithm; + } + + /** + * Sets the crypt algorithm (also known as cipher or cypher) that will be used for {@link encrypt} and {@link decrypt}. + * @param mixed either a string containing the cipther name or an array containing the full parameters to call mcrypt_module_open(). + */ + public function setCryptAlgorithm($value) + { + $this->_cryptAlgorithm = $value; + } + + /** + * Encrypts data with {@link getEncryptionKey EncryptionKey}. + * @param string data to be encrypted. + * @return string the encrypted data + * @throws TNotSupportedException if PHP Mcrypt extension is not loaded + */ + public function encrypt($data) + { + $module=$this->openCryptModule(); + $key = $this->substr(md5($this->getEncryptionKey()), 0, mcrypt_enc_get_key_size($module)); + srand(); + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); + mcrypt_generic_init($module, $key, $iv); + $encrypted = $iv.mcrypt_generic($module, $data); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return $encrypted; + } + + /** + * Decrypts data with {@link getEncryptionKey EncryptionKey}. + * @param string data to be decrypted. + * @return string the decrypted data + * @throws TNotSupportedException if PHP Mcrypt extension is not loaded + */ + public function decrypt($data) + { + $module=$this->openCryptModule(); + $key = $this->substr(md5($this->getEncryptionKey()), 0, mcrypt_enc_get_key_size($module)); + $ivSize = mcrypt_enc_get_iv_size($module); + $iv = $this->substr($data, 0, $ivSize); + mcrypt_generic_init($module, $key, $iv); + $decrypted = mdecrypt_generic($module, $this->substr($data, $ivSize, $this->strlen($data))); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return $decrypted; + } + + /** + * Opens the mcrypt module with the configuration specified in {@link cryptAlgorithm}. + * @return resource the mycrypt module handle. + * @since 3.2.1 + */ + protected function openCryptModule() + { + if(extension_loaded('mcrypt')) + { + if(is_array($this->_cryptAlgorithm)) + $module=@call_user_func_array('mcrypt_module_open',$this->_cryptAlgorithm); + else + $module=@mcrypt_module_open($this->_cryptAlgorithm,'', MCRYPT_MODE_CBC,''); + + if($module===false) + throw new TNotSupportedException('securitymanager_mcryptextension_initfailed'); + + return $module; + } + else + throw new TNotSupportedException('securitymanager_mcryptextension_required'); + } + + /** + * Prefixes data with an HMAC. + * @param string data to be hashed. + * @return string data prefixed with HMAC + */ + public function hashData($data) + { + $hmac = $this->computeHMAC($data); + return $hmac.$data; + } + + /** + * Validates if data is tampered. + * @param string data to be validated. The data must be previously + * generated using {@link hashData()}. + * @return string the real data with HMAC stripped off. False if the data + * is tampered. + */ + public function validateData($data) + { + $len=$this->strlen($this->computeHMAC('test')); + + if($this->strlen($data) < $len) + return false; + + $hmac = $this->substr($data, 0, $len); + $data2=$this->substr($data, $len, $this->strlen($data)); + return $hmac === $this->computeHMAC($data2) ? $data2 : false; + } + + /** + * Computes the HMAC for the data with {@link getValidationKey ValidationKey}. + * @param string data to be generated HMAC + * @return string the HMAC for the data + */ + protected function computeHMAC($data) + { + $key = $this->getValidationKey(); + + if(function_exists('hash_hmac')) + return hash_hmac($this->_hashAlgorithm, $data, $key); + + if(!strcasecmp($this->_hashAlgorithm,'sha1')) + { + $pack = 'H40'; + $func = 'sha1'; + } else { + $pack = 'H32'; + $func = 'md5'; + } + + $key = str_pad($func($key), 64, chr(0)); + return $func((str_repeat(chr(0x5C), 64) ^ substr($key, 0, 64)) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ substr($key, 0, 64)) . $data))); + } + + /** + * Returns the length of the given string. + * If available uses the multibyte string function mb_strlen. + * @param string $string the string being measured for length + * @return int the length of the string + */ + private function strlen($string) + { + return $this->_mbstring ? mb_strlen($string,'8bit') : strlen($string); + } + + /** + * Returns the portion of string specified by the start and length parameters. + * If available uses the multibyte string function mb_substr + * @param string $string the input string. Must be one character or longer. + * @param int $start the starting position + * @param int $length the desired portion length + * @return string the extracted part of string, or FALSE on failure or an empty string. + */ + private function substr($string,$start,$length) + { + return $this->_mbstring ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length); + } +} + +/** + * TSecurityManagerValidationMode class. + * + * This class has been deprecated since version 3.2.1. + * + * TSecurityManagerValidationMode defines the enumerable type for the possible validation modes + * that can be used by {@link TSecurityManager}. + * + * The following enumerable values are defined: + * - MD5: an MD5 hash is generated from the data and used for validation. + * - SHA1: an SHA1 hash is generated from the data and used for validation. + * + * @author Qiang Xue + * @version $Id: TSecurityManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0.4 + */ +class TSecurityManagerValidationMode extends TEnumerable +{ + const MD5 = 'MD5'; + const SHA1 = 'SHA1'; +} diff --git a/gui/baculum/framework/Security/TUser.php b/gui/baculum/framework/Security/TUser.php new file mode 100644 index 0000000000..3a7a3fa0d9 --- /dev/null +++ b/gui/baculum/framework/Security/TUser.php @@ -0,0 +1,222 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TUser.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ + +/** + * Using IUserManager interface + */ +Prado::using('System.Security.IUserManager'); + +/** + * TUser class + * + * TUser implements basic user functionality for a Prado application. + * To get the name of the user, use {@link getName Name} property. + * The property {@link getIsGuest IsGuest} tells if the user a guest/anonymous user. + * To obtain or test the roles that the user is in, use property + * {@link getRoles Roles} and call {@link isInRole()}, respectively. + * + * TUser is meant to be used together with {@link IUserManager}. + * + * @author Qiang Xue + * @version $Id: TUser.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +class TUser extends TComponent implements IUser +{ + /** + * @var array persistent state + */ + private $_state; + /** + * @var boolean whether user state is changed + */ + private $_stateChanged=false; + /** + * @var IUserManager user manager + */ + private $_manager; + + /** + * Constructor. + * @param IUserManager user manager + */ + public function __construct(IUserManager $manager) + { + $this->_state=array(); + $this->_manager=$manager; + $this->setName($manager->getGuestName()); + } + + /** + * @return IUserManager user manager + */ + public function getManager() + { + return $this->_manager; + } + + /** + * @return string username, defaults to empty string. + */ + public function getName() + { + return $this->getState('Name',''); + } + + /** + * @param string username + */ + public function setName($value) + { + $this->setState('Name',$value,''); + } + + /** + * @return boolean if the user is a guest, defaults to true. + */ + public function getIsGuest() + { + return $this->getState('IsGuest',true); + } + + /** + * @param boolean if the user is a guest + */ + public function setIsGuest($value) + { + if($isGuest=TPropertyValue::ensureBoolean($value)) + { + $this->setName($this->_manager->getGuestName()); + $this->setRoles(array()); + } + $this->setState('IsGuest',$isGuest); + } + + /** + * @return array list of roles that the user is of + */ + public function getRoles() + { + return $this->getState('Roles',array()); + } + + /** + * @return array|string list of roles that the user is of. If it is a string, roles are assumed by separated by comma + */ + public function setRoles($value) + { + if(is_array($value)) + $this->setState('Roles',$value,array()); + else + { + $roles=array(); + foreach(explode(',',$value) as $role) + { + if(($role=trim($role))!=='') + $roles[]=$role; + } + $this->setState('Roles',$roles,array()); + } + } + + /** + * @param string role to be tested. Note, role is case-insensitive. + * @return boolean whether the user is of this role + */ + public function isInRole($role) + { + foreach($this->getRoles() as $r) + if(strcasecmp($role,$r)===0) + return true; + return false; + } + + /** + * @return string user data that is serialized and will be stored in session + */ + public function saveToString() + { + return serialize($this->_state); + } + + /** + * @param string user data that is serialized and restored from session + * @return IUser the user object + */ + public function loadFromString($data) + { + if(!empty($data)) + $this->_state=unserialize($data); + if(!is_array($this->_state)) + $this->_state=array(); + return $this; + } + + /** + * Returns the value of a variable that is stored in user session. + * + * This function is designed to be used by TUser descendant classes + * who want to store additional user information in user session. + * A variable, if stored in user session using {@link setState} can be + * retrieved back using this function. + * + * @param string variable name + * @param mixed default value + * @return mixed the value of the variable. If it doesn't exist, the provided default value will be returned + * @see setState + */ + protected function getState($key,$defaultValue=null) + { + return isset($this->_state[$key])?$this->_state[$key]:$defaultValue; + } + + /** + * Stores a variable in user session. + * + * This function is designed to be used by TUser descendant classes + * who want to store additional user information in user session. + * By storing a variable using this function, the variable may be retrieved + * back later using {@link getState}. The variable will be persistent + * across page requests during a user session. + * + * @param string variable name + * @param mixed variable value + * @param mixed default value. If $value===$defaultValue, the variable will be removed from persistent storage. + * @see getState + */ + protected function setState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_state[$key]); + else + $this->_state[$key]=$value; + $this->_stateChanged=true; + } + + /** + * @return boolean whether user session state is changed (i.e., setState() is called) + */ + public function getStateChanged() + { + return $this->_stateChanged; + } + + /** + * @param boolean whether user session state is changed + */ + public function setStateChanged($value) + { + $this->_stateChanged=TPropertyValue::ensureBoolean($value); + } +} + diff --git a/gui/baculum/framework/Security/TUserManager.php b/gui/baculum/framework/Security/TUserManager.php new file mode 100644 index 0000000000..5cb716dd26 --- /dev/null +++ b/gui/baculum/framework/Security/TUserManager.php @@ -0,0 +1,402 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + */ + +/** + * Using TUser class + */ +Prado::using('System.Security.TUser'); + +/** + * TUserManager class + * + * TUserManager manages a static list of users {@link TUser}. + * The user information is specified via module configuration using the following XML syntax, + * + * + * + * + * + * + * + * + * + * PHP configuration style: + * + * array( + * 'users' => array( + * 'class' => 'System.Security.TUserManager', + * 'properties' => array( + * 'PasswordMode' => 'Clear', + * ), + * 'users' => array( + * array('name'=>'Joe','password'=>'demo'), + * array('name'=>'John','password'=>'demo'), + * ), + * 'roles' => array( + * array('name'=>'Administrator','users'=>'John'), + * array('name'=>'Writer','users'=>'Joe,John'), + * ), + * ), + * ) + * + * + * In addition, user information can also be loaded from an external file + * specified by {@link setUserFile UserFile} property. Note, the property + * only accepts a file path in namespace format. The user file format is + * similar to the above sample. + * + * The user passwords may be specified as clear text, SH1 or MD5 hashed by setting + * {@link setPasswordMode PasswordMode} as Clear, SHA1 or MD5. + * The default name for a guest user is Guest. It may be changed + * by setting {@link setGuestName GuestName} property. + * + * TUserManager may be used together with {@link TAuthManager} which manages + * how users are authenticated and authorized in a Prado application. + * + * @author Qiang Xue + * @author Carl Mathisen + * @version $Id: TUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0 + */ +class TUserManager extends TModule implements IUserManager +{ + /** + * extension name to the user file + */ + const USER_FILE_EXT='.xml'; + + /** + * @var array list of users managed by this module + */ + private $_users=array(); + /** + * @var array list of roles managed by this module + */ + private $_roles=array(); + /** + * @var string guest name + */ + private $_guestName='Guest'; + /** + * @var TUserManagerPasswordMode password mode + */ + private $_passwordMode=TUserManagerPasswordMode::MD5; + /** + * @var boolean whether the module has been initialized + */ + private $_initialized=false; + /** + * @var string user/role information file + */ + private $_userFile=null; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * It loads user/role information from the module configuration. + * @param mixed module configuration + */ + public function init($config) + { + $this->loadUserData($config); + if($this->_userFile!==null) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $userFile = include $this->_userFile; + $this->loadUserDataFromPhp($userFile); + } + else + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_userFile); + $this->loadUserDataFromXml($dom); + } + } + $this->_initialized=true; + } + + /* + * Loads user/role information + * @param mixed the variable containing the user information + */ + private function loadUserData($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $this->loadUserDataFromPhp($config); + else + $this->loadUserDataFromXml($config); + } + + /** + * Loads user/role information from an php array. + * @param array the array containing the user information + */ + private function loadUserDataFromPhp($config) + { + if(isset($config['users']) && is_array($config['users'])) + { + foreach($config['users'] as $user) + { + $name = trim(strtolower(isset($user['name'])?$user['name']:'')); + $password = isset($user['password'])?$user['password']:''; + $this->_users[$name] = $password; + $roles = isset($user['roles'])?$user['roles']:''; + if($roles!=='') + { + foreach(explode(',',$roles) as $role) + { + if(($role=trim($role))!=='') + $this->_roles[$name][]=$role; + } + } + } + } + if(isset($config['roles']) && is_array($config['roles'])) + { + foreach($config['roles'] as $role) + { + $name = isset($role['name'])?$role['name']:''; + $users = isset($role['users'])?$role['users']:''; + foreach(explode(',',$users) as $user) + { + if(($user=trim($user))!=='') + $this->_roles[strtolower($user)][]=$name; + } + } + } + } + + /** + * Loads user/role information from an XML node. + * @param TXmlElement the XML node containing the user information + */ + private function loadUserDataFromXml($xmlNode) + { + foreach($xmlNode->getElementsByTagName('user') as $node) + { + $name=trim(strtolower($node->getAttribute('name'))); + $this->_users[$name]=$node->getAttribute('password'); + if(($roles=trim($node->getAttribute('roles')))!=='') + { + foreach(explode(',',$roles) as $role) + { + if(($role=trim($role))!=='') + $this->_roles[$name][]=$role; + } + } + } + foreach($xmlNode->getElementsByTagName('role') as $node) + { + foreach(explode(',',$node->getAttribute('users')) as $user) + { + if(($user=trim($user))!=='') + $this->_roles[strtolower($user)][]=$node->getAttribute('name'); + } + } + } + + /** + * Returns an array of all users. + * Each array element represents a single user. + * The array key is the username in lower case, and the array value is the + * corresponding user password. + * @return array list of users + */ + public function getUsers() + { + return $this->_users; + } + + /** + * Returns an array of user role information. + * Each array element represents the roles for a single user. + * The array key is the username in lower case, and the array value is + * the roles (represented as an array) that the user is in. + * @return array list of user role information + */ + public function getRoles() + { + return $this->_roles; + } + + /** + * @return string the full path to the file storing user/role information + */ + public function getUserFile() + { + return $this->_userFile; + } + + /** + * @param string user/role data file path (in namespace form). The file format is XML + * whose content is similar to that user/role block in application configuration. + * @throws TInvalidOperationException if the module is already initialized + * @throws TConfigurationException if the file is not in proper namespace format + */ + public function setUserFile($value) + { + if($this->_initialized) + throw new TInvalidOperationException('usermanager_userfile_unchangeable'); + else if(($this->_userFile=Prado::getPathOfNamespace($value,self::USER_FILE_EXT))===null || !is_file($this->_userFile)) + throw new TConfigurationException('usermanager_userfile_invalid',$value); + } + + /** + * @return string guest name, defaults to 'Guest' + */ + public function getGuestName() + { + return $this->_guestName; + } + + /** + * @param string name to be used for guest users. + */ + public function setGuestName($value) + { + $this->_guestName=$value; + } + + /** + * @return TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. Default to TUserManagerPasswordMode::MD5. + */ + public function getPasswordMode() + { + return $this->_passwordMode; + } + + /** + * @param TUserManagerPasswordMode how password is stored, clear text, or MD5 or SHA1 hashed. + */ + public function setPasswordMode($value) + { + $this->_passwordMode=TPropertyValue::ensureEnum($value,'TUserManagerPasswordMode'); + } + + /** + * Validates if the username and password are correct. + * @param string user name + * @param string password + * @return boolean true if validation is successful, false otherwise. + */ + public function validateUser($username,$password) + { + if($this->_passwordMode===TUserManagerPasswordMode::MD5) + $password=md5($password); + else if($this->_passwordMode===TUserManagerPasswordMode::SHA1) + $password=sha1($password); + $username=strtolower($username); + return (isset($this->_users[$username]) && $this->_users[$username]===$password); + } + + /** + * Returns a user instance given the user name. + * @param string user name, null if it is a guest. + * @return TUser the user instance, null if the specified username is not in the user database. + */ + public function getUser($username=null) + { + if($username===null) + { + $user=new TUser($this); + $user->setIsGuest(true); + return $user; + } + else + { + $username=strtolower($username); + if(isset($this->_users[$username])) + { + $user=new TUser($this); + $user->setName($username); + $user->setIsGuest(false); + if(isset($this->_roles[$username])) + $user->setRoles($this->_roles[$username]); + return $user; + } + else + return null; + } + } + + /** + * Returns a user instance according to auth data stored in a cookie. + * @param THttpCookie the cookie storing user authentication information + * @return TUser the user instance generated based on the cookie auth data, null if the cookie does not have valid auth data. + * @since 3.1.1 + */ + public function getUserFromCookie($cookie) + { + if(($data=$cookie->getValue())!=='') + { + $data=unserialize($data); + if(is_array($data) && count($data)===2) + { + list($username,$token)=$data; + if(isset($this->_users[$username]) && $token===md5($username.$this->_users[$username])) + return $this->getUser($username); + } + } + return null; + } + + /** + * Saves user auth data into a cookie. + * @param THttpCookie the cookie to receive the user auth data. + * @since 3.1.1 + */ + public function saveUserToCookie($cookie) + { + $user=$this->getApplication()->getUser(); + $username=strtolower($user->getName()); + if(isset($this->_users[$username])) + { + $data=array($username,md5($username.$this->_users[$username])); + $cookie->setValue(serialize($data)); + } + } + + /** + * Sets a user as a guest. + * User name is changed as guest name, and roles are emptied. + * @param TUser the user to be changed to a guest. + */ + public function switchToGuest($user) + { + $user->setIsGuest(true); + } +} + +/** + * TUserManagerPasswordMode class. + * TUserManagerPasswordMode defines the enumerable type for the possible modes + * that user passwords can be specified for a {@link TUserManager}. + * + * The following enumerable values are defined: + * - Clear: the password is in plain text + * - MD5: the password is recorded as the MD5 hash value of the original password + * - SHA1: the password is recorded as the SHA1 hash value of the original password + * + * @author Qiang Xue + * @version $Id: TUserManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Security + * @since 3.0.4 + */ +class TUserManagerPasswordMode extends TEnumerable +{ + const Clear='Clear'; + const MD5='MD5'; + const SHA1='SHA1'; +} + diff --git a/gui/baculum/framework/TApplication.php b/gui/baculum/framework/TApplication.php new file mode 100644 index 0000000000..7769a9e081 --- /dev/null +++ b/gui/baculum/framework/TApplication.php @@ -0,0 +1,1879 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TApplication.php 3317 2013-09-03 10:19:09Z ctrlaltca $ + * @package System + */ + +/** + * Includes core interfaces essential for TApplication class + */ +require_once(PRADO_DIR.'/interfaces.php'); + +/** + * Includes core classes essential for TApplication class + */ +Prado::using('System.TApplicationComponent'); +Prado::using('System.TModule'); +Prado::using('System.TService'); +Prado::using('System.Exceptions.TErrorHandler'); +Prado::using('System.Caching.TCache'); +Prado::using('System.IO.TTextWriter'); +Prado::using('System.Collections.TPriorityList'); +Prado::using('System.Collections.TPriorityMap'); +Prado::using('System.Collections.TStack'); +Prado::using('System.Xml.TXmlDocument'); +Prado::using('System.Security.TAuthorizationRule'); +Prado::using('System.Security.TSecurityManager'); +Prado::using('System.Web.THttpUtility'); +Prado::using('System.Web.Javascripts.TJavaScript'); +Prado::using('System.Web.THttpRequest'); +Prado::using('System.Web.THttpResponse'); +Prado::using('System.Web.THttpSession'); +Prado::using('System.Web.Services.TPageService'); +Prado::using('System.Web.TAssetManager'); +Prado::using('System.I18N.TGlobalization'); + +/** + * TApplication class. + * + * TApplication coordinates modules and services, and serves as a configuration + * context for all Prado components. + * + * TApplication uses a configuration file to specify the settings of + * the application, the modules, the services, the parameters, and so on. + * + * TApplication adopts a modular structure. A TApplication instance is a composition + * of multiple modules. A module is an instance of class implementing + * {@link IModule} interface. Each module accomplishes certain functionalities + * that are shared by all Prado components in an application. + * There are default modules and user-defined modules. The latter offers extreme + * flexibility of extending TApplication in a plug-and-play fashion. + * Modules cooperate with each other to serve a user request by following + * a sequence of lifecycles predefined in TApplication. + * + * TApplication has four modes that can be changed by setting {@link setMode Mode} + * property (in the application configuration file). + * - Off mode will prevent the application from serving user requests. + * - Debug mode is mainly used during application development. It ensures + * the cache is always up-to-date if caching is enabled. It also allows + * exceptions are displayed with rich context information if they occur. + * - Normal mode is mainly used during production stage. Exception information + * will only be recorded in system error logs. The cache is ensured to be + * up-to-date if it is enabled. + * - Performance mode is similar to Normal mode except that it + * does not ensure the cache is up-to-date. + * + * TApplication dispatches each user request to a particular service which + * finishes the actual work for the request with the aid from the application + * modules. + * + * TApplication maintains a lifecycle with the following stages: + * - [construct] : construction of the application instance + * - [initApplication] : load application configuration and instantiate modules and the requested service + * - onBeginRequest : this event happens right after application initialization + * - onAuthentication : this event happens when authentication is needed for the current request + * - onAuthenticationComplete : this event happens right after the authentication is done for the current request + * - onAuthorization : this event happens when authorization is needed for the current request + * - onAuthorizationComplete : this event happens right after the authorization is done for the current request + * - onLoadState : this event happens when application state needs to be loaded + * - onLoadStateComplete : this event happens right after the application state is loaded + * - onPreRunService : this event happens right before the requested service is to run + * - runService : the requested service runs + * - onSaveState : this event happens when application needs to save its state + * - onSaveStateComplete : this event happens right after the application saves its state + * - onPreFlushOutput : this event happens right before the application flushes output to client side. + * - flushOutput : the application flushes output to client side. + * - onEndRequest : this is the last stage a request is being completed + * - [destruct] : destruction of the application instance + * Modules and services can attach their methods to one or several of the above + * events and do appropriate processing when the events are raised. By this way, + * the application is able to coordinate the activities of modules and services + * in the above order. To terminate an application before the whole lifecycle + * completes, call {@link completeRequest}. + * + * Examples: + * - Create and run a Prado application: + * + * $application=new TApplication($configFile); + * $application->run(); + * + * + * @author Qiang Xue + * @version $Id: TApplication.php 3317 2013-09-03 10:19:09Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TApplication extends TComponent +{ + /** + * possible application mode. + * @deprecated deprecated since version 3.0.4 (use TApplicationMode constants instead) + */ + const STATE_OFF='Off'; + const STATE_DEBUG='Debug'; + const STATE_NORMAL='Normal'; + const STATE_PERFORMANCE='Performance'; + + /** + * Page service ID + */ + const PAGE_SERVICE_ID='page'; + /** + * Application configuration file name + */ + const CONFIG_FILE_XML='application.xml'; + /** + * File extension for external config files + */ + const CONFIG_FILE_EXT_XML='.xml'; + /** + * Configuration file type, application.xml and config.xml + */ + const CONFIG_TYPE_XML = 'xml'; + /** + * Application configuration file name + */ + const CONFIG_FILE_PHP='application.php'; + /** + * File extension for external config files + */ + const CONFIG_FILE_EXT_PHP='.php'; + /** + * Configuration file type, application.php and config.php + */ + const CONFIG_TYPE_PHP = 'php'; + /** + * Runtime directory name + */ + const RUNTIME_PATH='runtime'; + /** + * Config cache file + */ + const CONFIGCACHE_FILE='config.cache'; + /** + * Global data file + */ + const GLOBAL_FILE='global.cache'; + + /** + * @var array list of events that define application lifecycles + */ + private static $_steps=array( + 'onBeginRequest', + 'onLoadState', + 'onLoadStateComplete', + 'onAuthentication', + 'onAuthenticationComplete', + 'onAuthorization', + 'onAuthorizationComplete', + 'onPreRunService', + 'runService', + 'onSaveState', + 'onSaveStateComplete', + 'onPreFlushOutput', + 'flushOutput' + ); + + /** + * @var string application ID + */ + private $_id; + /** + * @var string unique application ID + */ + private $_uniqueID; + /** + * @var boolean whether the request is completed + */ + private $_requestCompleted=false; + /** + * @var integer application state + */ + private $_step; + /** + * @var array available services and their configurations indexed by service IDs + */ + private $_services; + /** + * @var IService current service instance + */ + private $_service; + /** + * @var array list of loaded application modules + */ + private $_modules=array(); + /** + * @var array list of application modules yet to be loaded + */ + private $_lazyModules=array(); + /** + * @var TMap list of application parameters + */ + private $_parameters; + /** + * @var string configuration file + */ + private $_configFile; + /** + * @var string configuration file extension + */ + private $_configFileExt; + /** + * @var string configuration type + */ + private $_configType; + /** + * @var string application base path + */ + private $_basePath; + /** + * @var string directory storing application state + */ + private $_runtimePath; + /** + * @var boolean if any global state is changed during the current request + */ + private $_stateChanged=false; + /** + * @var array global variables (persistent across sessions, requests) + */ + private $_globals=array(); + /** + * @var string cache file + */ + private $_cacheFile; + /** + * @var TErrorHandler error handler module + */ + private $_errorHandler; + /** + * @var THttpRequest request module + */ + private $_request; + /** + * @var THttpResponse response module + */ + private $_response; + /** + * @var THttpSession session module, could be null + */ + private $_session; + /** + * @var ICache cache module, could be null + */ + private $_cache; + /** + * @var IStatePersister application state persister + */ + private $_statePersister; + /** + * @var IUser user instance, could be null + */ + private $_user; + /** + * @var TGlobalization module, could be null + */ + private $_globalization; + /** + * @var TSecurityManager security manager module + */ + private $_security; + /** + * @var TAssetManager asset manager module + */ + private $_assetManager; + /** + * @var TAuthorizationRuleCollection collection of authorization rules + */ + private $_authRules; + /** + * @var TApplicationMode application mode + */ + private $_mode=TApplicationMode::Debug; + + /** + * @var string Customizable page service ID + */ + private $_pageServiceID = self::PAGE_SERVICE_ID; + + /** + * Constructor. + * Sets application base path and initializes the application singleton. + * Application base path refers to the root directory storing application + * data and code not directly accessible by Web users. + * By default, the base path is assumed to be the protected + * directory under the directory containing the current running script. + * @param string application base path or configuration file path. + * If the parameter is a file, it is assumed to be the application + * configuration file, and the directory containing the file is treated + * as the application base path. + * If it is a directory, it is assumed to be the application base path, + * and within that directory, a file named application.xml + * will be looked for. If found, the file is considered as the application + * configuration file. + * @param boolean whether to cache application configuration. Defaults to true. + * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid. + */ + public function __construct($basePath='protected',$cacheConfig=true, $configType=self::CONFIG_TYPE_XML) + { + // register application as a singleton + Prado::setApplication($this); + $this->setConfigurationType($configType); + $this->resolvePaths($basePath); + + if($cacheConfig) + $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE; + + // generates unique ID by hashing the runtime path + $this->_uniqueID=md5($this->_runtimePath); + $this->_parameters=new TMap; + $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null)); + + Prado::setPathOfAlias('Application',$this->_basePath); + } + + /** + * Resolves application-relevant paths. + * This method is invoked by the application constructor + * to determine the application configuration file, + * application root path and the runtime path. + * @param string the application root path or the application configuration file + * @see setBasePath + * @see setRuntimePath + * @see setConfigurationFile + */ + protected function resolvePaths($basePath) + { + // determine configuration path and file + if(empty($basePath) || ($basePath=realpath($basePath))===false) + throw new TConfigurationException('application_basepath_invalid',$basePath); + if(is_dir($basePath) && is_file($basePath.DIRECTORY_SEPARATOR.$this->getConfigurationFileName())) + $configFile=$basePath.DIRECTORY_SEPARATOR.$this->getConfigurationFileName(); + else if(is_file($basePath)) + { + $configFile=$basePath; + $basePath=dirname($configFile); + } + else + $configFile=null; + + // determine runtime path + $runtimePath=$basePath.DIRECTORY_SEPARATOR.self::RUNTIME_PATH; + if(is_writable($runtimePath)) + { + if($configFile!==null) + { + $runtimePath.=DIRECTORY_SEPARATOR.basename($configFile).'-'.Prado::getVersion(); + if(!is_dir($runtimePath)) + { + if(@mkdir($runtimePath)===false) + throw new TConfigurationException('application_runtimepath_failed',$runtimePath); + @chmod($runtimePath, PRADO_CHMOD); //make it deletable + } + $this->setConfigurationFile($configFile); + } + $this->setBasePath($basePath); + $this->setRuntimePath($runtimePath); + } + else + throw new TConfigurationException('application_runtimepath_invalid',$runtimePath); + + } + + /** + * Executes the lifecycles of the application. + * This is the main entry function that leads to the running of the whole + * Prado application. + */ + public function run() + { + try + { + $this->initApplication(); + $n=count(self::$_steps); + $this->_step=0; + $this->_requestCompleted=false; + while($this->_step<$n) + { + if($this->_mode===self::STATE_OFF) + throw new THttpException(503,'application_unavailable'); + if($this->_requestCompleted) + break; + $method=self::$_steps[$this->_step]; + Prado::trace("Executing $method()",'System.TApplication'); + $this->$method(); + $this->_step++; + } + } + catch(Exception $e) + { + $this->onError($e); + } + $this->onEndRequest(); + } + + /** + * Completes current request processing. + * This method can be used to exit the application lifecycles after finishing + * the current cycle. + */ + public function completeRequest() + { + $this->_requestCompleted=true; + } + + /** + * @return boolean whether the current request is processed. + */ + public function getRequestCompleted() + { + return $this->_requestCompleted; + } + + /** + * Returns a global value. + * + * A global value is one that is persistent across users sessions and requests. + * @param string the name of the value to be returned + * @param mixed the default value. If $key is not found, $defaultValue will be returned + * @return mixed the global value corresponding to $key + */ + public function getGlobalState($key,$defaultValue=null) + { + return isset($this->_globals[$key])?$this->_globals[$key]:$defaultValue; + } + + /** + * Sets a global value. + * + * A global value is one that is persistent across users sessions and requests. + * Make sure that the value is serializable and unserializable. + * @param string the name of the value to be set + * @param mixed the global value to be set + * @param mixed the default value. If $key is not found, $defaultValue will be returned + * @param boolean wheter to force an immediate GlobalState save. defaults to false + */ + public function setGlobalState($key,$value,$defaultValue=null,$forceSave=false) + { + $this->_stateChanged=true; + if($value===$defaultValue) + unset($this->_globals[$key]); + else + $this->_globals[$key]=$value; + if($forceSave) + $this->saveGlobals(); + } + + /** + * Clears a global value. + * + * The value cleared will no longer be available in this request and the following requests. + * @param string the name of the value to be cleared + */ + public function clearGlobalState($key) + { + $this->_stateChanged=true; + unset($this->_globals[$key]); + } + + /** + * Loads global values from persistent storage. + * This method is invoked when {@link onLoadState OnLoadState} event is raised. + * After this method, values that are stored in previous requests become + * available to the current request via {@link getGlobalState}. + */ + protected function loadGlobals() + { + $this->_globals=$this->getApplicationStatePersister()->load(); + } + + /** + * Saves global values into persistent storage. + * This method is invoked when {@link onSaveState OnSaveState} event is raised. + */ + protected function saveGlobals() + { + if($this->_stateChanged) + { + $this->_stateChanged=false; + $this->getApplicationStatePersister()->save($this->_globals); + } + } + + /** + * @return string application ID + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string application ID + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return string page service ID + */ + public function getPageServiceID() + { + return $this->_pageServiceID; + } + + /** + * @param string page service ID + */ + public function setPageServiceID($value) + { + $this->_pageServiceID=$value; + } + + /** + * @return string an ID that uniquely identifies this Prado application from the others + */ + public function getUniqueID() + { + return $this->_uniqueID; + } + + /** + * @return TApplicationMode application mode. Defaults to TApplicationMode::Debug. + */ + public function getMode() + { + return $this->_mode; + } + + /** + * @param TApplicationMode application mode + */ + public function setMode($value) + { + $this->_mode=TPropertyValue::ensureEnum($value,'TApplicationMode'); + } + + /** + * @return string the directory containing the application configuration file (absolute path) + */ + public function getBasePath() + { + return $this->_basePath; + } + + /** + * @param string the directory containing the application configuration file + */ + public function setBasePath($value) + { + $this->_basePath=$value; + } + + /** + * @return string the application configuration file (absolute path) + */ + public function getConfigurationFile() + { + return $this->_configFile; + } + + /** + * @param string the application configuration file (absolute path) + */ + public function setConfigurationFile($value) + { + $this->_configFile=$value; + } + + /** + * @return string the application configuration file (absolute path) + */ + public function getConfigurationType() + { + return $this->_configType; + } + + /** + * @param string the application configuration type. 'xml' and 'php' are valid values + */ + public function setConfigurationType($value) + { + $this->_configType = $value; + } + + /** + * @return string the application configuration type. default is 'xml' + */ + public function getConfigurationFileExt() + { + if($this->_configFileExt===null) + { + switch($this->_configType) + { + case TApplication::CONFIG_TYPE_PHP: + $this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP; + break; + default: + $this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML; + } + } + return $this->_configFileExt; + } + + /** + * @return string the default configuration file name + */ + public function getConfigurationFileName() + { + static $fileName; + if($fileName == null) + { + switch($this->_configType) + { + case TApplication::CONFIG_TYPE_PHP: + $fileName = TApplication::CONFIG_FILE_PHP; + break; + default: + $fileName = TApplication::CONFIG_FILE_XML; + } + } + return $fileName; + } + + /** + * @return string the directory storing cache data and application-level persistent data. (absolute path) + */ + public function getRuntimePath() + { + return $this->_runtimePath; + } + + /** + * @param string the directory storing cache data and application-level persistent data. (absolute path) + */ + public function setRuntimePath($value) + { + $this->_runtimePath=$value; + if($this->_cacheFile) + $this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE; + // generates unique ID by hashing the runtime path + $this->_uniqueID=md5($this->_runtimePath); + } + + /** + * @return IService the currently requested service + */ + public function getService() + { + return $this->_service; + } + + /** + * @param IService the currently requested service + */ + public function setService($value) + { + $this->_service=$value; + } + + /** + * Adds a module to application. + * Note, this method does not do module initialization. + * @param string ID of the module + * @param IModule module object or null if the module has not been loaded yet + */ + public function setModule($id,IModule $module=null) + { + if(isset($this->_modules[$id])) + throw new TConfigurationException('application_moduleid_duplicated',$id); + else + $this->_modules[$id]=$module; + } + + /** + * @return IModule the module with the specified ID, null if not found + */ + public function getModule($id) + { + if(!array_key_exists($id, $this->_modules)) + return null; + + // force loading of a lazy module + if($this->_modules[$id]===null) + { + $module = $this->internalLoadModule($id, true); + $module[0]->init($module[1]); + } + + return $this->_modules[$id]; + } + + /** + * Returns a list of application modules indexed by module IDs. + * Modules that have not been loaded yet are returned as null objects. + * @return array list of loaded application modules, indexed by module IDs + */ + public function getModules() + { + return $this->_modules; + } + + /** + * Returns the list of application parameters. + * Since the parameters are returned as a {@link TMap} object, you may use + * the returned result to access, add or remove individual parameters. + * @return TMap the list of application parameters + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @return THttpRequest the request module + */ + public function getRequest() + { + if(!$this->_request) + { + $this->_request=new THttpRequest; + $this->_request->init(null); + } + return $this->_request; + } + + /** + * @param THttpRequest the request module + */ + public function setRequest(THttpRequest $request) + { + $this->_request=$request; + } + + /** + * @return THttpResponse the response module + */ + public function getResponse() + { + if(!$this->_response) + { + $this->_response=new THttpResponse; + $this->_response->init(null); + } + return $this->_response; + } + + /** + * @param THttpRequest the request module + */ + public function setResponse(THttpResponse $response) + { + $this->_response=$response; + } + + /** + * @return THttpSession the session module, null if session module is not installed + */ + public function getSession() + { + if(!$this->_session) + { + $this->_session=new THttpSession; + $this->_session->init(null); + } + return $this->_session; + } + + /** + * @param THttpSession the session module + */ + public function setSession(THttpSession $session) + { + $this->_session=$session; + } + + /** + * @return TErrorHandler the error handler module + */ + public function getErrorHandler() + { + if(!$this->_errorHandler) + { + $this->_errorHandler=new TErrorHandler; + $this->_errorHandler->init(null); + } + return $this->_errorHandler; + } + + /** + * @param TErrorHandler the error handler module + */ + public function setErrorHandler(TErrorHandler $handler) + { + $this->_errorHandler=$handler; + } + + /** + * @return TSecurityManager the security manager module + */ + public function getSecurityManager() + { + if(!$this->_security) + { + $this->_security=new TSecurityManager; + $this->_security->init(null); + } + return $this->_security; + } + + /** + * @param TSecurityManager the security manager module + */ + public function setSecurityManager(TSecurityManager $sm) + { + $this->_security=$sm; + } + + /** + * @return TAssetManager asset manager + */ + public function getAssetManager() + { + if(!$this->_assetManager) + { + $this->_assetManager=new TAssetManager; + $this->_assetManager->init(null); + } + return $this->_assetManager; + } + + /** + * @param TAssetManager asset manager + */ + public function setAssetManager(TAssetManager $value) + { + $this->_assetManager=$value; + } + + /** + * @return IStatePersister application state persister + */ + public function getApplicationStatePersister() + { + if(!$this->_statePersister) + { + $this->_statePersister=new TApplicationStatePersister; + $this->_statePersister->init(null); + } + return $this->_statePersister; + } + + /** + * @param IStatePersister application state persister + */ + public function setApplicationStatePersister(IStatePersister $persister) + { + $this->_statePersister=$persister; + } + + /** + * @return ICache the cache module, null if cache module is not installed + */ + public function getCache() + { + return $this->_cache; + } + + /** + * @param ICache the cache module + */ + public function setCache(ICache $cache) + { + $this->_cache=$cache; + } + + /** + * @return IUser the application user + */ + public function getUser() + { + return $this->_user; + } + + /** + * @param IUser the application user + */ + public function setUser(IUser $user) + { + $this->_user=$user; + } + + /** + * @param boolean whether to create globalization if it does not exist + * @return TGlobalization globalization module + */ + public function getGlobalization($createIfNotExists=true) + { + if($this->_globalization===null && $createIfNotExists) + { + $this->_globalization=new TGlobalization; + $this->_globalization->init(null); + } + return $this->_globalization; + } + + /** + * @param TGlobalization globalization module + */ + public function setGlobalization(TGlobalization $glob) + { + $this->_globalization=$glob; + } + + /** + * @return TAuthorizationRuleCollection list of authorization rules for the current request + */ + public function getAuthorizationRules() + { + if($this->_authRules===null) + $this->_authRules=new TAuthorizationRuleCollection; + return $this->_authRules; + } + + protected function getApplicationConfigurationClass() + { + return 'TApplicationConfiguration'; + } + + protected function internalLoadModule($id, $force=false) + { + list($moduleClass, $initProperties, $configElement)=$this->_lazyModules[$id]; + if(isset($initProperties['lazy']) && $initProperties['lazy'] && !$force) + { + Prado::trace("Postponed loading of lazy module $id ({$moduleClass})",'System.TApplication'); + $this->setModule($id, null); + return null; + } + + Prado::trace("Loading module $id ({$moduleClass})",'System.TApplication'); + $module=Prado::createComponent($moduleClass); + foreach($initProperties as $name=>$value) + { + if($name==='lazy') continue; + $module->setSubProperty($name,$value); + } + $this->setModule($id,$module); + // keep the key to avoid reuse of the old module id + $this->_lazyModules[$id]=null; + + return array($module,$configElement); + } + /** + * Applies an application configuration. + * @param TApplicationConfiguration the configuration + * @param boolean whether the configuration is specified within a service. + */ + public function applyConfiguration($config,$withinService=false) + { + if($config->getIsEmpty()) + return; + + // set path aliases and using namespaces + foreach($config->getAliases() as $alias=>$path) + Prado::setPathOfAlias($alias,$path); + foreach($config->getUsings() as $using) + Prado::using($using); + + // set application properties + if(!$withinService) + { + foreach($config->getProperties() as $name=>$value) + $this->setSubProperty($name,$value); + } + + if(empty($this->_services)) + $this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null)); + + // load parameters + foreach($config->getParameters() as $id=>$parameter) + { + if(is_array($parameter)) + { + $component=Prado::createComponent($parameter[0]); + foreach($parameter[1] as $name=>$value) + $component->setSubProperty($name,$value); + $this->_parameters->add($id,$component); + } + else + $this->_parameters->add($id,$parameter); + } + + // load and init modules specified in app config + $modules=array(); + foreach($config->getModules() as $id=>$moduleConfig) + { + if(!is_string($id)) + $id='_module'.count($this->_lazyModules); + $this->_lazyModules[$id]=$moduleConfig; + if($module = $this->internalLoadModule($id)) + $modules[]=$module; + } + foreach($modules as $module) + $module[0]->init($module[1]); + + // load service + foreach($config->getServices() as $serviceID=>$serviceConfig) + $this->_services[$serviceID]=$serviceConfig; + + // external configurations + foreach($config->getExternalConfigurations() as $filePath=>$condition) + { + if($condition!==true) + $condition=$this->evaluateExpression($condition); + if($condition) + { + if(($path=Prado::getPathOfNamespace($filePath,$this->getConfigurationFileExt()))===null || !is_file($path)) + throw new TConfigurationException('application_includefile_invalid',$filePath); + $cn=$this->getApplicationConfigurationClass(); + $c=new $cn; + $c->loadFromFile($path); + $this->applyConfiguration($c,$withinService); + } + } + } + + /** + * Loads configuration and initializes application. + * Configuration file will be read and parsed (if a valid cached version exists, + * it will be used instead). Then, modules are created and initialized; + * Afterwards, the requested service is created and initialized. + * @param string configuration file path (absolute or relative to current executing script) + * @param string cache file path, empty if no present or needed + * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type + */ + protected function initApplication() + { + Prado::trace('Initializing application','System.TApplication'); + + if($this->_configFile!==null) + { + if($this->_cacheFile===null || @filemtime($this->_cacheFile)_configFile)) + { + $config=new TApplicationConfiguration; + $config->loadFromFile($this->_configFile); + if($this->_cacheFile!==null) + file_put_contents($this->_cacheFile,Prado::serialize($config),LOCK_EX); + } + else + $config=Prado::unserialize(file_get_contents($this->_cacheFile)); + + $this->applyConfiguration($config,false); + } + + if(($serviceID=$this->getRequest()->resolveRequest(array_keys($this->_services)))===null) + $serviceID=$this->getPageServiceID(); + + $this->startService($serviceID); + } + + /** + * Starts the specified service. + * The service instance will be created. Its properties will be initialized + * and the configurations will be applied, if any. + * @param string service ID + */ + public function startService($serviceID) + { + if(isset($this->_services[$serviceID])) + { + list($serviceClass,$initProperties,$configElement)=$this->_services[$serviceID]; + $service=Prado::createComponent($serviceClass); + if(!($service instanceof IService)) + throw new THttpException(500,'application_service_invalid',$serviceClass); + if(!$service->getEnabled()) + throw new THttpException(500,'application_service_unavailable',$serviceClass); + $service->setID($serviceID); + $this->setService($service); + + foreach($initProperties as $name=>$value) + $service->setSubProperty($name,$value); + + if($configElement!==null) + { + $config=new TApplicationConfiguration; + if($this->getConfigurationType()==self::CONFIG_TYPE_PHP) + $config->loadFromPhp($configElement,$this->getBasePath()); + else + $config->loadFromXml($configElement,$this->getBasePath()); + $this->applyConfiguration($config,true); + } + + $service->init($configElement); + } + else + throw new THttpException(500,'application_service_unknown',$serviceID); + } + + /** + * Raises OnError event. + * This method is invoked when an exception is raised during the lifecycles + * of the application. + * @param mixed event parameter + */ + public function onError($param) + { + Prado::log($param->getMessage(),TLogger::ERROR,'System.TApplication'); + $this->raiseEvent('OnError',$this,$param); + $this->getErrorHandler()->handleError($this,$param); + } + + /** + * Raises OnBeginRequest event. + * At the time when this method is invoked, application modules are loaded + * and initialized, user request is resolved and the corresponding service + * is loaded and initialized. The application is about to start processing + * the user request. + */ + public function onBeginRequest() + { + $this->raiseEvent('OnBeginRequest',$this,null); + } + + /** + * Raises OnAuthentication event. + * This method is invoked when the user request needs to be authenticated. + */ + public function onAuthentication() + { + $this->raiseEvent('OnAuthentication',$this,null); + } + + /** + * Raises OnAuthenticationComplete event. + * This method is invoked right after the user request is authenticated. + */ + public function onAuthenticationComplete() + { + $this->raiseEvent('OnAuthenticationComplete',$this,null); + } + + /** + * Raises OnAuthorization event. + * This method is invoked when the user request needs to be authorized. + */ + public function onAuthorization() + { + $this->raiseEvent('OnAuthorization',$this,null); + } + + /** + * Raises OnAuthorizationComplete event. + * This method is invoked right after the user request is authorized. + */ + public function onAuthorizationComplete() + { + $this->raiseEvent('OnAuthorizationComplete',$this,null); + } + + /** + * Raises OnLoadState event. + * This method is invoked when the application needs to load state (probably stored in session). + */ + public function onLoadState() + { + $this->loadGlobals(); + $this->raiseEvent('OnLoadState',$this,null); + } + + /** + * Raises OnLoadStateComplete event. + * This method is invoked right after the application state has been loaded. + */ + public function onLoadStateComplete() + { + $this->raiseEvent('OnLoadStateComplete',$this,null); + } + + /** + * Raises OnPreRunService event. + * This method is invoked right before the service is to be run. + */ + public function onPreRunService() + { + $this->raiseEvent('OnPreRunService',$this,null); + } + + /** + * Runs the requested service. + */ + public function runService() + { + if($this->_service) + $this->_service->run(); + } + + /** + * Raises OnSaveState event. + * This method is invoked when the application needs to save state (probably stored in session). + */ + public function onSaveState() + { + $this->raiseEvent('OnSaveState',$this,null); + $this->saveGlobals(); + } + + /** + * Raises OnSaveStateComplete event. + * This method is invoked right after the application state has been saved. + */ + public function onSaveStateComplete() + { + $this->raiseEvent('OnSaveStateComplete',$this,null); + } + + /** + * Raises OnPreFlushOutput event. + * This method is invoked right before the application flushes output to client. + */ + public function onPreFlushOutput() + { + $this->raiseEvent('OnPreFlushOutput',$this,null); + } + + /** + * Flushes output to client side. + * @param boolean whether to continue buffering after flush if buffering was active + */ + public function flushOutput($continueBuffering = true) + { + $this->getResponse()->flush($continueBuffering); + } + + /** + * Raises OnEndRequest event. + * This method is invoked when the application completes the processing of the request. + */ + public function onEndRequest() + { + $this->flushOutput(false); // flush all remaining content in the buffer + $this->saveGlobals(); // save global state + $this->raiseEvent('OnEndRequest',$this,null); + } +} + +/** + * TApplicationMode class. + * TApplicationMode defines the possible mode that an application can be set at by + * setting {@link TApplication::setMode Mode}. + * In particular, the following modes are defined + * - Off: the application is not running. Any request to the application will obtain an error. + * - Debug: the application is running in debug mode. + * - Normal: the application is running in normal production mode. + * - Performance: the application is running in performance mode. + * @author Qiang Xue + * @version $Id: TApplication.php 3317 2013-09-03 10:19:09Z ctrlaltca $ + * @package System + * @since 3.0.4 + */ +class TApplicationMode extends TEnumerable +{ + const Off='Off'; + const Debug='Debug'; + const Normal='Normal'; + const Performance='Performance'; +} + + +/** + * TApplicationConfiguration class. + * + * This class is used internally by TApplication to parse and represent application configuration. + * + * @author Qiang Xue + * @author Carl G. Mathisen + * @version $Id: TApplication.php 3317 2013-09-03 10:19:09Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TApplicationConfiguration extends TComponent +{ + /** + * @var array list of application initial property values, indexed by property names + */ + private $_properties=array(); + /** + * @var array list of namespaces to be used + */ + private $_usings=array(); + /** + * @var array list of path aliases, indexed by alias names + */ + private $_aliases=array(); + /** + * @var array list of module configurations + */ + private $_modules=array(); + /** + * @var array list of service configurations + */ + private $_services=array(); + /** + * @var array list of parameters + */ + private $_parameters=array(); + /** + * @var array list of included configurations + */ + private $_includes=array(); + /** + * @var boolean whether this configuration contains actual stuff + */ + private $_empty=true; + + /** + * Parses the application configuration file. + * @param string configuration file name + * @throws TConfigurationException if there is any parsing error + */ + public function loadFromFile($fname) + { + if(Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $fcontent = include $fname; + $this->loadFromPhp($fcontent,dirname($fname)); + } + else + { + $dom=new TXmlDocument; + $dom->loadFromFile($fname); + $this->loadFromXml($dom,dirname($fname)); + } + } + + /** + * @return boolean whether this configuration contains actual stuff + */ + public function getIsEmpty() + { + return $this->_empty; + } + + /** + * Parses the application configuration given in terms of a PHP array. + * @param array the PHP array + * @param string the context path (for specifying relative paths) + */ + public function loadFromPhp($config, $configPath) + { + // application properties + if(isset($config['application'])) + { + foreach($config['application'] as $name=>$value) + { + $this->_properties[$name]=$value; + } + $this->_empty = false; + } + + if(isset($config['paths']) && is_array($config['paths'])) + $this->loadPathsPhp($config['paths'],$configPath); + + if(isset($config['modules']) && is_array($config['modules'])) + $this->loadModulesPhp($config['modules'],$configPath); + + if(isset($config['services']) && is_array($config['services'])) + $this->loadServicesPhp($config['services'],$configPath); + + if(isset($config['parameters']) && is_array($config['parameters'])) + $this->loadParametersPhp($config['parameters'], $configPath); + + if(isset($config['includes']) && is_array($config['includes'])) + $this->loadExternalXml($config['includes'],$configPath); + } + + /** + * Parses the application configuration given in terms of a TXmlElement. + * @param TXmlElement the XML element + * @param string the context path (for specifying relative paths) + */ + public function loadFromXml($dom,$configPath) + { + // application properties + foreach($dom->getAttributes() as $name=>$value) + { + $this->_properties[$name]=$value; + $this->_empty=false; + } + + foreach($dom->getElements() as $element) + { + switch($element->getTagName()) + { + case 'paths': + $this->loadPathsXml($element,$configPath); + break; + case 'modules': + $this->loadModulesXml($element,$configPath); + break; + case 'services': + $this->loadServicesXml($element,$configPath); + break; + case 'parameters': + $this->loadParametersXml($element,$configPath); + break; + case 'include': + $this->loadExternalXml($element,$configPath); + break; + default: + //throw new TConfigurationException('appconfig_tag_invalid',$element->getTagName()); + break; + } + } + } + + /** + * Loads the paths PHP array + * @param array the paths PHP array + * @param string the context path (for specifying relative paths) + */ + protected function loadPathsPhp($pathsNode, $configPath) + { + if(isset($pathsNode['aliases']) && is_array($pathsNode['aliases'])) + { + foreach($pathsNode['aliases'] as $id=>$path) + { + $path=str_replace('\\','/',$path); + if(preg_match('/^\\/|.:\\/|.:\\\\/',$path)) // if absolute path + $p=realpath($path); + else + $p=realpath($configPath.DIRECTORY_SEPARATOR.$path); + if($p===false || !is_dir($p)) + throw new TConfigurationException('appconfig_aliaspath_invalid',$id,$path); + if(isset($this->_aliases[$id])) + throw new TConfigurationException('appconfig_alias_redefined',$id); + $this->_aliases[$id]=$p; + } + } + + if(isset($pathsNode['using']) && is_array($pathsNode['using'])) + { + foreach($pathsNode['using'] as $namespace) + { + $this->_usings[] = $namespace; + } + } + } + + /** + * Loads the paths XML node. + * @param TXmlElement the paths XML node + * @param string the context path (for specifying relative paths) + */ + protected function loadPathsXml($pathsNode,$configPath) + { + foreach($pathsNode->getElements() as $element) + { + switch($element->getTagName()) + { + case 'alias': + { + if(($id=$element->getAttribute('id'))!==null && ($path=$element->getAttribute('path'))!==null) + { + $path=str_replace('\\','/',$path); + if(preg_match('/^\\/|.:\\/|.:\\\\/',$path)) // if absolute path + $p=realpath($path); + else + $p=realpath($configPath.DIRECTORY_SEPARATOR.$path); + if($p===false || !is_dir($p)) + throw new TConfigurationException('appconfig_aliaspath_invalid',$id,$path); + if(isset($this->_aliases[$id])) + throw new TConfigurationException('appconfig_alias_redefined',$id); + $this->_aliases[$id]=$p; + } + else + throw new TConfigurationException('appconfig_alias_invalid'); + $this->_empty=false; + break; + } + case 'using': + { + if(($namespace=$element->getAttribute('namespace'))!==null) + $this->_usings[]=$namespace; + else + throw new TConfigurationException('appconfig_using_invalid'); + $this->_empty=false; + break; + } + default: + throw new TConfigurationException('appconfig_paths_invalid',$element->getTagName()); + } + } + } + + /** + * Loads the modules PHP array. + * @param array the modules PHP array + * @param string the context path (for specifying relative paths) + */ + protected function loadModulesPhp($modulesNode, $configPath) + { + foreach($modulesNode as $id=>$module) + { + if(!isset($module['class'])) + throw new TConfigurationException('appconfig_moduletype_required',$id); + $type = $module['class']; + unset($module['class']); + $properties = array(); + if(isset($module['properties'])) + { + $properties = $module['properties']; + unset($module['properties']); + } + $properties['id'] = $id; + $this->_modules[$id]=array($type,$properties,$module); + $this->_empty=false; + } + } + + /** + * Loads the modules XML node. + * @param TXmlElement the modules XML node + * @param string the context path (for specifying relative paths) + */ + protected function loadModulesXml($modulesNode,$configPath) + { + foreach($modulesNode->getElements() as $element) + { + if($element->getTagName()==='module') + { + $properties=$element->getAttributes(); + $id=$properties->itemAt('id'); + $type=$properties->remove('class'); + if($type===null) + throw new TConfigurationException('appconfig_moduletype_required',$id); + $element->setParent(null); + if($id===null) + $this->_modules[]=array($type,$properties->toArray(),$element); + else + $this->_modules[$id]=array($type,$properties->toArray(),$element); + $this->_empty=false; + } + else + throw new TConfigurationException('appconfig_modules_invalid',$element->getTagName()); + } + } + + /** + * Loads the services PHP array. + * @param array the services PHP array + * @param string the context path (for specifying relative paths) + */ + protected function loadServicesPhp($servicesNode,$configPath) + { + foreach($servicesNode as $id => $service) + { + if(!isset($service['class'])) + throw new TConfigurationException('appconfig_servicetype_required'); + $type = $service['class']; + $properties = isset($service['properties']) ? $service['properties'] : array(); + unset($service['properties']); + $properties['id'] = $id; + $this->_services[$id] = array($type,$properties,$service); + $this->_empty = false; + } + } + + /** + * Loads the services XML node. + * @param TXmlElement the services XML node + * @param string the context path (for specifying relative paths) + */ + protected function loadServicesXml($servicesNode,$configPath) + { + foreach($servicesNode->getElements() as $element) + { + if($element->getTagName()==='service') + { + $properties=$element->getAttributes(); + if(($id=$properties->itemAt('id'))===null) + throw new TConfigurationException('appconfig_serviceid_required'); + if(($type=$properties->remove('class'))===null) + throw new TConfigurationException('appconfig_servicetype_required',$id); + $element->setParent(null); + $this->_services[$id]=array($type,$properties->toArray(),$element); + $this->_empty=false; + } + else + throw new TConfigurationException('appconfig_services_invalid',$element->getTagName()); + } + } + + /** + * Loads the parameters PHP array. + * @param array the parameters PHP array + * @param string the context path (for specifying relative paths) + */ + protected function loadParametersPhp($parametersNode,$configPath) + { + foreach($parametersNode as $id => $parameter) + { + if(is_array($parameter)) + { + if(isset($parameter['class'])) + { + $type = $parameter['class']; + unset($parameter['class']); + $properties = isset($service['properties']) ? $service['properties'] : array(); + $properties['id'] = $id; + $this->_parameters[$id] = array($type,$properties); + } + } + else + { + $this->_parameters[$id] = $parameter; + } + } + } + + /** + * Loads the parameters XML node. + * @param TXmlElement the parameters XML node + * @param string the context path (for specifying relative paths) + */ + protected function loadParametersXml($parametersNode,$configPath) + { + foreach($parametersNode->getElements() as $element) + { + if($element->getTagName()==='parameter') + { + $properties=$element->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('appconfig_parameterid_required'); + if(($type=$properties->remove('class'))===null) + { + if(($value=$properties->remove('value'))===null) + $this->_parameters[$id]=$element; + else + $this->_parameters[$id]=$value; + } + else + $this->_parameters[$id]=array($type,$properties->toArray()); + $this->_empty=false; + } + else + throw new TConfigurationException('appconfig_parameters_invalid',$element->getTagName()); + } + } + + /** + * Loads the external PHP array. + * @param array the application PHP array + * @param string the context path (for specifying relative paths) + */ + protected function loadExternalPhp($includeNode,$configPath) + { + foreach($includeNode as $include) + { + $when = isset($include['when'])?true:false; + if(!isset($include['file'])) + throw new TConfigurationException('appconfig_includefile_required'); + $filePath = $include['file']; + if(isset($this->_includes[$filePath])) + $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')'; + else + $$this->_includes[$filePath]=$when; + $this->_empty=false; + } + } + + /** + * Loads the external XML configurations. + * @param TXmlElement the application DOM element + * @param string the context path (for specifying relative paths) + */ + protected function loadExternalXml($includeNode,$configPath) + { + if(($when=$includeNode->getAttribute('when'))===null) + $when=true; + if(($filePath=$includeNode->getAttribute('file'))===null) + throw new TConfigurationException('appconfig_includefile_required'); + if(isset($this->_includes[$filePath])) + $this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')'; + else + $this->_includes[$filePath]=$when; + $this->_empty=false; + } + + /** + * Returns list of page initial property values. + * Each array element represents a single property with the key + * being the property name and the value the initial property value. + * @return array list of page initial property values + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * Returns list of path alias definitions. + * The definitions are aggregated (top-down) from configuration files along the path + * to the specified page. Each array element represents a single alias definition, + * with the key being the alias name and the value the absolute path. + * @return array list of path alias definitions + */ + public function getAliases() + { + return $this->_aliases; + } + + /** + * Returns list of namespaces to be used. + * The namespaces are aggregated (top-down) from configuration files along the path + * to the specified page. Each array element represents a single namespace usage, + * with the value being the namespace to be used. + * @return array list of namespaces to be used + */ + public function getUsings() + { + return $this->_usings; + } + + /** + * Returns list of module configurations. + * The module configurations are aggregated (top-down) from configuration files + * along the path to the specified page. Each array element represents + * a single module configuration, with the key being the module ID and + * the value the module configuration. Each module configuration is + * stored in terms of an array with the following content + * ([0]=>module type, [1]=>module properties, [2]=>complete module configuration) + * The module properties are an array of property values indexed by property names. + * The complete module configuration is a TXmlElement object representing + * the raw module configuration which may contain contents enclosed within + * module tags. + * @return array list of module configurations to be used + */ + public function getModules() + { + return $this->_modules; + } + + /** + * @return array list of service configurations + */ + public function getServices() + { + return $this->_services; + } + + /** + * Returns list of parameter definitions. + * The parameter definitions are aggregated (top-down) from configuration files + * along the path to the specified page. Each array element represents + * a single parameter definition, with the key being the parameter ID and + * the value the parameter definition. A parameter definition can be either + * a string representing a string-typed parameter, or an array. + * The latter defines a component-typed parameter whose format is as follows, + * ([0]=>component type, [1]=>component properties) + * The component properties are an array of property values indexed by property names. + * @return array list of parameter definitions to be used + */ + public function getParameters() + { + return $this->_parameters; + } + + /** + * @return array list of external configuration files. Each element is like $filePath=>$condition + */ + public function getExternalConfigurations() + { + return $this->_includes; + } +} + +/** + * TApplicationStatePersister class. + * TApplicationStatePersister provides a file-based persistent storage + * for application state. Application state, when serialized, is stored + * in a file named 'global.cache' under the 'runtime' directory of the application. + * Cache will be exploited if it is enabled. + * + * @author Qiang Xue + * @version $Id: TApplication.php 3317 2013-09-03 10:19:09Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TApplicationStatePersister extends TModule implements IStatePersister +{ + /** + * Name of the value stored in cache + */ + const CACHE_NAME='prado:appstate'; + + /** + * Initializes module. + * @param TXmlElement module configuration (may be null) + */ + public function init($config) + { + $this->getApplication()->setApplicationStatePersister($this); + } + + /** + * @return string the file path storing the application state + */ + protected function getStateFilePath() + { + return $this->getApplication()->getRuntimePath().'/global.cache'; + } + + /** + * Loads application state from persistent storage. + * @return mixed application state + */ + public function load() + { + if(($cache=$this->getApplication()->getCache())!==null && ($value=$cache->get(self::CACHE_NAME))!==false) + return Prado::unserialize($value); + else + { + if(($content=@file_get_contents($this->getStateFilePath()))!==false) + return Prado::unserialize($content); + else + return null; + } + } + + /** + * Saves application state in persistent storage. + * @param mixed application state + */ + public function save($state) + { + $content=Prado::serialize($state); + $saveFile=true; + if(($cache=$this->getApplication()->getCache())!==null) + { + if($cache->get(self::CACHE_NAME)===$content) + $saveFile=false; + else + $cache->set(self::CACHE_NAME,$content); + } + if($saveFile) + { + $fileName=$this->getStateFilePath(); + file_put_contents($fileName,$content,LOCK_EX); + } + } + +} diff --git a/gui/baculum/framework/TApplicationComponent.php b/gui/baculum/framework/TApplicationComponent.php new file mode 100644 index 0000000000..a307d54b5e --- /dev/null +++ b/gui/baculum/framework/TApplicationComponent.php @@ -0,0 +1,118 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TApplicationComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * TApplicationComponent class + * + * TApplicationComponent is the base class for all components that are + * application-related, such as controls, modules, services, etc. + * + * TApplicationComponent mainly defines a few properties that are shortcuts + * to some commonly used methods. The {@link getApplication Application} + * property gives the application instance that this component belongs to; + * {@link getService Service} gives the current running service; + * {@link getRequest Request}, {@link getResponse Response} and {@link getSession Session} + * return the request and response modules, respectively; + * And {@link getUser User} gives the current user instance. + * + * Besides, TApplicationComponent defines two shortcut methods for + * publishing private files: {@link publishAsset} and {@link publishFilePath}. + * + * @author Qiang Xue + * @version $Id: TApplicationComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TApplicationComponent extends TComponent +{ + /** + * @return TApplication current application instance + */ + public function getApplication() + { + return Prado::getApplication(); + } + + /** + * @return IService the current service + */ + public function getService() + { + return Prado::getApplication()->getService(); + } + + /** + * @return THttpRequest the current user request + */ + public function getRequest() + { + return Prado::getApplication()->getRequest(); + } + + /** + * @return THttpResponse the response + */ + public function getResponse() + { + return Prado::getApplication()->getResponse(); + } + + /** + * @return THttpSession user session + */ + public function getSession() + { + return Prado::getApplication()->getSession(); + } + + /** + * @return IUser information about the current user + */ + public function getUser() + { + return Prado::getApplication()->getUser(); + } + + /** + * Publishes a private asset and gets its URL. + * This method will publish a private asset (file or directory) + * and gets the URL to the asset. Note, if the asset refers to + * a directory, all contents under that directory will be published. + * Also note, it is recommended that you supply a class name as the second + * parameter to the method (e.g. publishAsset($assetPath,__CLASS__) ). + * By doing so, you avoid the issue that child classes may not work properly + * because the asset path will be relative to the directory containing the child class file. + * + * @param string path of the asset that is relative to the directory containing the specified class file. + * @param string name of the class whose containing directory will be prepend to the asset path. If null, it means get_class($this). + * @return string URL to the asset path. + */ + public function publishAsset($assetPath,$className=null) + { + if($className===null) + $className=get_class($this); + $class=new ReflectionClass($className); + $fullPath=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$assetPath; + return $this->publishFilePath($fullPath); + } + + /** + * Publishes a file or directory and returns its URL. + * @param string absolute path of the file or directory to be published + * @return string URL to the published file or directory + */ + public function publishFilePath($fullPath, $checkTimestamp=false) + { + return Prado::getApplication()->getAssetManager()->publishFilePath($fullPath, $checkTimestamp); + } +} + diff --git a/gui/baculum/framework/TComponent.php b/gui/baculum/framework/TComponent.php new file mode 100644 index 0000000000..e9fa5a657e --- /dev/null +++ b/gui/baculum/framework/TComponent.php @@ -0,0 +1,2413 @@ + + * + * Global Events, intra-object events, Class behaviors, expanded behaviors + * @author Brad Anderson + * + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * TComponent class + * + * TComponent is the base class for all PRADO components. + * TComponent implements the protocol of defining, using properties, behaviors, + * and events. + * + * A property is defined by a getter method, and/or a setter method. + * Properties can be accessed in the way like accessing normal object members. + * Reading or writing a property will cause the invocation of the corresponding + * getter or setter method, e.g., + * + * $a=$this->Text; // equivalent to $a=$this->getText(); + * $this->Text='abc'; // equivalent to $this->setText('abc'); + * + * The signatures of getter and setter methods are as follows, + * + * // getter, defines a readable property 'Text' + * function getText() { ... } + * // setter, defines a writable property 'Text', with $value being the value to be set to the property + * function setText($value) { ... } + * + * Property names are case-insensitive. It is recommended that they are written + * in the format of concatenated words, with the first letter of each word + * capitalized (e.g. DisplayMode, ItemStyle). + * + * Javascript Get and Set + * + * Since Prado 3.2 a new class of javascript-friendly properties have been introduced + * to better deal with potential security problems like cross-site scripting issues. + * All the data that gets sent clientside inside a javascript block is now encoded by default. + * Sometimes there's the need to bypass this encoding and be able to send raw javascript code. + * This new class of javascript-friendly properties are identified by their name + * starting with 'js' (case insensitive): + * + * // getter, defines a readable property 'Text' + * function getJsText() { ... } + * // setter, defines a writable property 'Text', with $value being the value to be set to the property + * function setJsText(TJavaScriptLiteral $value) { ... } + * + * Js-friendly properties can be accessed using both their Js-less name and their Js-enabled name: + * + * // set some simple text as property value + * $component->Text = 'text'; + * // set some javascript code as property value + * $component->JsText = 'raw javascript'; + * + * In the first case, the property value will automatically gets encoded when sent + * clientside inside a javascript block. + * In the second case, the property will be 'marked' as being a safe javascript + * statement and will not be encoded when rendered inside a javascript block. + * This special handling makes use of the {@link TJavaScriptLiteral} class. + * + * Events + * + * An event is defined by the presence of a method whose name starts with 'on'. + * The event name is the method name and is thus case-insensitive. + * An event can be attached with one or several methods (called event handlers). + * An event can be raised by calling {@link raiseEvent} method, upon which + * the attached event handlers will be invoked automatically in the order they + * are attached to the event. Event handlers must have the following signature, + * + * function eventHandlerFuncName($sender,$param) { ... } + * + * where $sender refers to the object who is responsible for the raising of the event, + * and $param refers to a structure that may contain event-specific information. + * To raise an event (assuming named as 'Click') of a component, use + * + * $component->raiseEvent('OnClick'); + * $component->raiseEvent('OnClick', $this, $param); + * + * To attach an event handler to an event, use one of the following ways, + * + * $component->OnClick=$callback; // or $component->OnClick->add($callback); + * $component->attachEventHandler('OnClick',$callback); + * + * The first two ways make use of the fact that $component->OnClick refers to + * the event handler list {@link TPriorityList} for the 'OnClick' event. + * The variable $callback contains the definition of the event handler that can + * be either a string referring to a global function name, or an array whose + * first element refers to an object and second element a method name/path that + * is reachable by the object, e.g. + * - 'buttonClicked' : buttonClicked($sender,$param); + * - array($object,'buttonClicked') : $object->buttonClicked($sender,$param); + * - array($object,'MainContent.SubmitButton.buttonClicked') : + * $object->MainContent->SubmitButton->buttonClicked($sender,$param); + * + * With the addition of behaviors, a more expansive event model is needed. There + * are two new event types (global and dynamic events) as well as a more comprehensive + * behavior model that includes class wide behaviors. + * + * A global event is defined by all events whose name starts with 'fx'. + * The event name is potentially a method name and is thus case-insensitive. All 'fx' events + * are valid as the whole 'fx' event/method space is global in nature. Any object may patch into + * any global event by defining that event as a method. Global events have priorities + * just like 'on' events; so as to be able to order the event execution. Due to the + * nature of all events which start with 'fx' being valid, in effect, every object + * has every 'fx' global event. It is simply an issue of tapping into the desired + * global event. + * + * A global event that starts with 'fx' can be called even if the object does not + * implement the method of the global event. A call to a non-existing 'fx' method + * will, at minimal, function and return null. If a method argument list has a first + * parameter, it will be returned instead of null. This allows filtering and chaining. + * 'fx' methods do not automatically install and uninstall. To install and uninstall an + * object's global event listeners, call the object's {@link listen} and + * {@link unlisten} methods, respectively. An object may auto-install its global event + * during {@link __construct} by overriding {@link getAutoGlobalListen} and returning true. + * + * As of PHP version 5.3, nulled objects without code references will still continue to persist + * in the global event queue because {@link __destruct} is not automatically called. In the common + * __destruct method, if an object is listening to global events, then {@link unlisten} is called. + * {@link unlisten} is required to be manually called before an object is + * left without references if it is currently listening to any global events. This includes + * class wide behaviors. + * + * An object that contains a method that starts with 'fx' will have those functions + * automatically receive those events of the same name after {@link listen} is called on the object. + * + * An object may listen to a global event without defining an 'fx' method of the same name by + * adding an object method to the global event list. For example + * + * $component->fxGlobalCheck=$callback; // or $component->OnClick->add($callback); + * $component->attachEventHandler('fxGlobalCheck',array($object, 'someMethod')); + * + * + * Events between Objects and their behaviors, Dynamic Events + * + * An intra-object/behavior event is defined by methods that start with 'dy'. Just as with + * 'fx' global events, every object has every dynamic event. Any call to a method that + * starts with 'dy' will be handled, regardless of whether it is implemented. These + * events are for communicating with attached behaviors. + * + * Dynamic events can be used in a variety of ways. They can be used to tell behaviors + * when a non-behavior method is called. Dynamic events could be used as data filters. + * They could also be used to specify when a piece of code is to be run, eg. should the + * loop process be performed on a particular piece of data. In this way, some control + * is handed to the behaviors over the process and/or data. + * + * If there are no handlers for an 'fx' or 'dy' event, it will return the first + * parameter of the argument list. If there are no arguments, these events + * will return null. If there are handlers an 'fx' method will be called directly + * within the object. Global 'fx' events are triggered by calling {@link raiseEvent}. + * For dynamic events where there are behaviors that respond to the dynamic events, a + * {@link TCallChain} is developed. A call chain allows the behavior dynamic event + * implementations to call further implementing behaviors within a chain. + * + * If an object implements {@link IDynamicMethods}, all global and object dynamic + * events will be sent to {@link __dycall}. In the case of global events, all + * global events will trigger this method. In the case of behaviors, all undefined + * dynamic events which are called will be passed through to this method. + * + * + * Behaviors + * + * There are two types of behaviors. There are individual object behaviors and + * there are class wide behaviors. Class behaviors depend upon object behaviors. + * + * When a new class implements {@link IBehavior} or {@link IClassBehavior} or + * extends {@link TBehavior} or {@link TClassBehavior}, it may be added to an + * object by calling the object's {@link attachBehavior}. The behaviors associated + * name can then be used to {@link enableBehavior} or {@link disableBehavior} + * the specific behavior. + * + * All behaviors may be turned on and off via {@link enableBehaviors} and + * {@link disableBehaviors}, respectively. To check if behaviors are on or off + * a call to {@link getBehaviorsEnabled} will provide the variable. + * + * Attaching and detaching whole sets of behaviors is done using + * {@link attachBehaviors} and {@link detachBehaviors}. {@link clearBehaviors} + * removes all of an object's behaviors. + * + * {@link asa} returns a behavior of a specific name. {@link isa} is the + * behavior inclusive function that acts as the PHP operator {@link instanceof}. + * A behavior could provide the functionality of a specific class thus causing + * the host object to act similarly to a completely different class. A behavior + * would then implement {@link IInstanceCheck} to provide the identity of the + * different class. + * + * Class behaviors are similar to object behaviors except that the class behavior + * is the implementation for all instances of the class. A class behavior + * will have the object upon which is being called be prepended to the parameter + * list. This way the object is known across the class behavior implementation. + * + * Class behaviors are attached using {@link attachClassBehavior} and detached + * using {@link detachClassBehavior}. Class behaviors are important in that + * they will be applied to all new instances of a particular class. In this way + * class behaviors become default behaviors to a new instances of a class in + * {@link __construct}. Detaching a class behavior will remove the behavior + * from the default set of behaviors created for an object when the object + * is instanced. + * + * Class behaviors are also added to all existing instances via the global 'fx' + * event mechanism. When a new class behavior is added, the event + * {@link fxAttachClassBehavior} is raised and all existing instances that are + * listening to this global event (primarily after {@link listen} is called) + * will have this new behavior attached. A similar process is used when + * detaching class behaviors. Any objects listening to the global 'fx' event + * {@link fxDetachClassBehavior} will have a class behavior removed. + * + * Dynamic Intra-Object Events + * + * Dynamic events start with 'dy'. This mechanism is used to allow objects + * to communicate with their behaviors directly. The entire 'dy' event space + * is valid. All attached, enabled behaviors that implement a dynamic event + * are called when the host object calls the dynamic event. If there is no + * implementation or behaviors, this returns null when no parameters are + * supplied and will return the first parameter when there is at least one + * parameter in the dynamic event. + * + * null == $this->dyBehaviorEvent(); + * 5 == $this->dyBehaviorEvent(5); //when no behaviors implement this dynamic event + * + * + * Dynamic events can be chained together within behaviors to allow for data + * filtering. Dynamic events are implemented within behaviors by defining the + * event as a method. + * + * class TObjectBehavior extends TBehavior { + * public function dyBehaviorEvent($param1, $callchain) { + * //Do something, eg: $param1 += 13; + * return $callchain->dyBehaviorEvent($param1); + * } + * } + * + * This implementation of a behavior and dynamic event will flow through to the + * next behavior implementing the dynamic event. The first parameter is always + * return when it is supplied. Otherwise a dynamic event returns null. + * + * In the case of a class behavior, the object is also prepended to the dynamic + * event. + * + * class TObjectClassBehavior extends TClassBehavior { + * public function dyBehaviorEvent($hostobject, $param1, $callchain) { + * //Do something, eg: $param1 += $hostobject->getNumber(); + * return $callchain->dyBehaviorEvent($param1); + * } + * } + * + * When calling a dynamic event, only the parameters are passed. The host object + * and the call chain are built into the framework. + * + * Global Event and Dynamic event catching + * + * Given that all global 'fx' events and dynamic 'dy' events are valid and + * operational, there is a mechanism for catching events called that are not + * implemented (similar to the built-in PHP method {@link __call}). When + * a dynamic or global event is called but a behavior does not implement it, + * yet desires to know when an undefined dynamic event is run, the behavior + * implements the interface {@link IDynamicMethods} and method {@link __dycall}. + * + * In the case of dynamic events, {@link __dycall} is supplied with the method + * name and its parameters. When a global event is raised, via {@link raiseEvent}, + * the method is the event name and the parameters are supplied. + * + * When implemented, this catch-all mechanism is called for event global event event + * when implemented outside of a behavior. Within a behavior, it will also be called + * when the object to which the behavior is attached calls any unimplemented dynamic + * event. This is the fall-back mechanism for informing a class and/or behavior + * of when an global and/or undefined dynamic event is executed. + * + * @author Qiang Xue + * @author Brad Anderson + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TComponent +{ + /** + * @var array event handler lists + */ + private $_e=array(); + + /** + * @var boolean if listening is enabled. Automatically turned on or off in + * constructor according to {@link getAutoGlobalListen}. Default false, off + */ + private $_listeningenabled=false; + + /** + * @var array static registered global event handler lists + */ + private static $_ue=array(); + + /** + * @var boolean if object behaviors are on or off. default true, on + */ + private $_behaviorsenabled=true; + + /** + * @var TPriorityMap list of object behaviors + */ + private $_m=null; + + /** + * @var array static global class behaviors, these behaviors are added upon instantiation of a class + */ + private static $_um=array(); + + + /** + * @const string the name of the global {@link raiseEvent} listener + */ + const GLOBAL_RAISE_EVENT_LISTENER='fxGlobalListener'; + + + /** + * The common __construct + * If desired by the new object, this will auto install and listen to global event functions + * as defined by the object via 'fx' methods. This also attaches any predefined behaviors. + * This function installs all class behaviors in a class hierarchy from the deepest subclass + * through each parent to the top most class, TComponent. + */ + public function __construct() { + if($this->getAutoGlobalListen()) + $this->listen(); + + $classes=array_reverse($this->getClassHierarchy(true)); + foreach($classes as $class) { + if(isset(self::$_um[$class])) + $this->attachBehaviors(self::$_um[$class]); + } + } + + + /** + * Tells TComponent whether or not to automatically listen to global events. + * Defaults to false because PHP variable cleanup is affected if this is true. + * When unsetting a variable that is listening to global events, {@link unlisten} + * must explicitly be called when cleaning variables allocation or else the global + * event registry will contain references to the old object. This is true for PHP 5.4 + * + * Override this method by a subclass to change the setting. When set to true, this + * will enable {@link __construct} to call {@link listen}. + * + * @return boolean whether or not to auto listen to global events during {@link __construct}, default false + */ + public function getAutoGlobalListen() { + return false; + } + + + /** + * The common __destruct + * This unlistens from the global event routines if listening + * + * PHP 5.3 does not __destruct objects when they are nulled and thus unlisten must be + * called must be explicitly called. + */ + public function __destruct() { + if($this->_listeningenabled) + $this->unlisten(); + } + + + /** + * This utility function is a private array filter method. The array values + * that start with 'fx' are filtered in. + */ + private function filter_prado_fx($name) { + return strncasecmp($name,'fx',2)===0; + } + + + /** + * This returns an array of the class name and the names of all its parents. The base object first, + * {@link TComponent}, and the deepest subclass is last. + * @param boolean optional should the names be all lowercase true/false + * @return array array of strings being the class hierarchy of $this. + */ + public function getClassHierarchy($lowercase = false) + { + $class=get_class($this); + $classes=array($class); + while($class=get_parent_class($class)){array_unshift($classes,$class);} + if($lowercase) + return array_map('strtolower',$classes); + return $classes; + } + + + /** + * This adds an object's fx event handlers into the global broadcaster to listen into any + * broadcast global events called through {@link raiseEvent} + * + * Behaviors may implement the function: + * + * public function dyListen($globalEvents[, $chain]) { + * $this->listen(); //eg + * } + * + * to be executed when listen is called. All attached behaviors are notified through dyListen. + * + * @return numeric the number of global events that were registered to the global event registry + */ + public function listen() { + if($this->_listeningenabled) + return; + + $fx=array_filter(get_class_methods($this),array($this,'filter_prado_fx')); + + foreach($fx as $func) + $this->attachEventHandler($func,array($this,$func)); + + if(is_a($this,'IDynamicMethods')) { + $this->attachEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER,array($this,'__dycall')); + array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER); + } + + $this->_listeningenabled=true; + + $this->dyListen($fx); + + return count($fx); + } + + /** + * this removes an object's fx events from the global broadcaster + * + * Behaviors may implement the function: + * + * public function dyUnlisten($globalEvents[, $chain]) { + * $this->behaviorUnlisten(); //eg + * } + * + * to be executed when listen is called. All attached behaviors are notified through dyUnlisten. + * + * @return numeric the number of global events that were unregistered from the global event registry + */ + public function unlisten() { + if(!$this->_listeningenabled) + return; + + $fx=array_filter(get_class_methods($this),array($this,'filter_prado_fx')); + + foreach($fx as $func) + $this->detachEventHandler($func,array($this,$func)); + + if(is_a($this,'IDynamicMethods')) { + $this->detachEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER,array($this,'__dycall')); + array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER); + } + + $this->_listeningenabled=false; + + $this->dyUnlisten($fx); + + return count($fx); + } + + /** + * Gets the state of listening to global events + * @return boolean is Listening to global broadcast enabled + */ + public function getListeningToGlobalEvents() + { + return $this->_listeningenabled; + } + + + /** + * Calls a method. + * Do not call this method directly. This is a PHP magic method that we override + * to allow behaviors, dynamic events (intra-object/behavior events), + * undefined dynamic and global events, and + * to allow using the following syntax to call a property setter or getter. + * + * $this->getPropertyName($value); // if there's a $this->getjsPropertyName() method + * $this->setPropertyName($value); // if there's a $this->setjsPropertyName() method + * + * + * Additional object behaviors override class behaviors. + * dynamic and global events do not fail even if they aren't implemented. + * Any intra-object/behavior dynamic events that are not implemented by the behavior + * return the first function paramater or null when no parameters are specified. + * + * @param string method name that doesn't exist and is being called on the object + * @param mixed method parameters + * @throws TInvalidOperationException If the property is not defined or read-only or + * method is undefined + * @return mixed result of the method call, or false if 'fx' or 'dy' function but + * is not found in the class, otherwise it runs + */ + public function __call($method, $args) + { + $getset=substr($method,0,3); + if(($getset=='get')||($getset=='set')) + { + $propname=substr($method,3); + $jsmethod=$getset.'js'.$propname; + if(method_exists($this,$jsmethod)) + { + if(count($args)>0) + if($args[0]&&!($args[0] instanceof TJavaScriptString)) + $args[0]=new TJavaScriptString($args[0]); + return call_user_func_array(array($this,$jsmethod),$args); + } + + if (($getset=='set')&&method_exists($this,'getjs'.$propname)) + throw new TInvalidOperationException('component_property_readonly',get_class($this),$method); + } + + if($this->_m!==null&&$this->_behaviorsenabled) + { + if(strncasecmp($method,'dy',2)===0) + { + $callchain=new TCallChain($method); + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&(method_exists($behavior,$method)||($behavior instanceof IDynamicMethods))) + { + $behavior_args=$args; + if($behavior instanceof IClassBehavior) + array_unshift($behavior_args,$this); + $callchain->addCall(array($behavior,$method),$behavior_args); + } + + } + if($callchain->getCount()>0) + return call_user_func_array(array($callchain,'call'),$args); + } + else + { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&method_exists($behavior,$method)) + { + if($behavior instanceof IClassBehavior) + array_unshift($args,$this); + return call_user_func_array(array($behavior,$method),$args); + } + } + } + } + + if(strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0) + { + if($this instanceof IDynamicMethods) + return $this->__dycall($method,$args); + return isset($args[0])?$args[0]:null; + } + + throw new TApplicationException('component_method_undefined',get_class($this),$method); + } + + + /** + * Returns a property value or an event handler list by property or event name. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to read a property: + * + * $value=$component->PropertyName; + * $value=$component->jsPropertyName; // return JavaScript literal + * + * and to obtain the event handler list for an event, + * + * $eventHandlerList=$component->EventName; + * + * This will also return the global event handler list when specifing an 'fx' + * event, + * + * $globalEventHandlerList=$component->fxEventName; + * + * When behaviors are enabled, this will return the behavior of a specific + * name, a property of a behavior, or an object 'on' event defined by the behavior. + * @param string the property name or the event name + * @return mixed the property value or the event handler list as {@link TPriorityList} + * @throws TInvalidOperationException if the property/event is not defined. + */ + public function __get($name) + { + if(method_exists($this,$getter='get'.$name)) + { + // getting a property + return $this->$getter(); + } + else if(method_exists($this,$jsgetter='getjs'.$name)) + { + // getting a javascript property + return (string)$this->$jsgetter(); + } + else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) + { + // getting an event (handler list) + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new TPriorityList; + return $this->_e[$name]; + } + else if(strncasecmp($name,'fx',2)===0) + { + // getting a global event (handler list) + $name=strtolower($name); + if(!isset(self::$_ue[$name])) + self::$_ue[$name]=new TPriorityList; + return self::$_ue[$name]; + } + else if($this->_behaviorsenabled) + { + // getting a behavior property/event (handler list) + if(isset($this->_m[$name])) + return $this->_m[$name]; + else if($this->_m!==null) + { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&& + (property_exists($behavior,$name)||$behavior->canGetProperty($name)||$behavior->hasEvent($name))) + return $behavior->$name; + } + } + } + throw new TInvalidOperationException('component_property_undefined',get_class($this),$name); + } + + /** + * Sets value of a component property. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to set a property or attach an event handler. + * + * $this->PropertyName=$value; + * $this->jsPropertyName=$value; // $value will be treated as a JavaScript literal + * $this->EventName=$handler; + * $this->fxEventName=$handler; //global event listener + * + * When behaviors are enabled, this will also set a behaviors properties and events. + * @param string the property name or event name + * @param mixed the property value or event handler + * @throws TInvalidOperationException If the property is not defined or read-only. + */ + public function __set($name,$value) + { + if(method_exists($this,$setter='set'.$name)) + { + if(strncasecmp($name,'js',2)===0&&$value&&!($value instanceof TJavaScriptLiteral)) + $value = new TJavaScriptLiteral($value); + return $this->$setter($value); + } + else if(method_exists($this,$jssetter='setjs'.$name)) + { + if($value&&!($value instanceof TJavaScriptString)) + $value=new TJavaScriptString($value); + return $this->$jssetter($value); + } + else if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0) + { + return $this->attachEventHandler($name,$value); + } + else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled) + { + $sets=0; + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&& + (property_exists($behavior,$name)||$behavior->canSetProperty($name)||$behavior->hasEvent($name))) { + $behavior->$name=$value; + $sets++; + } + } + if($sets)return $value; + + } + + if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name)) + { + throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); + } + else + { + throw new TInvalidOperationException('component_property_undefined',get_class($this),$name); + } + } + + /** + * Checks if a property value is null, there are no events in the object + * event list or global event list registered under the name, and, if + * behaviors are enabled, + * Do not call this method. This is a PHP magic method that we override + * to allow using isset() to detect if a component property is set or not. + * This also works for global events. When behaviors are enabled, it + * will check for a behavior of the specified name, and also check + * the behavior for events and properties. + * @param string the property name or the event name + * @since 3.2.3 + */ + public function __isset($name) + { + if(method_exists($this,$getter='get'.$name)) + return $this->$getter()!==null; + else if(method_exists($this,$jsgetter='getjs'.$name)) + return $this->$jsgetter()!==null; + else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name)) + { + $name=strtolower($name); + return isset($this->_e[$name])&&$this->_e[$name]->getCount(); + } + else if(strncasecmp($name,'fx',2)===0) + { + $name=strtolower($name); + return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount(); + } + else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled) + { + if(isset($this->_m[$name])) + return true; + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) + return isset($behavior->$name); + } + + } + else + return false; + } + + /** + * Sets a component property to be null. Clears the object or global + * events. When enabled, loops through all behaviors and unsets the + * property or event. + * Do not call this method. This is a PHP magic method that we override + * to allow using unset() to set a component property to be null. + * @param string the property name or the event name + * @throws TInvalidOperationException if the property is read only. + * @since 3.2.3 + */ + public function __unset($name) + { + if(method_exists($this,$setter='set'.$name)) + $this->$setter(null); + else if(method_exists($this,$jssetter='setjs'.$name)) + $this->$jssetter(null); + else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name)) + $this->_e[strtolower($name)]->clear(); + else if(strncasecmp($name,'fx',2)===0) + $this->getEventHandlers($name)->remove(array($this, $name)); + else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled) + { + if(isset($this->_m[$name])) + $this->detachBehavior($name); + else { + $unset=0; + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) { + unset($behavior->$name); + $unset++; + } + } + if(!$unset&&method_exists($this,'get'.$name)) + throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); + } + } else if(method_exists($this,'get'.$name)) + throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); + } + + /** + * Determines whether a property is defined. + * A property is defined if there is a getter or setter method + * defined in the class. Note, property names are case-insensitive. + * @param string the property name + * @return boolean whether the property is defined + */ + public function hasProperty($name) + { + return $this->canGetProperty($name)||$this->canSetProperty($name); + } + + /** + * Determines whether a property can be read. + * A property can be read if the class has a getter method + * for the property name. Note, property name is case-insensitive. + * This also checks for getjs. When enabled, it loops through all + * active behaviors for the get property when undefined by the object. + * @param string the property name + * @return boolean whether the property can be read + */ + public function canGetProperty($name) + { + if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name)) + return true; + else if($this->_m!==null&&$this->_behaviorsenabled) + { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canGetProperty($name)) + return true; + } + } + return false; + } + + /** + * Determines whether a property can be set. + * A property can be written if the class has a setter method + * for the property name. Note, property name is case-insensitive. + * This also checks for setjs. When enabled, it loops through all + * active behaviors for the set property when undefined by the object. + * @param string the property name + * @return boolean whether the property can be written + */ + public function canSetProperty($name) + { + if(method_exists($this,'set'.$name)||method_exists($this,'setjs'.$name)) + return true; + else if($this->_m!==null&&$this->_behaviorsenabled) + { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canSetProperty($name)) + return true; + } + } + return false; + } + + /** + * Evaluates a property path. + * A property path is a sequence of property names concatenated by '.' character. + * For example, 'Parent.Page' refers to the 'Page' property of the component's + * 'Parent' property value (which should be a component also). + * When a property is not defined by an object, this also loops through all + * active behaviors of the object. + * @param string property path + * @return mixed the property path value + */ + public function getSubProperty($path) + { + $object=$this; + foreach(explode('.',$path) as $property) + $object=$object->$property; + return $object; + } + + /** + * Sets a value to a property path. + * A property path is a sequence of property names concatenated by '.' character. + * For example, 'Parent.Page' refers to the 'Page' property of the component's + * 'Parent' property value (which should be a component also). + * When a property is not defined by an object, this also loops through all + * active behaviors of the object. + * @param string property path + * @param mixed the property path value + */ + public function setSubProperty($path,$value) + { + $object=$this; + if(($pos=strrpos($path,'.'))===false) + $property=$path; + else + { + $object=$this->getSubProperty(substr($path,0,$pos)); + $property=substr($path,$pos+1); + } + $object->$property=$value; + } + + /** + * Determines whether an event is defined. + * An event is defined if the class has a method whose name is the event name + * prefixed with 'on', 'fx', or 'dy'. + * Every object responds to every 'fx' and 'dy' event as they are in a universally + * accepted event space. 'on' event must be declared by the object. + * When enabled, this will loop through all active behaviors for 'on' events + * defined by the behavior. + * Note, event name is case-insensitive. + * @param string the event name + * @return boolean + */ + public function hasEvent($name) + { + if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0||strncasecmp($name,'dy',2)===0) + return true; + + else if($this->_m!==null&&$this->_behaviorsenabled) + { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name)) + return true; + } + } + return false; + } + + /** + * Checks if an event has any handlers. This function also checks through all + * the behaviors for 'on' events when behaviors are enabled. + * 'dy' dynamic events are not handled by this function. + * @param string the event name + * @return boolean whether an event has been attached one or several handlers + */ + public function hasEventHandler($name) + { + $name=strtolower($name); + if(strncasecmp($name,'fx',2)===0) + return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount()>0; + else + { + if(isset($this->_e[$name])&&$this->_e[$name]->getCount()>0) + return true; + else if($this->_m!==null&&$this->_behaviorsenabled) { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEventHandler($name)) + return true; + } + } + } + return false; + } + + /** + * Returns the list of attached event handlers for an 'on' or 'fx' event. This function also + * checks through all the behaviors for 'on' event lists when behaviors are enabled. + * @return TPriorityList list of attached event handlers for an event + * @throws TInvalidOperationException if the event is not defined + */ + public function getEventHandlers($name) + { + if(strncasecmp($name,'on',2)===0&&method_exists($this,$name)) + { + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new TPriorityList; + return $this->_e[$name]; + } + else if(strncasecmp($name,'fx',2)===0) + { + $name=strtolower($name); + if(!isset(self::$_ue[$name])) + self::$_ue[$name]=new TPriorityList; + return self::$_ue[$name]; + } + else if($this->_m!==null&&$this->_behaviorsenabled) + { + foreach($this->_m->toArray() as $behavior) + { + if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name)) + return $behavior->getEventHandlers($name); + } + } + throw new TInvalidOperationException('component_event_undefined',get_class($this),$name); + } + + /** + * Attaches an event handler to an event. + * + * The handler must be a valid PHP callback, i.e., a string referring to + * a global function name, or an array containing two elements with + * the first element being an object and the second element a method name + * of the object. In Prado, you can also use method path to refer to + * an event handler. For example, array($object,'Parent.buttonClicked') + * uses a method path that refers to the method $object->Parent->buttonClicked(...). + * + * The event handler must be of the following signature, + * + * function handlerName($sender, $param) {} + * function handlerName($sender, $param, $name) {} + * + * where $sender represents the object that raises the event, + * and $param is the event parameter. $name refers to the event name + * being handled. + * + * This is a convenient method to add an event handler. + * It is equivalent to {@link getEventHandlers}($name)->add($handler). + * For complete management of event handlers, use {@link getEventHandlers} + * to get the event handler list first, and then do various + * {@link TPriorityList} operations to append, insert or remove + * event handlers. You may also do these operations like + * getting and setting properties, e.g., + * + * $component->OnClick[]=array($object,'buttonClicked'); + * $component->OnClick->insertAt(0,array($object,'buttonClicked')); + * + * which are equivalent to the following + * + * $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked')); + * $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked')); + * + * + * Due to the nature of {@link getEventHandlers}, any active behaviors defining + * new 'on' events, this method will pass through to the behavior transparently. + * + * @param string the event name + * @param callback the event handler + * @param numeric|null the priority of the handler, defaults to null which translates into the + * default priority of 10.0 within {@link TPriorityList} + * @throws TInvalidOperationException if the event does not exist + */ + public function attachEventHandler($name,$handler,$priority=null) + { + $this->getEventHandlers($name)->add($handler,$priority); + } + + /** + * Detaches an existing event handler. + * This method is the opposite of {@link attachEventHandler}. It will detach + * any 'on' events definedb by an objects active behaviors as well. + * @param string event name + * @param callback the event handler to be removed + * @param numeric|false|null the priority of the handler, defaults to false which translates + * to an item of any priority within {@link TPriorityList}; null means the default priority + * @return boolean if the removal is successful + */ + public function detachEventHandler($name,$handler,$priority=false) + { + if($this->hasEventHandler($name)) + { + try + { + $this->getEventHandlers($name)->remove($handler,$priority); + return true; + } + catch(Exception $e) + { + } + } + return false; + } + + /** + * Raises an event. This raises both inter-object 'on' events and global 'fx' events. + * This method represents the happening of an event and will + * invoke all attached event handlers for the event in {@link TPriorityList} order. + * This method does not handle intra-object/behavior dynamic 'dy' events. + * + * There are ways to handle event responses. By defailt {@link EVENT_RESULT_FILTER}, + * all event responses are stored in an array, filtered for null responses, and returned. + * If {@link EVENT_RESULT_ALL} is specified, all returned results will be stored along + * with the sender and param in an array + * + * $result[] = array('sender'=>$sender,'param'=>$param,'response'=>$response); + * + * + * If {@link EVENT_RESULT_FEED_FORWARD} is specified, then each handler result is then + * fed forward as the parameters for the next event. This allows for events to filter data + * directly by affecting the event parameters + * + * If a callable function is set in the response type or the post function filter is specified then the + * result of each called event handler is post processed by the callable function. Used in + * combination with {@link EVENT_RESULT_FEED_FORWARD}, any event (and its result) can be chained. + * + * When raising a global 'fx' event, registered handlers in the global event list for + * {@link GLOBAL_RAISE_EVENT_LISTENER} are always added into the set of event handlers. In this way, + * these global events are always raised for every global 'fx' event. The registered handlers for global + * raiseEvent events have priorities. Any registered global raiseEvent event handlers with a priority less than zero + * are added before the main event handlers being raised and any registered global raiseEvent event handlers + * with a priority equal or greater than zero are added after the main event handlers being raised. In this way + * all {@link GLOBAL_RAISE_EVENT_LISTENER} handlers are always called for every raised 'fx' event. + * + * Behaviors may implement the following functions: + * + * public function dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction[, $chain]) { + * return $name; //eg, the event name may be filtered/changed + * } + * public function dyIntraRaiseEventTestHandler($handler,$sender,$param,$name[, $chain]) { + * return true; //should this particular handler be executed? true/false + * } + * public function dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response[, $chain]) { + * //contains the per handler response + * } + * public function dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction[, $chain]) { + * return $responses; + * } + * + * to be executed when raiseEvent is called. The 'intra' dynamic events are called per handler in + * the handler loop. + * + * dyPreRaiseEvent has the effect of being able to change the event being raised. This intra + * object/behavior event returns the name of the desired event to be raised. It will pass through + * if no dynamic event is specified, or if the original event name is returned. + * dyIntraRaiseEventTestHandler returns true or false as to whether a specific handler should be + * called for a specific raised event (and associated event arguments) + * dyIntraRaiseEventPostHandler does not return anything. This allows behaviors to access the results + * of an event handler in the per handler loop. + * dyPostRaiseEvent returns the responses. This allows for any post processing of the event + * results from the sum of all event handlers + * + * When handling a catch-all {@link __dycall}, the method name is the name of the event + * and the parameters are the sender, the param, and then the name of the event. + * + * @param string the event name + * @param mixed the event sender object + * @param TEventParameter the event parameter + * @param numeric how the results of the event are tabulated. default: {@link EVENT_RESULT_FILTER} The default filters out + * null responses. optional + * @param function any per handler filtering of the response result needed is passed through + * this if not null. default: null. optional + * @return mixed the results of the event + * @throws TInvalidOperationException if the event is undefined + * @throws TInvalidDataValueException If an event handler is invalid + */ + public function raiseEvent($name,$sender,$param,$responsetype=null,$postfunction=null) + { + $p=$param; + if(is_callable($responsetype)) + { + $postfunction=$responsetype; + $responsetype=null; + } + + if($responsetype===null) + $responsetype=TEventResults::EVENT_RESULT_FILTER; + + $name=strtolower($name); + $responses=array(); + + $name=$this->dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction); + + if($this->hasEventHandler($name)||$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER)) + { + $handlers=$this->getEventHandlers($name); + $handlerArray=$handlers->toArray(); + if(strncasecmp($name,'fx',2)===0&&$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER)) + { + $globalhandlers=$this->getEventHandlers(TComponent::GLOBAL_RAISE_EVENT_LISTENER); + $handlerArray=array_merge($globalhandlers->toArrayBelowPriority(0),$handlerArray,$globalhandlers->toArrayAbovePriority(0)); + } + $response=null; + foreach($handlerArray as $handler) + { + if($this->dyIntraRaiseEventTestHandler($handler,$sender,$param,$name)===false) + continue; + + if(is_string($handler)) + { + if(($pos=strrpos($handler,'.'))!==false) + { + $object=$this->getSubProperty(substr($handler,0,$pos)); + $method=substr($handler,$pos+1); + if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0) + { + if($method=='__dycall') + $response=$object->__dycall($name,array($sender,$param,$name)); + else + $response=$object->$method($sender,$param,$name); + } + else + throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler); + } + else + $response=call_user_func($handler,$sender,$param,$name); + } + else if(is_callable($handler,true)) + { + list($object,$method)=$handler; + if(is_string($object)) + $response=call_user_func($handler,$sender,$param,$name); + else + { + if(($pos=strrpos($method,'.'))!==false) + { + $object=$this->getSubProperty(substr($method,0,$pos)); + $method=substr($method,$pos+1); + } + if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0) + { + if($method=='__dycall') + $response=$object->__dycall($name,array($sender,$param,$name)); + else + $response=$object->$method($sender,$param,$name); + } + else + throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]); + } + } + else + throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler)); + + $this->dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response); + + if($postfunction) + $response=call_user_func_array($postfunction,array($sender,$param,$this,$response)); + + if($responsetype&TEventResults::EVENT_RESULT_ALL) + $responses[]=array('sender'=>$sender,'param'=>$param,'response'=>$response); + else + $responses[]=$response; + + if($response!==null&&($responsetype&TEventResults::EVENT_RESULT_FEED_FORWARD)) + $param=$response; + + } + } + else if(strncasecmp($name,'on',2)===0&&!$this->hasEvent($name)) + throw new TInvalidOperationException('component_event_undefined',get_class($this),$name); + + if($responsetype&TEventResults::EVENT_RESULT_FILTER) + $responses=array_filter($responses); + + $responses=$this->dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction); + + return $responses; + } + + /** + * Evaluates a PHP expression in the context of this control. + * + * Behaviors may implement the function: + * + * public function dyEvaluateExpressionFilter($expression, $chain) { + * return $chain->dyEvaluateExpressionFilter(str_replace('foo', 'bar', $expression)); //example + * } + * + * to be executed when evaluateExpression is called. All attached behaviors are notified through + * dyEvaluateExpressionFilter. The chaining is important in this function due to the filtering + * pass-through effect. + * + * @param string PHP expression + * @return mixed the expression result + * @throws TInvalidOperationException if the expression is invalid + */ + public function evaluateExpression($expression) + { + $expression=$this->dyEvaluateExpressionFilter($expression); + try + { + if(eval("\$result=$expression;")===false) + throw new Exception(''); + return $result; + } + catch(Exception $e) + { + throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage()); + } + } + + /** + * Evaluates a list of PHP statements. + * + * Behaviors may implement the function: + * + * public function dyEvaluateStatementsFilter($statements, $chain) { + * return $chain->dyEvaluateStatementsFilter(str_replace('foo', 'bar', $statements)); //example + * } + * + * to be executed when evaluateStatements is called. All attached behaviors are notified through + * dyEvaluateStatementsFilter. The chaining is important in this function due to the filtering + * pass-through effect. + * + * @param string PHP statements + * @return string content echoed or printed by the PHP statements + * @throws TInvalidOperationException if the statements are invalid + */ + public function evaluateStatements($statements) + { + $statements=$this->dyEvaluateStatementsFilter($statements); + try + { + ob_start(); + if(eval($statements)===false) + throw new Exception(''); + $content=ob_get_contents(); + ob_end_clean(); + return $content; + } + catch(Exception $e) + { + throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage()); + } + } + + /** + * This method is invoked after the component is instantiated by a template. + * When this method is invoked, the component's properties have been initialized. + * The default implementation of this method will invoke + * the potential parent component's {@link addParsedObject}. + * This method can be overridden. + * + * Behaviors may implement the function: + * + * public function dyCreatedOnTemplate($parent, $chain) { + * return $chain->dyCreatedOnTemplate($parent); //example + * } + * + * to be executed when createdOnTemplate is called. All attached behaviors are notified through + * dyCreatedOnTemplate. + * + * @param TComponent potential parent of this control + * @see addParsedObject + */ + public function createdOnTemplate($parent) + { + $parent=$this->dyCreatedOnTemplate($parent); + $parent->addParsedObject($this); + } + + /** + * Processes an object that is created during parsing template. + * The object can be either a component or a static text string. + * This method can be overridden to customize the handling of newly created objects in template. + * Only framework developers and control developers should use this method. + * + * Behaviors may implement the function: + * + * public function dyAddParsedObject($object[, $chain]) { + * } + * + * to be executed when addParsedObject is called. All attached behaviors are notified through + * dyAddParsedObject. + * + * @param string|TComponent text string or component parsed and instantiated in template + * @see createdOnTemplate + */ + public function addParsedObject($object) + { + $this->dyAddParsedObject($object); + } + + + /** + * This is the method registered for all instanced objects should a class behavior be added after + * the class is instanced. Only when the class to which the behavior is being added is in this + * object's class hierarchy, via {@link getClassHierarchy}, is the behavior added to this instance. + * @param $sender the application + * @param $param TClassBehaviorEventParameter + * @since 3.2.3 + */ + public function fxAttachClassBehavior($sender,$param) { + if(in_array($param->getClass(),$this->getClassHierarchy(true))) + return $this->attachBehavior($param->getName(),$param->getBehavior(),$param->getPriority()); + } + + + /** + * This is the method registered for all instanced objects should a class behavior be removed after + * the class is instanced. Only when the class to which the behavior is being added is in this + * object's class hierarchy, via {@link getClassHierarchy}, is the behavior removed from this instance. + * @param $sender the application + * @param $param TClassBehaviorEventParameter + * @since 3.2.3 + */ + public function fxDetachClassBehavior($sender,$param) { + if(in_array($param->getClass(),$this->getClassHierarchy(true))) + return $this->detachBehavior($param->getName(),$param->getPriority()); + } + + + /** + * This will add a class behavior to all classes instanced (that are listening) and future newly instanced objects. + * This registers the behavior for future instances and pushes the changes to all the instances that are listening as well. + * The universal class behaviors are stored in an inverted stack with the latest class behavior being at the first position in the array. + * This is done so class behaviors are added last first. + * @param string name the key of the class behavior + * @param object|string class behavior or name of the object behavior per instance + * @param string|class string of class or class on which to attach this behavior. Defaults to null which will error + * but more important, if this is on PHP 5.3 it will use Late Static Binding to derive the class + * it should extend. + * + * TPanel::attachClassBehavior('javascripts', (new TJsPanelBehavior())->init($this)); + * + * @param numeric|null priority of behavior, default: null the default priority of the {@link TPriorityList} Optional. + * @throws TInvalidOperationException if the class behavior is being added to a {@link TComponent}; due to recursion. + * @throws TInvalidOperationException if the class behavior is already defined + * @since 3.2.3 + */ + public static function attachClassBehavior($name,$behavior,$class=null,$priority=null) { + if(!$class&&function_exists('get_called_class')) + $class=get_called_class(); + if(!$class) + throw new TInvalidOperationException('component_no_class_provided_nor_late_binding'); + + if(!is_string($name)) + $name=get_class($name); + $class=strtolower($class); + if($class==='tcomponent') + throw new TInvalidOperationException('component_no_tcomponent_class_behaviors'); + if(empty(self::$_um[$class])) + self::$_um[$class]=array(); + if(isset(self::$_um[$class][$name])) + throw new TInvalidOperationException('component_class_behavior_defined',$class,$name); + $param=new TClassBehaviorEventParameter($class,$name,$behavior,$priority); + self::$_um[$class]=array($name=>$param)+self::$_um[$class]; + $behaviorObject=is_string($behavior)?new $behavior:$behavior; + return $behaviorObject->raiseEvent('fxAttachClassBehavior',null,$param); + } + + + /** + * This will remove a behavior from a class. It unregisters it from future instances and + * pulls the changes from all the instances that are listening as well. + * PHP 5.3 uses Late Static Binding to derive the static class upon which this method is called. + * @param $name the key of the class behavior + * @param $class string class on which to attach this behavior. Defaults to null. + * @param $priority numeric|null|false priority. false is any priority, null is default + * {@link TPriorityList} priority, and numeric is a specific priority. + * @throws Exception if the the class cannot be derived from Late Static Binding and is not + * not supplied as a parameter. + * @since 3.2.3 + */ + public static function detachClassBehavior($name,$class=null,$priority=false) { + if(!$class&&function_exists('get_called_class')) + $class=get_called_class(); + if(!$class) + throw new TInvalidOperationException('component_no_class_provided_nor_late_binding'); + + $class=strtolower($class); + if(!is_string($name)) + $name=get_class($name); + if(empty(self::$_um[$class])||!isset(self::$_um[$class][$name])) + return false; + $param=self::$_um[$class][$name]; + $behavior=$param->getBehavior(); + unset(self::$_um[$class][$name]); + $behaviorObject=is_string($behavior)?new $behavior:$behavior; + return $behaviorObject->raiseEvent('fxDetachClassBehavior',null,$param); + } + + /** + * Returns the named behavior object. + * The name 'asa' stands for 'as a'. + * @param string the behavior name + * @return IBehavior the behavior object, or null if the behavior does not exist + * @since 3.2.3 + */ + public function asa($behaviorname) + { + return isset($this->_m[$behaviorname])?$this->_m[$behaviorname]:null; + } + + /** + * Returns whether or not the object or any of the behaviors are of a particular class. + * The name 'isa' stands for 'is a'. This first checks if $this is an instanceof the class. + * It then checks each Behavior. If a behavior implements {@link IInstanceCheck}, + * then the behavior can determine what it is an instanceof. If this behavior function returns true, + * then this method returns true. If the behavior instance checking function returns false, + * then no further checking is performed as it is assumed to be correct. + * + * If the behavior instance check function returns nothing or null or the behavior + * doesn't implement the {@link IInstanceCheck} interface, then the default instanceof occurs. + * The default isa behavior is to check if the behavior is an instanceof the class. + * + * The behavior {@link IInstanceCheck} is to allow a behavior to have the host object + * act as a completely different object. + * + * @param class or string + * @return boolean whether or not the object or a behavior is an instance of a particular class + * @since 3.2.3 + */ + public function isa($class) + { + if($this instanceof $class) + return true; + if($this->_m!==null&&$this->_behaviorsenabled) + foreach($this->_m->toArray() as $behavior){ + if(($behavior instanceof IBehavior)&&!$behavior->getEnabled()) + continue; + + $check = null; + if(($behavior->isa('IInstanceCheck'))&&$check=$behavior->isinstanceof($class,$this)) + return true; + if($check===null&&($behavior->isa($class))) + return true; + } + return false; + } + + /** + * Attaches a list of behaviors to the component. + * Each behavior is indexed by its name and should be an instance of + * {@link IBehavior}, a string specifying the behavior class, or a + * {@link TClassBehaviorEventParameter}. + * @param array list of behaviors to be attached to the component + * @since 3.2.3 + */ + public function attachBehaviors($behaviors) + { + foreach($behaviors as $name=>$behavior) + if($behavior instanceof TClassBehaviorEventParameter) + $this->attachBehavior($behavior->getName(),$behavior->getBehavior(),$behavior->getPriority()); + else + $this->attachBehavior($name,$behavior); + } + + /** + * Detaches select behaviors from the component. + * Each behavior is indexed by its name and should be an instance of + * {@link IBehavior}, a string specifying the behavior class, or a + * {@link TClassBehaviorEventParameter}. + * @param array list of behaviors to be detached from the component + * @since 3.2.3 + */ + public function detachBehaviors($behaviors) + { + if($this->_m!==null) + { + foreach($behaviors as $name=>$behavior) + if($behavior instanceof TClassBehaviorEventParameter) + $this->detachBehavior($behavior->getName(),$behavior->getPriority()); + else + $this->detachBehavior(is_string($behavior)?$behavior:$name); + } + } + + /** + * Detaches all behaviors from the component. + * @since 3.2.3 + */ + public function clearBehaviors() + { + if($this->_m!==null) + { + foreach($this->_m->toArray() as $name=>$behavior) + $this->detachBehavior($name); + $this->_m=null; + } + } + + /** + * Attaches a behavior to this component. + * This method will create the behavior object based on the given + * configuration. After that, the behavior object will be initialized + * by calling its {@link IBehavior::attach} method. + * + * Already attached behaviors may implement the function: + * + * public function dyAttachBehavior($name,$behavior[, $chain]) { + * } + * + * to be executed when attachBehavior is called. All attached behaviors are notified through + * dyAttachBehavior. + * + * @param string the behavior's name. It should uniquely identify this behavior. + * @param mixed the behavior configuration. This is passed as the first + * parameter to {@link YiiBase::createComponent} to create the behavior object. + * @return IBehavior the behavior object + * @since 3.2.3 + */ + public function attachBehavior($name,$behavior,$priority=null) + { + if(is_string($behavior)) + $behavior=Prado::createComponent($behavior); + if(!($behavior instanceof IBaseBehavior)) + throw new TInvalidDataTypeException('component_not_a_behavior',get_class($behavior)); + if($behavior instanceof IBehavior) + $behavior->setEnabled(true); + if($this->_m===null) + $this->_m=new TPriorityMap; + $behavior->attach($this); + $this->dyAttachBehavior($name,$behavior); + $this->_m->add($name,$behavior,$priority); + return $behavior; + } + + /** + * Detaches a behavior from the component. + * The behavior's {@link IBehavior::detach} method will be invoked. + * + * Behaviors may implement the function: + * + * public function dyDetachBehavior($name,$behavior[, $chain]) { + * } + * + * to be executed when detachBehavior is called. All attached behaviors are notified through + * dyDetachBehavior. + * + * @param string the behavior's name. It uniquely identifies the behavior. + * @param numeric the behavior's priority. This defaults to false, aka any priority. + * @return IBehavior the detached behavior. Null if the behavior does not exist. + * @since 3.2.3 + */ + public function detachBehavior($name,$priority=false) + { + if($this->_m!=null&&isset($this->_m[$name])) + { + $this->_m[$name]->detach($this); + $behavior=$this->_m->itemAt($name); + $this->_m->remove($name,$priority); + $this->dyDetachBehavior($name,$behavior); + return $behavior; + } + } + + /** + * Enables all behaviors attached to this component independent of the behaviors + * + * Behaviors may implement the function: + * + * public function dyEnableBehaviors($name,$behavior[, $chain]) { + * } + * + * to be executed when enableBehaviors is called. All attached behaviors are notified through + * dyEnableBehaviors. + * @since 3.2.3 + */ + public function enableBehaviors() + { + if(!$this->_behaviorsenabled) + { + $this->_behaviorsenabled=true; + $this->dyEnableBehaviors(); + } + } + + /** + * Disables all behaviors attached to this component independent of the behaviors + * + * Behaviors may implement the function: + * + * public function dyDisableBehaviors($name,$behavior[, $chain]) { + * } + * + * to be executed when disableBehaviors is called. All attached behaviors are notified through + * dyDisableBehaviors. + * @since 3.2.3 + */ + public function disableBehaviors() + { + if($this->_behaviorsenabled) + { + $this->dyDisableBehaviors(); + $this->_behaviorsenabled=false; + } + } + + + /** + * Returns if all the behaviors are turned on or off for the object. + * @return boolean whether or not all behaviors are enabled (true) or not (false) + * @since 3.2.3 + */ + public function getBehaviorsEnabled() + { + return $this->_behaviorsenabled; + } + + /** + * Enables an attached object behavior. This cannot enable or disable whole class behaviors. + * A behavior is only effective when it is enabled. + * A behavior is enabled when first attached. + * + * Behaviors may implement the function: + * + * public function dyEnableBehavior($name,$behavior[, $chain]) { + * } + * + * to be executed when enableBehavior is called. All attached behaviors are notified through + * dyEnableBehavior. + * + * @param string the behavior's name. It uniquely identifies the behavior. + * @since 3.2.3 + */ + public function enableBehavior($name) + { + if($this->_m!=null&&isset($this->_m[$name])){ + if($this->_m[$name] instanceof IBehavior) { + $this->_m[$name]->setEnabled(true); + $this->dyEnableBehavior($name,$this->_m[$name]); + return true; + } + return false; + } + return null; + } + + /** + * Disables an attached behavior. This cannot enable or disable whole class behaviors. + * A behavior is only effective when it is enabled. + * + * Behaviors may implement the function: + * + * public function dyDisableBehavior($name,$behavior[, $chain]) { + * } + * + * to be executed when disableBehavior is called. All attached behaviors are notified through + * dyDisableBehavior. + * + * @param string the behavior's name. It uniquely identifies the behavior. + * @since 3.2.3 + */ + public function disableBehavior($name) + { + if($this->_m!=null&&isset($this->_m[$name])){ + if($this->_m[$name] instanceof IBehavior) { + $this->_m[$name]->setEnabled(false); + $this->dyDisableBehavior($name,$this->_m[$name]); + return true; + } + return false; + } + return null; + } + + /** + * Do not call this method. This is a PHP magic method that will be called automatically + * after any unserialization; it can perform reinitialization tasks on the object. + */ + public function __wakeup() + { + if ($this->_e===null) + $this->_e = array(); + } + + /** + * Returns an array with the names of all variables of that object that should be serialized. + * Do not call this method. This is a PHP magic method that will be called automatically + * prior to any serialization. + */ + public function __sleep() + { + $a = (array)$this; + $a = array_keys($a); + $exprops = array(); + if($this->_listeningenabled===false) + $exprops[] = "\0TComponent\0_listeningenabled"; + if($this->_behaviorsenabled===true) + $exprops[] = "\0TComponent\0_behaviorsenabled"; + if ($this->_e===array()) + $exprops[] = "\0TComponent\0_e"; + if ($this->_m===null) + $exprops[] = "\0TComponent\0_m"; + return array_diff($a,$exprops); + } +} + + +/** + * IDynamicMethods interface. + * IDynamicMethods marks an object to receive undefined global or dynamic events. + * + * @author Brad Anderson + * @version $Id$ + * @package System + * @since 3.2.3 + */ +interface IDynamicMethods +{ + public function __dycall($method,$args); +} + + + +/** + * TClassBehaviorEventParameter class. + * TClassBehaviorEventParameter is the parameter sent with the class behavior changes. + * + * @author Brad Anderson + * @version $Id$ + * @package System + * @since 3.2.3 + */ +class TClassBehaviorEventParameter extends TEventParameter +{ + private $_class; + private $_name; + private $_behavior; + private $_priority; + + /** + * Holds the parameters for the Class Behavior Events + * @param string $class this is the class to get the behavior + * @param string $name the name of the behavior + * @param object $behavior this is the behavior to implement the class behavior + */ + public function __construct($class,$name,$behavior,$priority) + { + $this->_class=$class; + $this->_name=$name; + $this->_behavior=$behavior; + $this->_priority=$priority; + } + + /** + * This is the class to get the behavior + * @return string the class to get the behavior + */ + public function getClass() + { + return $this->_class; + } + + /** + * name of the behavior + * @return string the name to get the behavior + */ + public function getName() + { + return $this->_name; + } + + /** + * This is the behavior which the class is to get + * @return object the behavior to implement + */ + public function getBehavior() + { + return $this->_behavior; + } + + /** + * This is the priority which the behavior is to get + * @return numeric the priority of the behavior + */ + public function getPriority() + { + return $this->_priority; + } +} + + +/** + * TEnumerable class. + * TEnumerable is the base class for all enumerable types. + * To define an enumerable type, extend TEnumberable and define string constants. + * Each constant represents an enumerable value. + * The constant name must be the same as the constant value. + * For example, + * + * class TTextAlign extends TEnumerable + * { + * const Left='Left'; + * const Right='Right'; + * } + * + * Then, one can use the enumerable values such as TTextAlign::Left and + * TTextAlign::Right. + * + * @author Qiang Xue + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TEnumerable implements Iterator +{ + private $_enums=array(); + + public function __construct() { + $reflection=new ReflectionClass($this); + $this->_enums=$reflection->getConstants(); + } + + public function current() { + return current($this->_enums); + } + + public function key() { + return key($this->_enums); + } + + public function next() { + return next($this->_enums); + } + + public function rewind() { + reset($this->_enums); + } + + public function valid() { + return $this->current()!==false; + } +} + +/** + * TPropertyValue class + * + * TPropertyValue is a utility class that provides static methods + * to convert component property values to specific types. + * + * TPropertyValue is commonly used in component setter methods to ensure + * the new property value is of specific type. + * For example, a boolean-typed property setter method would be as follows, + * + * function setPropertyName($value) { + * $value=TPropertyValue::ensureBoolean($value); + * // $value is now of boolean type + * } + * + * + * Properties can be of the following types with specific type conversion rules: + * - string: a boolean value will be converted to 'true' or 'false'. + * - boolean: string 'true' (case-insensitive) will be converted to true, + * string 'false' (case-insensitive) will be converted to false. + * - integer + * - float + * - array: string starting with '(' and ending with ')' will be considered as + * as an array expression and will be evaluated. Otherwise, an array + * with the value to be ensured is returned. + * - object + * - enum: enumerable type, represented by an array of strings. + * + * @author Qiang Xue + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TPropertyValue +{ + /** + * Converts a value to boolean type. + * Note, string 'true' (case-insensitive) will be converted to true, + * string 'false' (case-insensitive) will be converted to false. + * If a string represents a non-zero number, it will be treated as true. + * @param mixed the value to be converted. + * @return boolean + */ + public static function ensureBoolean($value) + { + if (is_string($value)) + return strcasecmp($value,'true')==0 || $value!=0; + else + return (boolean)$value; + } + + /** + * Converts a value to string type. + * Note, a boolean value will be converted to 'true' if it is true + * and 'false' if it is false. + * @param mixed the value to be converted. + * @return string + */ + public static function ensureString($value) + { + if (TJavaScript::isJsLiteral($value)) + return $value; + if (is_bool($value)) + return $value?'true':'false'; + else + return (string)$value; + } + + /** + * Converts a value to integer type. + * @param mixed the value to be converted. + * @return integer + */ + public static function ensureInteger($value) + { + return (integer)$value; + } + + /** + * Converts a value to float type. + * @param mixed the value to be converted. + * @return float + */ + public static function ensureFloat($value) + { + return (float)$value; + } + + /** + * Converts a value to array type. If the value is a string and it is + * in the form (a,b,c) then an array consisting of each of the elements + * will be returned. If the value is a string and it is not in this form + * then an array consisting of just the string will be returned. If the value + * is not a string then + * @param mixed the value to be converted. + * @return array + */ + public static function ensureArray($value) + { + if(is_string($value)) + { + $value = trim($value); + $len = strlen($value); + if ($len >= 2 && $value[0] == '(' && $value[$len-1] == ')') + { + eval('$array=array'.$value.';'); + return $array; + } + else + return $len>0?array($value):array(); + } + else + return (array)$value; + } + + /** + * Converts a value to object type. + * @param mixed the value to be converted. + * @return object + */ + public static function ensureObject($value) + { + return (object)$value; + } + + /** + * Converts a value to enum type. + * + * This method checks if the value is of the specified enumerable type. + * A value is a valid enumerable value if it is equal to the name of a constant + * in the specified enumerable type (class). + * For more details about enumerable, see {@link TEnumerable}. + * + * For backward compatibility, this method also supports sanity + * check of a string value to see if it is among the given list of strings. + * @param mixed the value to be converted. + * @param mixed class name of the enumerable type, or array of valid enumeration values. If this is not an array, + * the method considers its parameters are of variable length, and the second till the last parameters are enumeration values. + * @return string the valid enumeration value + * @throws TInvalidDataValueException if the original value is not in the string array. + */ + public static function ensureEnum($value,$enums) + { + static $types=array(); + if(func_num_args()===2 && is_string($enums)) + { + if(!isset($types[$enums])) + $types[$enums]=new ReflectionClass($enums); + if($types[$enums]->hasConstant($value)) + return $value; + else + throw new TInvalidDataValueException( + 'propertyvalue_enumvalue_invalid',$value, + implode(' | ',$types[$enums]->getConstants())); + } + else if(!is_array($enums)) + { + $enums=func_get_args(); + array_shift($enums); + } + if(in_array($value,$enums,true)) + return $value; + else + throw new TInvalidDataValueException('propertyvalue_enumvalue_invalid',$value,implode(' | ',$enums)); + } + + /** + * Converts the value to 'null' if the given value is empty + * @param mixed value to be converted + * @return mixed input or NULL if input is empty + */ + public static function ensureNullIfEmpty($value) + { + return empty($value)?null:$value; + } +} + +/** + * TEventParameter class. + * TEventParameter is the base class for all event parameter classes. + * + * @author Qiang Xue + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TEventParameter extends TComponent +{ +} + +class TEventResults extends TEnumerable { + const EVENT_RESULT_FEED_FORWARD=1; + const EVENT_RESULT_FILTER=2; + const EVENT_RESULT_ALL=4; +} + +/** + * TComponentReflection class. + * + * TComponentReflection provides functionalities to inspect the public/protected + * properties, events and methods defined in a class. + * + * The following code displays the properties and events defined in {@link TDataGrid}, + * + * $reflection=new TComponentReflection('TDataGrid'); + * Prado::varDump($reflection->getProperties()); + * Prado::varDump($reflection->getEvents()); + * + * + * @author Qiang Xue + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +class TComponentReflection extends TComponent +{ + private $_className; + private $_properties=array(); + private $_events=array(); + private $_methods=array(); + + /** + * Constructor. + * @param object|string the component instance or the class name + * @throws TInvalidDataTypeException if the object is not a component + */ + public function __construct($component) + { + if(is_string($component) && class_exists($component,false)) + $this->_className=$component; + else if(is_object($component)) + $this->_className=get_class($component); + else + throw new TInvalidDataTypeException('componentreflection_class_invalid'); + $this->reflect(); + } + + private function isPropertyMethod($method) + { + $methodName=$method->getName(); + return $method->getNumberOfRequiredParameters()===0 + && strncasecmp($methodName,'get',3)===0 + && isset($methodName[3]); + } + + private function isEventMethod($method) + { + $methodName=$method->getName(); + return strncasecmp($methodName,'on',2)===0 + && isset($methodName[2]); + } + + private function reflect() + { + $class=new ReflectionClass($this->_className); + $properties=array(); + $events=array(); + $methods=array(); + $isComponent=is_subclass_of($this->_className,'TComponent') || strcasecmp($this->_className,'TComponent')===0; + foreach($class->getMethods() as $method) + { + if($method->isPublic() || $method->isProtected()) + { + $methodName=$method->getName(); + if(!$method->isStatic() && $isComponent) + { + if($this->isPropertyMethod($method)) + $properties[substr($methodName,3)]=$method; + else if($this->isEventMethod($method)) + { + $methodName[0]='O'; + $events[$methodName]=$method; + } + } + if(strncmp($methodName,'__',2)!==0) + $methods[$methodName]=$method; + } + } + $reserved=array(); + ksort($properties); + foreach($properties as $name=>$method) + { + $this->_properties[$name]=array( + 'type'=>$this->determinePropertyType($method), + 'readonly'=>!$class->hasMethod('set'.$name), + 'protected'=>$method->isProtected(), + 'class'=>$method->getDeclaringClass()->getName(), + 'comments'=>$method->getDocComment() + ); + $reserved['get'.strtolower($name)]=1; + $reserved['set'.strtolower($name)]=1; + } + ksort($events); + foreach($events as $name=>$method) + { + $this->_events[$name]=array( + 'class'=>$method->getDeclaringClass()->getName(), + 'protected'=>$method->isProtected(), + 'comments'=>$method->getDocComment() + ); + $reserved[strtolower($name)]=1; + } + ksort($methods); + foreach($methods as $name=>$method) + { + if(!isset($reserved[strtolower($name)])) + $this->_methods[$name]=array( + 'class'=>$method->getDeclaringClass()->getName(), + 'protected'=>$method->isProtected(), + 'static'=>$method->isStatic(), + 'comments'=>$method->getDocComment() + ); + } + } + + /** + * Determines the property type. + * This method uses the doc comment to determine the property type. + * @param ReflectionMethod + * @return string the property type, '{unknown}' if type cannot be determined from comment + */ + protected function determinePropertyType($method) + { + $comment=$method->getDocComment(); + if(preg_match('/@return\\s+(.*?)\\s+/',$comment,$matches)) + return $matches[1]; + else + return '{unknown}'; + } + + /** + * @return string class name of the component + */ + public function getClassName() + { + return $this->_className; + } + + /** + * @return array list of component properties. Array keys are property names. + * Each array element is of the following structure: + * [type]=>property type, + * [readonly]=>whether the property is read-only, + * [protected]=>whether the method is protected or not + * [class]=>the class where the property is inherited from, + * [comments]=>comments associated with the property. + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * @return array list of component events. Array keys are event names. + * Each array element is of the following structure: + * [protected]=>whether the event is protected or not + * [class]=>the class where the event is inherited from. + * [comments]=>comments associated with the event. + */ + public function getEvents() + { + return $this->_events; + } + + /** + * @return array list of public/protected methods. Array keys are method names. + * Each array element is of the following structure: + * [protected]=>whether the method is protected or not + * [static]=>whether the method is static or not + * [class]=>the class where the property is inherited from, + * [comments]=>comments associated with the event. + */ + public function getMethods() + { + return $this->_methods; + } +} + +/** + * IBaseBehavior interface is the base behavior class from which all other + * behaviors types are derived + * + * @author Brad Anderson + * @version $Id$ + * @package System + * @since 3.2.3 + */ +interface IBaseBehavior { + /** + * Attaches the behavior object to the component. + * @param CComponent the component that this behavior is to be attached to. + */ + public function attach($component); + /** + * Detaches the behavior object from the component. + * @param CComponent the component that this behavior is to be detached from. + */ + public function detach($component); +} + +/** + * IBehavior interfaces is implemented by instance behavior classes. + * + * A behavior is a way to enhance a component with additional methods and + * events that are defined in the behavior class and not available in the + * class. Objects may signal behaviors through dynamic events. + * + * @author Brad Anderson + * @version $Id$ + * @package System + * @since 3.2.3 + */ +interface IBehavior extends IBaseBehavior +{ + /** + * @return boolean whether this behavior is enabled + */ + public function getEnabled(); + /** + * @param boolean whether this behavior is enabled + */ + public function setEnabled($value); +} + + +/** + * IClassBehavior interface is implements behaviors across all instances of + * a particular class + * + * Any calls to functions not present in the original object but to behaviors + * derived from this class, will have inserted as the first argument parameter + * the object containing the behavior. + * + * For example: + * + * $objWithClassBehavior->MethodOfClassBehavior(1, 20); + * + * will be acted within the class behavior like this: + * + * public function MethodOfClassBehavior($object, $firstParam, $secondParam){ + * // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20 + * } + * + * + * This also holds for 'dy' events as well. For dynamic events, method arguments would be: + * + * public function dyMethodOfClassBehavior($object, $firstParam, $secondParam, $callchain){ + * // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20, $callchain instanceof {@link TCallChain} + * } + * + * + * @author Brad Anderson + * @version $Id$ + * @package System + * @since 3.2.3 + */ +interface IClassBehavior extends IBaseBehavior { +} + + +/** + * IInstanceCheck This interface allows objects to determine their own + * 'instanceof' results when {@link TComponent::isa} is called. This is + * important with behaviors because behaviors may want to look like + * particular objects other than themselves. + * + * @author Brad Anderson + * @version $Id$ + * @package System + * @since 3.2.3 + */ +interface IInstanceCheck { + /** + * The method checks $this or, if needed, the parameter $instance is of type + * class. In the case of a Class Behavior, the instance to which the behavior + * is attached may be important to determine if $this is an instance + * of a particular class. + * @param class|string the component that this behavior is checking if it is an instanceof. + * @param object the object which the behavior is attached to. default: null + * @return boolean|null if the this or the instance is of type class. When null, no information could be derived and + * the default mechanisms take over. + */ + public function isinstanceof($class,$instance=null); +} + +/** + * TJavaScriptLiteral class that encloses string literals that are not + * supposed to be escaped by {@link TJavaScript::encode() } + * + * Since Prado 3.2 all the data that gets sent clientside inside a javascript statement + * is encoded by default to avoid any kind of injection. + * Sometimes there's the need to bypass this encoding and send raw javascript code. + * To ensure that a string doesn't get encoded by {@link TJavaScript::encode() }, + * construct a new TJavaScriptLiteral: + * + * // a javascript test string + * $js="alert('hello')"; + * // the string in $raw will not be encoded when sent clientside inside a javascript block + * $raw=new TJavaScriptLiteral($js); + * // shortened form + * $raw=_js($js); + * + * + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.2.0 + */ +class TJavaScriptLiteral +{ + protected $_s; + + public function __construct($s) + { + $this->_s = $s; + } + + public function __toString() + { + return (string)$this->_s; + } + + public function toJavaScriptLiteral() + { + return $this->__toString(); + } +} + +/** + * TJavaScriptString class is an internal class that marks strings that will be + * forcibly encoded when rendered inside a javascript block + * + * @version $Id: TComponent.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.2.0 + */ +class TJavaScriptString extends TJavaScriptLiteral +{ + public function toJavaScriptLiteral() + { + return TJavaScript::jsonEncode((string)$this->_s,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); + } +} + diff --git a/gui/baculum/framework/TModule.php b/gui/baculum/framework/TModule.php new file mode 100644 index 0000000000..fee5101683 --- /dev/null +++ b/gui/baculum/framework/TModule.php @@ -0,0 +1,56 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TModule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * TModule class. + * + * TModule implements the basic methods required by IModule and may be + * used as the basic class for application modules. + * + * @author Qiang Xue + * @version $Id: TModule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +abstract class TModule extends TApplicationComponent implements IModule +{ + /** + * @var string module id + */ + private $_id; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + } + + /** + * @return string id of this module + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this module + */ + public function setID($value) + { + $this->_id=$value; + } +} + diff --git a/gui/baculum/framework/TService.php b/gui/baculum/framework/TService.php new file mode 100644 index 0000000000..1ec20017b0 --- /dev/null +++ b/gui/baculum/framework/TService.php @@ -0,0 +1,83 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * TService class. + * + * TService implements the basic methods required by IService and may be + * used as the basic class for application services. + * + * @author Qiang Xue + * @version $Id: TService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +abstract class TService extends TApplicationComponent implements IService +{ + /** + * @var string service id + */ + private $_id; + /** + * @var boolean whether the service is enabled + */ + private $_enabled=true; + + /** + * Initializes the service and attaches {@link run} to the RunService event of application. + * This method is required by IService and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + } + + /** + * @return string id of this service + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this service + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return boolean whether the service is enabled + */ + public function getEnabled() + { + return $this->_enabled; + } + + /** + * @param boolean whether the service is enabled + */ + public function setEnabled($value) + { + $this->_enabled=TPropertyValue::ensureBoolean($value); + } + + /** + * Runs the service. + */ + public function run() + { + } +} + diff --git a/gui/baculum/framework/TShellApplication.php b/gui/baculum/framework/TShellApplication.php new file mode 100644 index 0000000000..3552d8a774 --- /dev/null +++ b/gui/baculum/framework/TShellApplication.php @@ -0,0 +1,48 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TShellApplication.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * TShellApplication class. + * + * TShellApplication is the base class for developing command-line PRADO + * tools that share the same configurations as their Web application counterparts. + * + * A typical usage of TShellApplication in a command-line PHP script is as follows: + * + * require_once('path/to/prado.php'); + * $application=new TShellApplication('path/to/application.xml'); + * $application->run(); + * // perform command-line tasks here + * + * + * Since the application instance has access to all configurations, including + * path aliases, modules and parameters, the command-line script has nearly the same + * accessibility to resources as the PRADO Web applications. + * + * @author Qiang Xue + * @version $Id: TShellApplication.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.1.0 + */ +class TShellApplication extends TApplication +{ + /** + * Runs the application. + * This method overrides the parent implementation by initializing + * application with configurations specified when it is created. + */ + public function run() + { + $this->initApplication(); + } +} + diff --git a/gui/baculum/framework/Util/TBehavior.php b/gui/baculum/framework/Util/TBehavior.php new file mode 100644 index 0000000000..951a758f75 --- /dev/null +++ b/gui/baculum/framework/Util/TBehavior.php @@ -0,0 +1,87 @@ + + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2009 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * TBehavior is a convenient base class for behavior classes. + * @author Qiang Xue + * @version $Id: TBehaviour.php 3211 2012-10-31 02:35:01Z javalizard@gmail.com $ + * @package System.Util + * @since 3.2.3 + */ +class TBehavior extends TComponent implements IBehavior +{ + private $_enabled; + private $_owner; + + /** + * Declares events and the corresponding event handler methods. + * The events are defined by the {@link owner} component, while the handler + * methods by the behavior class. The handlers will be attached to the corresponding + * events when the behavior is attached to the {@link owner} component; and they + * will be detached from the events when the behavior is detached from the component. + * @return array events (array keys) and the corresponding event handler methods (array values). + */ + public function events() + { + return array(); + } + + /** + * Attaches the behavior object to the component. + * The default implementation will set the {@link owner} property + * and attach event handlers as declared in {@link events}. + * Make sure you call the parent implementation if you override this method. + * @param TComponent the component that this behavior is to be attached to. + */ + public function attach($owner) + { + $this->_owner=$owner; + foreach($this->events() as $event=>$handler) + $owner->attachEventHandler($event,array($this,$handler)); + } + + /** + * Detaches the behavior object from the component. + * The default implementation will unset the {@link owner} property + * and detach event handlers declared in {@link events}. + * Make sure you call the parent implementation if you override this method. + * @param TComponent the component that this behavior is to be detached from. + */ + public function detach($owner) + { + foreach($this->events() as $event=>$handler) + $owner->detachEventHandler($event,array($this,$handler)); + $this->_owner=null; + } + + /** + * @return TComponent the owner component that this behavior is attached to. + */ + public function getOwner() + { + return $this->_owner; + } + + /** + * @return boolean whether this behavior is enabled + */ + public function getEnabled() + { + return $this->_enabled; + } + + /** + * @param boolean whether this behavior is enabled + */ + public function setEnabled($value) + { + $this->_enabled=$value; + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Util/TCallChain.php b/gui/baculum/framework/Util/TCallChain.php new file mode 100644 index 0000000000..9d6144b368 --- /dev/null +++ b/gui/baculum/framework/Util/TCallChain.php @@ -0,0 +1,147 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2008-2013 Pradosoft + * @license http://www.pradosoft.com/license/ + */ + +/** + * TCallChain is a recursive event calling mechanism. This class implements + * the {@link IDynamicMethods} class so that any 'dy' event calls can be caught + * and patched through to the intended recipient + * @author Brad Anderson + * @version $Id: TCallChain.php 564 2009-01-21 22:07:10Z javalizard $ + * @package System.Util + * @since 3.2.3 + */ +class TCallChain extends TList implements IDynamicMethods +{ + /** + * @var {@link TListIterator} for moving through the chained method calls + */ + private $_iterator=null; + + /** + * @var string the method name of the call chain + */ + private $_method=null; + + /** + * This initializes the list and the name of the method to be called + * @param string the name of the function call + */ + public function __construct($method) { + $this->_method=$method; + parent::__construct(); + } + + + /** + * This initializes the list and the name of the method to be called + * @param string|array this is a callable function as a string or array with + * the object and method name as string + * @param array The array of arguments to the function call chain + */ + public function addCall($method,$args) + { + $this->add(array($method,$args)); + } + + /** + * This method calls the next Callable in the list. All of the method arguments + * coming into this method are substituted into the original method argument of + * call in the chain. + * + * If the original method call has these parameters + * + * $originalobject->dyExampleMethod('param1', 'param2', 'param3') + * + * + * $callchain->dyExampleMethod('alt1', 'alt2') + * + * then the next call in the call chain will recieve the parameters as if this were called + * + * $behavior->dyExampleMethod('alt1', 'alt2', 'param3', $callchainobject) + * + * + * When dealing with {@link IClassBehaviors}, the first parameter of the stored argument + * list in 'dy' event calls is always the object containing the behavior. This modifies + * the parameter replacement mechanism slightly to leave the object containing the behavior + * alone and only replacing the other parameters in the argument list. As per {@link __call}, + * any calls to a 'dy' event do not need the object containing the behavior as the addition of + * the object to the argument list as the first element is automatic for IClassBehaviors. + * + * The last parameter of the method parameter list for any callable in the call chain + * will be the TCallChain object itself. This is so that any behavior implementing + * these calls will have access to the call chain. Each callable should either call + * the TCallChain call method internally for direct chaining or call the method being + * chained (in which case the dynamic handler will pass through to this call method). + * + * If the dynamic intra object/behavior event is not called in the behavior implemented + * dynamic method, it will return to this method and call the following behavior + * implementation so as no behavior with an implementation of the dynamic event is left + * uncalled. This does break the call chain though and will not act as a "parameter filter". + * + * When there are no handlers or no handlers left, it returns the first parameter of the + * argument list. + * + */ + public function call() + { + $args=func_get_args(); + if($this->getCount()===0) + return isset($args[0])?$args[0]:null; + + if(!$this->_iterator) + { + $chain_array=array_reverse($this->toArray()); + $this->_iterator=new TListIterator($chain_array); + } + if($this->_iterator->valid()) + do { + $handler=$this->_iterator->current(); + $this->_iterator->next(); + if(is_array($handler[0])&&$handler[0][0] instanceof IClassBehavior) + array_splice($handler[1],1,count($args),$args); + else + array_splice($handler[1],0,count($args),$args); + $handler[1][]=$this; + $result=call_user_func_array($handler[0],$handler[1]); + } while($this->_iterator->valid()); + else + $result = $args[0]; + return $result; + } + + + /** + * This catches all the unpatched dynamic events. When the method call matches the + * call chain method, it passes the arguments to the original __call (of the dynamic + * event being unspecified in TCallChain) and funnels into the method {@link call}, + * so the next dynamic event handler can be called. + * If the original method call has these parameters + * + * $originalobject->dyExampleMethod('param1', 'param2', 'param3') + * + * and within the chained dynamic events, this can be called + * + * class DyBehavior extends TBehavior { + * public function dyExampleMethod($param1, $param2, $param3, $callchain) + * $callchain->dyExampleMethod($param1, $param2, $param3) + * } + * { + * + * to call the next event in the chain. + * @param string method name of the unspecified object method + * @param array arguments to the unspecified object method + */ + public function __dycall($method,$args) + { + if($this->_method==$method) + return call_user_func_array(array($this,'call'),$args); + return null; + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Util/TClassBehavior.php b/gui/baculum/framework/Util/TClassBehavior.php new file mode 100644 index 0000000000..9d29dbf4ad --- /dev/null +++ b/gui/baculum/framework/Util/TClassBehavior.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2008-2011 Pradosoft + * @license http://www.pradosoft.com/license/ + */ + +/** + * TClassBehavior is a convenient base class for whole class behaviors. + * @author Brad Anderson + * @version $Id: TClassBehavior.php 564 2009-01-21 22:07:10Z javalizard $ + * @package System.Util + * @since 3.2.3 + */ +class TClassBehavior extends TComponent implements IClassBehavior +{ + + /** + * Attaches the behavior object to the component. + * @param TComponent the component that this behavior is to be attached to. + */ + public function attach($component) + { + } + + /** + * Detaches the behavior object from the component. + * @param TComponent the component that this behavior is to be detached from. + */ + public function detach($component) + { + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Util/TDataFieldAccessor.php b/gui/baculum/framework/Util/TDataFieldAccessor.php new file mode 100644 index 0000000000..ae9df57b9c --- /dev/null +++ b/gui/baculum/framework/Util/TDataFieldAccessor.php @@ -0,0 +1,91 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataFieldAccessor.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + */ + +/** + * TDataFieldAccessor class + * + * TDataFieldAccessor is a utility class that provides access to a field of some data. + * The accessor attempts to obtain the field value in the following order: + * - If the data is an array, then the field is treated as an array index + * and the corresponding element value is returned; + * - If the data is a TMap or TList object, then the field is treated as a key + * into the map or list, and the corresponding value is returned. + * - If the data is an object, the field is treated as a property or sub-property + * defined with getter methods. For example, if the object has a method called + * getMyValue(), then field 'MyValue' will retrieve the result of this method call. + * If getMyValue() returns an object which contains a method getMySubValue(), + * then field 'MyValue.MySubValue' will return that method call result. + * + * @author Qiang Xue + * @version $Id: TDataFieldAccessor.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TDataFieldAccessor +{ + /** + * Evaluates the data value at the specified field. + * - If the data is an array, then the field is treated as an array index + * and the corresponding element value is returned; the field name can also include + * dots to access subarrays. For example a field named 'MyField.MySubField' will + * first try to access $data['MyField.MySubField'], then try $data['MyField']['MySubField']. + * - If the data is a TMap or TList object, then the field is treated as a key + * into the map or list, and the corresponding value is returned. + * - If the data is an object, the field is treated as a property or sub-property + * defined with getter methods. For example, if the object has a method called + * getMyValue(), then field 'MyValue' will retrieve the result of this method call. + * If getMyValue() returns an object which contains a method getMySubValue(), + * then field 'MyValue.MySubValue' will return that method call result. + * @param mixed data containing the field value, can be an array, TMap, TList or object. + * @param mixed field value + * @return mixed value at the specified field + * @throws TInvalidDataValueException if field or data is invalid + */ + public static function getDataFieldValue($data,$field) + { + try + { + if(is_array($data) || ($data instanceof ArrayAccess)) + { + if(isset($data[$field])) + return $data[$field]; + + $tmp = $data; + foreach (explode(".", $field) as $f) + $tmp = $tmp[$f]; + return $tmp; + } + else if(is_object($data)) + { + if(strpos($field,'.')===false) // simple field + { + if(method_exists($data, 'get'.$field)) + return call_user_func(array($data,'get'.$field)); + else + return $data->{$field}; + } + else // field in the format of xxx.yyy.zzz + { + $object=$data; + foreach(explode('.',$field) as $f) + $object = TDataFieldAccessor::getDataFieldValue($object, $f); + return $object; + } + } + } + catch(Exception $e) + { + throw new TInvalidDataValueException('datafieldaccessor_datafield_invalid',$field,$e->getMessage()); + } + throw new TInvalidDataValueException('datafieldaccessor_data_invalid',$field); + } +} diff --git a/gui/baculum/framework/Util/TDateTimeStamp.php b/gui/baculum/framework/Util/TDateTimeStamp.php new file mode 100644 index 0000000000..7a995b006f --- /dev/null +++ b/gui/baculum/framework/Util/TDateTimeStamp.php @@ -0,0 +1,204 @@ +setDate($year, $month, $day); + return (int) $dt->format('w'); + } + + /** + * Checks for leap year, returns true if it is. No 2-digit year check. Also + * handles julian calendar correctly. + * @param float year to check + * @return boolean true if is leap year + */ + public function isLeapYear($year) + { + $year = $this->digitCheck($year); + $dt = new DateTime(); + $dt->setDate($year, 1, 1); + return (bool) $dt->format('L'); + } + + /** + * Fix 2-digit years. Works for any century. + * Assumes that if 2-digit is more than 30 years in future, then previous century. + * @return integer change two digit year into multiple digits + */ + protected function digitCheck($y) + { + if ($y < 100){ + $yr = (integer) date("Y"); + $century = (integer) ($yr /100); + + if ($yr%100 > 50) { + $c1 = $century + 1; + $c0 = $century; + } else { + $c1 = $century; + $c0 = $century - 1; + } + $c1 *= 100; + // if 2-digit year is less than 30 years in future, set it to this century + // otherwise if more than 30 years in future, then we set 2-digit year to the prev century. + if (($y + $c1) < $yr+30) $y = $y + $c1; + else $y = $y + $c0*100; + } + return $y; + } + + public function get4DigitYear($y) + { + return $this->digitCheck($y); + } + + /** + * @return integer get local time zone offset from GMT + */ + public function getGMTDiff($ts=false) + { + $dt = new DateTime(); + if($ts) + $dt->setTimeStamp($ts); + else + $dt->setDate(1970, 1, 2); + + return (int) $dt->format('Z'); + } + + /** + * @return array an array with date info. + */ + function parseDate($txt=false) + { + if ($txt === false) return getdate(); + + $dt = new DateTime($txt); + + return array( + 'seconds' => (int) $dt->format('s'), + 'minutes' => (int) $dt->format('i'), + 'hours' => (int) $dt->format('G'), + 'mday' => (int) $dt->format('j'), + 'wday' => (int) $dt->format('w'), + 'mon' => (int) $dt->format('n'), + 'year' => (int) $dt->format('Y'), + 'yday' => (int) $dt->format('z'), + 'weekday' => $dt->format('l'), + 'month' => $dt->format('F'), + 0 => (int) $dt->format('U'), + ); + } + + /** + * @return array an array with date info. + */ + function getDate($d=false,$fast=false) + { + if ($d === false) return getdate(); + + $dt = new DateTime(); + $dt->setTimestamp($d); + + return array( + 'seconds' => (int) $dt->format('s'), + 'minutes' => (int) $dt->format('i'), + 'hours' => (int) $dt->format('G'), + 'mday' => (int) $dt->format('j'), + 'wday' => (int) $dt->format('w'), + 'mon' => (int) $dt->format('n'), + 'year' => (int) $dt->format('Y'), + 'yday' => (int) $dt->format('z'), + 'weekday' => $dt->format('l'), + 'month' => $dt->format('F'), + 0 => (int) $dt->format('U'), + ); + } + + /** + * @return boolean true if valid date, semantic check only. + */ + public function isValidDate($y,$m,$d) + { + if ($this->isLeapYear($y)) + $marr =& self::$_month_leaf; + else + $marr =& self::$_month_normal; + + if ($m > 12 || $m < 1) return false; + + if ($d > 31 || $d < 1) return false; + + if ($marr[$m] < $d) return false; + + if ($y < 1000 && $y > 3000) return false; + + return true; + } + + /** + * @return string formatted date based on timestamp $d + */ + function formatDate($fmt,$ts=false,$is_gmt=false) + { + $dt = new DateTime(); + if($is_gmt) + $dt->setTimeZone(new DateTimeZone('UTC')); + $dt->setTimestamp($ts); + + return $dt->format($fmt); + } + + /** + * @return integer|float a timestamp given a local time + */ + function getTimeStamp($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_gmt=false) + { + $dt = new DateTime(); + if($is_gmt) + $dt->setTimeZone(new DateTimeZone('UTC')); + $dt->setDate($year!==false ? $year : date('Y'), + $mon!==false ? $mon : date('m'), + $day!==false ? $day : date('d')); + $dt->setTime($hr, $min, $sec); + return (int) $dt->format('U'); + } +} + diff --git a/gui/baculum/framework/Util/TLogRouter.php b/gui/baculum/framework/Util/TLogRouter.php new file mode 100644 index 0000000000..32273c0c4f --- /dev/null +++ b/gui/baculum/framework/Util/TLogRouter.php @@ -0,0 +1,1207 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + */ + +Prado::using('System.Data.TDbConnection'); + +/** + * TLogRouter class. + * + * TLogRouter manages routes that record log messages in different media different ways. + * For example, a file log route {@link TFileLogRoute} records log messages + * in log files. An email log route {@link TEmailLogRoute} sends log messages + * to email addresses. + * + * Log routes may be configured in application or page folder configuration files + * or an external configuration file specified by {@link setConfigFile ConfigFile}. + * The format is as follows, + * + * + * + * + * PHP configuration style: + * + * + * + * You can specify multiple routes with different filtering conditions and different + * targets, even if the routes are of the same type. + * + * @author Qiang Xue + * @author Carl G. Mathisen + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TLogRouter extends TModule +{ + /** + * @var array list of routes available + */ + private $_routes=array(); + /** + * @var string external configuration file + */ + private $_configFile=null; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + public function init($config) + { + if($this->_configFile!==null) + { + if(is_file($this->_configFile)) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $phpConfig = include $this->_configFile; + $this->loadConfig($phpConfig); + } + else + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadConfig($dom); + } + } + else + throw new TConfigurationException('logrouter_configfile_invalid',$this->_configFile); + } + $this->loadConfig($config); + $this->getApplication()->attachEventHandler('OnEndRequest',array($this,'collectLogs')); + } + + /** + * Loads configuration from an XML element or PHP array + * @param mixed configuration node + * @throws TConfigurationException if log route class or type is not specified + */ + private function loadConfig($config) + { + if(is_array($config)) + { + if(isset($config['routes']) && is_array($config['routes'])) + { + foreach($config['routes'] as $route) + { + $properties = isset($route['properties'])?$route['properties']:array(); + if(!isset($route['class'])) + throw new TConfigurationException('logrouter_routeclass_required'); + $route=Prado::createComponent($route['class']); + if(!($route instanceof TLogRoute)) + throw new TConfigurationException('logrouter_routetype_invalid'); + foreach($properties as $name=>$value) + $route->setSubproperty($name,$value); + $this->_routes[]=$route; + $route->init($route); + } + } + } + else + { + foreach($config->getElementsByTagName('route') as $routeConfig) + { + $properties=$routeConfig->getAttributes(); + if(($class=$properties->remove('class'))===null) + throw new TConfigurationException('logrouter_routeclass_required'); + $route=Prado::createComponent($class); + if(!($route instanceof TLogRoute)) + throw new TConfigurationException('logrouter_routetype_invalid'); + foreach($properties as $name=>$value) + $route->setSubproperty($name,$value); + $this->_routes[]=$route; + $route->init($routeConfig); + } + } + } + + /** + * Adds a TLogRoute instance to the log router. + * + * @param TLogRoute $route + * @throws TInvalidDataTypeException if the route object is invalid + */ + public function addRoute($route) + { + if(!($route instanceof TLogRoute)) + throw new TInvalidDataTypeException('logrouter_routetype_invalid'); + $this->_routes[]=$route; + $route->init(null); + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TConfigurationException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null) + throw new TConfigurationException('logrouter_configfile_invalid',$value); + } + + /** + * Collects log messages from a logger. + * This method is an event handler to application's EndRequest event. + * @param mixed event parameter + */ + public function collectLogs($param) + { + $logger=Prado::getLogger(); + foreach($this->_routes as $route) + $route->collectLogs($logger); + } +} + +/** + * TLogRoute class. + * + * TLogRoute is the base class for all log route classes. + * A log route object retrieves log messages from a logger and sends it + * somewhere, such as files, emails. + * The messages being retrieved may be filtered first before being sent + * to the destination. The filters include log level filter and log category filter. + * + * To specify level filter, set {@link setLevels Levels} property, + * which takes a string of comma-separated desired level names (e.g. 'Error, Debug'). + * To specify category filter, set {@link setCategories Categories} property, + * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO'). + * + * Level filter and category filter are combinational, i.e., only messages + * satisfying both filter conditions will they be returned. + * + * @author Qiang Xue + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +abstract class TLogRoute extends TApplicationComponent +{ + /** + * @var array lookup table for level names + */ + protected static $_levelNames=array( + TLogger::DEBUG=>'Debug', + TLogger::INFO=>'Info', + TLogger::NOTICE=>'Notice', + TLogger::WARNING=>'Warning', + TLogger::ERROR=>'Error', + TLogger::ALERT=>'Alert', + TLogger::FATAL=>'Fatal' + ); + /** + * @var array lookup table for level values + */ + protected static $_levelValues=array( + 'debug'=>TLogger::DEBUG, + 'info'=>TLogger::INFO, + 'notice'=>TLogger::NOTICE, + 'warning'=>TLogger::WARNING, + 'error'=>TLogger::ERROR, + 'alert'=>TLogger::ALERT, + 'fatal'=>TLogger::FATAL + ); + /** + * @var integer log level filter (bits) + */ + private $_levels=null; + /** + * @var array log category filter + */ + private $_categories=null; + + /** + * Initializes the route. + * @param TXmlElement configurations specified in {@link TLogRouter}. + */ + public function init($config) + { + } + + /** + * @return integer log level filter + */ + public function getLevels() + { + return $this->_levels; + } + + /** + * @param integer|string integer log level filter (in bits). If the value is + * a string, it is assumed to be comma-separated level names. Valid level names + * include 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'. + */ + public function setLevels($levels) + { + if(is_integer($levels)) + $this->_levels=$levels; + else + { + $this->_levels=null; + $levels=strtolower($levels); + foreach(explode(',',$levels) as $level) + { + $level=trim($level); + if(isset(self::$_levelValues[$level])) + $this->_levels|=self::$_levelValues[$level]; + } + } + } + + /** + * @return array list of categories to be looked for + */ + public function getCategories() + { + return $this->_categories; + } + + /** + * @param array|string list of categories to be looked for. If the value is a string, + * it is assumed to be comma-separated category names. + */ + public function setCategories($categories) + { + if(is_array($categories)) + $this->_categories=$categories; + else + { + $this->_categories=null; + foreach(explode(',',$categories) as $category) + { + if(($category=trim($category))!=='') + $this->_categories[]=$category; + } + } + } + + /** + * @param integer level value + * @return string level name + */ + protected function getLevelName($level) + { + return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown'; + } + + /** + * @param string level name + * @return integer level value + */ + protected function getLevelValue($level) + { + return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0; + } + + /** + * Formats a log message given different fields. + * @param string message content + * @param integer message level + * @param string message category + * @param integer timestamp + * @return string formatted message + */ + protected function formatLogMessage($message,$level,$category,$time) + { + return @gmdate('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n"; + } + + /** + * Retrieves log messages from logger to log route specific destination. + * @param TLogger logger instance + */ + public function collectLogs(TLogger $logger) + { + $logs=$logger->getLogs($this->getLevels(),$this->getCategories()); + if(!empty($logs)) + $this->processLogs($logs); + } + + /** + * Processes log messages and sends them to specific destination. + * Derived child classes must implement this method. + * @param array list of messages. Each array elements represents one message + * with the following structure: + * array( + * [0] => message + * [1] => level + * [2] => category + * [3] => timestamp); + */ + abstract protected function processLogs($logs); +} + +/** + * TFileLogRoute class. + * + * TFileLogRoute records log messages in files. + * The log files are stored under {@link setLogPath LogPath} and the file name + * is specified by {@link setLogFile LogFile}. If the size of the log file is + * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation + * is performed, which renames the current log file by suffixing the file name + * with '.1'. All existing log files are moved backwards one place, i.e., '.2' + * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles} + * specifies how many files to be kept. + * + * @author Qiang Xue + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TFileLogRoute extends TLogRoute +{ + /** + * @var integer maximum log file size + */ + private $_maxFileSize=512; // in KB + /** + * @var integer number of log files used for rotation + */ + private $_maxLogFiles=2; + /** + * @var string directory storing log files + */ + private $_logPath=null; + /** + * @var string log file name + */ + private $_logFile='prado.log'; + + /** + * @return string directory storing log files. Defaults to application runtime path. + */ + public function getLogPath() + { + if($this->_logPath===null) + $this->_logPath=$this->getApplication()->getRuntimePath(); + return $this->_logPath; + } + + /** + * @param string directory (in namespace format) storing log files. + * @throws TConfigurationException if log path is invalid + */ + public function setLogPath($value) + { + if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath)) + throw new TConfigurationException('filelogroute_logpath_invalid',$value); + } + + /** + * @return string log file name. Defaults to 'prado.log'. + */ + public function getLogFile() + { + return $this->_logFile; + } + + /** + * @param string log file name + */ + public function setLogFile($value) + { + $this->_logFile=$value; + } + + /** + * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB). + */ + public function getMaxFileSize() + { + return $this->_maxFileSize; + } + + /** + * @param integer maximum log file size in kilo-bytes (KB). + * @throws TInvalidDataValueException if the value is smaller than 1. + */ + public function setMaxFileSize($value) + { + $this->_maxFileSize=TPropertyValue::ensureInteger($value); + if($this->_maxFileSize<=0) + throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid'); + } + + /** + * @return integer number of files used for rotation. Defaults to 2. + */ + public function getMaxLogFiles() + { + return $this->_maxLogFiles; + } + + /** + * @param integer number of files used for rotation. + */ + public function setMaxLogFiles($value) + { + $this->_maxLogFiles=TPropertyValue::ensureInteger($value); + if($this->_maxLogFiles<1) + throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid'); + } + + /** + * Saves log messages in files. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile(); + if(@filesize($logFile)>$this->_maxFileSize*1024) + $this->rotateFiles(); + foreach($logs as $log) + error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile); + } + + /** + * Rotates log files. + */ + protected function rotateFiles() + { + $file=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile(); + for($i=$this->_maxLogFiles;$i>0;--$i) + { + $rotateFile=$file.'.'.$i; + if(is_file($rotateFile)) + { + if($i===$this->_maxLogFiles) + unlink($rotateFile); + else + rename($rotateFile,$file.'.'.($i+1)); + } + } + if(is_file($file)) + rename($file,$file.'.1'); + } +} + +/** + * TEmailLogRoute class. + * + * TEmailLogRoute sends selected log messages to email addresses. + * The target email addresses may be specified via {@link setEmails Emails} property. + * Optionally, you may set the email {@link setSubject Subject} and the + * {@link setSentFrom SentFrom} address. + * + * @author Qiang Xue + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TEmailLogRoute extends TLogRoute +{ + /** + * Regex pattern for email address. + */ + const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/'; + /** + * Default email subject. + */ + const DEFAULT_SUBJECT='Prado Application Log'; + /** + * @var array list of destination email addresses. + */ + private $_emails=array(); + /** + * @var string email subject + */ + private $_subject=''; + /** + * @var string email sent from address + */ + private $_from=''; + + /** + * Initializes the route. + * @param TXmlElement configurations specified in {@link TLogRouter}. + * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and + * 'sendmail_from' in php.ini is also empty. + */ + public function init($config) + { + if($this->_from==='') + $this->_from=ini_get('sendmail_from'); + if($this->_from==='') + throw new TConfigurationException('emaillogroute_sentfrom_required'); + } + + /** + * Sends log messages to specified email addresses. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $message=''; + foreach($logs as $log) + $message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]); + $message=wordwrap($message,70); + $returnPath = ini_get('sendmail_path') ? "Return-Path:{$this->_from}\r\n" : ''; + foreach($this->_emails as $email) + mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n{$returnPath}"); + + } + + /** + * @return array list of destination email addresses + */ + public function getEmails() + { + return $this->_emails; + } + + /** + * @return array|string list of destination email addresses. If the value is + * a string, it is assumed to be comma-separated email addresses. + */ + public function setEmails($emails) + { + if(is_array($emails)) + $this->_emails=$emails; + else + { + $this->_emails=array(); + foreach(explode(',',$emails) as $email) + { + $email=trim($email); + if(preg_match(self::EMAIL_PATTERN,$email)) + $this->_emails[]=$email; + } + } + } + + /** + * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT + */ + public function getSubject() + { + if($this->_subject===null) + $this->_subject=self::DEFAULT_SUBJECT; + return $this->_subject; + } + + /** + * @param string email subject. + */ + public function setSubject($value) + { + $this->_subject=$value; + } + + /** + * @return string send from address of the email + */ + public function getSentFrom() + { + return $this->_from; + } + + /** + * @param string send from address of the email + */ + public function setSentFrom($value) + { + $this->_from=$value; + } +} + +/** + * TBrowserLogRoute class. + * + * TBrowserLogRoute prints selected log messages in the response. + * + * @author Xiang Wei Zhuo + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TBrowserLogRoute extends TLogRoute +{ + /** + * @var string css class for indentifying the table structure in the dom tree + */ + private $_cssClass=null; + + public function processLogs($logs) + { + if(empty($logs) || $this->getApplication()->getMode()==='Performance') return; + $first = $logs[0][3]; + $even = true; + $response = $this->getApplication()->getResponse(); + $response->write($this->renderHeader()); + for($i=0,$n=count($logs);$i<$n;++$i) + { + if ($i<$n-1) + { + $timing['delta'] = $logs[$i+1][3] - $logs[$i][3]; + $timing['total'] = $logs[$i+1][3] - $first; + } + else + { + $timing['delta'] = '?'; + $timing['total'] = $logs[$i][3] - $first; + } + $timing['even'] = !($even = !$even); + $response->write($this->renderMessage($logs[$i],$timing)); + } + $response->write($this->renderFooter()); + } + + /** + * @param string the css class of the control + */ + public function setCssClass($value) + { + $this->_cssClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the css class of the control + */ + public function getCssClass() + { + return TPropertyValue::ensureString($this->_cssClass); + } + + protected function renderHeader() + { + $string = ''; + if($className=$this->getCssClass()) + { + $string = << + + + Application Log + + +   + CategoryMessageTime Spent (s)Cumulated Time Spent (s) + +EOD; + } + else + { + $string = << + + + Application Log + + +   + CategoryMessageTime Spent (s)Cumulated Time Spent (s) + +EOD; + } + return $string; + } + + protected function renderMessage($log, $info) + { + $string = ''; + $total = sprintf('%0.6f', $info['total']); + $delta = sprintf('%0.6f', $info['delta']); + $msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info + $msg = THttpUtility::htmlEncode($msg); + if($this->getCssClass()) + { + $colorCssClass = $log[1]; + $messageCssClass = $info['even'] ? 'even' : 'odd'; + $string = << +   + {$log[2]} + {$msg} + {$delta} + {$total} + +EOD; + } + else + { + $bgcolor = $info['even'] ? "#fff" : "#eee"; + $color = $this->getColorLevel($log[1]); + $string = << +   + {$log[2]} + {$msg} + {$delta} + {$total} + +EOD; + } + return $string; + } + + protected function getColorLevel($level) + { + switch($level) + { + case TLogger::DEBUG: return 'green'; + case TLogger::INFO: return 'black'; + case TLogger::NOTICE: return '#3333FF'; + case TLogger::WARNING: return '#33FFFF'; + case TLogger::ERROR: return '#ff9933'; + case TLogger::ALERT: return '#ff00ff'; + case TLogger::FATAL: return 'red'; + } + return ''; + } + + protected function renderFooter() + { + $string = ''; + if($this->getCssClass()) + { + $string .= ''; + foreach(self::$_levelValues as $name => $level) + { + $string .= ''.strtoupper($name).""; + } + } + else + { + $string .= ""; + foreach(self::$_levelValues as $name => $level) + { + $string .= "getColorLevel($level); + $string .= ";margin: 0.5em; padding:0.01em;\">".strtoupper($name).""; + } + } + $string .= ''; + return $string; + } +} + + +/** + * TDbLogRoute class + * + * TDbLogRoute stores log messages in a database table. + * To specify the database table, set {@link setConnectionID ConnectionID} to be + * the ID of a {@link TDataSourceConfig} module and {@link setLogTableName LogTableName}. + * If they are not setting, an SQLite3 database named 'sqlite3.log' will be created and used + * under the runtime directory. + * + * By default, the database table name is 'pradolog'. It has the following structure: + * + * CREATE TABLE pradolog + * ( + * log_id INTEGER NOT NULL PRIMARY KEY, + * level INTEGER, + * category VARCHAR(128), + * logtime VARCHAR(20), + * message VARCHAR(255) + * ); + * + * + * @author Qiang Xue + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.1.2 + */ +class TDbLogRoute extends TLogRoute +{ + /** + * @var string the ID of TDataSourceConfig module + */ + private $_connID=''; + /** + * @var TDbConnection the DB connection instance + */ + private $_db; + /** + * @var string name of the DB log table + */ + private $_logTable='pradolog'; + /** + * @var boolean whether the log DB table should be created automatically + */ + private $_autoCreate=true; + + /** + * Destructor. + * Disconnect the db connection. + */ + public function __destruct() + { + if($this->_db!==null) + $this->_db->setActive(false); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. + * It initializes the database for logging purpose. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if the DB table does not exist. + */ + public function init($config) + { + $db=$this->getDbConnection(); + $db->setActive(true); + + $sql='SELECT * FROM '.$this->_logTable.' WHERE 0=1'; + try + { + $db->createCommand($sql)->query()->close(); + } + catch(Exception $e) + { + // DB table not exists + if($this->_autoCreate) + $this->createDbTable(); + else + throw new TConfigurationException('db_logtable_inexistent',$this->_logTable); + } + + parent::init($config); + } + + /** + * Stores log messages into database. + * @param array list of log messages + */ + protected function processLogs($logs) + { + $sql='INSERT INTO '.$this->_logTable.'(level, category, logtime, message) VALUES (:level, :category, :logtime, :message)'; + $command=$this->getDbConnection()->createCommand($sql); + foreach($logs as $log) + { + $command->bindValue(':message',$log[0]); + $command->bindValue(':level',$log[1]); + $command->bindValue(':category',$log[2]); + $command->bindValue(':logtime',$log[3]); + $command->execute(); + } + } + + /** + * Creates the DB table for storing log messages. + * @todo create sequence for PostgreSQL + */ + protected function createDbTable() + { + $db = $this->getDbConnection(); + $driver=$db->getDriverName(); + $autoidAttributes = ''; + if($driver==='mysql') + $autoidAttributes = 'AUTO_INCREMENT'; + + $sql='CREATE TABLE '.$this->_logTable.' ( + log_id INTEGER NOT NULL PRIMARY KEY ' . $autoidAttributes . ', + level INTEGER, + category VARCHAR(128), + logtime VARCHAR(20), + message VARCHAR(255))'; + $db->createCommand($sql)->execute(); + } + + /** + * Creates the DB connection. + * @param string the module ID for TDataSourceConfig + * @return TDbConnection the created DB connection + * @throws TConfigurationException if module ID is invalid or empty + */ + protected function createDbConnection() + { + if($this->_connID!=='') + { + $config=$this->getApplication()->getModule($this->_connID); + if($config instanceof TDataSourceConfig) + return $config->getDbConnection(); + else + throw new TConfigurationException('dblogroute_connectionid_invalid',$this->_connID); + } + else + { + $db=new TDbConnection; + // default to SQLite3 database + $dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.log'; + $db->setConnectionString('sqlite:'.$dbFile); + return $db; + } + } + + /** + * @return TDbConnection the DB connection instance + */ + public function getDbConnection() + { + if($this->_db===null) + $this->_db=$this->createDbConnection(); + return $this->_db; + } + + /** + * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set. + */ + public function getConnectionID() + { + return $this->_connID; + } + + /** + * Sets the ID of a TDataSourceConfig module. + * The datasource module will be used to establish the DB connection for this log route. + * @param string ID of the {@link TDataSourceConfig} module + */ + public function setConnectionID($value) + { + $this->_connID=$value; + } + + /** + * @return string the name of the DB table to store log content. Defaults to 'pradolog'. + * @see setAutoCreateLogTable + */ + public function getLogTableName() + { + return $this->_logTable; + } + + /** + * Sets the name of the DB table to store log content. + * Note, if {@link setAutoCreateLogTable AutoCreateLogTable} is false + * and you want to create the DB table manually by yourself, + * you need to make sure the DB table is of the following structure: + * (key CHAR(128) PRIMARY KEY, value BLOB, expire INT) + * @param string the name of the DB table to store log content + * @see setAutoCreateLogTable + */ + public function setLogTableName($value) + { + $this->_logTable=$value; + } + + /** + * @return boolean whether the log DB table should be automatically created if not exists. Defaults to true. + * @see setAutoCreateLogTable + */ + public function getAutoCreateLogTable() + { + return $this->_autoCreate; + } + + /** + * @param boolean whether the log DB table should be automatically created if not exists. + * @see setLogTableName + */ + public function setAutoCreateLogTable($value) + { + $this->_autoCreate=TPropertyValue::ensureBoolean($value); + } + +} + +/** + * TFirebugLogRoute class. + * + * TFirebugLogRoute prints selected log messages in the firebug log console. + * + * {@link http://www.getfirebug.com/ FireBug Website} + * + * @author Enrico Stahn , Christophe Boulain + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.1.2 + */ +class TFirebugLogRoute extends TBrowserLogRoute +{ + protected function renderHeader () + { + $string = << +/*getFirebugLoggingFunction($log[1]); + $total = sprintf('%0.6f', $info['total']); + $delta = sprintf('%0.6f', $info['delta']); + $msg = trim($this->formatLogMessage($log[0], $log[1], $log[2], '')); + $msg = preg_replace('/\(line[^\)]+\)$/', '', $msg); //remove line number info + $msg = "[{$total}] [{$delta}] ".$msg; // Add time spent and cumulated time spent + $string = $logfunc . '(\'' . addslashes($msg) . '\');' . "\n"; + + return $string; + } + + + protected function renderFooter () + { + $string = << + +EOD; + + return $string; + } + + protected function getFirebugLoggingFunction($level) + { + switch ($level) + { + case TLogger::DEBUG: + case TLogger::INFO: + case TLogger::NOTICE: + return 'console.log'; + case TLogger::WARNING: + return 'console.warn'; + case TLogger::ERROR: + case TLogger::ALERT: + case TLogger::FATAL: + return 'console.error'; + default: + return 'console.log'; + } + } + +} + +/** + * TFirePhpLogRoute class. + * + * TFirePhpLogRoute prints log messages in the firebug log console via firephp. + * + * {@link http://www.getfirebug.com/ FireBug Website} + * {@link http://www.firephp.org/ FirePHP Website} + * + * @author Yves Berkholz + * @version $Id: TLogRouter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.1.5 + */ +class TFirePhpLogRoute extends TLogRoute +{ + /** + * Default group label + */ + const DEFAULT_LABEL = 'System.Util.TLogRouter(TFirePhpLogRoute)'; + + private $_groupLabel = null; + + public function processLogs($logs) + { + if(empty($logs) || $this->getApplication()->getMode()==='Performance') + return; + + if(headers_sent()) { + echo ' +
    + TFirePhpLogRoute.GroupLabel "' . $this -> getGroupLabel() . '" - + Routing to FirePHP impossible, because headers already sent! +
    + '; + $fallback = new TBrowserLogRoute(); + $fallback->processLogs($logs); + return; + } + + require_once Prado::getPathOfNamespace('System.3rdParty.FirePHPCore') . '/FirePHP.class.php'; + $firephp = FirePHP::getInstance(true); + $firephp->setOptions(array('useNativeJsonEncode' => false)); + $firephp->group($this->getGroupLabel(), array('Collapsed' => true)); + $firephp->log('Time, Message'); + + $first = $logs[0][3]; + $c = count($logs); + for($i=0,$n=$c;$i<$n;++$i) + { + $message = $logs[$i][0]; + $level = $logs[$i][1]; + $category = $logs[$i][2]; + + if ($i<$n-1) + { + $delta = $logs[$i+1][3] - $logs[$i][3]; + $total = $logs[$i+1][3] - $first; + } + else + { + $delta = '?'; + $total = $logs[$i][3] - $first; + } + + $message = sPrintF('+%0.6f: %s', $delta, preg_replace('/\(line[^\)]+\)$/', '', $message)); + $firephp->fb($message, $category, self::translateLogLevel($level)); + } + $firephp->log(sPrintF('%0.6f', $total), 'Cumulated Time'); + $firephp->groupEnd(); + } + + /** + * Translates a PRADO log level attribute into one understood by FirePHP for correct visualization + * @param string prado log level + * @return string FirePHP log level + */ + protected static function translateLogLevel($level) + { + switch($level) + { + case TLogger::INFO: + return FirePHP::INFO; + case TLogger::DEBUG: + case TLogger::NOTICE: + return FirePHP::LOG; + case TLogger::WARNING: + return FirePHP::WARN; + case TLogger::ERROR: + case TLogger::ALERT: + case TLogger::FATAL: + return FirePHP::ERROR; + default: + return FirePHP::LOG; + } + } + + /** + * @return string group label. Defaults to TFirePhpLogRoute::DEFAULT_LABEL + */ + public function getGroupLabel() + { + if($this->_groupLabel===null) + $this->_groupLabel=self::DEFAULT_LABEL; + + return $this->_groupLabel; + } + + /** + * @param string group label. + */ + public function setGroupLabel($value) + { + $this->_groupLabel=$value; + } +} diff --git a/gui/baculum/framework/Util/TLogger.php b/gui/baculum/framework/Util/TLogger.php new file mode 100644 index 0000000000..74fa22bf34 --- /dev/null +++ b/gui/baculum/framework/Util/TLogger.php @@ -0,0 +1,236 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLogger.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + */ + +/** + * TLogger class. + * + * TLogger records log messages in memory and implements the methods to + * retrieve the messages with filter conditions, including log levels, + * log categories, and by control. + * + * @author Qiang Xue + * @version $Id: TLogger.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TLogger extends TComponent +{ + /** + * Log levels. + */ + const DEBUG=0x01; + const INFO=0x02; + const NOTICE=0x04; + const WARNING=0x08; + const ERROR=0x10; + const ALERT=0x20; + const FATAL=0x40; + /** + * @var array log messages + */ + private $_logs=array(); + /** + * @var integer log levels (bits) to be filtered + */ + private $_levels; + /** + * @var array list of categories to be filtered + */ + private $_categories; + /** + * @var array list of control client ids to be filtered + */ + private $_controls; + /** + * @var float timestamp used to filter + */ + private $_timestamp; + + /** + * Logs a message. + * Messages logged by this method may be retrieved via {@link getLogs}. + * @param string message to be logged + * @param integer level of the message. Valid values include + * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, + * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. + * @param string category of the message + * @param string|TControl control of the message + */ + public function log($message,$level,$category='Uncategorized', $ctl=null) + { + if($ctl) { + if($ctl instanceof TControl) + $ctl = $ctl->ClientId; + else if(!is_string($ctl)) + $ctl = null; + } else + $ctl = null; + $this->_logs[]=array($message,$level,$category,microtime(true),memory_get_usage(),$ctl); + } + + /** + * Retrieves log messages. + * Messages may be filtered by log levels and/or categories and/or control client ids and/or timestamp. + * A level filter is specified by an integer, whose bits indicate the levels interested. + * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels. + * A category filter is specified by an array of categories to filter. + * A message whose category name starts with any filtering category + * will be returned. For example, a category filter array('System.Web','System.IO') + * will return messages under categories such as 'System.Web', 'System.IO', + * 'System.Web.UI', 'System.Web.UI.WebControls', etc. + * A control client id filter is specified by an array of control client id + * A message whose control client id starts with any filtering naming panels + * will be returned. For example, a category filter array('ctl0_body_header', + * 'ctl0_body_content_sidebar') + * will return messages under categories such as 'ctl0_body_header', 'ctl0_body_content_sidebar', + * 'ctl0_body_header_title', 'ctl0_body_content_sidebar_savebutton', etc. + * A timestamp filter is specified as an interger or float number. + * A message whose registered timestamp is less or equal the filter value will be returned. + * Level filter, category filter, control filter and timestamp filter are combinational, i.e., only messages + * satisfying all filter conditions will they be returned. + * @param integer level filter + * @param array category filter + * @param array control filter + * @return array list of messages. Each array elements represents one message + * with the following structure: + * array( + * [0] => message + * [1] => level + * [2] => category + * [3] => timestamp (by microtime(), float number)); + * [4] => memory in bytes + * [5] => control client id + */ + public function getLogs($levels=null,$categories=null,$controls=null,$timestamp=null) + { + $this->_levels=$levels; + $this->_categories=$categories; + $this->_controls=$controls; + $this->_timestamp=$timestamp; + if(empty($levels) && empty($categories) && empty($controls) && is_null($timestamp)) + return $this->_logs; + $logs = $this->_logs; + if(!empty($levels)) + $logs = array_values(array_filter( array_filter($logs,array($this,'filterByLevels')) )); + if(!empty($categories)) + $logs = array_values(array_filter( array_filter($logs,array($this,'filterByCategories')) )); + if(!empty($controls)) + $logs = array_values(array_filter( array_filter($logs,array($this,'filterByControl')) )); + if(!is_null($timestamp)) + $logs = array_values(array_filter( array_filter($logs,array($this,'filterByTimeStamp')) )); + return $logs; + } + + /** + * Deletes log messages from the queue. + * Messages may be filtered by log levels and/or categories and/or control client ids and/or timestamp. + * A level filter is specified by an integer, whose bits indicate the levels interested. + * For example, (TLogger::INFO | TLogger::WARNING) specifies INFO and WARNING levels. + * A category filter is specified by an array of categories to filter. + * A message whose category name starts with any filtering category + * will be deleted. For example, a category filter array('System.Web','System.IO') + * will delete messages under categories such as 'System.Web', 'System.IO', + * 'System.Web.UI', 'System.Web.UI.WebControls', etc. + * A control client id filter is specified by an array of control client id + * A message whose control client id starts with any filtering naming panels + * will be deleted. For example, a category filter array('ctl0_body_header', + * 'ctl0_body_content_sidebar') + * will delete messages under categories such as 'ctl0_body_header', 'ctl0_body_content_sidebar', + * 'ctl0_body_header_title', 'ctl0_body_content_sidebar_savebutton', etc. + * A timestamp filter is specified as an interger or float number. + * A message whose registered timestamp is less or equal the filter value will be returned. + * Level filter, category filter, control filter and timestamp filter are combinational, i.e., only messages + * satisfying all filter conditions will they be returned. + * @param integer level filter + * @param array category filter + * @param array control filter + */ + public function deleteLogs($levels=null,$categories=null,$controls=null,$timestamp=null) + { + $this->_levels=$levels; + $this->_categories=$categories; + $this->_controls=$controls; + $this->_timestamp=$timestamp; + if(empty($levels) && empty($categories) && empty($controls) && is_null($timestamp)) + { + $this->_logs=array(); + return; + } + $logs = $this->_logs; + if(!empty($levels)) + $logs = array_filter( array_filter($logs,array($this,'filterByLevels')) ); + if(!empty($categories)) + $logs = array_filter( array_filter($logs,array($this,'filterByCategories')) ); + if(!empty($controls)) + $logs = array_filter( array_filter($logs,array($this,'filterByControl')) ); + if(!is_null($timestamp)) + $logs = array_filter( array_filter($logs,array($this,'filterByTimeStamp')) ); + $this->_logs = array_values( array_diff_key($this->_logs, $logs) ); + } + + /** + * Filter function used by {@link getLogs}. + * @param array element to be filtered + */ + private function filterByCategories($value) + { + foreach($this->_categories as $category) + { + // element 2 is the category + if($value[2]===$category || strpos($value[2],$category.'.')===0) + return $value; + } + return false; + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ + private function filterByLevels($value) + { + // element 1 are the levels + if($value[1] & $this->_levels) + return $value; + else + return false; + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ + private function filterByControl($value) + { + // element 5 are the control client ids + foreach($this->_controls as $control) + { + if($value[5]===$control || strpos($value[5],$control)===0) + return $value; + } + return false; + } + + /** + * Filter function used by {@link getLogs} + * @param array element to be filtered + */ + private function filterByTimeStamp($value) + { + // element 3 is the timestamp + if($value[3] <= $this->_timestamp) + return $value; + else + return false; + } +} + diff --git a/gui/baculum/framework/Util/TParameterModule.php b/gui/baculum/framework/Util/TParameterModule.php new file mode 100644 index 0000000000..5b345b4ebd --- /dev/null +++ b/gui/baculum/framework/Util/TParameterModule.php @@ -0,0 +1,173 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TParameterModule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + */ + +/** + * TParameterModule class + * + * TParameterModule enables loading application parameters from external + * storage other than the application configuration. + * To load parameters from an XML file, configure the module by setting + * its {@link setParameterFile ParameterFile} property. + * Note, the property only accepts a file path in namespace format with + * file extension being '.xml'. The file format is as follows, which is + * similar to the parameter portion in an application configuration, + * + * + * + * + * + * + * + * In addition, any content enclosed within the module tag is also treated + * as parameters, e.g., + * + * + * + * + * + * + * + * If a parameter is defined both in the external file and within the module + * tag, the former takes precedence. + * + * @author Qiang Xue + * @author Carl G. Mathisen + * @version $Id: TParameterModule.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TParameterModule extends TModule +{ + /** + * @deprecated since 3.2 + */ + const PARAM_FILE_EXT='.xml'; + private $_initialized=false; + private $_paramFile=null; + + /** + * Initializes the module by loading parameters. + * @param mixed content enclosed within the module tag + */ + public function init($config) + { + $this->loadParameters($config); + if($this->_paramFile!==null) + { + $configFile = null; + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_XML && ($cache=$this->getApplication()->getCache())!==null) + { + $cacheKey='TParameterModule:'.$this->_paramFile; + if(($configFile=$cache->get($cacheKey))===false) + { + $configFile=new TXmlDocument; + $configFile->loadFromFile($this->_paramFile); + $cache->set($cacheKey,$configFile,0,new TFileCacheDependency($this->_paramFile)); + } + } + else + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $configFile = include $this->_paramFile; + } + else + { + $configFile=new TXmlDocument; + $configFile->loadFromFile($this->_paramFile); + } + } + $this->loadParameters($configFile); + } + $this->_initialized=true; + } + + /** + * Loads parameters into application. + * @param mixed XML of PHP representation of the parameters + * @throws TConfigurationException if the parameter file format is invalid + */ + protected function loadParameters($config) + { + $parameters=array(); + if(is_array($config)) + { + foreach($config as $id => $parameter) + { + if(is_array($parameter) && isset($parameter['class'])) + { + $properties = isset($parameter['properties'])?$parameter['properties']:array(); + $parameters[$id]=array($parameter['class'],$properties); + } + else + { + $parameters[$id] = $parameter; + } + } + } + else if($config instanceof TXmlElement) + { + foreach($config->getElementsByTagName('parameter') as $node) + { + $properties=$node->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('parametermodule_parameterid_required'); + if(($type=$properties->remove('class'))===null) + { + if(($value=$properties->remove('value'))===null) + $parameters[$id]=$node; + else + $parameters[$id]=$value; + } + else + $parameters[$id]=array($type,$properties->toArray()); + } + } + + $appParams=$this->getApplication()->getParameters(); + foreach($parameters as $id=>$parameter) + { + if(is_array($parameter)) + { + $component=Prado::createComponent($parameter[0]); + foreach($parameter[1] as $name=>$value) + $component->setSubProperty($name,$value); + $appParams->add($id,$component); + } + else + $appParams->add($id,$parameter); + } + } + + /** + * @return string the parameter file path + */ + public function getParameterFile() + { + return $this->_paramFile; + } + + /** + * @param string the parameter file path. It must be in namespace format + * and the file extension is '.xml'. + * @throws TInvalidOperationException if the module is initialized + * @throws TConfigurationException if the file is invalid + */ + public function setParameterFile($value) + { + if($this->_initialized) + throw new TInvalidOperationException('parametermodule_parameterfile_unchangeable'); + else if(($this->_paramFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null || !is_file($this->_paramFile)) + throw new TConfigurationException('parametermodule_parameterfile_invalid',$value); + } +} + diff --git a/gui/baculum/framework/Util/TRpcClient.php b/gui/baculum/framework/Util/TRpcClient.php new file mode 100644 index 0000000000..fbfb528a24 --- /dev/null +++ b/gui/baculum/framework/Util/TRpcClient.php @@ -0,0 +1,358 @@ + + * @link http://www.pradosoft.com/ + * @copyright 2010 Bigpoint GmbH + * @license http://www.pradosoft.com/license/ + * @version $Id: TRpcClient.php 137 2010-03-27 22:13:36Z rrogge $ + * @since 3.2 + * @package System.Util + */ + +/** + * TRpcClient class + * + * Note: When using setIsNotification(true), *every* following request is also + * considered to be a notification until you use setIsNotification(false). + * + * Usage: + * + * First, you can use the factory: + *
    + * $_rpcClient = TRpcClient::create('xml', 'http://host/server');
    + * $_result = $_rpcClient->remoteMethodName($param, $otherParam);
    + * 
    + * + * or as oneliner: + *
    + * $_result = TRpcClient::create('json', 'http://host/server')->remoteMethod($param, ...);
    + * 
    + * + * Second, you can also use the specific implementation directly: + *
    + * $_rpcClient = new TXmlRpcClient('http://host/server');
    + * $_result = $_rpcClient->remoteMethod($param, ...);
    + * 
    + * + * or as oneliner: + *
    + * $_result = TXmlRpcClient('http://host/server')->hello();
    + * 
    + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Util + * @since 3.2 + */ + +class TRpcClient extends TApplicationComponent +{ + /** + * @var string url of the RPC server + */ + private $_serverUrl; + + /** + * @var boolean whether the request is a notification and therefore should not care about the result (default: false) + */ + private $_isNotification = false; + + // magics + + /** + * @param string url to RPC server + * @param boolean whether requests are considered to be notifications (completely ignoring the response) (default: false) + */ + public function __construct($serverUrl, $isNotification = false) + { + $this->_serverUrl = $serverUrl; + $this->_isNotification = TPropertyValue::ensureBoolean($isNotification); + } + + // methods + + /** + * Creates an instance of the requested RPC client type + * @return TRpcClient instance + * @throws TApplicationException if an unsupported RPC client type was specified + */ + public static function create($type, $serverUrl, $isNotification = false) + { + if(($_handler = constant('TRpcClientTypesEnumerable::'.strtoupper($type))) === null) + throw new TApplicationException('rpcclient_unsupported_handler'); + + return new $_handler($serverUrl, $isNotification); + } + + /** + * Creates a stream context resource + * @param mixed $content + * @param string $contentType mime type + */ + protected function createStreamContext($content, $contentType) + { + return stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'header' => "Content-Type: {$contentType}", + 'content' => $content + ) + )); + } + + /** + * Performs the actual request + * @param string RPC server URL + * @param array payload data + * @param string request mime type + */ + protected function performRequest($serverUrl, $payload, $mimeType) + { + if(($_response = @file_get_contents($serverUrl, false, $this->createStreamContext($payload, $mimeType))) === false) + throw new TRpcClientRequestException('Request failed ("'.$http_response_header[0].'")'); + + return $_response; + } + + // getter/setter + + /** + * @return boolean whether requests are considered to be notifications (completely ignoring the response) + */ + public function getIsNotification() + { + return $this->_isNotification; + } + + /** + * @param string boolean whether the requests are considered to be notifications (completely ignoring the response) (default: false) + */ + public function setIsNotification($bool) + { + $this->_isNotification = TPropertyValue::ensureBoolean($bool); + } + + /** + * @return string url of the RPC server + */ + public function getServerUrl() + { + return $this->_serverUrl; + } + + /** + * @param string url of the RPC server + */ + public function setServerUrl($value) + { + $this->_serverUrl = $value; + } +} + +/** + * TRpcClientTypesEnumerable class + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Util + * @since 3.2 + */ + +class TRpcClientTypesEnumerable extends TEnumerable +{ + const JSON = 'TJsonRpcClient'; + const XML = 'TXmlRpcClient'; +} + +/** + * TRpcClientRequestException class + * + * This Exception is fired if the RPC request fails because of transport problems e.g. when + * there is no RPC server responding on the given remote host. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Util + * @since 3.2 + */ + +class TRpcClientRequestException extends TApplicationException +{ +} + +/** + * TRpcClientResponseException class + * + * This Exception is fired when the + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Util + * @since 3.2 + */ + +class TRpcClientResponseException extends TApplicationException +{ + /** + * @param string error message + * @param integer error code (optional) + */ + public function __construct($errorMessage, $errorCode = null) + { + $this->setErrorCode($errorCode); + + parent::__construct($errorMessage); + } +} + +/** + * TJsonRpcClient class + * + * Note: When using setIsNotification(true), *every* following request is also + * considered to be a notification until you use setIsNotification(false). + * + * Usage: + *
    + * $_rpcClient = new TJsonRpcClient('http://host/server');
    + * $_result = $_rpcClient->remoteMethod($param, $otherParam);
    + * // or
    + * $_result = TJsonRpcClient::create('http://host/server')->remoteMethod($param, $otherParam);
    + * 
    + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Util + * @since 3.2 + */ + +class TJsonRpcClient extends TRpcClient +{ + // magics + + /** + * @param string RPC method name + * @param array RPC method parameters + * @return mixed RPC request result + * @throws TRpcClientRequestException if the client fails to connect to the server + * @throws TRpcClientResponseException if the response represents an RPC fault + */ + public function __call($method, $parameters) + { + // send request + $_response = $this->performRequest($this->getServerUrl(), $this->encodeRequest($method, $parameters), 'application/json'); + + // skip response handling if the request was just a notification request + if($this->isNotification) + return true; + + // decode response + if(($_response = json_decode($_response, true)) === null) + throw new TRpcClientResponseException('Empty response received'); + + // handle error response + if(!is_null($_response['error'])) + throw new TRpcClientResponseException($_response['error']); + + return $_response['result']; + } + + // methods + + /** + * @param string method name + * @param array method parameters + */ + public function encodeRequest($method, $parameters) + { + static $_requestId; + $_requestId = ($_requestId === null) ? 1 : $_requestId + 1; + + return json_encode(array( + 'method' => $method, + 'params' => $parameters, + 'id' => $this->isNotification ? null : $_requestId + )); + } + + /** + * Creates an instance of TJsonRpcClient + * @param string url of the rpc server + * @param boolean whether the requests are considered to be notifications (completely ignoring the response) (default: false) + */ + public static function create($type, $serverUrl, $isNotification = false) + { + return new self($serverUrl, $isNotification); + } +} + +/** + * TXmlRpcClient class + * + * Note: When using setIsNotification(true), *every* following request is also + * considered to be a notification until you use setIsNotification(false). + * + * Usage: + *
    + * $_rpcClient = new TXmlRpcClient('http://remotehost/rpcserver');
    + * $_rpcClient->remoteMethod($param, $otherParam);
    + * 
    + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Util + * @since 3.2 + */ + +class TXmlRpcClient extends TRpcClient +{ + // magics + + /** + * @param string RPC method name + * @param array RPC method parameters + * @return mixed RPC request result + * @throws TRpcClientRequestException if the client fails to connect to the server + * @throws TRpcClientResponseException if the response represents an RPC fault + */ + public function __call($method, $parameters) + { + // send request + $_response = $this->performRequest($this->getServerUrl(), $this->encodeRequest($method, $parameters), 'text/xml'); + + // skip response handling if the request was just a notification request + if($this->isNotification) + return true; + + // decode response + if(($_response = xmlrpc_decode($_response)) === null) + throw new TRpcClientResponseException('Empty response received'); + + // handle error response + if(xmlrpc_is_fault($_response)) + throw new TRpcClientResponseException($_response['faultString'], $_response['faultCode']); + + return $_response; + } + + // methods + + /** + * @param string method name + * @param array method parameters + */ + public function encodeRequest($method, $parameters) + { + return xmlrpc_encode_request($method, $parameters); + } + + /** + * Creates an instance of TXmlRpcClient + * @param string url of the rpc server + * @param boolean whether the requests are considered to be notifications (completely ignoring the response) (default: false) + */ + public static function create($type, $serverUrl, $isNotification = false) + { + return new self($serverUrl, $isNotification); + } +} diff --git a/gui/baculum/framework/Util/TSimpleDateFormatter.php b/gui/baculum/framework/Util/TSimpleDateFormatter.php new file mode 100644 index 0000000000..295a2d60a4 --- /dev/null +++ b/gui/baculum/framework/Util/TSimpleDateFormatter.php @@ -0,0 +1,371 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSimpleDateFormatter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + */ + +/** + * TSimpleDateFormatter class. + * + * Formats and parses dates using the SimpleDateFormat pattern. + * This pattern is compatible with the I18N and java's SimpleDateFormatter. + * + * Pattern | Description + * ---------------------------------------------------- + * d | Day of month 1 to 31, no padding + * dd | Day of monath 01 to 31, zero leading + * M | Month digit 1 to 12, no padding + * MM | Month digit 01 to 12, zero leading + * yy | 2 year digit, e.g., 96, 05 + * yyyy | 4 year digit, e.g., 2005 + * ---------------------------------------------------- + * + * + * Usage example, to format a date + * + * $formatter = new TSimpleDateFormatter("dd/MM/yyy"); + * echo $formatter->format(time()); + * + * + * To parse the date string into a date timestamp. + * + * $formatter = new TSimpleDateFormatter("d-M-yyy"); + * echo $formatter->parse("24-6-2005"); + * + * + * @author Wei Zhuo + * @version $Id: TSimpleDateFormatter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TSimpleDateFormatter +{ + /** + * Formatting pattern. + * @var string + */ + private $pattern; + + /** + * Charset, default is 'UTF-8' + * @var string + */ + private $charset = 'UTF-8'; + + /** + * Constructor, create a new date time formatter. + * @param string formatting pattern. + * @param string pattern and value charset + */ + public function __construct($pattern, $charset='UTF-8') + { + $this->setPattern($pattern); + $this->setCharset($charset); + } + + /** + * @return string formatting pattern. + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * @param string formatting pattern. + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + } + + /** + * @return string formatting charset. + */ + public function getCharset() + { + return $this->charset; + } + + /** + * @param string formatting charset. + */ + public function setCharset($charset) + { + $this->charset = $charset; + } + + /** + * Format the date according to the pattern. + * @param string|int the date to format, either integer or a string readable by strtotime. + * @return string formatted date. + */ + public function format($value) + { + $date = $this->getDate($value); + $bits['yyyy'] = $date['year']; + $bits['yy'] = substr("{$date['year']}", -2); + + $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT); + $bits['M'] = $date['mon']; + + $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT); + $bits['d'] = $date['mday']; + + $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern); + return str_replace(array_keys($bits), $bits, $pattern); + } + + public function getMonthPattern() + { + if(is_int(strpos($this->pattern, 'MMMM'))) + return 'MMMM'; + if(is_int(strpos($this->pattern, 'MMM'))) + return 'MMM'; + if(is_int(strpos($this->pattern, 'MM'))) + return 'MM'; + if(is_int(strpos($this->pattern, 'M'))) + return 'M'; + return false; + } + + public function getDayPattern() + { + if(is_int(strpos($this->pattern, 'dd'))) + return 'dd'; + if(is_int(strpos($this->pattern, 'd'))) + return 'd'; + return false; + } + + public function getYearPattern() + { + if(is_int(strpos($this->pattern, 'yyyy'))) + return 'yyyy'; + if(is_int(strpos($this->pattern, 'yy'))) + return 'yy'; + return false; + } + + public function getDayMonthYearOrdering() + { + $ordering = array(); + if(is_int($day= strpos($this->pattern, 'd'))) + $ordering['day'] = $day; + if(is_int($month= strpos($this->pattern, 'M'))) + $ordering['month'] = $month; + if(is_int($year= strpos($this->pattern, 'yy'))) + $ordering['year'] = $year; + asort($ordering); + return array_keys($ordering); + } + + /** + * Gets the time stamp from string or integer. + * @param string|int date to parse + * @return array date info array + */ + private function getDate($value) + { + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + if(is_numeric($value)) + return $s->getDate($value); + else + return $s->parseDate($value); + } + + /** + * @return boolean true if the given value matches with the date pattern. + */ + public function isValidDate($value) + { + if($value === null) { + return false; + } else { + return $this->parse($value, false) !== null; + } + } + + /** + * Parse the string according to the pattern. + * @param string|int date string or integer to parse + * @return int date time stamp + * @throws TInvalidDataValueException if date string is malformed. + */ + public function parse($value,$defaultToCurrentTime=true) + { + if(is_int($value) || is_float($value)) + return $value; + else if(!is_string($value)) + throw new TInvalidDataValueException('date_to_parse_must_be_string', $value); + + if(empty($this->pattern)) return time(); + + $date = time(); + + if($this->length(trim($value)) < 1) + return $defaultToCurrentTime ? $date : null; + + $pattern = $this->pattern; + + $i_val = 0; + $i_format = 0; + $pattern_length = $this->length($pattern); + $c = ''; + $token=''; + $x=null; $y=null; + + + if($defaultToCurrentTime) + { + $year = "{$date['year']}"; + $month = $date['mon']; + $day = $date['mday']; + } + else + { + $year = null; + $month = null; + $day = null; + } + + while ($i_format < $pattern_length) + { + $c = $this->charAt($pattern,$i_format); + $token=''; + while ($this->charEqual($pattern, $i_format, $c) + && ($i_format < $pattern_length)) + { + $token .= $this->charAt($pattern, $i_format++); + } + + if ($token=='yyyy' || $token=='yy' || $token=='y') + { + if ($token=='yyyy') { $x=4;$y=4; } + if ($token=='yy') { $x=2;$y=2; } + if ($token=='y') { $x=2;$y=4; } + $year = $this->getInteger($value,$i_val,$x,$y); + if($year === null) + return null; + //throw new TInvalidDataValueException('Invalid year', $value); + $i_val += strlen($year); + if(strlen($year) == 2) + { + $iYear = (int)$year; + if($iYear > 70) + $year = $iYear + 1900; + else + $year = $iYear + 2000; + } + $year = (int)$year; + } + elseif($token=='MM' || $token=='M') + { + $month=$this->getInteger($value,$i_val, + $this->length($token),2); + $iMonth = (int)$month; + if($month === null || $iMonth < 1 || $iMonth > 12 ) + return null; + //throw new TInvalidDataValueException('Invalid month', $value); + $i_val += strlen($month); + $month = $iMonth; + } + elseif ($token=='dd' || $token=='d') + { + $day = $this->getInteger($value,$i_val, + $this->length($token), 2); + $iDay = (int)$day; + if($day === null || $iDay < 1 || $iDay >31) + return null; + //throw new TInvalidDataValueException('Invalid day', $value); + $i_val += strlen($day); + $day = $iDay; + } + else + { + if($this->substring($value, $i_val, $this->length($token)) != $token) + return null; + //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value); + else + $i_val += $this->length($token); + } + } + if ($i_val != $this->length($value)) + return null; + //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value); + if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null)) + return null; + else + { + if(empty($year)) { + $year = date('Y'); + } + $day = (int)$day <= 0 ? 1 : (int)$day; + $month = (int)$month <= 0 ? 1 : (int)$month; + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + return $s->getTimeStamp(0, 0, 0, $month, $day, $year); + } + } + + /** + * Calculate the length of a string, may be consider iconv_strlen? + */ + private function length($string) + { + //use iconv_strlen or just strlen? + return strlen($string); + } + + /** + * Get the char at a position. + */ + private function charAt($string, $pos) + { + return $this->substring($string, $pos, 1); + } + + /** + * Gets a portion of a string, uses iconv_substr. + */ + private function substring($string, $start, $length) + { + return iconv_substr($string, $start, $length); + } + + /** + * Returns true if char at position equals a particular char. + */ + private function charEqual($string, $pos, $char) + { + return $this->charAt($string, $pos) == $char; + } + + /** + * Gets integer from part of a string, allows integers of any length. + * @param string string to retrieve the integer from. + * @param int starting position + * @param int minimum integer length + * @param int maximum integer length + * @return string integer portion of the string, null otherwise + */ + private function getInteger($str,$i,$minlength,$maxlength) + { + //match for digits backwards + for ($x = $maxlength; $x >= $minlength; $x--) + { + $token= $this->substring($str, $i,$x); + if ($this->length($token) < $minlength) + return null; + if (preg_match('/^\d+$/', $token)) + return $token; + } + return null; + } +} + diff --git a/gui/baculum/framework/Util/TVarDumper.php b/gui/baculum/framework/Util/TVarDumper.php new file mode 100644 index 0000000000..f49a127420 --- /dev/null +++ b/gui/baculum/framework/Util/TVarDumper.php @@ -0,0 +1,128 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TVarDumper.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + */ + +/** + * TVarDumper class. + * + * TVarDumper is intended to replace the buggy PHP function var_dump and print_r. + * It can correctly identify the recursively referenced objects in a complex + * object structure. It also has a recursive depth control to avoid indefinite + * recursive display of some peculiar variables. + * + * TVarDumper can be used as follows, + * + * echo TVarDumper::dump($var); + * + * + * @author Qiang Xue + * @version $Id: TVarDumper.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Util + * @since 3.0 + */ +class TVarDumper +{ + private static $_objects; + private static $_output; + private static $_depth; + + /** + * Converts a variable into a string representation. + * This method achieves the similar functionality as var_dump and print_r + * but is more robust when handling complex objects such as PRADO controls. + * @param mixed variable to be dumped + * @param integer maximum depth that the dumper should go into the variable. Defaults to 10. + * @return string the string representation of the variable + */ + public static function dump($var,$depth=10,$highlight=false) + { + self::$_output=''; + self::$_objects=array(); + self::$_depth=$depth; + self::dumpInternal($var,0); + if($highlight) + { + $result=highlight_string("/','',$result,1); + } + else + return self::$_output; + } + + private static function dumpInternal($var,$level) + { + switch(gettype($var)) + { + case 'boolean': + self::$_output.=$var?'true':'false'; + break; + case 'integer': + self::$_output.="$var"; + break; + case 'double': + self::$_output.="$var"; + break; + case 'string': + self::$_output.="'$var'"; + break; + case 'resource': + self::$_output.='{resource}'; + break; + case 'NULL': + self::$_output.="null"; + break; + case 'unknown type': + self::$_output.='{unknown}'; + break; + case 'array': + if(self::$_depth<=$level) + self::$_output.='array(...)'; + else if(empty($var)) + self::$_output.='array()'; + else + { + $keys=array_keys($var); + $spaces=str_repeat(' ',$level*4); + self::$_output.="array\n".$spaces.'('; + foreach($keys as $key) + { + self::$_output.="\n".$spaces." [$key] => "; + self::$_output.=self::dumpInternal($var[$key],$level+1); + } + self::$_output.="\n".$spaces.')'; + } + break; + case 'object': + if(($id=array_search($var,self::$_objects,true))!==false) + self::$_output.=get_class($var).'#'.($id+1).'(...)'; + else if(self::$_depth<=$level) + self::$_output.=get_class($var).'(...)'; + else + { + $id=array_push(self::$_objects,$var); + $className=get_class($var); + $members=(array)$var; + $keys=array_keys($members); + $spaces=str_repeat(' ',$level*4); + self::$_output.="$className#$id\n".$spaces.'('; + foreach($keys as $key) + { + $keyDisplay=strtr(trim($key),array("\0"=>':')); + self::$_output.="\n".$spaces." [$keyDisplay] => "; + self::$_output.=self::dumpInternal($members[$key],$level+1); + } + self::$_output.="\n".$spaces.')'; + } + break; + } + } +} + diff --git a/gui/baculum/framework/Web/Javascripts/JSMin.php b/gui/baculum/framework/Web/Javascripts/JSMin.php new file mode 100644 index 0000000000..95beff382e --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/JSMin.php @@ -0,0 +1,290 @@ + + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (PHP port) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 1.1.1 (2008-03-02) + * @link http://code.google.com/p/jsmin-php/ + */ + +class JSMin { + const ORD_LF = 10; + const ORD_SPACE = 32; + + protected $a = ''; + protected $b = ''; + protected $input = ''; + protected $inputIndex = 0; + protected $inputLength = 0; + protected $lookAhead = null; + protected $output = ''; + + // -- Public Static Methods -------------------------------------------------- + + public static function minify($js) { + $jsmin = new JSMin($js); + return $jsmin->min(); + } + + // -- Public Instance Methods ------------------------------------------------ + + public function __construct($input) { + $this->input = str_replace("\r\n", "\n", $input); + $this->inputLength = strlen($this->input); + } + + // -- Protected Instance Methods --------------------------------------------- + + protected function action($d) { + switch($d) { + case 1: + $this->output .= $this->a; + + case 2: + $this->a = $this->b; + + if ($this->a === "'" || $this->a === '"') { + for (;;) { + $this->output .= $this->a; + $this->a = $this->get(); + + if ($this->a === $this->b) { + break; + } + + if (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated string literal.'); + } + + if ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } + } + } + + case 3: + $this->b = $this->next(); + + if ($this->b === '/' && ( + $this->a === '(' || $this->a === ',' || $this->a === '=' || + $this->a === ':' || $this->a === '[' || $this->a === '!' || + $this->a === '&' || $this->a === '|' || $this->a === '?')) { + + $this->output .= $this->a . $this->b; + + for (;;) { + $this->a = $this->get(); + + if ($this->a === '/') { + break; + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMinException('Unterminated regular expression '. + 'literal.'); + } + + $this->output .= $this->a; + } + + $this->b = $this->next(); + } + } + } + + protected function get() { + $c = $this->lookAhead; + $this->lookAhead = null; + + if ($c === null) { + if ($this->inputIndex < $this->inputLength) { + $c = $this->input[$this->inputIndex]; + $this->inputIndex += 1; + } else { + $c = null; + } + } + + if ($c === "\r") { + return "\n"; + } + + if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) { + return $c; + } + + return ' '; + } + + protected function isAlphaNum($c) { + return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1; + } + + protected function min() { + $this->a = "\n"; + $this->action(3); + + while ($this->a !== null) { + switch ($this->a) { + case ' ': + if ($this->isAlphaNum($this->b)) { + $this->action(1); + } else { + $this->action(2); + } + break; + + case "\n": + switch ($this->b) { + case '{': + case '[': + case '(': + case '+': + case '-': + $this->action(1); + break; + + case ' ': + $this->action(3); + break; + + default: + if ($this->isAlphaNum($this->b)) { + $this->action(1); + } + else { + $this->action(2); + } + } + break; + + default: + switch ($this->b) { + case ' ': + if ($this->isAlphaNum($this->a)) { + $this->action(1); + break; + } + + $this->action(3); + break; + + case "\n": + switch ($this->a) { + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case "'": + $this->action(1); + break; + + default: + if ($this->isAlphaNum($this->a)) { + $this->action(1); + } + else { + $this->action(3); + } + } + break; + + default: + $this->action(1); + break; + } + } + } + + return $this->output; + } + + protected function next() { + $c = $this->get(); + + if ($c === '/') { + switch($this->peek()) { + case '/': + for (;;) { + $c = $this->get(); + + if (ord($c) <= self::ORD_LF) { + return $c; + } + } + + case '*': + $this->get(); + + for (;;) { + switch($this->get()) { + case '*': + if ($this->peek() === '/') { + $this->get(); + return ' '; + } + break; + + case null: + throw new JSMinException('Unterminated comment.'); + } + } + + default: + return $c; + } + } + + return $c; + } + + protected function peek() { + $this->lookAhead = $this->get(); + return $this->lookAhead; + } +} + +// -- Exceptions --------------------------------------------------------------- +class JSMinException extends Exception {} diff --git a/gui/baculum/framework/Web/Javascripts/TJavaScript.php b/gui/baculum/framework/Web/Javascripts/TJavaScript.php new file mode 100644 index 0000000000..0f6fef1c86 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/TJavaScript.php @@ -0,0 +1,283 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TJavaScript.php 3291 2013-05-09 17:44:58Z ctrlaltca $ + * @package System.Web.Javascripts + */ + +/** + * TJavaScript class. + * + * TJavaScript is a utility class containing commonly-used javascript-related + * functions. + * + * @author Wei Zhuo + * @version $Id: TJavaScript.php 3291 2013-05-09 17:44:58Z ctrlaltca $ + * @package System.Web.Javascripts + * @since 3.0 + */ +class TJavaScript +{ + /** + * Renders a list of javascript files + * @param array URLs to the javascript files + * @return string rendering result + */ + public static function renderScriptFiles($files) + { + $str=''; + foreach($files as $file) + $str.= self::renderScriptFile($file); + return $str; + } + + /** + * Renders a javascript file + * @param string URL to the javascript file + * @return string rendering result + */ + public static function renderScriptFile($file) + { + return '\n"; + } + + /** + * Renders a list of javascript blocks + * @param array javascript blocks + * @return string rendering result + */ + public static function renderScriptBlocks($scripts) + { + if(count($scripts)) + return "\n"; + else + return ''; + } + + /** + * Renders javascript block + * @param string javascript block + * @return string rendering result + */ + public static function renderScriptBlock($script) + { + return "\n"; + } + + /** + * Quotes a javascript string. + * After processing, the string is safely enclosed within a pair of + * quotation marks and can serve as a javascript string. + * @param string string to be quoted + * @return string the quoted string + */ + public static function quoteString($js) + { + return self::jsonEncode($js,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); + } + + /** + * @return Marks a string as a javascript function. Once marke, the string is considered as a + * raw javascript function that is not supposed to be encoded by {@link encode} + */ + public static function quoteJsLiteral($js) + { + if($js instanceof TJavaScriptLiteral) + return $js; + else + return new TJavaScriptLiteral($js); + } + + /** + * Deprecated, use {@link quoteJsLiteral} instead + */ + public static function quoteFunction($js) + { + return self::quoteJsLiteral($js); + } + + /** + * @return boolean true if the parameter is marked as a javascript function, i.e. if it's considered as a + * raw javascript function that is not supposed to be encoded by {@link encode} + */ + public static function isJsLiteral($js) + { + return ($js instanceof TJavaScriptLiteral); + } + + /** + * Deprecated, use {@link isJsLiteral} instead + */ + public static function isFunction($js) + { + return self::isJsLiteral($js); + } + + /** + * Encodes a PHP variable into javascript representation. + * + * Example: + * + * $options['onLoading'] = "doit"; + * $options['onComplete'] = "more"; + * echo TJavaScript::encode($options); + * //expects the following javascript code + * // {'onLoading':'doit','onComplete':'more'} + * + * + * For higher complexity data structures use {@link jsonEncode} and {@link jsonDecode} + * to serialize and unserialize. + * + * @param mixed PHP variable to be encoded + * @param boolean whether the output is a map or a list. + * @since 3.1.5 + * @param boolean wether to encode empty strings too. Default to false for BC. + * @return string the encoded string + */ + public static function encode($value,$toMap=true,$encodeEmptyStrings=false) + { + if(is_string($value)) + return self::quoteString($value); + else if(is_bool($value)) + return $value?'true':'false'; + else if(is_array($value)) + { + $results=''; + if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1)) + { + foreach($value as $k=>$v) + { + if($v!=='' || $encodeEmptyStrings) + { + if($results!=='') + $results.=','; + $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings); + } + } + return '{'.$results.'}'; + } + else + { + foreach($value as $v) + { + if($v!=='' || $encodeEmptyStrings) + { + if($results!=='') + $results.=','; + $results.=self::encode($v,$toMap, $encodeEmptyStrings); + } + } + return '['.$results.']'; + } + } + else if(is_integer($value)) + return "$value"; + else if(is_float($value)) + { + switch($value) + { + case -INF: + return 'Number.NEGATIVE_INFINITY'; + break; + case INF: + return 'Number.POSITIVE_INFINITY'; + break; + default: + $locale=localeConv(); + if($locale['decimal_point']=='.') + return "$value"; + else + return str_replace($locale['decimal_point'], '.', "$value"); + break; + } + } + else if(is_object($value)) + if ($value instanceof TJavaScriptLiteral) + return $value->toJavaScriptLiteral(); + else + return self::encode(get_object_vars($value),$toMap); + else if($value===null) + return 'null'; + else + return ''; + } + + /** + * Encodes a PHP variable into javascript string. + * This method invokes json_encode to perform the encoding. + * @param mixed variable to be encoded + * @return string encoded string + */ + public static function jsonEncode($value, $options = 0) + { + if (is_string($value) && + ($g=Prado::getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + $value=iconv($enc, 'UTF-8', $value); + $s = @json_encode($value,$options); + self::checkJsonError(); + return $s; + } + + /** + * Decodes a javascript string into PHP variable. + * This method invokes json_decode to perform the decoding. + * @param string string to be decoded + * @param bool whether to convert returned objects to associative arrays + * @param int recursion depth + * @return mixed decoded variable + */ + public static function jsonDecode($value, $assoc = false, $depth = 512) + { + $s= @json_decode($value, $assoc, $depth); + self::checkJsonError(); + return $s; + } + + private static function checkJsonError() + { + switch ($err = json_last_error()) + { + case JSON_ERROR_NONE: + return; + break; + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + break; + } + throw new Exception("JSON error ($err): $msg"); + } + + /** + * Minimize the size of a javascript script. + * This method is based on Douglas Crockford's JSMin. + * @param string code that you want to minimzie + * @return minimized version of the code + */ + public static function JSMin($code) + { + Prado::using('System.Web.Javascripts.JSMin'); + return JSMin::minify($code); + } +} + diff --git a/gui/baculum/framework/Web/Javascripts/packages.php b/gui/baculum/framework/Web/Javascripts/packages.php new file mode 100644 index 0000000000..1d98f60f70 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/packages.php @@ -0,0 +1,126 @@ + array( + PROTOTYPE_DIR.'/prototype.js', + SCRIPTACULOUS_DIR.'/builder.js', + ), + 'prado' => array( + 'prado/prado.js', + 'prado/scriptaculous-adapter.js', + 'prado/controls/controls.js', + SCRIPTACULOUS_DIR.'/effects.js' + ), + + 'effects' => array( + SCRIPTACULOUS_DIR.'/effects.js' + ), + + 'logger' => array( + 'prado/logger/logger.js', + ), + + 'validator' => array( + 'prado/validator/validation3.js' + ), + + 'datepicker' => array( + 'prado/datepicker/datepicker.js' + ), + + 'colorpicker' => array( + 'prado/colorpicker/colorpicker.js' + ), + + 'ajax' => array( + SCRIPTACULOUS_DIR.'/controls.js', + 'prado/activecontrols/json2.js', + 'prado/activecontrols/ajax3.js', + 'prado/activecontrols/activecontrols3.js', + ), + + 'dragdrop'=>array( + SCRIPTACULOUS_DIR.'/dragdrop.js', + 'prado/activecontrols/dragdrop.js' + ), + + 'dragdropextra'=>array( + 'prado/activecontrols/dragdropextra.js', + ), + + 'slider'=>array( + 'prado/controls/slider.js' + ), + + 'keyboard'=>array( + 'prado/controls/keyboard.js' + ), + + 'tabpanel'=>array( + 'prado/controls/tabpanel.js' + ), + + 'activedatepicker' => array( + 'prado/activecontrols/activedatepicker.js' + ), + + 'activefileupload' => array( + 'prado/activefileupload/activefileupload.js' + ), + + 'accordion'=>array( + 'prado/controls/accordion.js' + ), + + 'htmlarea'=>array( + 'prado/controls/htmlarea.js' + ), + + 'htmlarea4'=>array( + 'prado/controls/htmlarea4.js' + ), + + 'ratings' => array( + 'prado/ratings/ratings.js', + ), + + 'inlineeditor' => array( + 'prado/activecontrols/inlineeditor.js' + ), + +); + + +//package names and their dependencies +$dependencies = array( + 'prototype' => array('prototype'), + 'prado' => array('prototype', 'prado'), + 'effects' => array('prototype', 'prado', 'effects'), + 'validator' => array('prototype', 'prado', 'validator'), + 'logger' => array('prototype', 'prado', 'logger'), + 'datepicker' => array('prototype', 'prado', 'datepicker'), + 'colorpicker' => array('prototype', 'prado', 'colorpicker'), + 'ajax' => array('prototype', 'prado', 'effects', 'ajax'), + 'dragdrop' => array('prototype', 'prado', 'effects', 'ajax', 'dragdrop'), + 'slider' => array('prototype', 'prado', 'slider'), + 'keyboard' => array('prototype', 'prado', 'keyboard'), + 'tabpanel' => array('prototype', 'prado', 'tabpanel'), + 'activedatepicker' => array('prototype', 'prado', 'datepicker', 'ajax', 'activedatepicker'), + 'activefileupload' => array('prototype', 'prado', 'effects', 'ajax', 'activefileupload'), + 'dragdropextra' => array('prototype', 'prado', 'effects', 'ajax', 'dragdrop','dragdropextra'), + 'accordion' => array('prototype', 'prado', 'effects', 'accordion'), + 'htmlarea' => array('prototype', 'prado', 'htmlarea'), + 'htmlarea4' => array('prototype', 'prado', 'htmlarea4'), + 'ratings' => array('prototype', 'prado', 'effects', 'ajax', 'ratings'), + 'inlineeditor' => array('prototype', 'prado', 'effects', 'ajax', 'inlineeditor'), +); + +return array($packages, $dependencies); + diff --git a/gui/baculum/framework/Web/Javascripts/source/.htaccess b/gui/baculum/framework/Web/Javascripts/source/.htaccess new file mode 100644 index 0000000000..9ec9634225 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/.htaccess @@ -0,0 +1,16 @@ + + mod_gzip_on Yes + mod_gzip_dechunk Yes + mod_gzip_item_include file \.js$ + mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.* + + + + ExpiresActive On + ExpiresDefault "access plus 864000 seconds" + + + + Header set Cache-Control "max-age=864000, private" + + diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js new file mode 100644 index 0000000000..3e6fe5b762 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/activecontrols3.js @@ -0,0 +1,410 @@ +/** + * Generic postback control. + */ +Prado.WebUI.CallbackControl = Class.extend(Prado.WebUI.PostBackControl, +{ + onPostBack : function(event, options) + { + var request = new Prado.CallbackRequest(options.EventTarget, options); + request.dispatch(); + Event.stop(event); + } +}); + +/** + * TActiveButton control. + */ +Prado.WebUI.TActiveButton = Class.extend(Prado.WebUI.CallbackControl); +/** + * TActiveLinkButton control. + */ +Prado.WebUI.TActiveLinkButton = Class.extend(Prado.WebUI.CallbackControl); + +Prado.WebUI.TActiveImageButton = Class.extend(Prado.WebUI.TImageButton, +{ + onPostBack : function(event, options) + { + this.addXYInput(event,options); + var request = new Prado.CallbackRequest(options.EventTarget, options); + request.dispatch(); + Event.stop(event); + this.removeXYInput(event,options); + } +}); +/** + * Active check box. + */ +Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl, +{ + onPostBack : function(event, options) + { + var request = new Prado.CallbackRequest(options.EventTarget, options); + if(request.dispatch()==false) + Event.stop(event); + } +}); + +/** + * TActiveRadioButton control. + */ +Prado.WebUI.TActiveRadioButton = Class.extend(Prado.WebUI.TActiveCheckBox); + + +Prado.WebUI.TActiveCheckBoxList = Base.extend( +{ + constructor : function(options) + { + Prado.Registry.set(options.ListID, this); + for(var i = 0; i 0) + { + this.hasResults = true; + this.updateChoices(result); + } + else + { + this.active = false; + this.hasResults = false; + this.hide(); + } + } + } +}); + +/** + * Time Triggered Callback class. + */ +Prado.WebUI.TTimeTriggeredCallback = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = Object.extend({ Interval : 1 }, options || {}); + Prado.WebUI.TTimeTriggeredCallback.registerTimer(this); + }, + + startTimer : function() + { + if(typeof(this.timer) == 'undefined' || this.timer == null) + this.timer = this.setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); + }, + + stopTimer : function() + { + if(typeof(this.timer) != 'undefined') + { + this.clearInterval(this.timer); + this.timer = null; + } + }, + + resetTimer : function() + { + if(typeof(this.timer) != 'undefined') + { + this.clearInterval(this.timer); + this.timer = null; + this.timer = this.setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000); + } + }, + + onTimerEvent : function() + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.dispatch(); + }, + + setTimerInterval : function(value) + { + if (this.options.Interval != value){ + this.options.Interval = value; + this.resetTimer(); + } + }, + + onDone: function() + { + this.stopTimer(); + } +}); + +Object.extend(Prado.WebUI.TTimeTriggeredCallback, +{ + + //class methods + + timers : {}, + + registerTimer : function(timer) + { + Prado.WebUI.TTimeTriggeredCallback.timers[timer.options.ID] = timer; + }, + + start : function(id) + { + if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) + Prado.WebUI.TTimeTriggeredCallback.timers[id].startTimer(); + }, + + stop : function(id) + { + if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) + Prado.WebUI.TTimeTriggeredCallback.timers[id].stopTimer(); + }, + + setTimerInterval : function (id,value) + { + if(Prado.WebUI.TTimeTriggeredCallback.timers[id]) + Prado.WebUI.TTimeTriggeredCallback.timers[id].setTimerInterval(value); + } +}); + +Prado.WebUI.ActiveListControl = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + if(this.element) + { + this.options = options; + this.observe(this.element, "change", this.doCallback.bind(this)); + } + }, + + doCallback : function(event) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.dispatch(); + Event.stop(event); + } +}); + +Prado.WebUI.TActiveDropDownList = Class.create(Prado.WebUI.ActiveListControl); +Prado.WebUI.TActiveListBox = Class.create(Prado.WebUI.ActiveListControl); + +/** + * Observe event of a particular control to trigger a callback request. + */ +Prado.WebUI.TEventTriggeredCallback = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = options || {} ; + var element = $(options['ControlID']); + if(element) + this.observe(element, this.getEventName(element), this.doCallback.bind(this)); + }, + + getEventName : function(element) + { + var name = this.options.EventName; + if(typeof(name) == "undefined" && element.type) + { + switch (element.type.toLowerCase()) + { + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + return 'change'; + } + } + return typeof(name) == "undefined" || name == "undefined" ? 'click' : name; + }, + + doCallback : function(event) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.dispatch(); + if(this.options.StopEvent == true) + Event.stop(event); + } +}); + +/** + * Observe changes to a property of a particular control to trigger a callback. + */ +Prado.WebUI.TValueTriggeredCallback = Class.create(Prado.WebUI.Control, +{ + count : 1, + + observing : true, + + onInit : function(options) + { + this.options = options || {} ; + this.options.PropertyName = this.options.PropertyName || 'value'; + var element = $(options['ControlID']); + this.value = element ? element[this.options.PropertyName] : undefined; + Prado.WebUI.TValueTriggeredCallback.register(this); + this.startObserving(); + }, + + stopObserving : function() + { + this.clearTimeout(this.timer); + this.observing = false; + }, + + startObserving : function() + { + this.timer = this.setTimeout(this.checkChanges.bind(this), this.options.Interval*1000); + }, + + checkChanges : function() + { + var element = $(this.options.ControlID); + if(element) + { + var value = element[this.options.PropertyName]; + if(this.value != value) + { + this.doCallback(this.value, value); + this.value = value; + this.count=1; + } + else + this.count = this.count + this.options.Decay; + if(this.observing) + this.time = this.setTimeout(this.checkChanges.bind(this), + parseInt(this.options.Interval*1000*this.count)); + } + }, + + doCallback : function(oldValue, newValue) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + var param = {'OldValue' : oldValue, 'NewValue' : newValue}; + request.setCallbackParameter(param); + request.dispatch(); + }, + + onDone : function() + { + if (this.observing) + this.stopObserving(); + } +}); + +Object.extend(Prado.WebUI.TValueTriggeredCallback, +{ + //class methods + + timers : {}, + + register : function(timer) + { + Prado.WebUI.TValueTriggeredCallback.timers[timer.options.ID] = timer; + }, + + stop : function(id) + { + Prado.WebUI.TValueTriggeredCallback.timers[id].stopObserving(); + } +}); + +Prado.WebUI.TActiveTableCell = Class.create(Prado.WebUI.CallbackControl); +Prado.WebUI.TActiveTableRow = Class.create(Prado.WebUI.CallbackControl); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js new file mode 100755 index 0000000000..f7f63026c0 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/activedatepicker.js @@ -0,0 +1,87 @@ +/** + * TActiveDatePicker control + */ +Prado.WebUI.TActiveDatePicker = Class.create(Prado.WebUI.TDatePicker, +{ + onInit : function(options) + { + this.options = options || []; + this.control = $(options.ID); + this.dateSlot = new Array(42); + this.weekSlot = new Array(6); + this.minimalDaysInFirstWeek = 4; + this.selectedDate = this.newDate(); + this.positionMode = 'Bottom'; + + + //which element to trigger to show the calendar + if(this.options.Trigger) + { + this.trigger = $(this.options.Trigger) ; + var triggerEvent = this.options.TriggerEvent || "click"; + } + else + { + this.trigger = this.control; + var triggerEvent = this.options.TriggerEvent || "focus"; + } + + // Popup position + if(this.options.PositionMode == 'Top') + { + this.positionMode = this.options.PositionMode; + } + + Object.extend(this,options); + + if (this.options.ShowCalendar) + this.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); + + // Listen to change event + if(this.options.InputMode == "TextBox") + { + this.observe(this.control, "change", this.onDateChanged.bindEvent(this)); + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + if (day) this.observe (day, "change", this.onDateChanged.bindEvent(this)); + if (month) this.observe (month, "change", this.onDateChanged.bindEvent(this)); + if (year) this.observe (year, "change", this.onDateChanged.bindEvent(this)); + + } + + }, + + // Respond to change event on the textbox or dropdown list + // This method raises OnDateChanged event on client side if it has been defined, + // and raise the callback request + onDateChanged : function () + { + var date; + if (this.options.InputMode == "TextBox") + { + date=this.control.value; + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + if (day) day=day.selectedIndex+1; + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + if (month) month=month.selectedIndex; + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + if (year) year=year.value; + date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this); + } + if (typeof(this.options.OnDateChanged) == "function") this.options.OnDateChanged(this, date); + + if(this.options['AutoPostBack']==true) + { + // Make callback request + var request = new Prado.CallbackRequest(this.options.EventTarget,this.options); + request.dispatch(); + } + } +}); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js new file mode 100644 index 0000000000..e19f5d495d --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js @@ -0,0 +1,1175 @@ + +Prado.AjaxRequest = Class.create(); +Prado.AjaxRequest.prototype = Object.clone(Ajax.Request.prototype); + +/** + * Override Prototype's response implementation. + */ +Object.extend(Prado.AjaxRequest.prototype, +{ + /*initialize: function(request) + { + this.CallbackRequest = request; + this.transport = Ajax.getTransport(); + this.setOptions(request.options); + this.request(request.url); + },*/ + + /** + * Customize the response, dispatch onXXX response code events, and + * tries to execute response actions (javascript statements). + */ + respondToReadyState : function(readyState) + { + var event = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.getBodyDataPart(Prado.CallbackRequest.DATA_HEADER); + + if (event == 'Complete') + { + var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER); + if (redirectUrl) + document.location.href = redirectUrl; + + if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i)) + { + try + { + json = eval('(' + transport.responseText + ')'); + } + catch (e) + { + if(typeof(json) == "string") + json = Prado.CallbackRequest.decode(result); + } + } + + try + { + Prado.CallbackRequest.updatePageState(this,transport); + Prado.CallbackRequest.checkHiddenFields(this,transport); + var obj = this; + Prado.CallbackRequest.loadAssets(this,transport, function() + + { + try + { + Ajax.Responders.dispatch('on' + transport.status, obj, transport, json); + Prado.CallbackRequest.dispatchActions(transport,obj.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER)); + + ( + obj.options['on' + obj.transport.status] + || + obj.options['on' + (obj.success() ? 'Success' : 'Failure')] + || + Prototype.emptyFunction + ) (obj, json); + } + catch (e) + { + obj.dispatchException(e); + } + } + ); + } + catch (e) + { + this.dispatchException(e); + } + } + + try { + (this.options['on' + event] || Prototype.emptyFunction)(this, json); + Ajax.Responders.dispatch('on' + event, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + }, + + /** + * Gets header data assuming JSON encoding. + * @param string header name + * @return object header data as javascript structures. + */ + getHeaderData : function(name) + { + return this.getJsonData(this.getHeader(name)); + }, + + getBodyContentPart : function(name) + { + if(typeof(this.transport.responseText)=="string") + return Prado.Element.extractContent(this.transport.responseText, name); + }, + + getJsonData : function(json) + { + try + { + return eval('(' + json + ')'); + } + catch (e) + { + if(typeof(json) == "string") + return Prado.CallbackRequest.decode(json); + } + }, + + getBodyDataPart : function(name) + { + return this.getJsonData(this.getBodyContentPart(name)); + } +}); + +/** + * Prado Callback client-side request handler. + */ +Prado.CallbackRequest = Class.create(); + +/** + * Static definitions. + */ +Object.extend(Prado.CallbackRequest, +{ + /** + * Callback request target POST field name. + */ + FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET', + /** + * Callback request parameter POST field name. + */ + FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER', + /** + * Callback request page state field name, + */ + FIELD_CALLBACK_PAGESTATE : 'PRADO_PAGESTATE', + + FIELD_POSTBACK_TARGET : 'PRADO_POSTBACK_TARGET', + + FIELD_POSTBACK_PARAMETER : 'PRADO_POSTBACK_PARAMETER', + + /** + * List of form fields that will be collected during callback. + */ + PostDataLoaders : [], + /** + * Response data header name. + */ + DATA_HEADER : 'X-PRADO-DATA', + /** + * Response javascript execution statement header name. + */ + ACTION_HEADER : 'X-PRADO-ACTIONS', + /** + * Response errors/exceptions header name. + */ + ERROR_HEADER : 'X-PRADO-ERROR', + /** + * Page state header name. + */ + PAGESTATE_HEADER : 'X-PRADO-PAGESTATE', + /** + * Script list header name. + */ + SCRIPTLIST_HEADER : 'X-PRADO-SCRIPTLIST', + /** + * Stylesheet code header name. + */ + STYLESHEET_HEADER : 'X-PRADO-STYLESHEET', + /** + * Stylesheet list header name. + */ + STYLESHEETLIST_HEADER : 'X-PRADO-STYLESHEETLIST', + /** + * Hidden field list header name. + */ + HIDDENFIELDLIST_HEADER : 'X-PRADO-HIDDENFIELDLIST', + + REDIRECT_HEADER : 'X-PRADO-REDIRECT', + + requestQueue : [], + + //all request objects + requests : {}, + + getRequestById : function(id) + { + var requests = Prado.CallbackRequest.requests; + if(typeof(requests[id]) != "undefined") + return requests[id]; + }, + + dispatch : function(id) + { + var requests = Prado.CallbackRequest.requests; + if(typeof(requests[id]) != "undefined") + requests[id].dispatch(); + }, + + /** + * Add ids of inputs element to post in the request. + */ + addPostLoaders : function(ids) + { + var self = Prado.CallbackRequest; + self.PostDataLoaders = self.PostDataLoaders.concat(ids); + var list = []; + self.PostDataLoaders.each(function(id) + { + if(list.indexOf(id) < 0) + list.push(id); + }); + self.PostDataLoaders = list; + }, + + /** + * Dispatch callback response actions. + */ + dispatchActions : function(transport,actions) + { + var self = Prado.CallbackRequest; + if(actions && actions.length > 0) + actions.each(self.__run.bind(self,transport)); + }, + + /** + * Prase and evaluate a Callback clien-side action + */ + __run : function(transport, command) + { + var self = Prado.CallbackRequest; + self.transport = transport; + for(var method in command) + { + try + { + method.toFunction().apply(self,command[method]); + } + catch(e) + { + if(typeof(Logger) != "undefined") + self.Exception.onException(null,e); + else + debugger; + } + } + }, + + /** + * Respond to Prado Callback request exceptions. + */ + Exception : + { + /** + * Server returns 500 exception. Just log it. + */ + "on500" : function(request, transport, data) + { + var e = request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER); + if (e) + Logger.error("Callback Server Error "+e.code, this.formatException(e)); + else + Logger.error("Callback Server Error Unknown",''); + }, + + /** + * Callback OnComplete event,logs reponse and data to console. + */ + 'on200' : function(request, transport, data) + { + if(transport.status < 500) + { + var msg = 'HTTP '+transport.status+" with response : \n"; + if(transport.responseText.trim().length >0) + { + var f = RegExp('()([\\s\\S\\w\\W]*)()',"m"); + msg += transport.responseText.replace(f,'') + "\n"; + } + if(typeof(data)!="undefined" && data != null) + msg += "Data : \n"+inspect(data)+"\n"; + data = request.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER); + if(data && data.length > 0) + { + msg += "Actions : \n"; + data.each(function(action) + { + msg += inspect(action)+"\n"; + }); + } + Logger.info(msg); + } + }, + + /** + * Uncaught exceptions during callback response. + */ + onException : function(request,e) + { + var msg = ""; + $H(e).each(function(item) + { + msg += item.key+": "+item.value+"\n"; + }) + Logger.error('Uncaught Callback Client Exception:', msg); + }, + + /** + * Formats the exception message for display in console. + */ + formatException : function(e) + { + var msg = e.type + " with message \""+e.message+"\""; + msg += " in "+e.file+"("+e.line+")\n"; + msg += "Stack trace:\n"; + var trace = e.trace; + for(var i = 0; i"+trace[i]["function"]+"()"+"\n"; + } + msg += e.version+" "+e.time+"\n"; + return msg; + } + }, + + /** + * @return string JSON encoded data. + */ + encode : function(data) + { + return Prado.JSON.stringify(data); + }, + + /** + * @return mixed javascript data decoded from string using JSON decoding. + */ + decode : function(data) + { + if(typeof(data) == "string" && data.trim().length > 0) + return Prado.JSON.parse(data); + else + return null; + }, + + /** + * Dispatch a normal request, no timeouts or aborting of requests. + */ + dispatchNormalRequest : function(callback) + { + callback.options.postBody = callback._getPostData(), + callback.request(callback.url); + return true; + }, + + /** + * Abort the current priority request in progress. + */ + tryNextRequest : function() + { + var self = Prado.CallbackRequest; + //Logger.debug('trying next request'); + if(typeof(self.currentRequest) == 'undefined' || self.currentRequest==null) + { + if(self.requestQueue.length > 0) + return self.dispatchQueue(); + //else + //Logger.warn('empty queque'); + } + //else + //Logger.warn('current request ' + self.currentRequest.id); + }, + + /* + * Checks which scripts are used by the response and ensures they're loaded + */ + loadScripts : function(request, transport, callback) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.SCRIPTLIST_HEADER); + if (!this.ScriptsToLoad) this.ScriptsToLoad = new Array(); + this.ScriptLoadFinishedCallback = callback; + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid script list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + { + var url = json[key]; + if (!Prado.ScriptManager.isAssetLoaded(url)) + this.ScriptsToLoad.push(url); + } + } + this.loadNextScript(); + }, + + loadNextScript: function() + { + var done = (!this.ScriptsToLoad || (this.ScriptsToLoad.length==0)); + if (!done) + { + var url = this.ScriptsToLoad.shift(); var obj = this; + if ( + Prado.ScriptManager.ensureAssetIsLoaded(url, + function() { + obj.loadNextScript(); + } + ) + ) + this.loadNextScript(); + } + else + { + if (this.ScriptLoadFinishedCallback) + { + var cb = this.ScriptLoadFinishedCallback; + this.ScriptLoadFinishedCallback = null; + cb(); + } + } + }, + + loadStyleSheetsCode : function(request, transport) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.STYLESHEET_HEADER); + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid stylesheet list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + Prado.StyleSheetManager.createStyleSheetCode(json[key],null); + } + }, + + loadStyleSheetsAsync : function(request, transport) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER); + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid stylesheet list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + Prado.StyleSheetManager.ensureAssetIsLoaded(json[key],null); + } + }, + + loadStyleSheets : function(request, transport, callback) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER); + if (!this.StyleSheetsToLoad) this.StyleSheetsToLoad = new Array(); + this.StyleSheetLoadFinishedCallback = callback; + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid stylesheet list:"+data); + else + for(var key in json) + if (/^\d+$/.test(key)) + { + var url = json[key]; + if (!Prado.StyleSheetManager.isAssetLoaded(url)) + this.StyleSheetsToLoad.push(url); + } + } + this.loadNextStyleSheet(); + }, + + loadNextStyleSheet: function() + { + var done = (!this.StyleSheetsToLoad || (this.StyleSheetsToLoad.length==0)); + if (!done) + { + var url = this.StyleSheetsToLoad.shift(); var obj = this; + if ( + Prado.StyleSheetManager.ensureAssetIsLoaded(url, + function() { + obj.loadNextStyleSheet(); + } + ) + ) + this.loadNextStyleSheet(); + } + else + { + if (this.StyleSheetLoadFinishedCallback) + { + var cb = this.StyleSheetLoadFinishedCallback; + this.StyleSheetLoadFinishedCallback = null; + cb(); + } + } + }, + + /* + * Checks which assets are used by the response and ensures they're loaded + */ + loadAssets : function(request, transport, callback) + { + /* + + ! This is the callback-based loader for stylesheets, which loads them one-by-one, and + ! waits for all of them to be loaded before loading scripts and processing the rest of + ! the callback. + ! + ! That however is not neccessary, as stylesheets can be loaded asynchronously too. + ! + ! I leave this code here for the case that this turns out to be a compatibility issue + ! (for ex. I can imagine some scripts trying to access stylesheet properties and such) + ! so if need can be reactivated. If you do so, comment out the async stylesheet loader below! + + var obj = this; + this.loadStyleSheets(request,transport, function() { + obj.loadScripts(request,transport,callback); + }); + + */ + + this.loadStyleSheetsCode(request,transport); + + this.loadStyleSheetsAsync(request,transport); + + this.loadScripts(request,transport,callback); + }, + + checkHiddenField: function(name, value) + { + var id = name.replace(':','_'); + if (!document.getElementById(id)) + { + var field = document.createElement('input'); + field.setAttribute('type','hidden'); + field.id = id; + field.name = name; + field.value = value; + document.body.appendChild(field); + } + }, + + checkHiddenFields : function(request, transport) + { + var self = Prado.CallbackRequest; + var data = request.getBodyContentPart(self.HIDDENFIELDLIST_HEADER); + if (typeof(data) == "string" && data.length > 0) + { + json = Prado.CallbackRequest.decode(data); + if(typeof(json) != "object") + Logger.warn("Invalid hidden field list:"+data); + else + for(var key in json) + this.checkHiddenField(key,json[key]); + } + }, + + /** + * Updates the page state. It will update only if EnablePageStateUpdate and + * HasPriority options are both true. + */ + updatePageState : function(request, transport) + { + var self = Prado.CallbackRequest; + var pagestate = $(self.FIELD_CALLBACK_PAGESTATE); + var enabled = request.ActiveControl.EnablePageStateUpdate && request.ActiveControl.HasPriority; + var aborted = typeof(self.currentRequest) == 'undefined' || self.currentRequest == null; + if(enabled && !aborted && pagestate) + { + var data = request.getBodyContentPart(self.PAGESTATE_HEADER); + if(typeof(data) == "string" && data.length > 0) + pagestate.value = data; + else + { + if(typeof(Logger) != "undefined") + Logger.warn("Missing page state:"+data); + //Logger.warn('## bad state: setting current request to null'); + self.endCurrentRequest(); + //self.tryNextRequest(); + return false; + } + } + self.endCurrentRequest(); + //Logger.warn('## state updated: setting current request to null'); + //self.tryNextRequest(); + return true; + }, + + enqueue : function(callback) + { + var self = Prado.CallbackRequest; + self.requestQueue.push(callback); + //Logger.warn("equeued "+callback.id+", current queque length="+self.requestQueue.length); + self.tryNextRequest(); + }, + + dispatchQueue : function() + { + var self = Prado.CallbackRequest; + //Logger.warn("dispatching queque, length="+self.requestQueue.length+" request="+self.currentRequest); + var callback = self.requestQueue.shift(); + self.currentRequest = callback; + + //get data + callback.options.postBody = callback._getPostData(), + + //callback.request = new Prado.AjaxRequest(callback); + callback.timeout = setTimeout(function() + { + //Logger.warn("priority timeout"); + self.abortRequest(callback.id); + },callback.ActiveControl.RequestTimeOut); + callback.request(callback.url); + //Logger.debug("dispatched "+self.currentRequest.id + " ...") + }, + + endCurrentRequest : function() + { + var self = Prado.CallbackRequest; + if(typeof(self.currentRequest) != 'undefined' && self.currentRequest != null) + clearTimeout(self.currentRequest.timeout); + self.currentRequest=null; + }, + + abortRequest : function(id) + { + //Logger.warn("abort id="+id); + var self = Prado.CallbackRequest; + if(typeof(self.currentRequest) != 'undefined' + && self.currentRequest != null && self.currentRequest.id == id) + { + var request = self.currentRequest; + if(request.transport.readyState < 4) + request.transport.abort(); + //Logger.warn('## aborted: setting current request to null'); + self.endCurrentRequest(); + } + self.tryNextRequest(); + } +}); + +/** + * Automatically aborts the current request when a priority request has returned. + */ +Ajax.Responders.register({onComplete : function(request) +{ + if(request && request instanceof Prado.AjaxRequest) + { + if(request.ActiveControl.HasPriority) + Prado.CallbackRequest.tryNextRequest(); + } +}}); + +//Add HTTP exception respones when logger is enabled. +Event.OnLoad(function() +{ + if(typeof Logger != "undefined") + Ajax.Responders.register(Prado.CallbackRequest.Exception); +}); + +/** + * Create and prepare a new callback request. + * Call the dispatch() method to start the callback request. + * + * request = new Prado.CallbackRequest(UniqueID, callback); + * request.dispatch(); + * + */ +Prado.CallbackRequest.prototype = Object.extend(Prado.AjaxRequest.prototype, +{ + + /** + * Prepare and inititate a callback request. + */ + initialize : function(id, options) + { + /** + * Callback URL, same url as the current page. + */ + this.url = this.getCallbackUrl(); + + this.transport = Ajax.getTransport(); + this.Enabled = true; + this.id = id; + this.randomId = this.randomString(); + + if(typeof(id)=="string"){ + Prado.CallbackRequest.requests[id+"__"+this.randomId] = this; + } + + this.setOptions(Object.extend( + { + RequestTimeOut : 30000, // 30 second timeout. + EnablePageStateUpdate : true, + HasPriority : true, + CausesValidation : true, + ValidationGroup : null, + PostInputs : true + }, options || {})); + + this.ActiveControl = this.options; + Prado.CallbackRequest.requests[id+"__"+this.randomId].ActiveControl = this.options; + }, + + /** + * Sets the request options + * @return {Array} request options. + */ + setOptions: function(options){ + + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + if(Object.isString(this.options.parameters)){ + this.options.parameters = this.options.parameters.toQueryParams(); + } + }, + + /** + * Gets the url from the forms that contains the PRADO_PAGESTATE + * @return {String} callback url. + */ + getCallbackUrl : function() + { + return $('PRADO_PAGESTATE').form.action; + }, + + /** + * Sets the request parameter + * @param {Object} parameter value + */ + setCallbackParameter : function(value) + { + var requestId = this.id+"__"+this.randomId; + this.ActiveControl['CallbackParameter'] = value; + Prado.CallbackRequest.requests[requestId].ActiveControl['CallbackParameter'] = value; + }, + + /** + * @return {Object} request paramater value. + */ + getCallbackParameter : function() + { + return Prado.CallbackRequest.requests[this.id+"__"+this.randomId].ActiveControl['CallbackParameter']; + }, + + /** + * Sets the callback request timeout. + * @param {integer} timeout in milliseconds + */ + setRequestTimeOut : function(timeout) + { + this.ActiveControl['RequestTimeOut'] = timeout; + }, + + /** + * @return {integer} request timeout in milliseconds + */ + getRequestTimeOut : function() + { + return this.ActiveControl['RequestTimeOut']; + }, + + /** + * Set true to enable validation on callback dispatch. + * @param {boolean} true to validate + */ + setCausesValidation : function(validate) + { + this.ActiveControl['CausesValidation'] = validate; + }, + + /** + * @return {boolean} validate on request dispatch + */ + getCausesValidation : function() + { + return this.ActiveControl['CausesValidation']; + }, + + /** + * Sets the validation group to validate during request dispatch. + * @param {string} validation group name + */ + setValidationGroup : function(group) + { + this.ActiveControl['ValidationGroup'] = group; + }, + + /** + * @return {string} validation group name. + */ + getValidationGroup : function() + { + return this.ActiveControl['ValidationGroup']; + }, + + /** + * Dispatch the callback request. + */ + dispatch : function() + { + //Logger.info("dispatching request"); + //trigger tinyMCE to save data. + if(typeof tinyMCE != "undefined") + tinyMCE.triggerSave(); + + if(this.ActiveControl.CausesValidation && typeof(Prado.Validation) != "undefined") + { + var form = this.ActiveControl.Form || Prado.Validation.getForm(); + if(Prado.Validation.validate(form,this.ActiveControl.ValidationGroup,this) == false) + return false; + } + + if(this.ActiveControl.onPreDispatch) + this.ActiveControl.onPreDispatch(this,null); + + if(!this.Enabled) + return; + + // Opera don't have onLoading/onLoaded state, so, simulate them just + // before sending the request. + if (Prototype.Browser.Opera) + { + if (this.ActiveControl.onLoading) + { + this.ActiveControl.onLoading(this,null); + Ajax.Responders.dispatch('onLoading',this, this.transport,null); + } + if (this.ActiveControl.onLoaded) + { + this.ActiveControl.onLoaded(this,null); + Ajax.Responders.dispatch('onLoaded',this, this.transport,null); + } + } + + var result; + if(this.ActiveControl.HasPriority) + { + return Prado.CallbackRequest.enqueue(this); + //return Prado.CallbackRequest.dispatchPriorityRequest(this); + } + else + return Prado.CallbackRequest.dispatchNormalRequest(this); + }, + + abort : function() + { + return Prado.CallbackRequest.abortRequest(this.id); + }, + + /** + * Collects the form inputs, encode the parameters, and sets the callback + * target id. The resulting string is the request content body. + * @return string request body content containing post data. + */ + _getPostData : function() + { + var data = {}; + var callback = Prado.CallbackRequest; + if(this.ActiveControl.PostInputs != false) + { + callback.PostDataLoaders.each(function(name) + { + var elements=$A(document.getElementsByName(name)); + if(elements.size() == 0) + { + name += '[]'; + elements=$A(document.getElementsByName(name)); + } + elements.each(function(element) + { + //IE will try to get elements with ID == name as well. + if(element.type && element.name == name) + { + var value = $F(element); + if(typeof(value) != "undefined" && value != null) + data[name] = value; + } + }) + }) + } + if(typeof(this.ActiveControl.CallbackParameter) != "undefined") + data[callback.FIELD_CALLBACK_PARAMETER] = callback.encode(this.getCallbackParameter()); + var pageState = $F(callback.FIELD_CALLBACK_PAGESTATE); + if(typeof(pageState) != "undefined") + data[callback.FIELD_CALLBACK_PAGESTATE] = pageState; + data[callback.FIELD_CALLBACK_TARGET] = this.id; + if(this.ActiveControl.EventTarget) + data[callback.FIELD_POSTBACK_TARGET] = this.ActiveControl.EventTarget; + if(this.ActiveControl.EventParameter) + data[callback.FIELD_POSTBACK_PARAMETER] = this.ActiveControl.EventParameter; + return $H(data).toQueryString(); + }, + + /** + * Creates a random string with a length of 8 chars. + * @return string + */ + randomString : function() + { + chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + randomString = ""; + for(x=0;x<8;x++) + randomString += chars.charAt(Math.floor(Math.random() * 62)); + return randomString + } +}); + +/** + * Create a new callback request using default settings. + * @param string callback handler unique ID. + * @param mixed parameter to pass to callback handler on the server side. + * @param function client side onSuccess event handler. + * @param object additional request options. + * @return boolean always false. + */ +Prado.Callback = function(UniqueID, parameter, onSuccess, options) +{ + var callback = + { + 'CallbackParameter' : parameter || '', + 'onSuccess' : onSuccess || Prototype.emptyFunction + }; + + Object.extend(callback, options || {}); + + var request = new Prado.CallbackRequest(UniqueID, callback); + request.dispatch(); + return false; +}; + + + +/** + * Asset manager classes for lazy loading of scripts and stylesheets + * @author Gabor Berczi (gabor.berczi@devworx.hu) + */ + +if (typeof(Prado.AssetManagerClass)=="undefined") { + + Prado.AssetManagerClass = Class.create(); + Prado.AssetManagerClass.prototype = { + + initialize: function() { + this.loadedAssets = new Array(); + this.discoverLoadedAssets(); + }, + + + /** + * Detect which assets are already loaded by page markup. + * This is done by looking up all elements and registering the values of their src attributes. + */ + discoverLoadedAssets: function() { + + // wait until document has finished loading to avoid javascript errors + if (!document.body) return; + + var assets = this.findAssetUrlsInMarkup(); + for(var i=0;i element in page header + var asset = this.createAssetElement(url); + + if (callback) + { + asset.onreadystatechange = this.assetReadyStateChanged.bind(this, url, asset, callback, false); + asset.onload = this.assetReadyStateChanged.bind(this, url, asset, callback, true); + asset.onerror = this.assetLoadFailed.bind(this, url, asset, callback); + asset.assetCallbackFired = false; + } + + var head = document.getElementsByTagName('head')[0]; + head.appendChild(asset); + + // mark this asset as loaded + this.markAssetAsLoaded(url); + + return (callback!=false); + }, + + /** + * Check whether a asset is loaded into the page, and if itsn't, load it now + * @param string url of the asset to check/load + * @return boolean returns true if asset is already loaded, or false, if loading has just started. callback will be called when loading has finished. + */ + ensureAssetIsLoaded: function(url, callback) { + url = this.makeFullUrl(url); + if (this.loadedAssets.indexOf(url)==-1) + { + this.startAssetLoad(url,callback); + return false; + } + else + return true; + } + + } + +}; + + Prado.ScriptManagerClass = Class.extend(Prado.AssetManagerClass, { + + findAssetUrlsInMarkup: function() { + var urls = new Array(); + var scripts = document.getElementsByTagName('script'); + for(var i=0;i0)) + urls.push(href); + } + return urls; + }, + + createAssetElement: function(url) { + var asset = document.createElement('link'); + asset.rel = 'stylesheet'; + asset.media = 'screen'; + asset.setAttribute('type', 'text/css'); + asset.href = url; +// asset.async = false; // HTML5 only + return asset; + }, + + createStyleSheetCode: function(code) { + var asset = document.createElement('style'); + asset.setAttribute('type', 'text/css'); + asset.innerText = code; + + var head = document.getElementsByTagName('head')[0]; + head.appendChild(asset); + } + + }); + + if (typeof(Prado.ScriptManager)=="undefined") Prado.ScriptManager = new Prado.ScriptManagerClass(); + if (typeof(Prado.StyleSheetManager)=="undefined") Prado.StyleSheetManager = new Prado.StyleSheetManagerClass(); + + // make sure we scan for loaded scripts again when the page has been loaded + var discover = function() { + Prado.ScriptManager.discoverLoadedAssets(); + Prado.StyleSheetManager.discoverLoadedAssets(); + } + if (window.attachEvent) window.attachEvent('onload', discover); + else if (window.addEventListener) window.addEventListener('load', discover, false); + diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js new file mode 100755 index 0000000000..511c2ab901 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/dragdrop.js @@ -0,0 +1,61 @@ +/** + * DropContainer control + */ + +Prado.WebUI.DropContainer = Class.create(Prado.WebUI.CallbackControl, +{ + onInit: function(options) + { + this.options = options; + Object.extend (this.options, + { + onDrop: this.onDrop.bind(this) + }); + + Droppables.add (options.ID, this.options); + }, + + onDrop: function(dragElement, dropElement, event) + { + var elementId=dragElement.id.replace(/clone_/,""); + var req = new Prado.CallbackRequest(this.options.EventTarget, this.options); + var curleft = curtop = 0; + var obj = dropElement; + if (obj.offsetParent) { + curleft = obj.offsetLeft + curtop = obj.offsetTop + while (obj = obj.offsetParent) { + curleft += obj.offsetLeft + curtop += obj.offsetTop + } + } + var scrOfX = 0, scrOfY = 0; + if( typeof( window.pageYOffset ) == 'number' ) { + //Netscape compliant + scrOfY = window.pageYOffset; + scrOfX = window.pageXOffset; + } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) { + //DOM compliant + scrOfY = document.body.scrollTop; + scrOfX = document.body.scrollLeft; + } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) { + //IE6 standards compliant mode + scrOfY = document.documentElement.scrollTop; + scrOfX = document.documentElement.scrollLeft; + } + req.setCallbackParameter({ + DragElementID : elementId, + ScreenX : event.screenX, + ScreenY : event.screenY, + OffsetX : event.offsetX || event.clientX - curleft + scrOfX, + OffsetY : event.offsetY || event.clientY - curtop + scrOfY, + ClientX : event.clientX, + ClientY : event.clientY, + AltKey : event.altKey, + CtrlKey : event.ctrlKey, + ShiftKey : event.shiftKey + }); + req.dispatch(); + + } +}); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/dragdropextra.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/dragdropextra.js new file mode 100644 index 0000000000..1cec6f93e7 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/dragdropextra.js @@ -0,0 +1,233 @@ +// DragDropExtra Scriptaculous Enhancement, version 0.5 +// (c) 2007-2008 Christopher Williams, Iterative Designs +// +// v0.5 release +// - Fixed bug where 2nd drag on an element in IE would result in funny placement of the +// element. [shammond42] +// v0.4 release +// - Fixed issue with dragging and dropping in IE7 due to an exception being thrown and not properly reseting in FinishDrag. +// v0.3 release +// - Fixed bug found by Phillip Sauerbeck psauerbeck@gmail. Tests added based on Phillip's efforts. +// v0.2 release +// - Minor bug fix for the releasing of objects after they have been dropped, prevents memory leak. +// v0.1 release +// - initial release for the super ghosting capability +// - Drags from one scrolling list to the other (overflow:auto) +// - Retains the original object so that it can remain present despite being dragged +// +// dragdropextra.js is freely distributable under the terms of an MIT-style license. +// For details, see the Iterative Designs web site: http://www.iterativedesigns.com/ +// Parts of this code have been taken from the original dragdrop.js library which is +// copyrighted by (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, +// http://mir.aculo.us) and (c) 2005-2007 Sammi Williams +// (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) and available under +// a MIT-style license. + +Draggable.prototype.startDrag = function(event) { + this.dragging = true; + if(!this.delta) + this.delta = this.currentDelta(); + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this.element._originallyAbsolute) + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + if(this.options.superghosting) { + Position.prepare(); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + body = document.getElementsByTagName("body")[0]; + me = this.element; + this._clone = me.cloneNode(true); + if (Prototype.Browser.IE) { + // Clear event handing from the clone + // Solves the second drag issue in IE + this._clone.clearAttributes(); + this._clone.mergeAttributes(me.cloneNode(false)); + } + me.parentNode.insertBefore(this._clone, me); + me.id = "clone_"+me.id; + me.hide(); + + Position.absolutize(me); + me.parentNode.removeChild(me); + body.appendChild(me); + //Retain height and width of object only if it has been nulled out. -v0.3 Fix + if (me.style.width == "0px" || me.style.height == "0px") { + me.style.width=Element.getWidth(this._clone)+"px"; + me.style.height=Element.getHeight(this._clone)+"px"; + } + + //overloading in order to reduce repeated code weight. + this.originalScrollTop = (Element.getHeight(this._clone)/2); + + this.draw(pointer); + me.show(); + } + + if(this.options.scroll) { + if (this.options.scroll == window) { + var where = this._getWindowScroll(this.options.scroll); + this.originalScrollLeft = where.left; + this.originalScrollTop = where.top; + } else { + this.originalScrollLeft = this.options.scroll.scrollLeft; + this.originalScrollTop = this.options.scroll.scrollTop; + } + } + + Draggables.notify('onStart', this, event); + + if(this.options.starteffect) this.options.starteffect(this.element); +} + + + + +Draggable.prototype.draw = function(point) { + var pos = Position.cumulativeOffset(this.element); + if(this.options.ghosting) { + var r = Position.realOffset(this.element); + pos[0] += r[0] - Position.deltaX; + pos[1] += r[1] - Position.deltaY; + } + + var d = this.currentDelta(); + pos[0] -= d[0]; + pos[1] -= d[1]; + + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; + } + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) + }.bind(this)); + + if(this.options.snap) { + if(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)) + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*this.options.snap }.bind(this)) + } + }} + + if (this.options.superghosting) { + p[1] = point[1] - this.originalScrollTop; + } + + + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering +} + +Draggable.prototype.initDrag = function(event) { + if(!Object.isUndefined(Draggable._dragging[this.element]) && + Draggable._dragging[this.element]) return; + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if((tag_name = src.tagName.toUpperCase()) && ( + tag_name=='INPUT' || + tag_name=='SELECT' || + tag_name=='OPTION' || + tag_name=='BUTTON' || + tag_name=='TEXTAREA')) return; + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = Position.cumulativeOffset(this.element); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } +} + +Droppables.isAffected = function(point, element, drop) { + Position.prepare(); + positioned_within = Position.withinIncludingScrolloffsets(drop.element, point[0], point[1]) + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && positioned_within ); + + +} + +Draggable.prototype.finishDrag = function(event, success) { + this.dragging = false; + + if(this.options.quiet){ + Position.prepare(); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + Droppables.show(pointer, this.element); + } + + if(this.options.ghosting) { + if (!this.element._originallyAbsolute) + Position.relativize(this.element); + delete this.element._originallyAbsolute; + Element.remove(this._clone); + this._clone = null; + } + + var dropped = false; + if(success) { + dropped = Droppables.fire(event, this.element); + if (!dropped) dropped = false; + } + if(dropped && this.options.onDropped) this.options.onDropped(this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && Object.isFunction(revert)) revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + if (dropped == 0 || revert != 'failure') + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + + if(this.options.superghosting) { + body = document.getElementsByTagName("body")[0]; + Element.remove(this.element); + new Draggable(this._clone, this.options); + } + + + Draggables.deactivate(this); + Droppables.reset(); +} diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js new file mode 100644 index 0000000000..0260c21904 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/inlineeditor.js @@ -0,0 +1,302 @@ +Prado.WebUI.TInPlaceTextBox = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + + this.isSaving = false; + this.isEditing = false; + this.editField = null; + this.readOnly = options.ReadOnly; + + this.options = Object.extend( + { + LoadTextFromSource : false, + TextMode : 'SingleLine' + + }, options || {}); + this.element = $(this.options.ID); + Prado.WebUI.TInPlaceTextBox.register(this); + this.createEditorInput(); + this.initializeListeners(); + }, + + /** + * Initialize the listeners. + */ + initializeListeners : function() + { + this.onclickListener = this.enterEditMode.bindAsEventListener(this); + this.observe(this.element, 'click', this.onclickListener); + if (this.options.ExternalControl) + this.observe($(this.options.ExternalControl), 'click', this.onclickListener); + }, + + /** + * Changes the panel to an editable input. + * @param {Event} evt event source + */ + enterEditMode : function(evt) + { + if (this.isSaving || this.isEditing || this.readOnly) return; + this.isEditing = true; + this.onEnterEditMode(); + this.createEditorInput(); + this.showTextBox(); + this.editField.disabled = false; + if(this.options.LoadTextOnEdit) + this.loadExternalText(); + Prado.Element.focus(this.editField); + if (evt) + Event.stop(evt); + return false; + }, + + exitEditMode : function(evt) + { + this.isEditing = false; + this.isSaving = false; + this.editField.disabled = false; + this.element.innerHTML = this.editField.value; + this.showLabel(); + }, + + showTextBox : function() + { + Element.hide(this.element); + Element.show(this.editField); + }, + + showLabel : function() + { + Element.show(this.element); + Element.hide(this.editField); + }, + + /** + * Create the edit input field. + */ + createEditorInput : function() + { + if(this.editField == null) + this.createTextBox(); + + this.editField.value = this.getText(); + }, + + loadExternalText : function() + { + this.editField.disabled = true; + this.onLoadingText(); + var options = new Array('__InlineEditor_loadExternalText__', this.getText()); + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.setCausesValidation(false); + request.setCallbackParameter(options); + request.ActiveControl.onSuccess = this.onloadExternalTextSuccess.bind(this); + request.ActiveControl.onFailure = this.onloadExternalTextFailure.bind(this); + request.dispatch(); + }, + + /** + * Create a new input textbox or textarea + */ + createTextBox : function() + { + var cssClass= this.element.className || ''; + var inputName = this.options.EventTarget; + var options = {'className' : cssClass, name : inputName, id : this.options.TextBoxID}; + if(this.options.TextMode == 'SingleLine') + { + if(this.options.MaxLength > 0) + options['maxlength'] = this.options.MaxLength; + if(this.options.Columns > 0) + options['size'] = this.options.Columns; + this.editField = INPUT(options); + } + else + { + if(this.options.Rows > 0) + options['rows'] = this.options.Rows; + if(this.options.Columns > 0) + options['cols'] = this.options.Columns; + if(this.options.Wrap) + options['wrap'] = 'off'; + this.editField = TEXTAREA(options); + } + + this.editField.style.display="none"; + this.element.parentNode.insertBefore(this.editField,this.element) + + //handle return key within single line textbox + if(this.options.TextMode == 'SingleLine') + { + this.observe(this.editField, "keydown", function(e) + { + if(Event.keyCode(e) == Event.KEY_RETURN) + { + var target = Event.element(e); + if(target) + { + Event.fireEvent(target, "blur"); + Event.stop(e); + } + } + }); + } + + this.observe(this.editField, "blur", this.onTextBoxBlur.bind(this)); + this.observe(this.editField, "keypress", this.onKeyPressed.bind(this)); + }, + + /** + * @return {String} panel inner html text. + */ + getText: function() + { + return this.element.innerHTML; + }, + + /** + * Edit mode entered, calls optional event handlers. + */ + onEnterEditMode : function() + { + if(typeof(this.options.onEnterEditMode) == "function") + this.options.onEnterEditMode(this,null); + }, + + onTextBoxBlur : function(e) + { + var text = this.element.innerHTML; + if(this.options.AutoPostBack && text != this.editField.value) + { + if(this.isEditing) + this.onTextChanged(text); + } + else + { + this.element.innerHTML = this.editField.value; + this.isEditing = false; + if(this.options.AutoHide) + this.showLabel(); + } + }, + + onKeyPressed : function(e) + { + if (Event.keyCode(e) == Event.KEY_ESC) + { + this.editField.value = this.getText(); + this.isEditing = false; + if(this.options.AutoHide) + this.showLabel(); + } + else if (Event.keyCode(e) == Event.KEY_RETURN && this.options.TextMode != 'MultiLine') + Event.stop(e); + }, + + /** + * When the text input value has changed. + * @param {String} original text + */ + onTextChanged : function(text) + { + var request = new Prado.CallbackRequest(this.options.EventTarget, this.options); + request.setCallbackParameter(text); + request.ActiveControl.onSuccess = this.onTextChangedSuccess.bind(this); + request.ActiveControl.onFailure = this.onTextChangedFailure.bind(this); + if(request.dispatch()) + { + this.isSaving = true; + this.editField.disabled = true; + } + }, + + /** + * When loading external text. + */ + onLoadingText : function() + { + //Logger.info("on loading text"); + }, + + onloadExternalTextSuccess : function(request, parameter) + { + this.isEditing = true; + this.editField.disabled = false; + this.editField.value = this.getText(); + Prado.Element.focus(this.editField); + if(typeof(this.options.onSuccess)=="function") + this.options.onSuccess(sender,parameter); + }, + + onloadExternalTextFailure : function(request, parameter) + { + this.isSaving = false; + this.isEditing = false; + this.showLabel(); + if(typeof(this.options.onFailure)=="function") + this.options.onFailure(sender,parameter); + }, + + /** + * Text change successfully. + * @param {Object} sender + * @param {Object} parameter + */ + onTextChangedSuccess : function(sender, parameter) + { + this.isSaving = false; + this.isEditing = false; + if(this.options.AutoHide) + this.showLabel(); + this.element.innerHTML = parameter == null ? this.editField.value : parameter; + this.editField.disabled = false; + if(typeof(this.options.onSuccess)=="function") + this.options.onSuccess(sender,parameter); + }, + + onTextChangedFailure : function(sender, parameter) + { + this.editField.disabled = false; + this.isSaving = false; + this.isEditing = false; + if(typeof(this.options.onFailure)=="function") + this.options.onFailure(sender,parameter); + } +}); + + +Object.extend(Prado.WebUI.TInPlaceTextBox, +{ + //class methods + + textboxes : {}, + + register : function(obj) + { + Prado.WebUI.TInPlaceTextBox.textboxes[obj.options.TextBoxID] = obj; + }, + + setDisplayTextBox : function(id,value) + { + var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id]; + if(textbox) + { + if(value) + textbox.enterEditMode(null); + else + { + textbox.exitEditMode(null); + } + } + }, + + setReadOnly : function(id, value) + { + var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id]; + if (textbox) + { + textbox.readOnly=value; + } + } +}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/json2.js b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/json2.js new file mode 100644 index 0000000000..3eec6f205d --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activecontrols/json2.js @@ -0,0 +1,479 @@ +/* + http://www.JSON.org/json2.js + 2011-02-23 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!Prado.JSON) { + Prado.JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : gap ? + '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : gap ? + '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : + '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof Prado.JSON.stringify !== 'function') { + Prado.JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof Prado.JSON.parse !== 'function') { + Prado.JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html new file mode 100755 index 0000000000..44f50ce4de --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadBlank.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png new file mode 100755 index 0000000000..98badd7fce Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadComplete.png differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png new file mode 100755 index 0000000000..26c529fc80 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadError.png differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif new file mode 100755 index 0000000000..085ccaecaf Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/ActiveFileUploadIndicator.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js new file mode 100755 index 0000000000..5f20944e09 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/activefileupload/activefileupload.js @@ -0,0 +1,104 @@ +Prado.WebUI.TActiveFileUpload = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = options || {}; + Prado.WebUI.TActiveFileUpload.register(this); + + this.input = $(options.inputID); + this.flag = $(options.flagID); + this.form = $(options.formID); + + this.indicator = $(options.indicatorID); + this.complete = $(options.completeID); + this.error = $(options.errorID); + + // set up events + if (options.autoPostBack){ + this.observe(this.input,"change",this.fileChanged.bind(this)); + } + }, + + fileChanged : function(){ + // show the upload indicator, and hide the complete and error indicators (if they areSn't already). + this.flag.value = '1'; + this.complete.style.display = 'none'; + this.error.style.display = 'none'; + this.indicator.style.display = ''; + + // set the form to submit in the iframe, submit it, and then reset it. + this.oldtargetID = this.form.target; + this.oldFormAction = this.form.action; + this.oldFormMethod = this.form.method; + this.oldFormEnctype = this.form.enctype; + + this.form.action += (this.form.action.indexOf('?')!=-1 ? '&' : '?')+'TActiveFileUpload_InputId='+this.options.inputID+'&TActiveFileUpload_TargetId='+this.options.targetID; + this.form.target = this.options.targetID; + this.form.method = 'POST'; + this.form.enctype = 'multipart/form-data'; + this.form.submit(); + + this.form.action = this.oldFormAction; + this.form.target = this.oldtargetID; + this.form.method = this.oldFormMethod; + this.form.enctype = this.oldFormEnctype; + }, + + finishUpload : function(options){ + + if (this.options.targetID == options.targetID) + { + this.finishoptions = options; + var e = this; + var callback = + { + 'CallbackParameter' : options || '', + 'onSuccess' : function() { e.finishCallBack(true); }, + 'onFailure' : function() { e.finishCallBack(false); } + }; + + Object.extend(callback, this.options); + + var request = new Prado.CallbackRequest(this.options.EventTarget, callback); + request.dispatch(); + } + else + this.finishCallBack(true); + + }, + + finishCallBack : function(success){ + // hide the display indicator. + this.flag.value = ''; + this.indicator.style.display = 'none'; + // show the complete indicator. + if ((this.finishoptions.errorCode == 0) && (success)) { + this.complete.style.display = ''; + this.input.value = ''; + } else { + this.error.style.display = ''; + } + } + +}); + +Object.extend(Prado.WebUI.TActiveFileUpload, +{ + //class methods + + controls : {}, + + register : function(control) + { + Prado.WebUI.TActiveFileUpload.controls[control.options.ID] = control; + }, + + onFileUpload : function(options) + { + Prado.WebUI.TActiveFileUpload.controls[options.clientID].finishUpload(options); + }, + + fileChanged : function(controlID){ + Prado.WebUI.TActiveFileUpload.controls[controlID].fileChanged(); + } +}); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/background.png b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/background.png new file mode 100644 index 0000000000..91798cf54a Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/background.png differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/button.gif b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/button.gif new file mode 100644 index 0000000000..67f179751e Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/button.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js new file mode 100644 index 0000000000..142745cf1b --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/colorpicker.js @@ -0,0 +1,780 @@ +//-------------------- ricoColor.js +if(typeof(Rico) == "undefined") Rico = {}; + +Rico.Color = Class.create(); + +Rico.Color.prototype = { + + initialize: function(red, green, blue) { + this.rgb = { r: red, g : green, b : blue }; + }, + + setRed: function(r) { + this.rgb.r = r; + }, + + setGreen: function(g) { + this.rgb.g = g; + }, + + setBlue: function(b) { + this.rgb.b = b; + }, + + setHue: function(h) { + + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.h = h; + + // convert back to RGB... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setSaturation: function(s) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.s = s; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setBrightness: function(b) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.b = b; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); + }, + + darken: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); + }, + + brighten: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); + }, + + blend: function(other) { + this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); + this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); + this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); + }, + + isBright: function() { + var hsb = this.asHSB(); + return this.asHSB().b > 0.5; + }, + + isDark: function() { + return ! this.isBright(); + }, + + asRGB: function() { + return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; + }, + + asHex: function() { + return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); + }, + + asHSB: function() { + return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); + }, + + toString: function() { + return this.asHex(); + } + +}; + +Rico.Color.createFromHex = function(hexCode) { + + if ( hexCode.indexOf('#') == 0 ) + hexCode = hexCode.substring(1); + + var red = "ff", green = "ff", blue="ff"; + if(hexCode.length > 4) + { + red = hexCode.substring(0,2); + green = hexCode.substring(2,4); + blue = hexCode.substring(4,6); + } + else if(hexCode.length > 0 & hexCode.length < 4) + { + var r = hexCode.substring(0,1); + var g = hexCode.substring(1,2); + var b = hexCode.substring(2); + red = r+r; + green = g+g; + blue = b+b; + } + return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); +}; + +/** + * Factory method for creating a color from the background of + * an HTML element. + */ +Rico.Color.createColorFromBackground = function(elem) { + + var actualColor = Element.getStyle($(elem), "background-color"); + if ( actualColor == "transparent" && elem.parent ) + return Rico.Color.createColorFromBackground(elem.parent); + + if ( actualColor == null ) + return new Rico.Color(255,255,255); + + if ( actualColor.indexOf("rgb(") == 0 ) { + var colors = actualColor.substring(4, actualColor.length - 1 ); + var colorArray = colors.split(","); + return new Rico.Color( parseInt( colorArray[0] ), + parseInt( colorArray[1] ), + parseInt( colorArray[2] ) ); + + } + else if ( actualColor.indexOf("#") == 0 ) { + return Rico.Color.createFromHex(actualColor); + } + else + return new Rico.Color(255,255,255); +}; + +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { + + var red = 0; + var green = 0; + var blue = 0; + + if (saturation == 0) { + red = parseInt(brightness * 255.0 + 0.5); + green = red; + blue = red; + } + else { + var h = (hue - Math.floor(hue)) * 6.0; + var f = h - Math.floor(h); + var p = brightness * (1.0 - saturation); + var q = brightness * (1.0 - saturation * f); + var t = brightness * (1.0 - (saturation * (1.0 - f))); + + switch (parseInt(h)) { + case 0: + red = (brightness * 255.0 + 0.5); + green = (t * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 1: + red = (q * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 2: + red = (p * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (t * 255.0 + 0.5); + break; + case 3: + red = (p * 255.0 + 0.5); + green = (q * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 4: + red = (t * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 5: + red = (brightness * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (q * 255.0 + 0.5); + break; + } + } + + return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; +}; + +Rico.Color.RGBtoHSB = function(r, g, b) { + + var hue; + var saturation; + var brightness; + + var cmax = (r > g) ? r : g; + if (b > cmax) + cmax = b; + + var cmin = (r < g) ? r : g; + if (b < cmin) + cmin = b; + + brightness = cmax / 255.0; + if (cmax != 0) + saturation = (cmax - cmin)/cmax; + else + saturation = 0; + + if (saturation == 0) + hue = 0; + else { + var redc = (cmax - r)/(cmax - cmin); + var greenc = (cmax - g)/(cmax - cmin); + var bluec = (cmax - b)/(cmax - cmin); + + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + + hue = hue / 6.0; + if (hue < 0) + hue = hue + 1.0; + } + + return { h : hue, s : saturation, b : brightness }; +}; + + +Prado.WebUI.TColorPicker = Class.create(); + +Object.extend(Prado.WebUI.TColorPicker, +{ + palettes: + { + Small : [["fff", "fcc", "fc9", "ff9", "ffc", "9f9", "9ff", "cff", "ccf", "fcf"], + ["ccc", "f66", "f96", "ff6", "ff3", "6f9", "3ff", "6ff", "99f", "f9f"], + ["c0c0c0", "f00", "f90", "fc6", "ff0", "3f3", "6cc", "3cf", "66c", "c6c"], + ["999", "c00", "f60", "fc3", "fc0", "3c0", "0cc", "36f", "63f", "c3c"], + ["666", "900", "c60", "c93", "990", "090", "399", "33f", "60c", "939"], + ["333", "600", "930", "963", "660", "060", "366", "009", "339", "636"], + ["000", "300", "630", "633", "330", "030", "033", "006", "309", "303"]], + + Tiny : [["ffffff"/*white*/, "00ff00"/*lime*/, "008000"/*green*/, "0000ff"/*blue*/], + ["c0c0c0"/*silver*/, "ffff00"/*yellow*/, "ff00ff"/*fuchsia*/, "000080"/*navy*/], + ["808080"/*gray*/, "ff0000"/*red*/, "800080"/*purple*/, "000000"/*black*/]] + }, + + UIImages : + { + 'button.gif' : 'button.gif', +// 'target_black.gif' : 'target_black.gif', +// 'target_white.gif' : 'target_white.gif', + 'background.png' : 'background.png' +// 'slider.gif' : 'slider.gif', +// 'hue.gif' : 'hue.gif' + } +}); + +Object.extend(Prado.WebUI.TColorPicker.prototype, +{ + initialize : function(options) + { + var basics = + { + Palette : 'Small', + ClassName : 'TColorPicker', + Mode : 'Basic', + OKButtonText : 'OK', + CancelButtonText : 'Cancel', + ShowColorPicker : true + } + + this.element = null; + this.showing = false; + + options = Object.extend(basics, options); + this.options = options; + this.input = $(options['ID']); + this.button = $(options['ID']+'_button'); + this._buttonOnClick = this.buttonOnClick.bind(this); + if(options['ShowColorPicker']) + Event.observe(this.button, "click", this._buttonOnClick); + Event.observe(this.input, "change", this.updatePicker.bind(this)); + + Prado.Registry.set(options.ID, this); + }, + + updatePicker : function(e) + { + var color = Rico.Color.createFromHex(this.input.value); + this.button.style.backgroundColor = color.toString(); + }, + + buttonOnClick : function(event) + { + var mode = this.options['Mode']; + if(this.element == null) + { + var constructor = mode == "Basic" ? "getBasicPickerContainer": "getFullPickerContainer" + this.element = this[constructor](this.options['ID'], this.options['Palette']) + this.input.parentNode.appendChild(this.element); + this.element.style.display = "none"; + + if(Prado.Browser().ie) + { + this.iePopUp = document.createElement('iframe'); + this.iePopUp.src = Prado.WebUI.TColorPicker.UIImages['button.gif']; + this.iePopUp.style.position = "absolute" + this.iePopUp.scrolling="no" + this.iePopUp.frameBorder="0" + this.input.parentNode.appendChild(this.iePopUp); + } + if(mode == "Full") + this.initializeFullPicker(); + } + this.show(mode); + }, + + show : function(type) + { + if(!this.showing) + { + var pos = this.input.positionedOffset(); + pos[1] += this.input.offsetHeight; + + this.element.style.top = (pos[1]-1) + "px"; + this.element.style.left = pos[0] + "px"; + this.element.style.display = "block"; + + this.ieHack(type); + + //observe for clicks on the document body + this._documentClickEvent = this.hideOnClick.bindEvent(this, type); + this._documentKeyDownEvent = this.keyPressed.bindEvent(this, type); + Event.observe(document.body, "click", this._documentClickEvent); + Event.observe(document,"keydown", this._documentKeyDownEvent); + this.showing = true; + + if(type == "Full") + { + this.observeMouseMovement(); + var color = Rico.Color.createFromHex(this.input.value); + this.inputs.oldColor.style.backgroundColor = color.asHex(); + this.setColor(color,true); + } + } + }, + + hide : function(event) + { + if(this.showing) + { + if(this.iePopUp) + this.iePopUp.style.display = "none"; + + this.element.style.display = "none"; + this.showing = false; + Event.stopObserving(document.body, "click", this._documentClickEvent); + Event.stopObserving(document,"keydown", this._documentKeyDownEvent); + + if(this._observingMouseMove) + { + Event.stopObserving(document.body, "mousemove", this._onMouseMove); + this._observingMouseMove = false; + } + } + }, + + keyPressed : function(event,type) + { + if(Event.keyCode(event) == Event.KEY_ESC) + this.hide(event,type); + }, + + hideOnClick : function(ev) + { + if(!this.showing) return; + var el = Event.element(ev); + var within = false; + do + { within = within || String(el.className).indexOf('FullColorPicker') > -1 + within = within || el == this.button; + within = within || el == this.input; + if(within) break; + el = el.parentNode; + } + while(el); + if(!within) this.hide(ev); + }, + + ieHack : function() + { + // IE hack + if(this.iePopUp) + { + this.iePopUp.style.display = "block"; + this.iePopUp.style.top = (this.element.offsetTop) + "px"; + this.iePopUp.style.left = (this.element.offsetLeft)+ "px"; + this.iePopUp.style.width = Math.abs(this.element.offsetWidth)+ "px"; + this.iePopUp.style.height = (this.element.offsetHeight + 1)+ "px"; + } + }, + + getBasicPickerContainer : function(pickerID, palette) + { + var table = TABLE({className:'basic_colors palette_'+palette},TBODY()); + var colors = Prado.WebUI.TColorPicker.palettes[palette]; + var pickerOnClick = this.cellOnClick.bind(this); + colors.each(function(color) + { + var row = document.createElement("tr"); + color.each(function(c) + { + var td = document.createElement("td"); + var img = IMG({src:Prado.WebUI.TColorPicker.UIImages['button.gif'],width:16,height:16}); + img.style.backgroundColor = "#"+c; + Event.observe(img,"click", pickerOnClick); + Event.observe(img,"mouseover", function(e) + { + Element.addClassName(Event.element(e), "pickerhover"); + }); + Event.observe(img,"mouseout", function(e) + { + Element.removeClassName(Event.element(e), "pickerhover"); + }); + td.appendChild(img); + row.appendChild(td); + }); + table.childNodes[0].appendChild(row); + }); + return DIV({className:this.options['ClassName']+" BasicColorPicker", + id:pickerID+"_picker"}, table); + }, + + cellOnClick : function(e) + { + var el = Event.element(e); + if(el.tagName.toLowerCase() != "img") + return; + var color = Rico.Color.createColorFromBackground(el); + this.updateColor(color); + }, + + updateColor : function(color) + { + this.input.value = color.toString().toUpperCase(); + this.button.style.backgroundColor = color.toString(); + if(typeof(this.onChange) == "function") + this.onChange(color); + if(this.options.OnColorSelected) + this.options.OnColorSelected(this,color); + }, + + getFullPickerContainer : function(pickerID) + { + //create the 3 buttons + this.buttons = + { + //Less : INPUT({value:'Less Colors', className:'button', type:'button'}), + OK : INPUT({value:this.options.OKButtonText, className:'button', type:'button'}), + Cancel : INPUT({value:this.options.CancelButtonText, className:'button', type:'button'}) + }; + + //create the 6 inputs + var inputs = {}; + ['H','S','V','R','G','B'].each(function(type) + { + inputs[type] = INPUT({type:'text',size:'3',maxlength:'3'}); + }); + + //create the HEX input + inputs['HEX'] = INPUT({className:'hex',type:'text',size:'6',maxlength:'6'}); + this.inputs = inputs; + + var images = Prado.WebUI.TColorPicker.UIImages; + + this.inputs['currentColor'] = SPAN({className:'currentColor'}); + this.inputs['oldColor'] = SPAN({className:'oldColor'}); + + var inputsTable = + TABLE({className:'inputs'}, TBODY(null, + TR(null, + TD({className:'currentcolor',colSpan:2}, + this.inputs['currentColor'], this.inputs['oldColor'])), + + TR(null, + TD(null,'H:'), + TD(null,this.inputs['H'], '??')), + + TR(null, + TD(null,'S:'), + TD(null,this.inputs['S'], '%')), + + TR(null, + TD(null,'V:'), + TD(null,this.inputs['V'], '%')), + + TR(null, + TD({className:'gap'},'R:'), + TD({className:'gap'},this.inputs['R'])), + + TR(null, + TD(null,'G:'), + TD(null, this.inputs['G'])), + + TR(null, + TD(null,'B:'), + TD(null, this.inputs['B'])), + + TR(null, + TD({className:'gap'},'#'), + TD({className:'gap'},this.inputs['HEX'])) + )); + + var UIimages = + { + selector : SPAN({className:'selector'}), + background : SPAN({className:'colorpanel'}), + slider : SPAN({className:'slider'}), + hue : SPAN({className:'strip'}) + } + + //png alpha channels for IE + if(Prado.Browser().ie) + { + var filter = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"; + UIimages['background'] = SPAN({className:'colorpanel',style:filter+"(src='"+images['background.png']+"' sizingMethod=scale);"}) + } + + this.inputs = Object.extend(this.inputs, UIimages); + + var pickerTable = + TABLE(null,TBODY(null, + TR({className:'selection'}, + TD({className:'colors'},UIimages['selector'],UIimages['background']), + TD({className:'hue'},UIimages['slider'],UIimages['hue']), + TD({className:'inputs'}, inputsTable) + ), + TR({className:'options'}, + TD({colSpan:3}, + this.buttons['OK'], + this.buttons['Cancel']) + ) + )); + + return DIV({className:this.options['ClassName']+" FullColorPicker", + id:pickerID+"_picker"},pickerTable); + }, + + initializeFullPicker : function() + { + var color = Rico.Color.createFromHex(this.input.value); + this.inputs.oldColor.style.backgroundColor = color.asHex(); + this.setColor(color,true); + + var i = 0; + for(var type in this.inputs) + { + Event.observe(this.inputs[type], "change", + this.onInputChanged.bindEvent(this,type)); + i++; + + if(i > 6) break; + } + + this.isMouseDownOnColor = false; + this.isMouseDownOnHue = false; + + this._onColorMouseDown = this.onColorMouseDown.bind(this); + this._onHueMouseDown = this.onHueMouseDown.bind(this); + this._onMouseUp = this.onMouseUp.bind(this); + this._onMouseMove = this.onMouseMove.bind(this); + + Event.observe(this.inputs.background, "mousedown", this._onColorMouseDown); + Event.observe(this.inputs.selector, "mousedown", this._onColorMouseDown); + Event.observe(this.inputs.hue, "mousedown", this._onHueMouseDown); + Event.observe(this.inputs.slider, "mousedown", this._onHueMouseDown); + + Event.observe(document.body, "mouseup", this._onMouseUp); + + this.observeMouseMovement(); + + Event.observe(this.buttons.Cancel, "click", this.hide.bindEvent(this,this.options['Mode'])); + Event.observe(this.buttons.OK, "click", this.onOKClicked.bind(this)); + }, + + observeMouseMovement : function() + { + if(!this._observingMouseMove) + { + Event.observe(document.body, "mousemove", this._onMouseMove); + this._observingMouseMove = true; + } + }, + + onColorMouseDown : function(ev) + { + this.isMouseDownOnColor = true; + this.onMouseMove(ev); + Event.stop(ev); + }, + + onHueMouseDown : function(ev) + { + this.isMouseDownOnHue = true; + this.onMouseMove(ev); + Event.stop(ev); + }, + + onMouseUp : function(ev) + { + this.isMouseDownOnColor = false; + this.isMouseDownOnHue = false; + Event.stop(ev); + }, + + onMouseMove : function(ev) + { + if(this.isMouseDownOnColor) + this.changeSV(ev); + if(this.isMouseDownOnHue) + this.changeH(ev); + Event.stop(ev); + }, + + changeSV : function(ev) + { + var px = Event.pointerX(ev); + var py = Event.pointerY(ev); + var pos = this.inputs.background.cumulativeOffset(); + + var x = this.truncate(px - pos[0],0,255); + var y = this.truncate(py - pos[1],0,255); + + + var s = x/255; + var b = (255-y)/255; + + var current_s = parseInt(this.inputs.S.value); + var current_b = parseInt(this.inputs.V.value); + + if(current_s == parseInt(s*100) && current_b == parseInt(b*100)) return; + + var h = this.truncate(this.inputs.H.value,0,360)/360; + + var color = new Rico.Color(); + color.rgb = Rico.Color.HSBtoRGB(h,s,b); + + + this.inputs.selector.style.left = x+"px"; + this.inputs.selector.style.top = y+"px"; + + this.inputs.currentColor.style.backgroundColor = color.asHex(); + + return this.setColor(color); + }, + + changeH : function(ev) + { + var py = Event.pointerY(ev); + var pos = this.inputs.background.cumulativeOffset(); + var y = this.truncate(py - pos[1],0,255); + + var h = (255-y)/255; + var current_h = this.truncate(this.inputs.H.value,0,360); + current_h = current_h == 0 ? 360 : current_h; + if(current_h == parseInt(h*360)) return; + + var s = parseInt(this.inputs.S.value)/100; + var b = parseInt(this.inputs.V.value)/100; + var color = new Rico.Color(); + color.rgb = Rico.Color.HSBtoRGB(h,s,b); + + var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b); + hue.setSaturation(1); hue.setBrightness(1); + + this.inputs.background.style.backgroundColor = hue.asHex(); + this.inputs.currentColor.style.backgroundColor = color.asHex(); + + this.inputs.slider.style.top = this.truncate(y,0,255)+"px"; + return this.setColor(color); + + }, + + onOKClicked : function(ev) + { + var r = this.truncate(this.inputs.R.value,0,255);///255; + var g = this.truncate(this.inputs.G.value,0,255);///255; + var b = this.truncate(this.inputs.B.value,0,255);///255; + var color = new Rico.Color(r,g,b); + this.updateColor(color); + this.inputs.oldColor.style.backgroundColor = color.asHex(); + this.hide(ev); + }, + + onInputChanged : function(ev, type) + { + if(this.isMouseDownOnColor || isMouseDownOnHue) + return; + + + switch(type) + { + case "H": case "S": case "V": + var h = this.truncate(this.inputs.H.value,0,360)/360; + var s = this.truncate(this.inputs.S.value,0,100)/100; + var b = this.truncate(this.inputs.V.value,0,100)/100; + var color = new Rico.Color(); + color.rgb = Rico.Color.HSBtoRGB(h,s,b); + return this.setColor(color,true); + case "R": case "G": case "B": + var r = this.truncate(this.inputs.R.value,0,255);///255; + var g = this.truncate(this.inputs.G.value,0,255);///255; + var b = this.truncate(this.inputs.B.value,0,255);///255; + var color = new Rico.Color(r,g,b); + return this.setColor(color,true); + case "HEX": + var color = Rico.Color.createFromHex(this.inputs.HEX.value); + return this.setColor(color,true); + } + }, + + setColor : function(color, update) + { + var hsb = color.asHSB(); + + this.inputs.H.value = parseInt(hsb.h*360); + this.inputs.S.value = parseInt(hsb.s*100); + this.inputs.V.value = parseInt(hsb.b*100); + this.inputs.R.value = color.rgb.r; + this.inputs.G.value = color.rgb.g; + this.inputs.B.value = color.rgb.b; + this.inputs.HEX.value = color.asHex().substring(1).toUpperCase(); + + var images = Prado.WebUI.TColorPicker.UIImages; + + var changeCss = color.isBright() ? 'removeClassName' : 'addClassName'; + Element[changeCss](this.inputs.selector, 'target_white'); + + if(update) + this.updateSelectors(color); + }, + + updateSelectors : function(color) + { + var hsb = color.asHSB(); + var pos = [hsb.s*255, hsb.b*255, hsb.h*255]; + + this.inputs.selector.style.left = this.truncate(pos[0],0,255)+"px"; + this.inputs.selector.style.top = this.truncate(255-pos[1],0,255)+"px"; + this.inputs.slider.style.top = this.truncate(255-pos[2],0,255)+"px"; + + var hue = new Rico.Color(color.rgb.r,color.rgb.g,color.rgb.b); + hue.setSaturation(1); hue.setBrightness(1); + this.inputs.background.style.backgroundColor = hue.asHex(); + this.inputs.currentColor.style.backgroundColor = color.asHex(); + }, + + truncate : function(value, min, max) + { + value = parseInt(value); + return value < min ? min : value > max ? max : value; + } +}); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/default.css b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/default.css new file mode 100644 index 0000000000..7bbc08d538 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/default.css @@ -0,0 +1,249 @@ + +/** Colors **/ + +.TColorPicker_button +{ +} + +.TColorPicker +{ + background-color: white; + border: 1px solid #919EA9; +} + +.FullColorPicker +{ + width: 388px; +} + +.BasicColorPicker .basic_colors td img +{ + border: 1px solid #919EA9; +} + +.BasicColorPicker .basic_colors .pickerhover +{ + border-color: white; +} + +.FullColorPicker .colors .colorpanel +{ + border: 1px solid #919EA9; +} + +.FullColorPicker .hue .strip +{ + border: 1px solid #919EA9; +} + +.FullColorPicker .inputs .currentcolor span +{ + border: 1px solid #919EA9; +} + +.FullColorPicker .options td +{ + border-top: 1px solid #919EA9; +} + +/** UI **/ +.TColorPicker_button img +{ + border: 2px ridge ActiveBorder; + background-color: #fff; + padding: 1px; + height: 16px; + width: 16px; + margin: 1px; + margin-bottom: -6px; + margin-bottom: expression(-4); /** IE hack **/ +} + +.TColorPicker +{ + position: absolute; +} + +.BasicColorPicker .palette_Tiny img +{ + width: 30px; + height: 30px; +} +.BasicColorPicker .basic_colors +{ + border-collapse: separate; + border-spacing: 2px; +} + +.BasicColorPicker .basic_colors td +{ + padding: 0; + font-size: 0; +} + +.FullColorPicker table +{ + border-collapse: collapse; + border-spacing: 0; +} + +.FullColorPicker .colors .colorpanel +{ + margin-left: 7px; + margin-top: -9px; + display: block; + width:256px; + height:256px; + background-image: url(background.png); +} + +* html .FullColorPicker .colors .colorpanel +{ + background-image: none; + margin-top: -10px; +} + +.FullColorPicker .colors +{ + padding-bottom: 10px; + padding-top: 17px; +} + +* html .FullColorPicker .colors +{ + margin-top: 10px; +} + +.FullColorPicker .colors .selector +{ + position: relative; + top: 0px; + left: 0px; + border: 0 none; + float: left; + margin-left: 2px; + margin-top: -12px; + display: block; + background-image: url(target_black.gif); + background-repeat: no-repeat; + width: 8px; + height:8px; + background-position: 50% +} + +* html .FullColorPicker .colors .selector +{ + margin-top: -16px; +} + +.FullColorPicker .colors span.target_white +{ + background-image: url(target_white.gif); +} + +.FullColorPicker .hue +{ + text-align: center; + width: 50px; +} + +.FullColorPicker .hue .strip +{ + margin-top: -5px; + margin-left: 9px; + display: block; + background-image: url(hue.gif); + width:19px; + height:256px; +} + +* html .FullColorPicker .hue .strip +{ + margin-left: 10px; +} + +.FullColorPicker .slider +{ + position: relative; + top: 0px; + margin-top: -5px; + background-image: url(slider.gif); + width: 40px; + height: 9px; + font-size: 0; + display: block; +} + +* html .FullColorPicker .slider +{ + margin-left: 10px; +} + +.FullColorPicker .inputs +{ + font-family: Tahoma; + font-size: 10pt; +} + +.FullColorPicker .inputs input +{ + width: 30px; + text-align: center; + height: 16px; + margin-right: 2px; +} + +.FullColorPicker .currentcolor +{ + text-align: center; +} + +.FullColorPicker .inputs .currentcolor span +{ + display: block; + width: 60px; + height: 30px; +} + +.FullColorPicker .inputs .currentcolor span.currentColor +{ + margin: 5px 5px 0 2px; + border-bottom: 0 none; +} + +.FullColorPicker .inputs .currentcolor span.oldColor +{ + margin: 0px 5px 5px 2px; + border-top: 0 none; +} + +.FullColorPicker input.hex +{ + width: 50px; + margin-right: 5px; +} + +.FullColorPicker input.button +{ + width: 90px; + margin: 3px; + font-size: 10px; +} + +.FullColorPicker .inputs .gap +{ + padding-top: 8px; +} + +.FullColorPicker .customs +{ + width: 100%; + border-collapse: separate; +} + +.FullColorPicker .options td +{ + height: 0px; + text-align: right; + padding: 7px 10px 7px 0px; +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/hue.gif b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/hue.gif new file mode 100644 index 0000000000..901e409c58 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/hue.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/slider.gif b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/slider.gif new file mode 100644 index 0000000000..92f9d74430 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/slider.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/spacer.gif b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/spacer.gif new file mode 100755 index 0000000000..fc2560981e Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/spacer.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/target_black.gif b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/target_black.gif new file mode 100644 index 0000000000..ebb89f58a7 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/target_black.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/target_white.gif b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/target_white.gif new file mode 100644 index 0000000000..c3527c864a Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/colorpicker/target_white.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/controls/accordion.js b/gui/baculum/framework/Web/Javascripts/source/prado/controls/accordion.js new file mode 100644 index 0000000000..90d0131634 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/controls/accordion.js @@ -0,0 +1,170 @@ +/* Simple Accordion Script + * Requires Prototype and Script.aculo.us Libraries + * By: Brian Crescimanno + * http://briancrescimanno.com + * Adapted to Prado & minor improvements: Gabor Berczi + * This work is licensed under the Creative Commons Attribution-Share Alike 3.0 + * http://creativecommons.org/licenses/by-sa/3.0/us/ + */ + +Prado.WebUI.TAccordion = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.accordion = $(options.ID); + this.options = options; + this.hiddenField = $(options.ID+'_1'); + + if (this.options.maxHeight) + { + this.maxHeight = this.options.maxHeight; + } else { + this.maxHeight = 0; + this.checkMaxHeight(); + } + + this.currentView = null; + this.oldView = null; + + var i = 0; + for(var view in this.options.Views) + { + var header = $(view+'_0'); + if(header) + { + this.observe(header, "click", this.elementClicked.bindEvent(this,view)); + if(this.hiddenField.value == i) + { + this.currentView = view; + if($(this.currentView).getHeight() != this.maxHeight) + $(this.currentView).setStyle({height: this.maxHeight+"px"}); + } + } + i++; + } + }, + + checkMaxHeight: function() + { + for(var viewID in this.options.Views) + { + var view = $(viewID); + if(view.getHeight() > this.maxHeight) + this.maxHeight = view.getHeight(); + } + }, + + elementClicked : function(event,viewID) + { + // dummy effect to force processing of click into the event queue + // is not actually supposed to change the appearance of the accordion + var obj = this; + new Effect.Opacity( + this.element, + { + from: 1.0, to: 1.0, duration: 0.0, + queue: { + position: 'end', + scope: 'accordion' + }, + afterFinish: function() { obj.processElementClick(event, viewID); } + } + ); + }, + + processElementClick : function(event,viewID) + { + var i = 0; + for(var index in this.options.Views) + { + if ($(index)) + { + var header = $(index+'_0'); + if(index == viewID) + { + this.oldView = this.currentView; + this.currentView = index; + + this.hiddenField.value=i; + } + } + i++; + } + if(this.oldView != this.currentView) + { + if(this.options.Duration > 0) + { + this.animate(); + } else { + $(this.currentView).setStyle({ height: this.maxHeight+"px" }); + $(this.currentView).show(); + $(this.oldView).hide(); + + var oldHeader = $(this.oldView+'_0'); + var currentHeader = $(this.currentView+'_0'); + oldHeader.className=this.options.HeaderCssClass; + currentHeader.className=this.options.ActiveHeaderCssClass; + } + } + }, + + animate: function() { + var effects = new Array(); + var options = { + sync: true, + queue: { + position: 'end', + scope: 'accordion' + }, + scaleFrom: 0, + scaleContent: false, + transition: Effect.Transitions.sinoidal, + scaleMode: { + originalHeight: this.maxHeight, + originalWidth: this.accordion.getWidth() + }, + scaleX: false, + scaleY: true + }; + + effects.push(new Effect.Scale(this.currentView, 100, options)); + + options = { + sync: true, + queue: { + position: 'end', + scope: 'accordion' + }, + scaleContent: false, + transition: Effect.Transitions.sinoidal, + scaleX: false, + scaleY: true + }; + + effects.push(new Effect.Scale(this.oldView, 0, options)); + + var oldHeader = $(this.oldView+'_0'); + var currentHeader = $(this.currentView+'_0'); + + new Effect.Parallel(effects, { + duration: this.options.Duration, + fps: 35, + queue: { + position: 'end', + scope: 'accordion' + }, + beforeStart: function() { + $(this.currentView).setStyle({ height: "0px" }); + $(this.currentView).show(); + + oldHeader.className=this.options.HeaderCssClass; + currentHeader.className=this.options.ActiveHeaderCssClass; + }.bind(this), + afterFinish: function() { + $(this.oldView).hide(); + $(this.currentView).setStyle({ height: this.maxHeight+"px" }); + }.bind(this) + }); + } +}); + diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/controls/controls.js b/gui/baculum/framework/Web/Javascripts/source/prado/controls/controls.js new file mode 100644 index 0000000000..8ea6afe36b --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/controls/controls.js @@ -0,0 +1,518 @@ +Prado.WebUI = Class.create(); + +Prado.WebUI.Control = Class.create({ + + initialize : function(options) + { + this.registered = false; + this.ID = options.ID; + this.element = $(this.ID); + this.observers = new Array(); + this.intervals = new Array(); + var e; + if (e = Prado.Registry.get(this.ID)) + this.replace(e, options); + else + this.register(options); + + if (this === Prado.Registry.get(this.ID)) + { + this.registered = true; + if(this.onInit) + this.onInit(options); + } + }, + + /** + * Registers the control wrapper in the Prado client side control registry + * @param array control wrapper options + */ + register : function(options) + { + return Prado.Registry.set(options.ID, this); + }, + + /** + * De-registers the control wrapper in the Prado client side control registry + */ + deregister : function() + { + // extra check so we don't ever deregister another wrapper + if (Prado.Registry.get(this.ID)===this) + return Prado.Registry.unset(this.ID); + else + debugger; // invoke debugger - this should never happen + }, + + /** + * Replaces and control wrapper for an already existing control in the Prado client side control registry + * @param object reference to the old wrapper + * @param array control wrapper options + */ + replace : function(oldwrapper, options) + { + // if there's some advanced state management in the wrapper going on, then + // this method could be used either to copy the current state of the control + // from the old wrapper to this new one (which then could live on, while the old + // one could get destroyed), or to copy the new, changed options to the old wrapper, + // (which could then left intact to keep working, while this new wrapper could be + // disposed of by exiting its initialization without installing any handlers or + // leaving any references to it) + // + + // for now this method is simply deinitializing and deregistering the old wrapper, + // and then registering the new wrapper for the control id + + if (oldwrapper.deinitialize) + oldwrapper.deinitialize(); + + return this.register(options); + }, + + /** + * Registers an event observer which will be automatically disposed of when the wrapper + * is deregistered + * @param element DOM element reference or id to attach the event handler to + * @param string event name to observe + * @param handler event handler function + */ + observe: function(element, eventName, handler) + { + var e = { _element: element, _eventName: eventName, _handler: handler }; + this.observers.push(e); + return Event.observe(e._element,e._eventName,e._handler); + }, + + /** + * Checks whether an event observer is installed and returns its index + * @param element DOM element reference or id the event handler was attached to + * @param string event name observed + * @param handler event handler function + * @result int false if the event handler is not installed, or 1-based index when installed + */ + findObserver: function(element, eventName, handler) + { + var e = { _element: element, _eventName: eventName, _handler: handler }; + var idx = -1; + for(var i=0;i0) + window.clearInterval(this.intervals.pop()); + + // automatically deregister all installed observers + while (this.observers.length>0) + { + var e = this.observers.pop(); + Event.stopObserving(e._element,e._eventName,e._handler); + } + } + else + debugger; // shouldn't happen + + this.deregister(); + + this.registered = false; + } + +}); + +Prado.WebUI.PostBackControl = Class.create(Prado.WebUI.Control, { + + onInit : function(options) + { + this._elementOnClick = null; + + if (!this.element) + debugger; // element not found + else + { + //capture the element's onclick function + if(typeof(this.element.onclick)=="function") + { + this._elementOnClick = this.element.onclick.bind(this.element); + this.element.onclick = null; + } + this.observe(this.element, "click", this.elementClicked.bindEvent(this,options)); + } + }, + + elementClicked : function(event, options) + { + var src = Event.element(event); + var doPostBack = true; + var onclicked = null; + + if(this._elementOnClick) + { + var onclicked = this._elementOnClick(event); + if(typeof(onclicked) == "boolean") + doPostBack = onclicked; + } + if(doPostBack && !Prado.Element.isDisabled(src)) + this.onPostBack(event,options); + if(typeof(onclicked) == "boolean" && !onclicked) + Event.stop(event); + }, + + onPostBack : function(event, options) + { + Prado.PostBack(event,options); + } + +}); + +Prado.WebUI.TButton = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TLinkButton = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TCheckBox = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TBulletedList = Class.create(Prado.WebUI.PostBackControl); +Prado.WebUI.TImageMap = Class.create(Prado.WebUI.PostBackControl); + +/** + * TImageButton client-side behaviour. With validation, Firefox needs + * to capture the x,y point of the clicked image in hidden form fields. + */ +Prado.WebUI.TImageButton = Class.create(Prado.WebUI.PostBackControl, +{ + /** + * Override parent onPostBack function, tried to add hidden forms + * inputs to capture x,y clicked point. + */ + onPostBack : function(event, options) + { + this.addXYInput(event,options); + Prado.PostBack(event, options); + this.removeXYInput(event,options); + }, + + /** + * Add hidden inputs to capture the x,y point clicked on the image. + * @param event DOM click event. + * @param array image button options. + */ + addXYInput : function(event,options) + { + var imagePos = this.element.cumulativeOffset(); + var clickedPos = [event.clientX, event.clientY]; + var x = clickedPos[0]-imagePos[0]+1; + var y = clickedPos[1]-imagePos[1]+1; + x = x < 0 ? 0 : x; + y = y < 0 ? 0 : y; + var id = options['EventTarget']; + var x_input = $(id+"_x"); + var y_input = $(id+"_y"); + if(x_input) + { + x_input.value = x; + } + else + { + x_input = INPUT({type:'hidden',name:id+'_x','id':id+'_x',value:x}); + this.element.parentNode.appendChild(x_input); + } + if(y_input) + { + y_input.value = y; + } + else + { + y_input = INPUT({type:'hidden',name:id+'_y','id':id+'_y',value:y}); + this.element.parentNode.appendChild(y_input); + } + }, + + /** + * Remove hidden inputs for x,y-click capturing + * @param event DOM click event. + * @param array image button options. + */ + removeXYInput : function(event,options) + { + var id = options['EventTarget']; + this.element.parentNode.removeChild($(id+"_x")); + this.element.parentNode.removeChild($(id+"_y")); + } +}); + + +/** + * Radio button, only initialize if not already checked. + */ +Prado.WebUI.TRadioButton = Class.create(Prado.WebUI.PostBackControl, +{ + initialize : function($super, options) + { + this.element = $(options['ID']); + if(this.element) + { + if(!this.element.checked) + $super(options); + } + } +}); + + +Prado.WebUI.TTextBox = Class.create(Prado.WebUI.PostBackControl, +{ + onInit : function(options) + { + this.options=options; + if(this.options['TextMode'] != 'MultiLine') + this.observe(this.element, "keydown", this.handleReturnKey.bind(this)); + if(this.options['AutoPostBack']==true) + this.observe(this.element, "change", Prado.PostBack.bindEvent(this,options)); + }, + + handleReturnKey : function(e) + { + if(Event.keyCode(e) == Event.KEY_RETURN) + { + var target = Event.element(e); + if(target) + { + if(this.options['AutoPostBack']==true) + { + Event.fireEvent(target, "change"); + Event.stop(e); + } + else + { + if(this.options['CausesValidation'] && typeof(Prado.Validation) != "undefined") + { + if(!Prado.Validation.validate(this.options['FormID'], this.options['ValidationGroup'], $(this.options['ID']))) + return Event.stop(e); + } + } + } + } + } +}); + +Prado.WebUI.TListControl = Class.create(Prado.WebUI.PostBackControl, +{ + onInit : function(options) + { + this.observe(this.element, "change", Prado.PostBack.bindEvent(this,options)); + } +}); + +Prado.WebUI.TListBox = Class.create(Prado.WebUI.TListControl); +Prado.WebUI.TDropDownList = Class.create(Prado.WebUI.TListControl); + +Prado.WebUI.DefaultButton = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.options = options; + this.observe(options['Panel'], 'keydown', this.triggerEvent.bindEvent(this)); + }, + + triggerEvent : function(ev, target) + { + var enterPressed = Event.keyCode(ev) == Event.KEY_RETURN; + var isTextArea = Event.element(ev).tagName.toLowerCase() == "textarea"; + var isHyperLink = Event.element(ev).tagName.toLowerCase() == "a" && Event.element(ev).hasAttribute("href"); + var isValidButton = Event.element(ev).tagName.toLowerCase() == "input" && Event.element(ev).type.toLowerCase() == "submit"; + + if(enterPressed && !isTextArea && !isValidButton && !isHyperLink) + { + var defaultButton = $(this.options['Target']); + if(defaultButton) + { + this.triggered = true; + Event.fireEvent(defaultButton, this.options['Event']); + Event.stop(ev); + } + } + } +}); + +Prado.WebUI.TTextHighlighter = Class.create(); +Prado.WebUI.TTextHighlighter.prototype = +{ + initialize:function(id) + { + if(!window.clipboardData) return; + var options = + { + href : 'javascript:;/'+'/copy code to clipboard', + onclick : 'Prado.WebUI.TTextHighlighter.copy(this)', + onmouseover : 'Prado.WebUI.TTextHighlighter.hover(this)', + onmouseout : 'Prado.WebUI.TTextHighlighter.out(this)' + } + var div = DIV({className:'copycode'}, A(options, 'Copy Code')); + document.write(DIV(null,div).innerHTML); + } +}; + +Object.extend(Prado.WebUI.TTextHighlighter, +{ + copy : function(obj) + { + var parent = obj.parentNode.parentNode.parentNode; + var text = ''; + for(var i = 0; i < parent.childNodes.length; i++) + { + var node = parent.childNodes[i]; + if(node.innerText) + text += node.innerText == 'Copy Code' ? '' : node.innerText; + else + text += node.nodeValue; + } + if(text.length > 0) + window.clipboardData.setData("Text", text); + }, + + hover : function(obj) + { + obj.parentNode.className = "copycode copycode_hover"; + }, + + out : function(obj) + { + obj.parentNode.className = "copycode"; + } +}); + + +Prado.WebUI.TCheckBoxList = Base.extend( +{ + constructor : function(options) + { + Prado.Registry.set(options.ListID, this); + for(var i = 0; i + * +*/ + + +Prado.WebUI.THtmlArea = Class.create(Prado.WebUI.Control, +{ + initialize: function($super, options) + { + options.ID = options.EditorOptions.elements; + $super(options); + }, + + onInit : function(options) + { + this.options = options; + + var obj = this; + this.ajaxresponder = { + onComplete : function(request) + { + if(request && (request instanceof Prado.AjaxRequest)) + obj.checkInstance(); + } + }; + this.registerAjaxHook(); + + this.registerInstance(); + }, + + registerInstance: function() + { + if (typeof tinyMCE_GZ == 'undefined') + { + if (typeof tinyMCE == 'undefined') + { + if (typeof Prado.CallbackRequest != 'undefined') + if (typeof Prado.CallbackRequest.transport != 'undefined') + { + // we're in a callback + // try it again in some time, as tinyMCE is most likely still loading + this.setTimeout(this.registerInstance.bind(this), 50); + return; + } + throw "TinyMCE libraries must be loaded first"; + } + Prado.WebUI.THtmlArea.tinyMCELoadState = 255; + this.initInstance(); + } + else + if (Prado.WebUI.THtmlArea.tinyMCELoadState==255) + this.initInstance(); + else + { + Prado.WebUI.THtmlArea.pendingRegistrations.push(this.options.ID); + if (Prado.WebUI.THtmlArea.tinyMCELoadState==0) + { + Prado.WebUI.THtmlArea.tinyMCELoadState = 1; + tinyMCE_GZ.init( + this.options.CompressionOptions, + this.compressedScriptsLoaded.bind(this) + ); + } + } + }, + + compressedScriptsLoaded: function() + { + Prado.WebUI.THtmlArea.tinyMCELoadState = 255; + var wrapper; + while(Prado.WebUI.THtmlArea.pendingRegistrations.length>0) + if (wrapper = Prado.Registry.get(Prado.WebUI.THtmlArea.pendingRegistrations.pop())) + wrapper.initInstance(); + }, + + initInstance: function() + { + tinyMCE.init(this.options.EditorOptions); + }, + + checkInstance: function() + { + if (!document.getElementById(this.ID)) + this.deinitialize(); + }, + + removePreviousInstance: function() + { + for(var i=0;i has been removed from DOM tree without deinitialzing the tinyMCE editor first) + } + + // doublecheck editor instance here and remove manually from tinyMCE-registry if neccessary + this.removePreviousInstance(); + + this.deRegisterAjaxHook(); + } +}); + +Object.extend(Prado.WebUI.THtmlArea, +{ + pendingRegistrations : [], + tinyMCELoadState : 0 +}); + + diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/controls/htmlarea4.js b/gui/baculum/framework/Web/Javascripts/source/prado/controls/htmlarea4.js new file mode 100644 index 0000000000..cd3c6a0e72 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/controls/htmlarea4.js @@ -0,0 +1,58 @@ + +/* + * + * HtmlArea (tinyMCE 4) wrapper + * + * @author Gabor Berczi + * +*/ + + +Prado.WebUI.THtmlArea4 = Class.create(Prado.WebUI.Control, +{ + initialize: function($super, options) + { + options.ID = options.EditorOptions.elements; + $super(options); + }, + + onInit : function(options) + { + this.options = options; + tinyMCE.init(this.options.EditorOptions); + }, + + removePreviousInstance: function() + { + for(var i=0;i has been removed from DOM tree without deinitialzing the tinyMCE editor first) + } + + // doublecheck editor instance here and remove manually from tinyMCE-registry if neccessary + this.removePreviousInstance(); + } +}); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/controls/keyboard.js b/gui/baculum/framework/Web/Javascripts/source/prado/controls/keyboard.js new file mode 100644 index 0000000000..25541074b1 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/controls/keyboard.js @@ -0,0 +1,161 @@ +Prado.WebUI.TKeyboard = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.cssClass = options['CssClass']; + this.forControl = document.getElementById(options['ForControl']); + this.autoHide = options['AutoHide']; + + this.flagShift = false; + this.flagCaps = false; + this.flagHover = false; + this.flagFocus = false; + + this.keys = new Array + ( + new Array('` ~ D', '1 ! D', '2 @ D', '3 # D', '4 $ D', '5 % D', '6 ^ D', '7 & D', '8 * D', '9 ( D', '0 ) D', '- _ D', '= + D', 'Bksp Bksp Bksp'), + new Array('Del Del Del', 'q Q L', 'w W L', 'e E L', 'r R L', 't T L', 'y Y L', 'u U L', 'i I L', 'o O L', 'p P L', '[ { D', '] } D', '\\ | \\'), + new Array('Caps Caps Caps', 'a A L', 's S L', 'd D L', 'f F L', 'g G L', 'h H L', 'j J L', 'k K L', 'l L L', '; : D', '\' " D', 'Exit Exit Exit'), + new Array('Shift Shift Shift', 'z Z L', 'x X L', 'c C L', 'v V L', 'b B L', 'n N L', 'm M L', ', < D', '. > D', '/ ? D', 'Shift Shift Shift') + ); + + if (this.isObject(this.forControl)) + { + this.forControl.keyboard = this; + this.forControl.onfocus = function() {this.keyboard.show(); }; + this.forControl.onblur = function() {if (this.keyboard.flagHover == false) this.keyboard.hide();}; + this.forControl.onkeydown = function(e) {if (!e) e = window.event; var key = (e.keyCode)?e.keyCode:e.which; if(key == 9) this.keyboard.hide();;}; + this.forControl.onselect = this.saveSelection; + this.forControl.onclick = this.saveSelection; + this.forControl.onkeyup = this.saveSelection; + } + + this.render(); + + this.tagKeyboard.onmouseover = function() {this.keyboard.flagHover = true;}; + this.tagKeyboard.onmouseout = function() {this.keyboard.flagHover = false;}; + + if (!this.autoHide) this.show(); + }, + + isObject : function(a) + { + return (typeof a == 'object' && !!a) || typeof a == 'function'; + }, + + createElement : function(tagName, attributes, parent) + { + var tagElement = document.createElement(tagName); + if (this.isObject(attributes)) for (attribute in attributes) tagElement[attribute] = attributes[attribute]; + if (this.isObject(parent)) parent.appendChild(tagElement); + return tagElement; + }, + + onmouseover : function() + { + this.className += ' Hover'; + }, + + onmouseout : function() + { + this.className = this.className.replace(/( Hover| Active)/ig, ''); + }, + + onmousedown : function() + { + this.className += ' Active'; + }, + + onmouseup : function() + { + this.className = this.className.replace(/( Active)/ig, ''); + this.keyboard.type(this.innerHTML); + }, + + render : function() + { + this.tagKeyboard = this.createElement('div', {className: this.cssClass, onselectstart: function() {return false;}}, this.element); + this.tagKeyboard.keyboard = this; + + for (var line = 0; line < this.keys.length; line++) + { + var tagLine = this.createElement('div', {className: 'Line'}, this.tagKeyboard); + for (var key = 0; key < this.keys[line].length; key++) + { + var split = this.keys[line][key].split(' '); + var tagKey = this.createElement('div', {className: 'Key ' + split[2]}, tagLine); + var tagKey1 = this.createElement('div', {className: 'Key1', innerHTML: split[0], keyboard: this, onmouseover: this.onmouseover, onmouseout: this.onmouseout, onmousedown: this.onmousedown, onmouseup: this.onmouseup}, tagKey); + var tagKey2 = this.createElement('div', {className: 'Key2', innerHTML: split[1], keyboard: this, onmouseover: this.onmouseover, onmouseout: this.onmouseout, onmousedown: this.onmousedown, onmouseup: this.onmouseup}, tagKey); + } + } + }, + + isShown : function() + { + return (this.tagKeyboard.style.visibility.toLowerCase() == 'visible'); + }, + + show : function() + { + if (this.isShown() == false) this.tagKeyboard.style.visibility = 'visible'; + }, + + hide : function() + { + if (this.isShown() == true && this.autoHide) {this.tagKeyboard.style.visibility = 'hidden'; } + }, + + type : function(key) + { + var input = this.forControl; + var command = key.toLowerCase(); + + if (command == 'exit') {this.hide();} + else if (input != 'undefined' && input != null && command == 'bksp') {this.insert(input, 'bksp');} + else if (input != 'undefined' && input != null && command == 'del') {this.insert(input, 'del');} + else if (command == 'shift') {this.tagKeyboard.className = this.flagShift?'Keyboard Off':'Keyboard Shift';this.flagShift = this.flagShift?false:true;} + else if (command == 'caps') {this.tagKeyboard.className = this.caps?'Keyboard Off':'Keyboard Caps';this.caps = this.caps?false:true;} + else if (input != 'undefined' && input != null) + { + if (this.flagShift == true) {this.flagShift = false; this.tagKeyboard.className = 'Keyboard Off';} + key = key.replace(/>/, '>'); key = key.replace(/</, '<'); key = key.replace(/&/, '&'); + this.insert(input, key); + } + + if (command != 'exit') input.focus(); + }, + + saveSelection : function() + { + if (this.keyboard.forControl.createTextRange) + { + this.keyboard.selection = document.selection.createRange().duplicate(); + return; + } + }, + + insert : function(field, value) + { + if (this.forControl.createTextRange && this.selection) + { + if (value == 'bksp') {this.selection.moveStart("character", -1); this.selection.text = '';} + else if (value == 'del') {this.selection.moveEnd("character", 1); this.selection.text = '';} + else {this.selection.text = value;} + this.selection.select(); + } + else + { + var selectStart = this.forControl.selectionStart; + var selectEnd = this.forControl.selectionEnd; + var start = (this.forControl.value).substring(0, selectStart); + var end = (this.forControl.value).substring(selectEnd, this.forControl.textLength); + + if (value == 'bksp') {start = start.substring(0, start.length - 1); selectStart -= 1; value = '';} + if (value == 'del') {end = end.substring(1, end.length); value = '';} + + this.forControl.value = start + value + end; + this.forControl.selectionStart = selectEnd + value.length; + this.forControl.selectionEnd = selectStart + value.length; + } + } +}); diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/controls/slider.js b/gui/baculum/framework/Web/Javascripts/source/prado/controls/slider.js new file mode 100644 index 0000000000..2e26ee5120 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/controls/slider.js @@ -0,0 +1,226 @@ +/** + * TSlider client class. + * This clas is mainly based on Scriptaculous Slider control (http://script.aculo.us) + */ + +Prado.WebUI.TSlider = Class.extend(Prado.WebUI.PostBackControl, +{ + onInit : function (options) + { + var slider = this; + this.options=options || {}; + this.track = $(options.ID+'_track'); + this.handle =$(options.ID+'_handle'); + this.progress = $(options.ID+'_progress'); + this.axis = this.options.axis || 'horizontal'; + this.range = this.options.range || $R(0,1); + this.value = 0; + this.maximum = this.options.maximum || this.range.end; + this.minimum = this.options.minimum || this.range.start; + this.hiddenField=$(this.options.ID+'_1'); + + // Will be used to align the handle onto the track, if necessary + this.alignX = parseInt(this.options.alignX || - this.track.offsetLeft); + this.alignY = parseInt(this.options.alignY || - this.track.offsetTop); + + this.trackLength = this.maximumOffset() - this.minimumOffset(); + this.handleLength = this.isVertical() ? + (this.handle.offsetHeight != 0 ? + this.handle.offsetHeight : this.handles.style.height.replace(/px$/,"")) : + (this.handle.offsetWidth != 0 ? this.handle.offsetWidth : + this.handle.style.width.replace(/px$/,"")); + + this.active = false; + this.dragging = false; + this.disabled = false; + + if(this.options.disabled) this.setDisabled(); + + // Allowed values array + this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false; + if(this.allowedValues) { + this.minimum = this.allowedValues.min(); + this.maximum = this.allowedValues.max(); + } + + this.eventMouseDown = this.startDrag.bindAsEventListener(this); + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.update.bindAsEventListener(this); + + // Initialize handle + this.setValue(parseFloat(slider.options.sliderValue)); + Element.makePositioned(this.handle); // fix IE + this.observe (this.handle, "mousedown", this.eventMouseDown); + + this.observe (this.track, "mousedown", this.eventMouseDown); + if (this.progress) this.observe (this.progress, "mousedown", this.eventMouseDown); + + this.observe (document, "mouseup", this.eventMouseUp); + this.observe (document, "mousemove", this.eventMouseMove); + + this.initialized=true; + + + if(this.options['AutoPostBack']==true) + this.observe(this.hiddenField, "change", Prado.PostBack.bindEvent(this,options)); + + }, + + setDisabled: function(){ + this.disabled = true; + }, + setEnabled: function(){ + this.disabled = false; + }, + getNearestValue: function(value){ + if(this.allowedValues){ + if(value >= this.allowedValues.max()) return(this.allowedValues.max()); + if(value <= this.allowedValues.min()) return(this.allowedValues.min()); + + var offset = Math.abs(this.allowedValues[0] - value); + var newValue = this.allowedValues[0]; + this.allowedValues.each( function(v) { + var currentOffset = Math.abs(v - value); + if(currentOffset <= offset){ + newValue = v; + offset = currentOffset; + } + }); + return newValue; + } + if(value > this.range.end) return this.range.end; + if(value < this.range.start) return this.range.start; + return value; + }, + + setValue: function(sliderValue){ + if(!this.active) { + this.updateStyles(); + } + this.value = this.getNearestValue(sliderValue); + var pixelValue= this.translateToPx(this.value); + this.handle.style[this.isVertical() ? 'top' : 'left'] = pixelValue; + if (this.progress) + this.progress.style[this.isVertical() ? 'height' : 'width'] = pixelValue; + + //this.drawSpans(); + if(!this.dragging || !this.event) this.updateFinished(); + }, + + setValueBy: function(delta) { + this.setValue(this.value + delta); + }, + + translateToPx: function(value) { + return Math.round( + ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * (value - this.range.start)) + "px"; + }, + + translateToValue: function(offset) { + return ((offset/(this.trackLength-this.handleLength) * (this.range.end-this.range.start)) + this.range.start); + }, + + getRange: function(range) { + var v = this.values.sortBy(Prototype.K); + range = range || 0; + return $R(v[range],v[range+1]); + }, + + minimumOffset: function(){ + return(this.isVertical() ? this.alignY : this.alignX); + }, + + maximumOffset: function(){ + return(this.isVertical() ? + (this.track.offsetHeight != 0 ? this.track.offsetHeight : + this.track.style.height.replace(/px$/,"")) - this.alignY : + (this.track.offsetWidth != 0 ? this.track.offsetWidth : + this.track.style.width.replace(/px$/,"")) - this.alignX); + }, + + isVertical: function(){ + return (this.axis == 'vertical'); + }, + + updateStyles: function() { + if (this.active) + Element.addClassName(this.handle, 'selected'); + else + Element.removeClassName(this.handle, 'selected'); + }, + + startDrag: function(event) { + if(Event.isLeftClick(event)) { + if(!this.disabled){ + this.active = true; + var handle = Event.element(event); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var track = handle; + if(track==this.track) { + var offsets = this.track.cumulativeOffset(); + this.event = event; + this.setValue(this.translateToValue( + (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2) + )); + var offsets = this.handle.cumulativeOffset(); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } else { + this.updateStyles(); + var offsets = this.handle.cumulativeOffset(); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + } + } + Event.stop(event); + } + }, + + update: function(event) { + if(this.active) { + if(!this.dragging) this.dragging = true; + this.draw(event); + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + Event.stop(event); + } + }, + + draw: function(event) { + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = this.track.cumulativeOffset(); + pointer[0] -= this.offsetX + offsets[0]; + pointer[1] -= this.offsetY + offsets[1]; + this.event = event; + this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] )); + if(this.initialized && this.options.onSlide) + this.options.onSlide(this.value, this); + }, + + endDrag: function(event) { + if(this.active && this.dragging) { + this.finishDrag(event, true); + Event.stop(event); + } + this.active = false; + this.dragging = false; + }, + + finishDrag: function(event, success) { + this.active = false; + this.dragging = false; + this.updateFinished(); + }, + + updateFinished: function() { + this.hiddenField.value=this.value; + this.updateStyles(); + if(this.initialized && this.options.onChange) + this.options.onChange(this.value, this); + this.event = null; + if (this.options['AutoPostBack']==true) + { + Event.fireEvent(this.hiddenField,"change"); + } + } + +}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/controls/tabpanel.js b/gui/baculum/framework/Web/Javascripts/source/prado/controls/tabpanel.js new file mode 100644 index 0000000000..bd0a74942a --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/controls/tabpanel.js @@ -0,0 +1,60 @@ +Prado.WebUI.TTabPanel = Class.create(Prado.WebUI.Control, +{ + onInit : function(options) + { + this.views = options.Views; + this.viewsvis = options.ViewsVis; + this.hiddenField = $(options.ID+'_1'); + this.activeCssClass = options.ActiveCssClass; + this.normalCssClass = options.NormalCssClass; + var length = options.Views.length; + for(var i = 0; i 40) return true; + + var current = this.selectedDate; + var d = current.valueOf(); + if(kc == Event.KEY_LEFT) + { + if(ev.ctrlKey || ev.shiftKey) // -1 month + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() - 1,current.getFullYear())) ); // no need to catch dec -> jan for the year + d = current.setMonth( current.getMonth() - 1 ); + } + else + d -= 86400000; //-1 day + } + else if (kc == Event.KEY_RIGHT) + { + if(ev.ctrlKey || ev.shiftKey) // +1 month + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth() + 1,current.getFullYear())) ); // no need to catch dec -> jan for the year + d = current.setMonth( current.getMonth() + 1 ); + } + else + d += 86400000; //+1 day + } + else if (kc == Event.KEY_UP) + { + if(ev.ctrlKey || ev.shiftKey) //-1 year + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() - 1)) ); // no need to catch dec -> jan for the year + d = current.setFullYear( current.getFullYear() - 1 ); + } + else + d -= 604800000; // -7 days + } + else if (kc == Event.KEY_DOWN) + { + if(ev.ctrlKey || ev.shiftKey) // +1 year + { + current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() + 1)) ); // no need to catch dec -> jan for the year + d = current.setFullYear( current.getFullYear() + 1 ); + } + else + d += 7 * 24 * 61 * 60 * 1000; // +7 days + } + this.setSelectedDate(d); + Event.stop(ev); + }, + + selectDate : function(ev) + { + var el = Event.element(ev); + while (el.nodeType != 1) + el = el.parentNode; + + while (el != null && el.tagName && el.tagName.toLowerCase() != "td") + el = el.parentNode; + + // if no td found, return + if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td") + return; + + var d = this.newDate(this.selectedDate); + var n = Number(el.firstChild.data); + if (isNaN(n) || n <= 0 || n == null) + return; + + d.setDate(n); + this.setSelectedDate(d); + this.hide(); + }, + + selectToday : function() + { + if(this.selectedDate.toISODate() == this.newDate().toISODate()) + this.hide(); + + this.setSelectedDate(this.newDate()); + }, + + clearSelection : function() + { + this.setSelectedDate(this.newDate()); + this.hide(); + }, + + monthSelect : function(ev) + { + this.setMonth(Form.Element.getValue(Event.element(ev))); + }, + + yearSelect : function(ev) + { + this.setYear(Form.Element.getValue(Event.element(ev))); + }, + + mouseWheelChange : function (event) + { + var delta = 0; + if (!event) event = document.parentWindow.event; + if (event.wheelDelta) { + delta = event.wheelDelta/120; + if (window.opera) delta = -delta; + } else if (event.detail) { delta = -event.detail/3; } + + var d = this.newDate(this.selectedDate); + var m = d.getMonth() + Math.round(delta); + this.setMonth(m,true); + return false; + }, + + // Respond to change event on the textbox or dropdown list + // This method raises OnDateChanged event on client side if it has been defined + onDateChanged : function () + { + if (this.options.OnDateChanged) + { + var date; + if (this.options.InputMode == "TextBox") + { + date=this.control.value; + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control).selectedIndex+1; + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control).selectedIndex; + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control).value; + date=new Date(year, month, day, 0,0,0).SimpleFormat(this.Format, this); + } + this.options.OnDateChanged(this, date); + } + }, + + fireChangeEvent: function(element, capped) + { + if (capped) + { + var obj = this; + + if (typeof(obj.changeeventtimer)!="undefined") + { + clearTimeout(obj.changeeventtimer); + obj.changeeventtimer = null; + } + obj.changeeventtimer = setTimeout( + function() { obj.changeeventtimer = null; Event.fireEvent(element, "change"); }, + 1500 + ); + } + else + Event.fireEvent(element, "change"); + }, + + onChange : function(ref, date, capevents) + { + if(this.options.InputMode == "TextBox") + { + this.control.value = this.formatDate(); + this.fireChangeEvent(this.control, capevents); + } + else + { + var day = Prado.WebUI.TDatePicker.getDayListControl(this.control); + var month = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + var year = Prado.WebUI.TDatePicker.getYearListControl(this.control); + var date = this.selectedDate; + if(day) + { + day.selectedIndex = date.getDate()-1; + } + if(month) + { + month.selectedIndex = date.getMonth(); + } + if(year) + { + var years = year.options; + var currentYear = date.getFullYear(); + for(var i = 0; i < years.length; i++) + years[i].selected = years[i].value.toInteger() == currentYear; + } + + day && this.fireChangeEvent(day, capevents); + month && this.fireChangeEvent(month, capevents); + year && this.fireChangeEvent(year, capevents); + } + }, + + formatDate : function() + { + return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : ''; + }, + + newDate : function(date) + { + if(!date) + date = new Date(); + if(typeof(date) == "string" || typeof(date) == "number") + date = new Date(date); + return new Date(Math.min(Math.max(date.getFullYear(),this.FromYear),this.UpToYear), date.getMonth(), date.getDate(), 0,0,0); + }, + + setSelectedDate : function(date, capevents) + { + if (date == null) + return; + var old=this.selectedDate; + this.selectedDate = this.newDate(date); + var dateChanged=(old - this.selectedDate != 0) || ( this.options.InputMode == "TextBox" && this.control.value != this.formatDate()); + + this.updateHeader(); + this.update(); + if (dateChanged && typeof(this.onChange) == "function") + this.onChange(this, date, capevents); + }, + + getElement : function() + { + return this._calDiv; + }, + + getSelectedDate : function () + { + return this.selectedDate == null ? null : this.newDate(this.selectedDate); + }, + + setYear : function(year) + { + var d = this.newDate(this.selectedDate); + d.setFullYear(year); + this.setSelectedDate(d); + }, + + setMonth : function (month, capevents) + { + var d = this.newDate(this.selectedDate); + d.setDate(Math.min(d.getDate(), this.getDaysPerMonth(month,d.getFullYear()))); + d.setMonth(month); + this.setSelectedDate(d,capevents); + }, + + nextMonth : function () + { + this.setMonth(this.selectedDate.getMonth()+1); + }, + + prevMonth : function () + { + this.setMonth(this.selectedDate.getMonth()-1); + }, + + getDaysPerMonth : function (month, year) + { + month = (Number(month)+12) % 12; + var days = [31,28,31,30,31,30,31,31,30,31,30,31]; + var res = days[month]; + if (month == 1 && ((!(year % 4) && (year % 100)) || !(year % 400))) //feburary, leap years has 29 + res++; + return res; + }, + + getDatePickerOffsetHeight : function() + { + if(this.options.InputMode == "TextBox") + return this.control.offsetHeight; + + var control = Prado.WebUI.TDatePicker.getDayListControl(this.control); + if(control) return control.offsetHeight; + + var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control); + if(control) return control.offsetHeight; + + var control = Prado.WebUI.TDatePicker.getYearListControl(this.control); + if(control) return control.offsetHeight; + return 0; + }, + + show : function() + { + this.create(); + + if(!this.showing) + { + var pos = this.control.positionedOffset(); + + pos[1] += this.getDatePickerOffsetHeight(); + this._calDiv.style.top = (pos[1]-1) + "px"; + this._calDiv.style.display = "block"; + this._calDiv.style.left = pos[0] + "px"; + + this.documentClickEvent = this.hideOnClick.bindEvent(this); + this.documentKeyDownEvent = this.keyPressed.bindEvent(this); + Event.observe(document.body, "click", this.documentClickEvent); + var date = this.getDateFromInput(); + if(date) + { + this.selectedDate = date; + this.setSelectedDate(date); + } + Event.observe(document,"keydown", this.documentKeyDownEvent); + this.showing = true; + + if(this.positionMode=='Top') + { + this._calDiv.style.top = ((pos[1]-1) - this.getDatePickerOffsetHeight() - this._calDiv.offsetHeight) + 'px'; + if(Prado.Browser().ie) + this.iePopup = this._calDiv.style.top; + } + this.ieHack(false); + } + }, + + getDateFromInput : function() + { + if(this.options.InputMode == "TextBox") + return Date.SimpleParse($F(this.control), this.Format); + else + return Prado.WebUI.TDatePicker.getDropDownDate(this.control); + }, + + //hide the calendar when clicked outside any calendar + hideOnClick : function(ev) + { + if(!this.showing) return; + var el = Event.element(ev); + var within = false; + do + { + within = within || (el.className && Element.hasClassName(el, "TDatePicker_"+this.CalendarStyle)); + within = within || el == this.trigger; + within = within || el == this.control; + if(within) break; + el = el.parentNode; + } + while(el); + if(!within) this.hide(); + }, + + + hide : function() + { + if(this.showing) + { + this._calDiv.style.display = "none"; + if(this.iePopUp) + this.iePopUp.style.display = "none"; + this.showing = false; + Event.stopObserving(document.body, "click", this.documentClickEvent); + Event.stopObserving(document,"keydown", this.documentKeyDownEvent); + } + }, + + update : function() + { + // Calculate the number of days in the month for the selected date + var date = this.selectedDate; + var today = (this.newDate()).toISODate(); + + var selected = date.toISODate(); + var d1 = new Date(date.getFullYear(), date.getMonth(), 1); + var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1); + var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000)); + + // Find out the weekDay index for the first of this month + var firstIndex = (d1.getDay() - this.FirstDayOfWeek) % 7 ; + if (firstIndex < 0) + firstIndex += 7; + + var index = 0; + while (index < firstIndex) { + this.dateSlot[index].value = -1; + this.dateSlot[index].data.data = String.fromCharCode(160); + this.dateSlot[index].data.parentNode.className = "empty"; + index++; + } + + for (var i = 1; i <= monthLength; i++, index++) { + var slot = this.dateSlot[index]; + var slotNode = slot.data.parentNode; + slot.value = i; + slot.data.data = i; + slotNode.className = "date"; + //slotNode.style.color = ""; + if (d1.toISODate() == today) { + slotNode.className += " today"; + } + if (d1.toISODate() == selected) { + // slotNode.style.color = "blue"; + slotNode.className += " selected"; + } + d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1); + } + + var lastDateIndex = index; + + while(index < 42) { + this.dateSlot[index].value = -1; + this.dateSlot[index].data.data = String.fromCharCode(160); + this.dateSlot[index].data.parentNode.className = "empty"; + ++index; + } + + }, + + hover : function(ev) + { + if(Event.element(ev).tagName) + { + if(ev.type == "mouseover") + Event.element(ev).addClassName("hover"); + else + Event.element(ev).removeClassName("hover"); + } + }, + + updateHeader : function () { + + var options = this._monthSelect.options; + var m = this.selectedDate.getMonth(); + for(var i=0; i < options.length; ++i) { + options[i].selected = false; + if (options[i].value == m) { + options[i].selected = true; + } + } + + options = this._yearSelect.options; + var year = this.selectedDate.getFullYear(); + for(var i=0; i < options.length; ++i) { + options[i].selected = false; + if (options[i].value == year) { + options[i].selected = true; + } + } + + } +}); + +Object.extend(Prado.WebUI.TDatePicker, +{ + /** + * @return Date the date from drop down list options. + */ + getDropDownDate : function(control) + { + var now=new Date(); + var year=now.getFullYear(); + var month=now.getMonth(); + var day=1; + + var month_list = Prado.WebUI.TDatePicker.getMonthListControl(control); + var day_list = Prado.WebUI.TDatePicker.getDayListControl(control); + var year_list = Prado.WebUI.TDatePicker.getYearListControl(control); + + var day = day_list ? $F(day_list) : 1; + var month = month_list ? $F(month_list) : now.getMonth(); + var year = year_list ? $F(year_list) : now.getFullYear(); + + return new Date(year,month,day, 0, 0, 0); + }, + + getYearListControl : function(control) + { + return $(control.id+"_year"); + }, + + getMonthListControl : function(control) + { + return $(control.id+"_month"); + }, + + getDayListControl : function(control) + { + return $(control.id+"_day"); + } +}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/datepicker/default.css b/gui/baculum/framework/Web/Javascripts/source/prado/datepicker/default.css new file mode 100644 index 0000000000..9532dad02b --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/datepicker/default.css @@ -0,0 +1,98 @@ +.TDatePicker_default +{ + border: 1px solid #919EA9; + background-color: White; + text-align: center; + font-size: 11px; + font-family: Tahoma, Arial, Helvetica, sans-serif; + cursor: default; +} + +.TDatePickerButton +{ + width: 30px; +} + +.TDatePickerImageButton +{ + padding: 2px; + border: 1px solid #919EA9; + vertical-align: top; + margin-left: 1px; +} + + +.TDatePickerImageButton:hover +{ + border-color: #ddd; +} + +.TDatePicker_default select +{ + font-size: 11px; +} + +.TDatePicker_default input.button +{ + font-size: 11px; + width: 32px; +} + +.TDatePicker_default .date +{ + padding: 4px 0; + border: 1px solid white; + text-align: center; +} +.TDatePicker_default .hover +{ + border: 1px solid blue; +} +.TDatePicker_default .selected +{ + background-color: blue; + border: 1px solid blue; + color: white; +} +.TDatePicker_default .today +{ + font-weight: bold; +} + +.TDatePicker_default td.empty +{ + border: 1px solid white; + cursor: default; + height: 22px; +} + +.TDatePicker_default th +{ + width: 28px; +} + +.TDatePicker_default .calendarBody +{ + text-align: center; + width: 210px; + margin: 3px 6px; +} + +.TDatePicker_default .grid +{ + border-spacing: 0px; +} + +.TDatePicker_default .calendarFooter +{ + margin: 2px; + border-top: 1px solid #919EA9; + padding-top: 2px; +} +.TDatePicker_default .todayButton +{ + font-size: 11px; + margin: 4px; + padding-left: 1em; + padding-right: 1em; +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/datepicker/spacer.gif b/gui/baculum/framework/Web/Javascripts/source/prado/datepicker/spacer.gif new file mode 100755 index 0000000000..fc2560981e Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/datepicker/spacer.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/logger/logger.js b/gui/baculum/framework/Web/Javascripts/source/prado/logger/logger.js new file mode 100644 index 0000000000..55cc1aa301 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/logger/logger.js @@ -0,0 +1,753 @@ +/* + +Created By: Corey Johnson +E-mail: probablyCorey@gmail.com + +Requires: Prototype Javascript library (http://prototype.conio.net/) + +Use it all you want. Just remember to give me some credit :) + +*/ + +// ------------ +// Custom Event +// ------------ + +CustomEvent = Class.create(); +CustomEvent.prototype = { + initialize : function() { + this.listeners = [] + }, + + addListener : function(method) { + this.listeners.push(method) + }, + + removeListener : function(method) { + var foundIndexes = this._findListenerIndexes(method) + + for(var i = 0; i < foundIndexes.length; i++) { + this.listeners.splice(foundIndexes[i], 1) + } + }, + + dispatch : function(handler) { + for(var i = 0; i < this.listeners.length; i++) { + try { + this.listeners[i](handler) + } + catch (e) { + alert("Could not run the listener " + this.listeners[i] + ". " + e) + } + } + }, + + // Private Methods + // --------------- + _findListenerIndexes : function(method) { + var indexes = [] + for(var i = 0; i < this.listeners.length; i++) { + if (this.listeners[i] == method) { + indexes.push(i) + } + } + + return indexes + } +}; + +// ------ +// Cookie +// ------ + +var Cookie = { + set : function(name, value, expirationInDays, path) { + var cookie = escape(name) + "=" + escape(value) + + if (expirationInDays) { + var date = new Date() + date.setDate(date.getDate() + expirationInDays) + cookie += "; expires=" + date.toGMTString() + } + + if (path) { + cookie += ";path=" + path + } + + document.cookie = cookie + + if (value && (expirationInDays == undefined || expirationInDays > 0) && !this.get(name)) { + Logger.error("Cookie (" + name + ") was not set correctly... The value was " + value.toString().length + " charachters long (This may be over the cookie limit)"); + } + }, + + get : function(name) { + var pattern = "(^|;)\\s*" + escape(name) + "=([^;]+)" + + var m = document.cookie.match(pattern) + if (m && m[2]) { + return unescape(m[2]) + } + else return null + }, + + getAll : function() { + var cookies = document.cookie.split(';') + var cookieArray = [] + + for (var i = 0; i < cookies.length; i++) { + try { + var name = unescape(cookies[i].match(/^\s*([^=]+)/m)[1]) + var value = unescape(cookies[i].match(/=(.*$)/m)[1]) + } + catch (e) { + continue + } + + cookieArray.push({name : name, value : value}) + + if (cookieArray[name] != undefined) { + Logger.waring("Trying to retrieve cookie named(" + name + "). There appears to be another property with this name though."); + } + + cookieArray[name] = value + } + + return cookieArray + }, + + clear : function(name) { + this.set(name, "", -1) + }, + + clearAll : function() { + var cookies = this.getAll() + + for(var i = 0; i < cookies.length; i++) { + this.clear(cookies[i].name) + } + + } +}; + +// ------ +// Logger +// ----- + +Logger = { + logEntries : [], + + onupdate : new CustomEvent(), + onclear : new CustomEvent(), + + + // Logger output + log : function(message, tag) { + var logEntry = new LogEntry(message, tag || "info") + this.logEntries.push(logEntry) + this.onupdate.dispatch(logEntry) + }, + + info : function(message) { + this.log(message, 'info') + if(typeof(console) != "undefined") + console.info(message); + }, + + debug : function(message) { + this.log(message, 'debug') + if(typeof(console) != "undefined") + console.debug(message); + }, + + warn : function(message) { + this.log(message, 'warning') + if(typeof(console) != "undefined") + console.warn(message); + }, + + error : function(message, error) { + this.log(message + ": \n" + error, 'error') + if(typeof(console) != "undefined") + console.error(message + ": \n" + error); + + }, + + clear : function () { + this.logEntries = [] + this.onclear.dispatch() + } +}; + +LogEntry = Class.create() +LogEntry.prototype = { + initialize : function(message, tag) { + this.message = message + this.tag = tag + } +}; + +LogConsole = Class.create(); +LogConsole.prototype = { + + // Properties + // ---------- + commandHistory : [], + commandIndex : 0, + + hidden : true, + + // Methods + // ------- + + initialize : function(toggleKey) { + this.outputCount = 0 + this.tagPattern = Cookie.get('tagPattern') || ".*" + + // I hate writing javascript in HTML... but what's a better alternative + this.logElement = document.createElement('div') + document.body.appendChild(this.logElement) + Element.hide(this.logElement) + + this.logElement.style.position = "absolute" + this.logElement.style.left = '0px' + this.logElement.style.width = '100%' + + this.logElement.style.textAlign = "left" + this.logElement.style.fontFamily = "lucida console" + this.logElement.style.fontSize = "100%" + this.logElement.style.backgroundColor = 'darkgray' + this.logElement.style.opacity = 0.9 + this.logElement.style.zIndex = 2000 + + // Add toolbarElement + this.toolbarElement = document.createElement('div') + this.logElement.appendChild(this.toolbarElement) + this.toolbarElement.style.padding = "0 0 0 2px" + + // Add buttons + this.buttonsContainerElement = document.createElement('span') + this.toolbarElement.appendChild(this.buttonsContainerElement) + + this.buttonsContainerElement.innerHTML += '' + this.buttonsContainerElement.innerHTML += '' + if(!Prado.Inspector.disabled) + this.buttonsContainerElement.innerHTML += '' + + + //Add Tag Filter + this.tagFilterContainerElement = document.createElement('span') + this.toolbarElement.appendChild(this.tagFilterContainerElement) + this.tagFilterContainerElement.style.cssFloat = 'left' + this.tagFilterContainerElement.appendChild(document.createTextNode("Log Filter")) + + this.tagFilterElement = document.createElement('input') + this.tagFilterContainerElement.appendChild(this.tagFilterElement) + this.tagFilterElement.style.width = '200px' + this.tagFilterElement.value = this.tagPattern + this.tagFilterElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out + + Event.observe(this.tagFilterElement, 'keyup', this.updateTags.bind(this)) + Event.observe(this.tagFilterElement, 'click', function() {this.tagFilterElement.select()}.bind(this)) + + // Add outputElement + this.outputElement = document.createElement('div') + this.logElement.appendChild(this.outputElement) + this.outputElement.style.overflow = "auto" + this.outputElement.style.clear = "both" + this.outputElement.style.height = "200px" + this.outputElement.style.backgroundColor = 'black' + + this.inputContainerElement = document.createElement('div') + this.inputContainerElement.style.width = "100%" + this.logElement.appendChild(this.inputContainerElement) + + this.inputElement = document.createElement('input') + this.inputContainerElement.appendChild(this.inputElement) + this.inputElement.style.width = '100%' + this.inputElement.style.borderWidth = '0px' // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0 + this.inputElement.style.margin = '0px' + this.inputElement.style.padding = '0px' + this.inputElement.value = 'Type command here' + this.inputElement.setAttribute('autocomplete', 'off') // So Firefox doesn't flip out + + Event.observe(this.inputElement, 'keyup', this.handleInput.bind(this)) + Event.observe(this.inputElement, 'click', function() {this.inputElement.select()}.bind(this)) + + if(document.all && !window.opera) + { + window.setInterval(this.repositionWindow.bind(this), 500) + } + else + { + this.logElement.style.position="fixed"; + this.logElement.style.bottom="0px"; + } + var self=this; + Event.observe(document, 'keydown', function(e) + { + if((e.altKey==true) && Event.keyCode(e) == toggleKey ) //Alt+J | Ctrl+J + self.toggle(); + }); + + // Listen to the logger.... + Logger.onupdate.addListener(this.logUpdate.bind(this)) + Logger.onclear.addListener(this.clear.bind(this)) + + // Preload log element with the log entries that have been entered + for (var i = 0; i < Logger.logEntries.length; i++) { + this.logUpdate(Logger.logEntries[i]) + } + + // Feed all errors into the logger (For some unknown reason I can only get this to work + // with an inline event declaration) + Event.observe(window, 'error', function(msg, url, lineNumber) {Logger.error("Error in (" + (url || location) + ") on line "+lineNumber+"", msg)}) + + // Allow acess key link + var accessElement = document.createElement('span') + accessElement.innerHTML = '' + document.body.appendChild(accessElement) + + if (Cookie.get('ConsoleVisible') == 'true') { + this.toggle() + } + }, + + toggle : function() { + if (this.logElement.style.display == 'none') { + this.show() + } + else { + this.hide() + } + }, + + show : function() { + Element.show(this.logElement) + this.outputElement.scrollTop = this.outputElement.scrollHeight // Scroll to bottom when toggled + if(document.all && !window.opera) + this.repositionWindow(); + Cookie.set('ConsoleVisible', 'true') + this.inputElement.select() + this.hidden = false; + }, + + hide : function() { + this.hidden = true; + Element.hide(this.logElement) + Cookie.set('ConsoleVisible', 'false') + }, + + output : function(message, style) { + // If we are at the bottom of the window, then keep scrolling with the output + var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight + + this.outputCount++ + style = (style ? style += ';' : '') + style += 'padding:1px;margin:0 0 5px 0' + + if (this.outputCount % 2 == 0) style += ";background-color:#101010" + + message = message || "undefined" + message = message.toString().escapeHTML() + + this.outputElement.innerHTML += "
    " + message + "
    " + + if (shouldScroll) { + this.outputElement.scrollTop = this.outputElement.scrollHeight + } + }, + + updateTags : function() { + var pattern = this.tagFilterElement.value + + if (this.tagPattern == pattern) return + + try { + new RegExp(pattern) + } + catch (e) { + return + } + + this.tagPattern = pattern + Cookie.set('tagPattern', this.tagPattern) + + this.outputElement.innerHTML = "" + + // Go through each log entry again + this.outputCount = 0; + for (var i = 0; i < Logger.logEntries.length; i++) { + this.logUpdate(Logger.logEntries[i]) + } + }, + + repositionWindow : function() { + var offset = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop + var pageHeight = self.innerHeight || document.documentElement.clientHeight || document.body.clientHeight + this.logElement.style.top = (offset + pageHeight - Element.getHeight(this.logElement)) + "px" + }, + + // Event Handlers + // -------------- + + logUpdate : function(logEntry) { + if (logEntry.tag.search(new RegExp(this.tagPattern, 'igm')) == -1) return + var style = '' + if (logEntry.tag.search(/error/) != -1) style += 'color:red' + else if (logEntry.tag.search(/warning/) != -1) style += 'color:orange' + else if (logEntry.tag.search(/debug/) != -1) style += 'color:green' + else if (logEntry.tag.search(/info/) != -1) style += 'color:white' + else style += 'color:yellow' + + this.output(logEntry.message, style) + }, + + clear : function(e) { + this.outputElement.innerHTML = "" + }, + + handleInput : function(e) { + if (e.keyCode == Event.KEY_RETURN ) { + var command = this.inputElement.value + + switch(command) { + case "clear": + Logger.clear() + break + + default: + var consoleOutput = "" + + try { + consoleOutput = eval(this.inputElement.value) + } + catch (e) { + Logger.error("Problem parsing input <" + command + ">", e) + break + } + + Logger.log(consoleOutput) + break + } + + if (this.inputElement.value != "" && this.inputElement.value != this.commandHistory[0]) { + this.commandHistory.unshift(this.inputElement.value) + } + + this.commandIndex = 0 + this.inputElement.value = "" + } + else if (e.keyCode == Event.KEY_UP && this.commandHistory.length > 0) { + this.inputElement.value = this.commandHistory[this.commandIndex] + + if (this.commandIndex < this.commandHistory.length - 1) { + this.commandIndex += 1 + } + } + else if (e.keyCode == Event.KEY_DOWN && this.commandHistory.length > 0) { + if (this.commandIndex > 0) { + this.commandIndex -= 1 + } + + this.inputElement.value = this.commandHistory[this.commandIndex] + } + else { + this.commandIndex = 0 + } + } +}; + + +// ------------------------- +// Helper Functions And Junk +// ------------------------- +function inspect(o) +{ + var objtype = typeof(o); + if (objtype == "undefined") { + return "undefined"; + } else if (objtype == "number" || objtype == "boolean") { + return o + ""; + } else if (o === null) { + return "null"; + } + + try { + var ostring = (o + ""); + } catch (e) { + return "[" + typeof(o) + "]"; + } + + if (typeof(o) == "function") + { + o = ostring.replace(/^\s+/, ""); + var idx = o.indexOf("{"); + if (idx != -1) { + o = o.substr(0, idx) + "{...}"; + } + return o; + } + + var reprString = function (o) + { + return ('"' + o.replace(/(["\\])/g, '\\$1') + '"' + ).replace(/[\f]/g, "\\f" + ).replace(/[\b]/g, "\\b" + ).replace(/[\n]/g, "\\n" + ).replace(/[\t]/g, "\\t" + ).replace(/[\r]/g, "\\r"); + }; + + if (objtype == "string") { + return reprString(o); + } + // recurse + var me = arguments.callee; + // short-circuit for objects that support "json" serialization + // if they return "self" then just pass-through... + var newObj; + if (typeof(o.__json__) == "function") { + newObj = o.__json__(); + if (o !== newObj) { + return me(newObj); + } + } + if (typeof(o.json) == "function") { + newObj = o.json(); + if (o !== newObj) { + return me(newObj); + } + } + // array + if (objtype != "function" && typeof(o.length) == "number") { + var res = []; + for (var i = 0; i < o.length; i++) { + var val = me(o[i]); + if (typeof(val) != "string") { + val = "undefined"; + } + res.push(val); + } + return "[" + res.join(", ") + "]"; + } + + // generic object code path + res = []; + for (var k in o) { + var useKey; + if (typeof(k) == "number") { + useKey = '"' + k + '"'; + } else if (typeof(k) == "string") { + useKey = reprString(k); + } else { + // skip non-string or number keys + continue; + } + val = me(o[k]); + if (typeof(val) != "string") { + // skip non-serializable values + continue; + } + res.push(useKey + ":" + val); + } + return "{" + res.join(", ") + "}"; +}; + +Array.prototype.contains = function(object) { + for(var i = 0; i < this.length; i++) { + if (object == this[i]) return true + } + + return false +}; + +// Helper Alias for simple logging +var puts = function() {return Logger.log(arguments[0], arguments[1])}; + +/************************************* + + Javascript Object Tree + version 1.0 + last revision:04.11.2004 + steve@slayeroffice.com + http://slayeroffice.com + + (c)2004 S.G. Chipman + + Please notify me of any modifications + you make to this code so that I can + update the version hosted on slayeroffice.com + + +************************************/ +if(typeof Prado == "undefined") + var Prado = {}; +Prado.Inspector = +{ + d : document, + types : new Array(), + objs : new Array(), + hidden : new Array(), + opera : window.opera, + displaying : '', + nameList : new Array(), + + format : function(str) { + if(typeof(str) != "string") return str; + str=str.replace(//g,">"); + return str; + }, + + parseJS : function(obj) { + var name; + if(typeof obj == "string") { name = obj; obj = eval(obj); } + win= typeof obj == 'undefined' ? window : obj; + this.displaying = name ? name : win.toString(); + for(js in win) { + try { + if(win[js] && js.toString().indexOf("Inspector")==-1 && (win[js]+"").indexOf("[native code]")==-1) { + + t=typeof(win[js]); + if(!this.objs[t.toString()]) { + this.types[this.types.length]=t; + this.objs[t]={}; + this.nameList[t] = new Array(); + } + this.nameList[t].push(js); + this.objs[t][js] = this.format(win[js]+""); + } + } catch(err) { } + } + + for(i=0;i-1){ + this.d.getElementById(spanID).innerHTML="[-]"; + } else { + this.d.getElementById(spanID).innerHTML="[+]"; + } + }, + + buildInspectionLevel : function() + { + var display = this.displaying; + var list = display.split("."); + var links = ["[object Window]"]; + var name = ''; + if(display.indexOf("[object ") >= 0) return links.join("."); + for(var i = 0; i < list.length; i++) + { + name += (name.length ? "." : "") + list[i]; + links[i+1] = ""+list[i]+""; + } + return links.join("."); + }, + + buildTree : function() { + mHTML = "
    Inspecting "+this.buildInspectionLevel()+"
    "; + mHTML +="
      "; + this.types.sort(); + var so_objIndex=0; + for(i=0;i[+]" + this.types[i] + " (" + this.nameList[this.types[i]].length + ")
        "; + this.hidden["ul"+i]=0; + for(e=0;e= 0 && /^[a-zA-Z_]/.test(prop)) + { + if(this.displaying.indexOf("[object ") < 0) + more = " more"; + else if(this.displaying.indexOf("[object Window]") >= 0) + more = " more"; + } + mHTML+="
      • [+]" + prop + "
        • " + value + more + "
        "; + this.hidden["mul"+so_objIndex]=0; + so_objIndex++; + } + mHTML+="
      "; + } + mHTML+="
    "; + this.d.getElementById("so_mContainer").innerHTML =mHTML; + }, + + handleKeyEvent : function(e) { + keyCode=document.all?window.event.keyCode:e.keyCode; + if(keyCode==27) { + this.cleanUp(); + } + }, + + cleanUp : function() + { + if(this.d.getElementById("so_mContainer")) + { + this.d.body.removeChild(this.d.getElementById("so_mContainer")); + this.d.body.removeChild(this.d.getElementById("so_mStyle")); + if(typeof Event != "undefined") + Event.stopObserving(this.d, "keydown", this.dKeyDownEvent); + this.types = new Array(); + this.objs = new Array(); + this.hidden = new Array(); + } + }, + + disabled : document.all && !this.opera, + + inspect : function(obj) + { + if(this.disabled)return alert("Sorry, this only works in Mozilla and Firefox currently."); + this.cleanUp(); + mObj=this.d.body.appendChild(this.d.createElement("div")); + mObj.id="so_mContainer"; + sObj=this.d.body.appendChild(this.d.createElement("style")); + sObj.id="so_mStyle"; + sObj.type="text/css"; + sObj.innerHTML = this.style; + this.dKeyDownEvent = this.handleKeyEvent.bind(this); + if(typeof Event != "undefined") + Event.observe(this.d, "keydown", this.dKeyDownEvent); + + this.parseJS(obj); + this.buildTree(); + + cObj=mObj.appendChild(this.d.createElement("div")); + cObj.className="credits"; + cObj.innerHTML = "[esc] to close
    Javascript Object Tree V2.0."; + + window.scrollTo(0,0); + }, + + style : "#so_mContainer { position:absolute; top:5px; left:5px; background-color:#E3EBED; text-align:left; font:9pt verdana; width:85%; border:2px solid #000; padding:5px; z-index:1000; color:#000; } " + + "#so_mContainer ul { padding-left:20px; } " + + "#so_mContainer ul li { display:block; list-style-type:none; list-style-image:url(); line-height:2em; -moz-border-radius:.75em; font:10px verdana; padding:0; margin:2px; color:#000; } " + + "#so_mContainer li:hover { background-color:#E3EBED; } " + + "#so_mContainer ul li span { position:relative; width:15px; height:15px; margin-right:4px; } " + + "#so_mContainer pre { background-color:#F9FAFB; border:1px solid #638DA1; height:auto; padding:5px; font:9px verdana; color:#000; } " + + "#so_mContainer .topLevel { margin:0; padding:0; } " + + "#so_mContainer .credits { float:left; width:200px; font:6.5pt verdana; color:#000; padding:2px; margin-left:5px; text-align:left; border-top:1px solid #000; margin-top:15px; width:75%; } " + + "#so_mContainer .credits a { font:9px verdana; font-weight:bold; color:#004465; text-decoration:none; background-color:transparent; }" +}; + +//similar function to var_dump in PHP, brings up the javascript object tree UI. +function var_dump(obj) +{ + Prado.Inspector.inspect(obj); +} + +//similar function to print_r for PHP +var print_r = inspect; + diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/prado.js b/gui/baculum/framework/Web/Javascripts/source/prado/prado.js new file mode 100644 index 0000000000..05f11dcd3c --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/prado.js @@ -0,0 +1,94 @@ +/** + * Prado base namespace + * @namespace Prado + */ +var Prado = +{ + /** + * Version of Prado clientscripts + * @var Version + */ + Version: '3.2.3', + + /** + * Registry for Prado components + * @var Registry + */ + Registry: $H(), + + /** + * Returns browser information. + *
    +	 * var browser = Prado.Browser();
    +	 * alert(browser.ie); //should ouput true if IE, false otherwise
    +	 * 
    + * @function {object} ? + * @version 1.0 + * @returns browserinfo + * @... {string} agent - Reported user agent + * @... {string} ver - Reported agent version + * @... {0|1} dom - 1 for DOM browsers + * @... {0|1} ns4 - 1 for Netscape 4 + * @... {0|1} ns6 - 1 for Netscape 6 and Firefox + * @... {boolean} ie3 - true for IE 3 + * @... {0|1} ie5 - 1 for IE 5 + * @... {0|1} ie6 - 1 for IE 6 + * @... {0|1} ie4 - 1 for IE 4 + * @... {0|1} ie - 1 for IE 4-6 + * @... {0|1} hotjava - 1 for HotJava + * @... {0|1} ver3 - 1 for IE3 and HotJava + * @... {0|1} opera - 1 for Opera + * @... {boolean} opera7 - true for Opera 7 + * @... {0|1} operaOld - 1 for older Opera + * @... {0|1} bw - 1 for IE 4-6, Netscape 4&6, Firefox and Opera + * @... {boolean} mac - true for mac systems + * @... {static} Version - Version of returned structure (1.0) + */ + Browser : function() + { + var info = { Version : "1.0" }; + var is_major = parseInt( navigator.appVersion ); + info.nver = is_major; + info.ver = navigator.appVersion; + info.agent = navigator.userAgent; + info.dom = document.getElementById ? 1 : 0; + info.opera = window.opera ? 1 : 0; + info.ie5 = ( info.ver.indexOf( "MSIE 5" ) > -1 && info.dom && !info.opera ) ? 1 : 0; + info.ie6 = ( info.ver.indexOf( "MSIE 6" ) > -1 && info.dom && !info.opera ) ? 1 : 0; + info.ie4 = ( document.all && !info.dom && !info.opera ) ? 1 : 0; + info.ie = info.ie4 || info.ie5 || info.ie6; + info.mac = info.agent.indexOf( "Mac" ) > -1; + info.ns6 = ( info.dom && parseInt( info.ver ) >= 5 ) ? 1 : 0; + info.ie3 = ( info.ver.indexOf( "MSIE" ) && ( is_major < 4 ) ); + info.hotjava = ( info.agent.toLowerCase().indexOf( 'hotjava' ) != -1 ) ? 1 : 0; + info.ns4 = ( document.layers && !info.dom && !info.hotjava ) ? 1 : 0; + info.bw = ( info.ie6 || info.ie5 || info.ie4 || info.ns4 || info.ns6 || info.opera ); + info.ver3 = ( info.hotjava || info.ie3 ); + info.opera7 = ( ( info.agent.toLowerCase().indexOf( 'opera 7' ) > -1 ) || ( info.agent.toLowerCase().indexOf( 'opera/7' ) > -1 ) ); + info.operaOld = info.opera && !info.opera7; + return info; + }, + + /** + * Import CSS from Url. + * @function ? + * @param doc - document DOM object + * @param css_file - Url to CSS file + */ + ImportCss : function(doc, css_file) + { + if (Prado.Browser().ie) + var styleSheet = doc.createStyleSheet(css_file); + else + { + var elm = doc.createElement("link"); + + elm.rel = "stylesheet"; + elm.href = css_file; + var headArr; + + if (headArr = doc.getElementsByTagName("head")) + headArr[0].appendChild(elm); + } + } +}; diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks.css b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks.css new file mode 100644 index 0000000000..2bf2e90471 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks.css @@ -0,0 +1,29 @@ +.TRatingList_blocks +{ + border-collapse: collapse; +} +.TRatingList_blocks input, .TRatingList_blocks label +{ + display: none; +} +.TRatingList_blocks td +{ + width: 17px; + height: 18px; + background-image: url(blocks_blank.gif); + background-repeat: no-repeat; + cursor: pointer; +} +.TRatingList_blocks td.rating_selected +{ + background-image: url(blocks_selected.gif); +} + +.TRatingList_blocks td.rating_hover +{ + background-image: url(blocks_hover.gif); +} +.TRatingList_blocks td.rating_half +{ + background-image: url(blocks_half.gif); +} diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks.png b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks.png new file mode 100644 index 0000000000..16a2b24990 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks.png differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_blank.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_blank.gif new file mode 100644 index 0000000000..a68d0e94db Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_blank.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_half.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_half.gif new file mode 100644 index 0000000000..4cff0148c6 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_half.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_hover.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_hover.gif new file mode 100644 index 0000000000..58ad749595 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_hover.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_selected.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_selected.gif new file mode 100644 index 0000000000..f91873e2c7 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/blocks_selected.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default.css b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default.css new file mode 100644 index 0000000000..c15a36bdb3 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default.css @@ -0,0 +1,29 @@ +.TRatingList_default +{ + border-collapse: collapse; +} +.TRatingList_default input, .TRatingList_default label +{ + display: none; +} +.TRatingList_default td +{ + width: 17px; + height: 18px; + background-image: url(default_blank.gif); + background-repeat: no-repeat; + cursor: pointer; +} +.TRatingList_default td.rating_selected +{ + background-image: url(default_selected.gif); +} + +.TRatingList_default td.rating_hover +{ + background-image: url(default_hover.gif); +} +.TRatingList_default td.rating_half +{ + background-image: url(default_half.gif); +} diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_blank.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_blank.gif new file mode 100644 index 0000000000..4e6fda3ca6 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_blank.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_half.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_half.gif new file mode 100644 index 0000000000..7cecf0ec10 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_half.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_hover.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_hover.gif new file mode 100644 index 0000000000..ad0cd28a9f Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_hover.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_selected.gif b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_selected.gif new file mode 100644 index 0000000000..a19ab39f7c Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/default_selected.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/ratings/ratings.js b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/ratings.js new file mode 100644 index 0000000000..1369c74009 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/ratings/ratings.js @@ -0,0 +1,206 @@ +Prado.WebUI.TRatingList = Base.extend( +{ + selectedIndex : -1, + rating: -1, + readOnly : false, + + constructor : function(options) + { + var cap = $(options.CaptionID); + this.options = Object.extend( + { + caption : cap ? cap.innerHTML : '' + }, options || {}); + + Prado.WebUI.TRatingList.register(this); + this._init(); + Prado.Registry.set(options.ListID, this); + this.selectedIndex = options.SelectedIndex; + this.rating = options.Rating; + this.readOnly = options.ReadOnly + if(options.Rating <= 0 && options.SelectedIndex >= 0) + this.rating = options.SelectedIndex+1; + this.setReadOnly(this.readOnly); + }, + + _init: function(options) + { + Element.addClassName($(this.options.ListID),this.options.Style); + this.radios = new Array(); + this._mouseOvers = new Array(); + this._mouseOuts = new Array(); + this._clicks = new Array(); + var index=0; + for(var i = 0; i halfMax ? base+1 : base; + for(var i = 0; i halfMax ? base+1 : base; + var hasHalf = remainder >= halfMin && remainder <= halfMax; + for(var i = 0; i index ? 'removeClassName' : 'addClassName'; + Element[action](node, "rating_selected"); + if(i==index+1 && hasHalf) + Element.addClassName(node, "rating_half"); + else + Element.removeClassName(node, "rating_half"); + Element.removeClassName(node,"rating_hover"); + } + }, + + getIndexCaption : function(index) + { + return index > -1 ? this.radios[index].value : this.options.caption; + }, + + showCaption : function(value) + { + var caption = $(this.options.CaptionID); + if(caption) caption.innerHTML = value; + $(this.options.ListID).title = value; + }, + + setCaption : function(value) + { + this.options.caption = value; + this.showCaption(value); + }, + + setReadOnly : function(value) + { + this.readOnly = value; + for(var i = 0; iPrototype's Function + * @namespace Function + */ +/** + * Similar to bindAsEventLister, but takes additional arguments. + * @function Function.bindEvent + */ +Function.prototype.bindEvent = function() +{ + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) + { + return __method.apply(object, [event || window.event].concat(args)); + } +}; + +/** + * Extension to + * Prototype's Class + * @namespace Class + */ + +/** + * Creates a new class by copying class definition from + * the base and optional definition. + * @function {Class} Class.extend + * @param {function} base - Base class to copy from. + * @param {optional Array} - Additional definition + * @returns Constructor for the extended class + */ +Class.extend = function(base, definition) +{ + var component = Class.create(); + Object.extend(component.prototype, base.prototype); + if(definition) + Object.extend(component.prototype, definition); + return component; +}; + +/** + * Base, version 1.0.2 + * Copyright 2006, Dean Edwards + * License: http://creativecommons.org/licenses/LGPL/2.1/ + * @class Base + */ +var Base = function() { + if (arguments.length) { + if (this == window) { // cast an object to this class + Base.prototype.extend.call(arguments[0], arguments.callee.prototype); + } else { + this.extend(arguments[0]); + } + } +}; + +Base.version = "1.0.2"; + +Base.prototype = { + extend: function(source, value) { + var extend = Base.prototype.extend; + if (arguments.length == 2) { + var ancestor = this[source]; + // overriding? + if ((ancestor instanceof Function) && (value instanceof Function) && + ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) { + var method = value; + // var _prototype = this.constructor.prototype; + // var fromPrototype = !Base._prototyping && _prototype[source] == ancestor; + value = function() { + var previous = this.base; + // this.base = fromPrototype ? _prototype[source] : ancestor; + this.base = ancestor; + var returnValue = method.apply(this, arguments); + this.base = previous; + return returnValue; + }; + // point to the underlying method + value.valueOf = function() { + return method; + }; + value.toString = function() { + return String(method); + }; + } + return this[source] = value; + } else if (source) { + var _prototype = {toSource: null}; + // do the "toString" and other methods manually + var _protected = ["toString", "valueOf"]; + // if we are prototyping then include the constructor + if (Base._prototyping) _protected[2] = "constructor"; + var name; + for (var i = 0; (name = _protected[i]); i++) { + if (source[name] != _prototype[name]) { + extend.call(this, name, source[name]); + } + } + // copy each of the source object's properties to this object + for (var name in source) { + if (!_prototype[name]) { + extend.call(this, name, source[name]); + } + } + } + return this; + }, + + base: function() { + // call this method from any other method to invoke that method's ancestor + } +}; + +Base.extend = function(_instance, _static) { + var extend = Base.prototype.extend; + if (!_instance) _instance = {}; + // build the prototype + Base._prototyping = true; + var _prototype = new this; + extend.call(_prototype, _instance); + var constructor = _prototype.constructor; + _prototype.constructor = this; + delete Base._prototyping; + // create the wrapper for the constructor function + var klass = function() { + if (!Base._prototyping) constructor.apply(this, arguments); + this.constructor = klass; + }; + klass.prototype = _prototype; + // build the class interface + klass.extend = this.extend; + klass.implement = this.implement; + klass.toString = function() { + return String(constructor); + }; + extend.call(klass, _static); + // single instance + var object = constructor ? klass : _prototype; + // class initialisation + if (object.init instanceof Function) object.init(); + return object; +}; + +Base.implement = function(_interface) { + if (_interface instanceof Function) _interface = _interface.prototype; + this.prototype.extend(_interface); +}; + +/** + * Performs a PostBack using javascript. + * @function Prado.PostBack + * @param event - Event that triggered this postback + * @param options - Postback options + * @... {string} FormID - Form that should be posted back + * @... {optional boolean} CausesValidation - Validate before PostBack if true + * @... {optional string} ValidationGroup - Group to Validate + * @... {optional string} ID - Validation ID + * @... {optional string} PostBackUrl - Postback URL + * @... {optional boolean} TrackFocus - Keep track of focused element if true + * @... {string} EventTarget - Id of element that triggered PostBack + * @... {string} EventParameter - EventParameter for PostBack + */ +Prado.PostBack = function(event,options) +{ + var form = $(options['FormID']); + var canSubmit = true; + + if(options['CausesValidation'] && typeof(Prado.Validation) != "undefined") + { + if(!Prado.Validation.validate(options['FormID'], options['ValidationGroup'], $(options['ID']))) + return Event.stop(event); + } + + if(options['PostBackUrl'] && options['PostBackUrl'].length > 0) + form.action = options['PostBackUrl']; + + if(options['TrackFocus']) + { + var lastFocus = $('PRADO_LASTFOCUS'); + if(lastFocus) + { + var active = document.activeElement; //where did this come from + if(active) + lastFocus.value = active.id; + else + lastFocus.value = options['EventTarget']; + } + } + + $('PRADO_POSTBACK_TARGET').value = options['EventTarget']; + $('PRADO_POSTBACK_PARAMETER').value = options['EventParameter']; + /** + * Since google toolbar prevents browser default action, + * we will always disable default client-side browser action + */ + /*if(options['StopEvent']) */ + Event.stop(event); + Event.fireEvent(form,"submit"); + + $('PRADO_POSTBACK_TARGET').value = ''; + $('PRADO_POSTBACK_PARAMETER').value = ''; +}; + +/** + * Prado utilities to manipulate DOM elements. + * @object Prado.Element + */ +Prado.Element = +{ + /** + * Set the value of a particular element. + * @function ? + * @param {string} element - Element id + * @param {string} value - New element value + */ + setValue : function(element, value) + { + var el = $(element); + if(el && typeof(el.value) != "undefined") + el.value = value; + }, + + /** + * Select options from a selectable element. + * @function ? + * @param {string} element - Element id + * @param {string} method - Name of any {@link Prado.Element.Selection} method + * @param {array|boolean|string} value - Values that should be selected + * @param {int} total - Number of elements + */ + select : function(element, method, value, total) + { + var el = $(element); + if(!el) return; + var selection = Prado.Element.Selection; + if(typeof(selection[method]) == "function") + { + var control = selection.isSelectable(el) ? [el] : selection.getListElements(element,total); + selection[method](control, value); + } + }, + + /** + * Trigger a click event on a DOM element. + * @function ? + * @param {string} element - Element id + */ + click : function(element) + { + var el = $(element); + if(el) + el.click(); + }, + + /** + * Check if an DOM element is disabled. + * @function {boolean} ? + * @param {string} element - Element id + * @returns true if element is disabled + */ + isDisabled : function(element) + { + if(!element.attributes['disabled']) //FF + return false; + var value = element.attributes['disabled'].nodeValue; + if(typeof(value)=="string") + return value.toLowerCase() == "disabled"; + else + return value == true; + }, + + /** + * Sets an attribute of a DOM element. + * @function ? + * @param {string} element - Element id + * @param {string} attribute - Name of attribute + * @param {string} value - Value of attribute + */ + setAttribute : function(element, attribute, value) + { + var el = $(element); + if(!el) return; + if((attribute == "disabled" || attribute == "multiple" || attribute == "readonly" || attribute == "href") && value==false) + el.removeAttribute(attribute); + else if(attribute.match(/^on/i)) //event methods + { + try + { + eval("(func = function(event){"+value+"})"); + el[attribute] = func; + } + catch(e) + { + debugger; + throw "Error in evaluating '"+value+"' for attribute "+attribute+" for element "+element.id; + } + } + else + el.setAttribute(attribute, value); + }, + + /** + * Sets the options for a select element. + * @function ? + * @param {string} element - Element id + * @param {array[]} options - Array of options, each an array of structure + * [ "optionText" , "optionValue" , "optionGroup" ] + */ + setOptions : function(element, options) + { + var el = $(element); + if(!el) return; + var previousGroup = null; + var optGroup=null; + if(el && el.tagName.toLowerCase() == "select") + { + while(el.childNodes.length > 0) + el.removeChild(el.lastChild); + + var optDom = Prado.Element.createOptions(options); + for(var i = 0; i < optDom.length; i++) + el.appendChild(optDom[i]); + } + }, + + /** + * Create opt-group options from an array of options. + * @function {array} ? + * @param {array[]} options - Array of options, each an array of structure + * [ "optionText" , "optionValue" , "optionGroup" ] + * @returns Array of option DOM elements + */ + createOptions : function(options) + { + var previousGroup = null; + var optgroup=null; + var result = []; + for(var i = 0; i 2) + { + var group = option[2]; + if(group!=previousGroup) + { + if(previousGroup!=null && optgroup!=null) + { + result.push(optgroup); + previousGroup=null; + optgroup=null; + } + optgroup = document.createElement('optgroup'); + optgroup.label = group; + previousGroup = group; + } + } + var opt = document.createElement('option'); + opt.text = option[0]; + opt.innerHTML = option[0]; + opt.value = option[1]; + if(optgroup!=null) + optgroup.appendChild(opt); + else + result.push(opt); + } + if(optgroup!=null) + result.push(optgroup); + return result; + }, + + /** + * Set focus (delayed) on a particular element. + * @function ? + * @param {string} element - Element id + */ + focus : function(element) + { + var obj = $(element); + if(typeof(obj) != "undefined" && typeof(obj.focus) != "undefined") + setTimeout(function(){ obj.focus(); }, 100); + return false; + }, + + /** + * Replace a DOM element either with given content or + * with content from a CallBack response boundary + * using a replacement method. + * @function ? + * @param {string|element} element - DOM element or element id + * @param {string} method - Name of method to use for replacement + * @param {optional string} content - New content of element + * @param {optional string} boundary - Boundary of new content + */ + replace : function(element, method, content, boundary) + { + if(boundary) + { + var result = Prado.Element.extractContent(this.transport.responseText, boundary); + if(result != null) + content = result; + } + if(typeof(element) == "string") + { + if($(element)) + method.toFunction().apply(this,[element,""+content]); + } + else + { + method.toFunction().apply(this,[""+content]); + } + }, + + /** + * Appends a javascript block to the document. + * @function ? + * @param {string} boundary - Boundary containing the javascript code + */ + appendScriptBlock : function(boundary) + { + var content = Prado.Element.extractContent(this.transport.responseText, boundary); + if(content == null) + return; + + var el = document.createElement("script"); + el.type = "text/javascript"; + el.id = 'inline_' + boundary; + el.text = content; + + (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(el); + el.parentNode.removeChild(el); + }, + + /** + * Extract content from a text by its boundary id. + * Boundaries have this form: + *
    +	 * <!--123456-->Democontent<!--//123456-->
    +	 * 
    + * @function {string} ? + * @param {string} text - Text that contains boundaries + * @param {string} boundary - Boundary id + * @returns Content from given boundaries + */ + extractContent : function(text, boundary) + { + var tagStart = ''; + var tagEnd = ''; + var start = text.indexOf(tagStart); + if(start > -1) + { + start += tagStart.length; + var end = text.indexOf(tagEnd,start); + if(end > -1) + return text.substring(start,end); + } + return null; + /*var f = RegExp('(?:)((?:.|\n|\r)+?)(?:)',"m"); + var result = text.match(f); + if(result && result.length >= 2) + return result[1]; + else + return null;*/ + }, + + /** + * Evaluate a javascript snippet from a string. + * @function ? + * @param {string} content - String containing the script + */ + evaluateScript : function(content) + { + try + { + content.evalScripts(); + } + catch(e) + { + if(typeof(Logger) != "undefined") + Logger.error('Error during evaluation of script "'+content+'"'); + else + debugger; + throw e; + } + }, + + /** + * Set CSS style with Camelized keys. + * See Prototype's + * Element.setStyle for details. + * @function ? + * @param {string|element} element - DOM element or element id + * @param {object} styles - Object with style properties/values + */ + setStyle : function (element, styles) + { + var s = {} + // Camelize all styles keys + for (var property in styles) + { + s[property.camelize()]=styles[property].camelize(); + } + Element.setStyle(element, s); + } +}; + +/** + * Utilities for selections. + * @object Prado.Element.Selection + */ +Prado.Element.Selection = +{ + /** + * Check if an DOM element can be selected. + * @function {boolean} ? + * @param {element} el - DOM elemet + * @returns true if element is selectable + */ + isSelectable : function(el) + { + if(el && el.type) + { + switch(el.type.toLowerCase()) + { + case 'checkbox': + case 'radio': + case 'select': + case 'select-multiple': + case 'select-one': + return true; + } + } + return false; + }, + + /** + * Set checked attribute of a checkbox or radiobutton to value. + * @function {boolean} ? + * @param {element} el - DOM element + * @param {boolean} value - New value of checked attribute + * @returns New value of checked attribute + */ + inputValue : function(el, value) + { + switch(el.type.toLowerCase()) + { + case 'checkbox': + case 'radio': + return el.checked = value; + } + }, + + /** + * Set selected attribute for elements options by value. + * If value is boolean, all elements options selected attribute will be set + * to value. Otherwhise all options that have the given value will be selected. + * @function ? + * @param {element[]} elements - Array of selectable DOM elements + * @param {boolean|string} value - Value of options that should be selected or boolean value of selection status + */ + selectValue : function(elements, value) + { + elements.each(function(el) + { + $A(el.options).each(function(option) + { + if(typeof(value) == "boolean") + option.selected = value; + else if(option.value == value) + option.selected = true; + }); + }) + }, + + /** + * Set selected attribute for elements options by array of values. + * @function ? + * @param {element[]} elements - Array of selectable DOM elements + * @param {string[]} value - Array of values to select + */ + selectValues : function(elements, values) + { + var selection = this; + values.each(function(value) + { + selection.selectValue(elements,value); + }) + }, + + /** + * Set selected attribute for elements options by option index. + * @function ? + * @param {element[]} elements - Array of selectable DOM elements + * @param {int} index - Index of option to select + */ + selectIndex : function(elements, index) + { + elements.each(function(el) + { + if(el.type.toLowerCase() == 'select-one') + el.selectedIndex = index; + else + { + for(var i = 0; iScriptaculous' Builder + * @namespace Builder + */ + +Object.extend(Builder, +{ + /** + * Export scriptaculous builder utilities as window[functions] + * @function ? + */ + exportTags:function() + { + var tags=["BUTTON","TT","PRE","H1","H2","H3","BR","CANVAS","HR","LABEL","TEXTAREA","FORM","STRONG","SELECT","OPTION","OPTGROUP","LEGEND","FIELDSET","P","UL","OL","LI","TD","TR","THEAD","TBODY","TFOOT","TABLE","TH","INPUT","SPAN","A","DIV","IMG", "CAPTION"]; + tags.each(function(tag) + { + window[tag]=function() + { + var args=$A(arguments); + if(args.length==0) + return Builder.node(tag,null); + if(args.length==1) + return Builder.node(tag,args[0]); + if(args.length>1) + return Builder.node(tag,args.shift(),args); + + }; + }); + } +}); +Builder.exportTags(); + +/** + * Extension to + * Prototype's String + * @namespace String + */ +Object.extend(String.prototype, { + + /** + * Add padding to string + * @function {string} ? + * @param {string} side - "left" to pad the string on the left, "right" to pad right. + * @param {int} len - Minimum string length. + * @param {string} chr - Character(s) to pad + * @returns Padded string + */ + pad : function(side, len, chr) { + if (!chr) chr = ' '; + var s = this; + var left = side.toLowerCase()=='left'; + while (s.lengthExample: + *
     
    +	 * "Prado.AJAX.Callback.Action.setValue".toFunction()
    +	 * 
    + * @function {function} ? + * @returns Reference to the corresponding function + */ + toFunction : function() + { + var commands = this.split(/\./); + var command = window; + commands.each(function(action) + { + if(command[new String(action)]) + command=command[new String(action)]; + }); + if(typeof(command) == "function") + return command; + else + { + if(typeof Logger != "undefined") + Logger.error("Missing function", this); + + throw new Error ("Missing function '"+this+"'"); + } + }, + + /** + * Convert string into integer, returns null if not integer. + * @function {int} ? + * @returns Integer, null if string does not represent an integer. + */ + toInteger : function() + { + var exp = /^\s*[-\+]?\d+\s*$/; + if (this.match(exp) == null) + return null; + var num = parseInt(this, 10); + return (isNaN(num) ? null : num); + }, + + /** + * Convert string into a double/float value. Internationalization + * is not supported + * @function {double} ? + * @param {string} decimalchar - Decimal character, defaults to "." + * @returns Double, null if string does not represent a float value + */ + toDouble : function(decimalchar) + { + if(this.length <= 0) return null; + decimalchar = decimalchar || "."; + var exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + decimalchar + "(\\d+))?\\s*$"); + var m = this.match(exp); + + if (m == null) + return null; + m[1] = m[1] || ""; + m[2] = m[2] || "0"; + m[4] = m[4] || "0"; + + var cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4]; + var num = parseFloat(cleanInput); + return (isNaN(num) ? null : num); + }, + + /** + * Convert strings that represent a currency value to float. + * E.g. "10,000.50" will become "10000.50". The number + * of dicimal digits, grouping and decimal characters can be specified. + * The currency input format is very strict, null will be returned if + * the pattern does not match. + * @function {double} ? + * @param {string} groupchar - Grouping character, defaults to "," + * @param {int} digits - Number of decimal digits + * @param {string} decimalchar - Decimal character, defaults to "." + * @returns Double, null if string does not represent a currency value + */ + toCurrency : function(groupchar, digits, decimalchar) + { + groupchar = groupchar || ","; + decimalchar = decimalchar || "."; + digits = typeof(digits) == "undefined" ? 2 : digits; + + var exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + groupchar + ")*)(\\d+)" + + ((digits > 0) ? "(\\" + decimalchar + "(\\d{1," + digits + "}))?" : "") + + "\\s*$"); + var m = this.match(exp); + if (m == null) + return null; + var intermed = m[2] + m[5] ; + var cleanInput = m[1] + intermed.replace( + new RegExp("(\\" + groupchar + ")", "g"), "") + + ((digits > 0) ? "." + m[7] : ""); + var num = parseFloat(cleanInput); + return (isNaN(num) ? null : num); + }, + + /** + * Converts the string to a date by finding values that matches the + * date format pattern. + * @function {Date} ? + * @param {string} format - Date format pattern, e.g. MM-dd-yyyy + * @returns Date extracted from the string + */ + toDate : function(format) + { + return Date.SimpleParse(this, format); + } +}); + +/** + * Extension to + * Prototype's Event + * @namespace Event + */ +Object.extend(Event, +{ + /** + * Register a function to be executed when the page is loaded. + * Note that the page is only loaded if all resources (e.g. images) + * are loaded. + *
    Example: + *
    Show an alert box with message "Page Loaded!" when the + * page finished loading. + *
    +	 * Event.OnLoad(function(){ alert("Page Loaded!"); });
    +	 * 
    + * @function ? + * @param {function} fn - Function to execute when page is loaded. + */ + OnLoad : function (fn) + { + // opera onload is in document, not window + var w = document.addEventListener && + !window.addEventListener ? document : window; + Event.observe(w,'load',fn); + }, + + /** + * Returns the unicode character generated by key. + * @param {event} e - Keyboard event + * @function {int} ? + * @returns Unicode character code generated by the key that was struck. + */ + keyCode : function(e) + { + return e.keyCode != null ? e.keyCode : e.charCode + }, + + /** + * Checks if an Event is of type HTMLEvent. + * @function {boolean} ? + * @param {string} type - Event type or event name. + * @return true if event is of type HTMLEvent. + */ + isHTMLEvent : function(type) + { + var events = ['abort', 'blur', 'change', 'error', 'focus', + 'load', 'reset', 'resize', 'scroll', 'select', + 'submit', 'unload']; + return events.include(type); + }, + + /** + * Checks if an Event is a mouse event. + * @function {boolean} ? + * @param {string} type - Event type or event name + * @return true if event is of type MouseEvent. + */ + isMouseEvent : function(type) + { + var events = ['click', 'mousedown', 'mousemove', 'mouseout', + 'mouseover', 'mouseup']; + return events.include(type); + }, + + /** + * Dispatch the DOM event of a given type on a DOM + * element. Only HTMLEvent and MouseEvent can be + * dispatched, keyboard events or UIEvent can not be dispatch + * via javascript consistently. + * For the "submit" event the submit() method is called. + * @function ? + * @param {element|string} element - Element id string or DOM element. + * @param {string} type - Event type to dispatch. + */ + fireEvent : function(element,type) + { + element = $(element); + if(type == "submit") + return element.submit(); + if(document.createEvent) + { + if(Event.isHTMLEvent(type)) + { + var event = document.createEvent('HTMLEvents'); + event.initEvent(type, true, true); + } + else if(Event.isMouseEvent(type)) + { + var event = document.createEvent('MouseEvents'); + if (event.initMouseEvent) + { + event.initMouseEvent(type,true,true, + document.defaultView, 1, 0, 0, 0, 0, false, + false, false, false, 0, null); + } + else + { + // Safari + // TODO we should be initialising other mouse-event related attributes here + event.initEvent(type, true, true); + } + } + element.dispatchEvent(event); + } + else if(document.createEventObject) + { + var evObj = document.createEventObject(); + element.fireEvent('on'+type, evObj); + } + else if(typeof(element['on'+type]) == "function") + element['on'+type](); + } +}); + + +/** + * Extension to + * Prototype's Date + * @namespace Date + */ +Object.extend(Date.prototype, +{ + /** + * SimpleFormat + * @function ? + * @param {string} format - TODO + * @param {string} data - TODO + * @returns TODO + */ + SimpleFormat: function(format, data) + { + data = data || {}; + var bits = new Array(); + bits['d'] = this.getDate(); + bits['dd'] = String(this.getDate()).zerofill(2); + + bits['M'] = this.getMonth()+1; + bits['MM'] = String(this.getMonth()+1).zerofill(2); + if(data.AbbreviatedMonthNames) + bits['MMM'] = data.AbbreviatedMonthNames[this.getMonth()]; + if(data.MonthNames) + bits['MMMM'] = data.MonthNames[this.getMonth()]; + var yearStr = "" + this.getFullYear(); + yearStr = (yearStr.length == 2) ? '19' + yearStr: yearStr; + bits['yyyy'] = yearStr; + bits['yy'] = bits['yyyy'].toString().substr(2,2); + + // do some funky regexs to replace the format string + // with the real values + var frm = new String(format); + for (var sect in bits) + { + var reg = new RegExp("\\b"+sect+"\\b" ,"g"); + frm = frm.replace(reg, bits[sect]); + } + return frm; + }, + + /** + * toISODate + * @function {string} ? + * @returns TODO + */ + toISODate : function() + { + var y = this.getFullYear(); + var m = String(this.getMonth() + 1).zerofill(2); + var d = String(this.getDate()).zerofill(2); + return String(y) + String(m) + String(d); + } +}); + +Object.extend(Date, +{ + /** + * SimpleParse + * @function ? + * @param {string} format - TODO + * @param {string} data - TODO + * @returns TODO + */ + SimpleParse: function(value, format) + { + var val=String(value); + format=String(format); + + if(val.length <= 0) return null; + + if(format.length <= 0) return new Date(value); + + var isInteger = function (val) + { + var digits="1234567890"; + for (var i=0; i < val.length; i++) + { + if (digits.indexOf(val.charAt(i))==-1) { return false; } + } + return true; + }; + + var getInt = function(str,i,minlength,maxlength) + { + for (var x=maxlength; x>=minlength; x--) + { + var token=str.substring(i,i+x); + if (token.length < minlength) { return null; } + if (isInteger(token)) { return token; } + } + return null; + }; + + var i_val=0; + var i_format=0; + var c=""; + var token=""; + var token2=""; + var x,y; + var now=new Date(); + var year=now.getFullYear(); + var month=now.getMonth()+1; + var date=1; + + while (i_format < format.length) + { + // Get next token from format string + c=format.charAt(i_format); + token=""; + while ((format.charAt(i_format)==c) && (i_format < format.length)) + { + token += format.charAt(i_format++); + } + + // Extract contents of value based on format token + if (token=="yyyy" || token=="yy" || token=="y") + { + if (token=="yyyy") { x=4;y=4; } + if (token=="yy") { x=2;y=2; } + if (token=="y") { x=2;y=4; } + year=getInt(val,i_val,x,y); + if (year==null) { return null; } + i_val += year.length; + if (year.length==2) + { + if (year > 70) { year=1900+(year-0); } + else { year=2000+(year-0); } + } + } + + else if (token=="MM"||token=="M") + { + month=getInt(val,i_val,token.length,2); + if(month==null||(month<1)||(month>12)){return null;} + i_val+=month.length; + } + else if (token=="dd"||token=="d") + { + date=getInt(val,i_val,token.length,2); + if(date==null||(date<1)||(date>31)){return null;} + i_val+=date.length; + } + else + { + if (val.substring(i_val,i_val+token.length)!=token) {return null;} + else {i_val+=token.length;} + } + } + + // If there are any trailing characters left in the value, it doesn't match + if (i_val != val.length) { return null; } + + // Is date valid for month? + if (month==2) + { + // Check for leap year + if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year + if (date > 29){ return null; } + } + else { if (date > 28) { return null; } } + } + + if ((month==4)||(month==6)||(month==9)||(month==11)) + { + if (date > 30) { return null; } + } + + var newdate=new Date(year,month-1,date, 0, 0, 0); + return newdate; + } +}); + +/** + * Prado utilities for effects. + * @object Prado.Effect + */ +Prado.Effect = +{ + /** + * Highlights an element + * @function ? + * @param {element} element - DOM element to highlight + * @param {optional object} options - Highlight options + */ + Highlight : function (element,options) + { + new Effect.Highlight(element,options); + } +}; diff --git a/gui/baculum/framework/Web/Javascripts/source/prado/validator/validation3.js b/gui/baculum/framework/Web/Javascripts/source/prado/validator/validation3.js new file mode 100644 index 0000000000..0361389fcc --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prado/validator/validation3.js @@ -0,0 +1,1945 @@ +/** + * Prado client-side javascript validation fascade. + * + *

    There are 4 basic classes: {@link Prado.Validation}, + * {@link Prado.ValidationManager}, {@link Prado.WebUI.TValidationSummary} + * and {@link Prado.WebUI.TBaseValidator}, + * that interact together to perform validation. + * The {@link Prado.Validation} class co-ordinates together the + * validation scheme and is responsible for maintaining references + * to ValidationManagers.

    + * + *

    The {@link Prado.ValidationManager} class is responsible for + * maintaining refereneces + * to individual validators, validation summaries and their associated + * groupings.

    + * + *

    The {@link Prado.WebUI.TValidationSummary} takes care of displaying + * the validator error messages + * as html output or an alert output.

    + * + *

    The {@link Prado.WebUI.TBaseValidator} is the base class for all + * validators and contains + * methods to interact with the actual inputs, data type conversion.

    + * + *

    An instance of {@link Prado.ValidationManager} must be instantiated first for a + * particular form before instantiating validators and summaries.

    + * + *

    Usage example: adding a required field to a text box input with + * ID "input1" in a form with ID "form1".

    + *
    + * <script type="text/javascript" src="../prado.js"></script>
    + * <script type="text/javascript" src="../validator.js"></script>
    + * <form id="form1" action="...">
    + * <div>
    + * 	<input type="text" id="input1" />
    + *  <span id="validator1" style="display:none; color:red">*</span>
    + *  <input type="submit text="submit" />
    + * <script type="text/javascript">
    + * new Prado.ValidationManager({FormID : 'form1'});
    + * var options =
    + * {
    + *		ID :				'validator1',
    + *		FormID :			'form1',
    + *		ErrorMessage :		'*',
    + *		ControlToValidate : 'input1'
    + * }
    + * new Prado.WebUI.TRequiredFieldValidator(options);
    + * new Prado.WebUI.TValidationSummary({ID:'summary1',FormID:'form1'});
    + *
    + * //watch the form onsubmit event, check validators, stop if not valid.
    + * Event.observe("form1", "submit" function(ev)
    + * {
    + * 	 if(Prado.WebUI.Validation.isValid("form1") == false)
    + * 		Event.stop(ev);
    + * });
    + * </script>
    + * </div>
    + * </form>
    + * 
    + * + * @module validation + */ + +Prado.Validation = Class.create(); + +/** + * Global Validation Object. + * + *

    To validate the inputs of a particular form, call + * {@link Prado.Validation.validate}(formID, groupID) + * where formID is the HTML form ID, and the optional + * groupID if present will only validate the validators + * in a particular group.

    + *

    Use {@link Prado.Validation.validateControl}(controlClientID) + * to trigger validation for a single control.

    + * + * @object {static} Prado.Validation + */ +Object.extend(Prado.Validation, +{ + /** + * Hash of registered validation managers + * @var managers + */ + managers : {}, + + /** + * Validate the validators (those that DO NOT + * belong to a particular group) in the form specified by the + * formID parameter. If groupID is specified + * only validators belonging to that group will be validated. + * @function {boolean} ? + * @param {string} formID - ID of the form to validate + * @param {string} groupID - ID of the ValidationGroup to validate. + * @param {element} invoker - DOM element that calls for validation + * @returns true if validation succeeded + */ + validate : function(formID, groupID, invoker) + { + formID = formID || this.getForm(); + if(this.managers[formID]) + { + return this.managers[formID].validate(groupID, invoker); + } + else + { + throw new Error("Form '"+formID+"' is not registered with Prado.Validation"); + } + }, + + /** + * Validate all validators of a specific control. + * @function {boolean} ? + * @param {string} id - ID of DOM element to validate + * @return true if all validators are valid or no validators present, false otherwise. + */ + validateControl : function(id) + { + var formId=this.getForm(); + + if (this.managers[formId]) + { + return this.managers[formId].validateControl(id); + } else { + throw new Error("A validation manager needs to be created first."); + } + }, + + /** + * Return first registered form + * @function {string} ? + * @returns ID of first form. + */ + getForm : function() + { + var keys = $H(this.managers).keys(); + return keys[0]; + }, + + /** + * Check if the validators are valid for a particular form (and group). + * The validators states will not be changed. + * The validate function should be called first. + * @function {boolean} ? + * @param {string} formID - ID of the form to validate + * @param {string} groupID - ID of the ValiationGroup to validate. + * @return true if form is valid + */ + isValid : function(formID, groupID) + { + formID = formID || this.getForm(); + if(this.managers[formID]) + return this.managers[formID].isValid(groupID); + return true; + }, + + /** + * Reset the validators for a given group. + * The group is searched in the first registered form. + * @function ? + * @param {string} groupID - ID of the ValidationGroup to reset. + */ + reset : function(groupID) + { + var formID = this.getForm(); + if(this.managers[formID]) + this.managers[formID].reset(groupID); + }, + + /** + * Add a new validator to a particular form. + * @function {ValidationManager} ? + * @param {string} formID - ID of the form that the validator belongs to. + * @param {TBaseValidator} validator - Validator object + * @return ValidationManager for the form + */ + addValidator : function(formID, validator) + { + if(this.managers[formID]) + this.managers[formID].addValidator(validator); + else + throw new Error("A validation manager for form '"+formID+"' needs to be created first."); + return this.managers[formID]; + }, + + /** + * Add a new validation summary. + * @function {ValidationManager} ? + * @param {string} formID - ID of the form that the validation summary belongs to. + * @param {TValidationSummary} validator - TValidationSummary object + * @return ValidationManager for the form + */ + addSummary : function(formID, validator) + { + if(this.managers[formID]) + this.managers[formID].addSummary(validator); + else + throw new Error("A validation manager for form '"+formID+"' needs to be created first."); + return this.managers[formID]; + }, + + setErrorMessage : function(validatorID, message) + { + $H(Prado.Validation.managers).each(function(manager) + { + manager[1].validators.each(function(validator) + { + if(validator.options.ID == validatorID) + { + validator.options.ErrorMessage = message; + $(validatorID).innerHTML = message; + } + }); + }); + }, + + updateActiveCustomValidator : function(validatorID, isValid) + { + $H(Prado.Validation.managers).each(function(manager) + { + manager[1].validators.each(function(validator) + { + if(validator.options.ID == validatorID) + { + validator.updateIsValid(isValid); + } + }); + }); + } +}); + +/** + * Manages validators for a particular HTML form. + * + *

    The manager contains references to all the validators + * summaries, and their groupings for a particular form. + * Generally, {@link Prado.Validation} methods should be called rather + * than calling directly the ValidationManager.

    + * + * @class Prado.ValidationManager + */ +Prado.ValidationManager = Class.create(); +Prado.ValidationManager.prototype = +{ + /** + * Hash of registered validators by control's clientID + * @var controls + */ + controls: {}, + + /** + * Initialize ValidationManager. + * @constructor {protected} ? + * @param {object} options - Options for initialization + * @... {string} FormID - ID of form of this manager + */ + initialize : function(options) + { + if(!Prado.Validation.managers[options.FormID]) + { + /** + * List of validators + * @var {TBaseValidator[]} validators + */ + this.validators = []; + /** + * List of validation summaries + * @var {TValidationSummary[]} summaries + */ + this.summaries = []; + /** + * List of ValidationGroups + * @var {string[]} groups + */ + this.groups = []; + /** + * Options of this ValidationManager + * @var {object} options + */ + this.options = {}; + + this.options = options; + + Prado.Validation.managers[options.FormID] = this; + } + else + { + var manager = Prado.Validation.managers[options.FormID]; + this.validators = manager.validators; + this.summaries = manager.summaries; + this.groups = manager.groups; + this.options = manager.options; + } + }, + + /** + * Reset all validators in the given group. + * If group is null, validators without a group are used. + * @function ? + * @param {string} group - ID of ValidationGroup + */ + reset : function(group) + { + this.validatorPartition(group)[0].invoke('reset'); + this.updateSummary(group, true); + }, + + /** + * Validate the validators managed by this validation manager. + * If group is set, only validate validators in that group. + * @function {boolean} ? + * @param {optional string} group - ID of ValidationGroup + * @param {element} source - DOM element that calls for validation + * @return true if all validators are valid, false otherwise. + */ + validate : function(group, source) + { + var partition = this.validatorPartition(group); + var valid = partition[0].invoke('validate', source).all(); + this.focusOnError(partition[0]); + partition[1].invoke('hide'); + this.updateSummary(group, true); + return valid; + }, + + /** + * Perform validation for all validators of a single control. + * @function {boolean} ? + * @param {string} id - ID of DOM element to validate + * @return true if all validators are valid or no validators present, false otherwise. + */ + validateControl : function (id) + { + return this.controls[id] ? this.controls[id].invoke('validate',null).all() : true; + }, + + /** + * Focus on the first validator that is invalid and options.FocusOnError is true. + * @function ? + * @param {TBaseValidator[]} validators - Array of validator objects + */ + focusOnError : function(validators) + { + for(var i = 0; i < validators.length; i++) + { + if(!validators[i].isValid && validators[i].options.FocusOnError) + return Prado.Element.focus(validators[i].options.FocusElementID); + } + }, + + /** + * Get all validators in a group and all other validators. + * Returns an array with two arrays of validators. The first + * array contains all validators in the group if group is given, + * otherwhise all validators without a group. The second array + * contains all other validators. + * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? + * @param {optional string} group - ID of ValidationGroup + * @return Array with two arrays of validators. + */ + validatorPartition : function(group) + { + return group ? this.validatorsInGroup(group) : this.validatorsWithoutGroup(); + }, + + /** + * Get all validators in a group. + * Returns an array with two arrays of validators. The first + * array contains all validators in the group. The second array + * contains all other validators. + * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? + * @param {optional string} groupID - ID of ValidationGroup + * @return Array with two arrays of validators. + */ + validatorsInGroup : function(groupID) + { + if(this.groups.include(groupID)) + { + return this.validators.partition(function(val) + { + return val.group == groupID; + }); + } + else + return [[],[]]; + }, + + /** + * Get all validators without a group. + * Returns an array with two arrays of validators. The first + * array contains all validators without a group. The second + * array contains all other validators. + * @function {[ TBaseValidator[] , TBaseValidator[] ]} ? + * @return Array with two arrays of validators: Array[0] has all + * validators without a group, Array[1] all other validators. + */ + validatorsWithoutGroup : function() + { + return this.validators.partition(function(val) + { + return !val.group; + }); + }, + + /** + * Get the state of validators. + * If group is set, only validators in that group are checked. + * Otherwhise only validators without a group are checked. + * @function {booelan} ? + * @param {optional string} group - ID of ValidationGroup + * @return true if all validators (in a group, if supplied) are valid. + */ + isValid : function(group) + { + return this.validatorPartition(group)[0].pluck('isValid').all(); + }, + + /** + * Add a validator to this manager. + * @function ? + * @param {TBaseValidator} validator - Validator object + */ + addValidator : function(validator) + { + // Remove previously registered validator with same ID + // to prevent stale validators created by AJAX updates + this.removeValidator(validator); + + this.validators.push(validator); + if(validator.group && !this.groups.include(validator.group)) + this.groups.push(validator.group); + + if (typeof this.controls[validator.control.id] === 'undefined') + this.controls[validator.control.id] = Array(); + this.controls[validator.control.id].push(validator); + }, + + /** + * Add a validation summary. + * @function ? + * @param {TValidationSummary} summary - Validation summary. + */ + addSummary : function(summary) + { + this.summaries.push(summary); + }, + + /** + * Remove a validator from this manager + * @function ? + * @param {TBaseValidator} validator - Validator object + */ + removeValidator : function(validator) + { + this.validators = this.validators.reject(function(v) + { + return (v.options.ID==validator.options.ID); + }); + if (this.controls[validator.control.id]) + this.controls[validator.control.id].reject( function(v) + { + return (v.options.ID==validator.options.ID) + }); + }, + + /** + * Gets validators with errors. + * If group is set, only validators in that group are returned. + * Otherwhise only validators without a group are returned. + * @function {TBaseValidator[]} ? + * @param {optional string} group - ID of ValidationGroup + * @return array list of validators with error. + */ + getValidatorsWithError : function(group) + { + return this.validatorPartition(group)[0].findAll(function(validator) + { + return !validator.isValid; + }); + }, + + /** + * Update the summary of a particular group. + * If group is set, only the summary for validators in that + * group is updated. Otherwhise only the summary for validators + * without a group is updated. + * @function ? + * @param {optional string} group - ID of ValidationGroup + * @param {boolean} refresh - Wether the summary should be refreshed + */ + updateSummary : function(group, refresh) + { + var validators = this.getValidatorsWithError(group); + this.summaries.each(function(summary) + { + var inGroup = group && summary.group == group; + var noGroup = !group || !summary.group; + if(inGroup || noGroup) + summary.updateSummary(validators, refresh); + else + summary.hideSummary(true); + }); + } +}; + +/** + * TValidationSummary displays a summary of validation errors. + * + *

    The summary is displayed inline on a Web page, + * in a message box, or both. By default, a validation summary will collect + * ErrorMessage of all failed validators on the page. If + * ValidationGroup is not empty, only those validators who belong + * to the group will show their error messages in the summary.

    + * + *

    The summary can be displayed as a list, as a bulleted list, or as a single + * paragraph based on the DisplayMode option. + * The messages shown can be prefixed with HeaderText.

    + * + *

    The summary can be displayed on the Web page and in a message box by setting + * the ShowSummary and ShowMessageBox + * options, respectively.

    + * + * @class Prado.WebUI.TValidationSummary + */ +Prado.WebUI.TValidationSummary = Class.create(); +Prado.WebUI.TValidationSummary.prototype = +{ + /** + * Initialize TValidationSummary. + * @constructor {protected} ? + * @param {object} options - Options for initialization + * @... {string} ID - ID of validation summary element + * @... {string} FormID - ID of form of this manager + * @... {optional string} ValidationGroup - ID of ValidationGroup. + * @... {optional boolean} ShowMessageBox - true to show the summary in an alert box. + * @... {optional boolean} ShowSummary - true to show the inline summary. + * @... {optional string} HeaderText - Summary header text + * @... {optional string} DisplayMode - Summary display style, 'BulletList', 'List', 'SingleParagraph' + * @... {optional boolean} Refresh - true to update the summary upon validator state change. + * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'. + * @... {optional boolean} ScrollToSummary - true to scroll to the validation summary upon refresh. + * @... {optional function} OnHideSummary - Called on hide event. + * @... {optional function} OnShowSummary - Called on show event. + */ + initialize : function(options) + { + /** + * Validator options + * @var {object} options + */ + this.options = options; + /** + * ValidationGroup + * @var {string} group + */ + this.group = options.ValidationGroup; + /** + * Summary DOM element + * @var {element} messages + */ + this.messages = $(options.ID); + Prado.Registry.set(options.ID, this); + if(this.messages) + { + /** + * Current visibility state of summary + * @var {boolean} visible + */ + this.visible = this.messages.style.visibility != "hidden" + this.visible = this.visible && this.messages.style.display != "none"; + Prado.Validation.addSummary(options.FormID, this); + } + }, + + /** + * Update the validation summary. + * @function ? + * @param {TBaseValidator[]} validators - List of validators that failed validation. + * @param {boolean} update - true if visible summary should be updated + */ + updateSummary : function(validators, update) + { + if(validators.length <= 0) + { + if(update || this.options.Refresh != false) + { + return this.hideSummary(validators); + } + return; + } + + var refresh = update || this.visible == false || this.options.Refresh != false; + // Also, do not refresh summary if at least 1 validator is waiting for callback response. + // This will avoid the flickering of summary if the validator passes its test + refresh = refresh && validators.any(function(v) { return !v.requestDispatched; }); + + if(this.options.ShowSummary != false && refresh) + { + this.updateHTMLMessages(this.getMessages(validators)); + this.showSummary(validators); + } + + if(this.options.ScrollToSummary != false && refresh) + window.scrollTo(this.messages.offsetLeft-20, this.messages.offsetTop-20); + + if(this.options.ShowMessageBox == true && refresh) + { + this.alertMessages(this.getMessages(validators)); + this.visible = true; + } + }, + + /** + * Display the validator error messages as inline HTML. + * @function ? + * @param {string[]} messages - Array of error messages. + */ + updateHTMLMessages : function(messages) + { + while(this.messages.childNodes.length > 0) + this.messages.removeChild(this.messages.lastChild); + this.messages.insert(this.formatSummary(messages)); + }, + + /** + * Display the validator error messages as an alert box. + * @function ? + * @param {string[]} messages - Array of error messages. + */ + alertMessages : function(messages) + { + var text = this.formatMessageBox(messages); + setTimeout(function(){ alert(text); },20); + }, + + /** + * Get messages from validators. + * @function {string[]} ? + * @param {TBaseValidator[]} validators - Array of validators. + * @return Array of validator error messages. + */ + getMessages : function(validators) + { + var messages = []; + validators.each(function(validator) + { + var message = validator.getErrorMessage(); + if(typeof(message) == 'string' && message.length > 0) + messages.push(message); + }) + return messages; + }, + + /** + * Hide the validation summary. + * @function ? + * @param {TBaseValidator[]} validators - Array of validators. + */ + hideSummary : function(validators) + { if(typeof(this.options.OnHideSummary) == "function") + { + this.messages.style.visibility="visible"; + this.options.OnHideSummary(this,validators) + } + else + { + this.messages.style.visibility="hidden"; + if(this.options.Display == "None" || this.options.Display == "Dynamic") + this.messages.hide(); + } + this.visible = false; + }, + + /** + * Shows the validation summary. + * @function ? + * @param {TBaseValidator[]} validators - Array of validators. + */ + showSummary : function(validators) + { + this.messages.style.visibility="visible"; + if(typeof(this.options.OnShowSummary) == "function") + this.options.OnShowSummary(this,validators); + else + this.messages.show(); + this.visible = true; + }, + + /** + * Return the format parameters for the summary. + * @function {object} ? + * @param {string} type - Format type: "List", "SingleParagraph", "HeaderOnly" or "BulletList" (default) + * @return Object with format parameters: + * @... {string} header - Text for header + * @... {string} first - Text to prepend before message list + * @... {string} pre - Text to prepend before each message + * @... {string} post - Text to append after each message + * @... {string} first - Text to append after message list + */ + formats : function(type) + { + switch(type) + { + case "SimpleList": + return { header : "
    ", first : "", pre : "", post : "
    ", last : ""}; + case "SingleParagraph": + return { header : " ", first : "", pre : "", post : " ", last : "
    "}; + case "HeaderOnly": + return { header : "", first : ""}; + case "BulletList": + default: + return { header : "", first : "
      ", pre : "
    • ", post : "
    • ", last : "
    "}; + } + }, + + /** + * Format the message summary. + * @function {string} ? + * @param {string[]} messages - Array of error messages. + * @return Formatted message + */ + formatSummary : function(messages) + { + var format = this.formats(this.options.DisplayMode); + var output = this.options.HeaderText ? this.options.HeaderText + format.header : ""; + output += format.first; + messages.each(function(message) + { + output += message.length > 0 ? format.pre + message + format.post : ""; + }); +// for(var i = 0; i < messages.length; i++) + // output += (messages[i].length>0) ? format.pre + messages[i] + format.post : ""; + output += format.last; + return output; + }, + /** + * Format the message alert box. + * @function {string} ? + * @param {string[]} messages - Array of error messages. + * @return Formatted message for alert + */ + formatMessageBox : function(messages) + { + if(this.options.DisplayMode == 'HeaderOnly' && this.options.HeaderText) + return this.options.HeaderText; + + var output = this.options.HeaderText ? this.options.HeaderText + "\n" : ""; + for(var i = 0; i < messages.length; i++) + { + switch(this.options.DisplayMode) + { + case "List": + output += messages[i] + "\n"; + break; + case "BulletList": + default: + output += " - " + messages[i] + "\n"; + break; + case "SingleParagraph": + output += messages[i] + " "; + break; + } + } + return output; + } +}; + +/** + * TBaseValidator serves as the base class for validator controls. + * + *

    Validation is performed when a postback control, such as a TButton, + * a TLinkButton or a TTextBox (under AutoPostBack mode) is submitting + * the page and its CausesValidation option is true. + * The input control to be validated is specified by ControlToValidate + * option.

    + * + * @class Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TBaseValidator = Class.create(Prado.WebUI.Control, +{ + /** + * Initialize TBaseValidator. + * @constructor {protected} ? + * @param {object} options - Options for initialization. + * @... {string} ID - ID of validator + * @... {string} FormID - ID of form of this manager. + * @... {string} ControlToValidate - ID of control to validate. + * @... {optional string} InitialValue - Initial value of control to validate. + * @... {optional string} ErrorMessage - Validation error message. + * @... {optional string} ValidationGroup - ID of ValidationGroup. + * @... {optional string} Display - Display mode, 'None', 'Fixed', 'Dynamic'. + * @... {optional boolean} ObserveChanges - True to observer changes of ControlToValidate + * @... {optional boolean} FocusOnError - True to focus on validation error. + * @... {optional string} FocusElementID - ID of element to focus on error. + * @... {optional string} ControlCssClass - Css class to use on ControlToValidate on error + * @... {optional function} OnValidate - Called immediately after validation. + * @... {optional function} OnValidationSuccess - Called after successful validation. + * @... {optional function} OnValidationError - Called after validation error. + */ + initialize : function(options) + { + this.observers = new Array(); + this.intervals = new Array(); + + /* options.OnValidate = options.OnValidate || Prototype.emptyFunction; + options.OnSuccess = options.OnSuccess || Prototype.emptyFunction; + options.OnError = options.OnError || Prototype.emptyFunction; + */ + /** + * Wether the validator is enabled (default true) + * @var {boolean} enabled + */ + this.enabled = options.Enabled; + /** + * Visibility state of validator(default false) + * @var {boolean} visible + */ + this.visible = false; + /** + * State of validation (default true) + * @var {boolean} isValid + */ + this.isValid = true; + /** + * DOM elements that are observed by this validator + * @var {private element[]} _isObserving + */ + this._isObserving = {}; + /** + * ValidationGroup + * @var {string} group + */ + this.group = null; + /** + * Wether a request was dispatched (default false) + * @var {boolean} requestDispatched + */ + this.requestDispatched = false; + + /** + * Validator options + * @var {object} options + */ + this.options = options; + /** + * DOM element of control to validate + * @var {element} control + */ + this.control = $(options.ControlToValidate); + /** + * DOM element of validator + * @var {element} message + */ + this.message = $(options.ID); + + Prado.Registry.set(options.ID, this); + + if (this.onInit) this.onInit(); + + if(this.control && this.message) + { + this.group = options.ValidationGroup; + + /** + * ValidationManager of this validator + * @var {ValidationManager} manager + */ + this.manager = Prado.Validation.addValidator(options.FormID, this); + } + }, + + /** + * Get error message. + * @function {string} ? + * @return Validation error message. + */ + getErrorMessage : function() + { + return this.options.ErrorMessage; + }, + + /** + * Update the validator. + * Updating the validator control will set the validator + * visible property to true. + * @function ? + */ + updateControl: function() + { + this.refreshControlAndMessage(); + + //if(this.options.FocusOnError && !this.isValid ) + // Prado.Element.focus(this.options.FocusElementID); + + this.visible = true; + }, + + /** + * Updates span and input CSS class. + * @function ? + */ + refreshControlAndMessage : function() + { + this.visible = true; + if(this.message) + { + if(this.options.Display == "Dynamic") + { + var msg=this.message; + this.isValid ? msg.hide() : msg.show(); + } + this.message.style.visibility = this.isValid ? "hidden" : "visible"; + } + if(this.control) + this.updateControlCssClass(this.control, this.isValid); + }, + + /** + * Update CSS class of control to validate. + * Add a css class to the input control if validator is invalid, + * removes the css class if valid. + * @function ? + * @param {element} control - DOM element of control to validate + * @param {boolean} valid - Validation state of control + */ + updateControlCssClass : function(control, valid) + { + var CssClass = this.options.ControlCssClass; + if(typeof(CssClass) == "string" && CssClass.length > 0) + { + if(valid) + { + if (control.lastValidator == this.options.ID) + { + control.lastValidator = null; + control.removeClassName(CssClass); + } + } + else + { + control.lastValidator = this.options.ID; + control.addClassName(CssClass); + } + } + }, + + /** + * Hide the validator messages and remove any validation changes. + * @function ? + */ + hide : function() + { + this.reset(); + this.visible = false; + }, + + /** + * Reset validator. + * Sets isValid = true and updates the validator display. + * @function ? + */ + reset : function() + { + this.isValid = true; + this.updateControl(); + }, + + /** + * Perform validation. + * Calls evaluateIsValid() function to set the value of isValid property. + * Triggers onValidate event and onSuccess or onError event. + * @function {boolean} ? + * @param {element} invoker - DOM element that triggered validation + * @return Valdation state of control. + */ + validate : function(invoker) + { + //try to find the control. + if(!this.control) + this.control = $(this.options.ControlToValidate); + + if(!this.control || this.control.disabled) + { + this.isValid = true; + return this.isValid; + } + + if(typeof(this.options.OnValidate) == "function") + { + if(this.requestDispatched == false) + this.options.OnValidate(this, invoker); + } + + if(this.enabled && !this.control.getAttribute('disabled')) + this.isValid = this.evaluateIsValid(); + else + this.isValid = true; + + this.updateValidationDisplay(invoker); + this.observeChanges(this.control); + + return this.isValid; + }, + + /** + * Update validation display. + * Updates the validation messages and the control to validate. + * @param {element} invoker - DOM element that triggered validation + */ + updateValidationDisplay : function(invoker) + { + if(this.isValid) + { + if(typeof(this.options.OnValidationSuccess) == "function") + { + if(this.requestDispatched == false) + { + this.refreshControlAndMessage(); + this.options.OnValidationSuccess(this, invoker); + } + } + else + this.updateControl(); + } + else + { + if(typeof(this.options.OnValidationError) == "function") + { + if(this.requestDispatched == false) + { + this.refreshControlAndMessage(); + this.options.OnValidationError(this, invoker) + } + } + else + this.updateControl(); + } + }, + + /** + * Add control to observe for changes. + * Re-validates upon change. If the validator is not visible, + * no updates are propagated. + * @function ? + * @param {element} control - DOM element of control to observe + */ + observeChanges : function(control) + { + if(!control) return; + + var canObserveChanges = this.options.ObserveChanges != false; + var currentlyObserving = this._isObserving[control.id+this.options.ID]; + + if(canObserveChanges && !currentlyObserving) + { + var validator = this; + + this.observe(control, 'change', function() + { + if(validator.visible) + { + validator.validate(); + validator.manager.updateSummary(validator.group); + } + }); + this._isObserving[control.id+this.options.ID] = true; + } + }, + + /** + * Trim a string. + * @function {string} ? + * @param {string} value - String that should be trimmed. + * @return Trimmed string, empty string if value is not string. + */ + trim : function(value) + { + return typeof(value) == "string" ? value.trim() : ""; + }, + + /** + * Convert the value to a specific data type. + * @function {mixed|null} ? + * @param {string} dataType - Data type: "Integer", "Double", "Date" or "String" + * @param {mixed} value - Value to convert. + * @return Converted data value. + */ + convert : function(dataType, value) + { + if(typeof(value) == "undefined") + value = this.getValidationValue(); + var string = new String(value); + switch(dataType) + { + case "Integer": + return string.toInteger(); + case "Double" : + case "Float" : + return string.toDouble(this.options.DecimalChar); + case "Date": + if(typeof(value) != "string") + return value; + else + { + var value = string.toDate(this.options.DateFormat); + if(value && typeof(value.getTime) == "function") + return value.getTime(); + else + return null; + } + case "String": + return string.toString(); + } + return value; + }, + + /** + * Get value that should be validated. + * The ControlType property comes from TBaseValidator::getClientControlClass() + * Be sure to update the TBaseValidator::$_clientClass if new cases are added. + * @function {mixed} ? + * @param {optional element} control - Control to get value from (default: this.control) + * @return Control value to validate + */ + getRawValidationValue : function(control) + { + if(!control) + control = this.control + switch(this.options.ControlType) + { + case 'TDatePicker': + if(control.type == "text") + { + var value = this.trim($F(control)); + + if(this.options.DateFormat) + { + var date = value.toDate(this.options.DateFormat); + return date == null ? value : date; + } + else + return value; + } + else + { + this.observeDatePickerChanges(); + + return Prado.WebUI.TDatePicker.getDropDownDate(control);//.getTime(); + } + case 'THtmlArea': + case 'THtmlArea4': + if(typeof tinyMCE != "undefined") + tinyMCE.triggerSave(); + return $F(control); + case 'TRadioButton': + if(this.options.GroupName) + return this.getRadioButtonGroupValue(); + default: + if(this.isListControlType()) + return this.getFirstSelectedListValue(); + else + return $F(control); + } + }, + + /** + * Get a trimmed value that should be validated. + * The ControlType property comes from TBaseValidator::getClientControlClass() + * Be sure to update the TBaseValidator::$_clientClass if new cases are added. + * @function {mixed} ? + * @param {optional element} control - Control to get value fron (default: this.control) + * @return Control value to validate + */ + getValidationValue : function(control) + { + var value = this.getRawValidationValue(control); + if(!control) + control = this.control + switch(this.options.ControlType) + { + case 'TDatePicker': + return value; + case 'THtmlArea': + case 'THtmlArea4': + return this.trim(value); + case 'TRadioButton': + return value; + default: + if(this.isListControlType()) + return value; + else + return this.trim(value); + } + }, + + /** + * Get value of radio button group + * @function {string} ? + * @return Value of a radio button group + */ + getRadioButtonGroupValue : function() + { + var name = this.control.name; + var value = ""; + $A(document.getElementsByName(name)).each(function(el) + { + if(el.checked) + value = el.value; + }); + return value; + }, + + /** + * Observe changes in the drop down list date picker, IE only. + * @function ? + */ + observeDatePickerChanges : function() + { + if(Prado.Browser().ie) + { + var DatePicker = Prado.WebUI.TDatePicker; + this.observeChanges(DatePicker.getDayListControl(this.control)); + this.observeChanges(DatePicker.getMonthListControl(this.control)); + this.observeChanges(DatePicker.getYearListControl(this.control)); + } + }, + + /** + * Gets number of selections and their values. + * @function {object} ? + * @param {element[]} elements - Elements to get values from. + * @param {string} initialValue - Initial value of element + * @return Object: + * @... {mixed[]} values - Array of selected values + * @... {int} checks - Number of selections + */ + getSelectedValuesAndChecks : function(elements, initialValue) + { + var checked = 0; + var values = []; + var isSelected = this.isCheckBoxType(elements[0]) ? 'checked' : 'selected'; + elements.each(function(element) + { + if(element[isSelected] && element.value != initialValue) + { + checked++; + values.push(element.value); + } + }); + return {'checks' : checked, 'values' : values}; + }, + + /** + * Get list elements of TCheckBoxList or TListBox. + * Gets an array of the list control item input elements, for TCheckBoxList + * checkbox input elements are returned, for TListBox HTML option elements + * are returned. + * @function {element[]} ? + * @return Array of list control option DOM elements. + */ + getListElements : function() + { + switch(this.options.ControlType) + { + case 'TCheckBoxList': case 'TRadioButtonList': + var elements = []; + for(var i = 0; i < this.options.TotalItems; i++) + { + var element = $(this.options.ControlToValidate+"_c"+i); + if(this.isCheckBoxType(element)) + elements.push(element); + } + return elements; + case 'TListBox': + var elements = []; + var element = $(this.options.ControlToValidate); + var type; + if(element && (type = element.type.toLowerCase())) + { + if(type == "select-one" || type == "select-multiple") + elements = $A(element.options); + } + return elements; + default: + return []; + } + }, + + /** + * Check if control is of type checkbox or radio. + * @function {boolean} ? + * @param {element} element - DOM element to check. + * @return True if element is of checkbox or radio type. + */ + isCheckBoxType : function(element) + { + if(element && element.type) + { + var type = element.type.toLowerCase(); + return type == "checkbox" || type == "radio"; + } + return false; + }, + + /** + * Check if control to validate is a TListControl type. + * @function {boolean} ? + * @return True if control to validate is a TListControl type. + */ + isListControlType : function() + { + var list = ['TCheckBoxList', 'TRadioButtonList', 'TListBox']; + return list.include(this.options.ControlType); + }, + + /** + * Get first selected list value or initial value if none found. + * @function {string} ? + * @return First selected list value, initial value if none found. + */ + getFirstSelectedListValue : function() + { + var initial = ""; + if(typeof(this.options.InitialValue) != "undefined") + initial = this.options.InitialValue; + var elements = this.getListElements(); + var selection = this.getSelectedValuesAndChecks(elements, initial); + return selection.values.length > 0 ? selection.values[0] : initial; + } +}); + + +/** + * TRequiredFieldValidator makes the associated input control a required field. + * + *

    The input control fails validation if its value does not change from + * the InitialValue option upon losing focus.

    + * + * @class Prado.WebUI.TRequiredFieldValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TRequiredFieldValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if the input value is not empty nor equal to the initial value. + */ + evaluateIsValid : function() + { + var a = this.getValidationValue(); + var b = this.trim(this.options.InitialValue); + return(a != b); + } +}); + + +/** + * TCompareValidator compares the value entered by the user into an input + * control with the value entered into another input control or a constant value. + * + *

    To compare the associated input control with another input control, + * set the ControlToCompare option to the ID path + * of the control to compare with. To compare the associated input control with + * a constant value, specify the constant value to compare with by setting the + * ValueToCompare option.

    + * + *

    The DataType property is used to specify the data type + * of both comparison values. Both values are automatically converted to this data + * type before the comparison operation is performed. The following value types are supported: + * - Integer A 32-bit signed integer data type. + * - Float A double-precision floating point number data type. + * - Date A date data type. The format can be set by the DateFormat option. + * - String A string data type.

    + * + * Use the Operator property to specify the type of comparison + * to perform. Valid operators include Equal, NotEqual, GreaterThan, GreaterThanEqual, + * LessThan and LessThanEqual. + * + * @class Prado.WebUI.TCompareValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TCompareValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor options. + * @constructor initialize + * @param {object} options - Additional constructor options: + * @... {string} ControlToCompare - Control with compare value. + * @... {string} ValueToCompare - Value to compare. + * @... {string} Operator - Type of comparison: "Equal", "NotEqual", "GreaterThan", + * "GreaterThanEqual", "LessThan" or "LessThanEqual". + * @... {string} Type - Type of values: "Integer", "Float", "Date" or "String". + * @... {string} DateFormat - Valid date format. + */ + + //_observingComparee : false, + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if comparision condition is met. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + if (value.length <= 0) + return true; + + var comparee = $(this.options.ControlToCompare); + + if(comparee) + var compareTo = this.getValidationValue(comparee); + else + var compareTo = this.options.ValueToCompare || ""; + + var isValid = this.compare(value, compareTo); + + if(comparee) + { + this.updateControlCssClass(comparee, isValid); + this.observeChanges(comparee); + } + return isValid; + }, + + /** + * Compare two operands. + * The operand values are casted to type defined + * by DataType option. False is returned if the first + * operand converts to null. Returns true if the second operand + * converts to null. The comparision is done based on the + * Operator option. + * @function ? + * @param {mixed} operand1 - First operand. + * @param {mixed} operand2 - Second operand. + */ + compare : function(operand1, operand2) + { + var op1, op2; + if((op1 = this.convert(this.options.DataType, operand1)) == null) + return false; + if ((op2 = this.convert(this.options.DataType, operand2)) == null) + return true; + switch (this.options.Operator) + { + case "NotEqual": + return (op1 != op2); + case "GreaterThan": + return (op1 > op2); + case "GreaterThanEqual": + return (op1 >= op2); + case "LessThan": + return (op1 < op2); + case "LessThanEqual": + return (op1 <= op2); + default: + return (op1 == op2); + } + } +}); + +/** + * TCustomValidator performs user-defined client-side validation on an + * input component. + * + *

    To create a client-side validation function, add the client-side + * validation javascript function to the page template. + * The function should have the following signature:

    + * + *
    + * <script type="text/javascript">
    + * function ValidationFunctionName(sender, parameter)
    + * {
    + *    if(parameter == ...)
    + *       return true;
    + *    else
    + *       return false;
    + * }
    + * </script>
    + * 
    + * + *

    Use the ClientValidationFunction option + * to specify the name of the client-side validation script function associated + * with the TCustomValidator.

    + * + * @class Prado.WebUI.TCustomValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor options. + * @constructor initialize + * @param {object} options - Additional constructor options: + * @... {function} ClientValidationFunction - Custom validation function. + */ + + /** + * Evaluate validation state + * Returns true if no valid custom validation function is present. + * @function {boolean} ? + * @return True if custom validation returned true. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + var clientFunction = this.options.ClientValidationFunction; + if(typeof(clientFunction) == "string" && clientFunction.length > 0) + { + var validate = clientFunction.toFunction(); + return validate(this, value); + } + return true; + } +}); + +/** + * Uses callback request to perform validation. + * + * @class Prado.WebUI.TActiveCustomValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TActiveCustomValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Override the parent implementation to store the invoker, in order to + * re-validate after the callback has returned + * Calls evaluateIsValid() function to set the value of isValid property. + * Triggers onValidate event and onSuccess or onError event. + * @function {boolean} ? + * @param {element} invoker - DOM element that triggered validation + * @return True if valid. + */ + validate : function(invoker) + { + this.invoker = invoker; + + //try to find the control. + if(!this.control) + this.control = $(this.options.ControlToValidate); + + if(!this.control || this.control.disabled) + { + this.isValid = true; + return this.isValid; + } + + if(typeof(this.options.OnValidate) == "function") + { + if(this.requestDispatched == false) + this.options.OnValidate(this, invoker); + } + + return true; + }, + + /** + * Send CallBack to start serverside validation. + * @function {boolean} ? + * @return True if valid. + */ + evaluateIsValid : function() + { + return this.isValid; + }, + + /** + * Parse CallBack response data on success. + * @function ? + * @param {CallbackRequest} request - CallbackRequest. + * @param {string} data - Response data. + */ + updateIsValid : function(data) + { + this.isValid = data; + this.requestDispatched = false; + if(typeof(this.options.onSuccess) == "function") + this.options.onSuccess(null,data); + this.updateValidationDisplay(); + this.manager.updateSummary(this.group); + } +}); + +/** + * TRangeValidator tests whether an input value is within a specified range. + * + *

    TRangeValidator uses three key properties to perform its validation.

    + * + *

    The MinValue and MaxValue options specify the minimum + * and maximum values of the valid range.

    + *

    The DataType option is + * used to specify the data type of the value and the minimum and maximum range values. + * The values are converted to this data type before the validation + * operation is performed. The following value types are supported:

    + * + * - Integer A 32-bit signed integer data type.
    + * - Float A double-precision floating point number data type.
    + * - Date A date data type. The date format can be specified by
    + * setting DateFormat option, which must be recognizable + * by Date.SimpleParse javascript function. + * - String A string data type. + * + * @class Prado.WebUI.TRangeValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TRangeValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor options. + * @constructor initialize + * @param {object} options - Additional constructor options: + * @... {string} MinValue - Minimum range value + * @... {string} MaxValue - Maximum range value + * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String" + * @... {string} DateFormat - Date format for data type Date. + */ + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value is in range or value is empty, + * false otherwhise and when type conversion failed. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + if(value.length <= 0) + return true; + if(typeof(this.options.DataType) == "undefined") + this.options.DataType = "String"; + + if(this.options.DataType != "StringLength") + { + var min = this.convert(this.options.DataType, this.options.MinValue || null); + var max = this.convert(this.options.DataType, this.options.MaxValue || null); + value = this.convert(this.options.DataType, value); + } + else + { + var min = this.options.MinValue || 0; + var max = this.options.MaxValue || Number.POSITIVE_INFINITY; + value = value.length; + } + + if(value == null) + return false; + + var valid = true; + + if(min != null) + valid = valid && (this.options.StrictComparison ? value > min : value >= min); + if(max != null) + valid = valid && (this.options.StrictComparison ? value < max : value <= max); + return valid; + } +}); + +/** + * TRegularExpressionValidator validates whether the value of an associated + * input component matches the pattern specified by a regular expression. + * + * @class Prado.WebUI.TRegularExpressionValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TRegularExpressionValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor option. + * @constructor initialize + * @param {object} options - Additional constructor option: + * @... {string} ValidationExpression - Regular expression to match against. + * @... {string} PatternModifiers - Pattern modifiers: combinations of g, i, and m + */ + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value matches regular expression or value is empty. + */ + evaluateIsValid : function() + { + var value = this.getRawValidationValue(); + if (value.length <= 0) + return true; + + var rx = new RegExp('^'+this.options.ValidationExpression+'$',this.options.PatternModifiers); + var matches = rx.exec(value); + return (matches != null && value == matches[0]); + } +}); + +/** + * TEmailAddressValidator validates whether the value of an associated + * input component is a valid email address. + * + * @class Prado.WebUI.TEmailAddressValidator + * @extends Prado.WebUI.TRegularExpressionValidator + */ +Prado.WebUI.TEmailAddressValidator = Prado.WebUI.TRegularExpressionValidator; + + +/** + * TListControlValidator checks the number of selection and their values + * for a TListControl that allows multiple selections. + * + * @class Prado.WebUI.TListControlValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TListControlValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if number of selections and/or their values match requirements. + */ + evaluateIsValid : function() + { + var elements = this.getListElements(); + if(elements && elements.length <= 0) + return true; + + this.observeListElements(elements); + + var selection = this.getSelectedValuesAndChecks(elements); + return this.isValidList(selection.checks, selection.values); + }, + + /** + * Observe list elements for of changes (only IE) + * @function ? + * @param {element[]} elements - Array of DOM elements to observe + */ + observeListElements : function(elements) + { + if(Prado.Browser().ie && this.isCheckBoxType(elements[0])) + { + var validator = this; + elements.each(function(element) + { + validator.observeChanges(element); + }); + } + }, + + /** + * Check if list is valid. + * Determine if the number of checked values and the checked values + * satisfy the required number of checks and/or the checked values + * equal to the required values. + * @function {boolean} ? + * @param {int} checked - Number of required checked values + * @param {string[]} values - Array of required checked values + * @return True if checked values and number of checks are satisfied. + */ + isValidList : function(checked, values) + { + var exists = true; + + //check the required values + var required = this.getRequiredValues(); + if(required.length > 0) + { + if(values.length < required.length) + return false; + required.each(function(requiredValue) + { + exists = exists && values.include(requiredValue); + }); + } + + var min = typeof(this.options.Min) == "undefined" ? + Number.NEGATIVE_INFINITY : this.options.Min; + var max = typeof(this.options.Max) == "undefined" ? + Number.POSITIVE_INFINITY : this.options.Max; + return exists && checked >= min && checked <= max; + }, + + /** + * Get list of required values. + * @function {string[]} ? + * @return Array of required values that must be selected. + */ + getRequiredValues : function() + { + var required = []; + if(this.options.Required && this.options.Required.length > 0) + required = this.options.Required.split(/,\s*/); + return required; + } +}); + + +/** + * TDataTypeValidator verifies if the input data is of the type specified + * by DataType option. + * + *

    The following data types are supported:

    + * + * - Integer A 32-bit signed integer data type.
    + * - Float A double-precision floating point number data type.
    + * - Date A date data type.
    + * - String A string data type.
    + * + *

    For Date type, the option DateFormat + * will be used to determine how to parse the date string.

    + * + * @class Prado.WebUI.TDataTypeValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Additional constructor option. + * @constructor initialize + * @param {object} options - Additional constructor option: + * @... {string} DataType - Value data type: "Integer", "Float", "Date" or "String" + * @... {string} DateFormat - Date format for data type Date. + */ + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value matches required data type. + */ + evaluateIsValid : function() + { + var value = this.getValidationValue(); + if(value.length <= 0) + return true; + return this.convert(this.options.DataType, value) != null; + } +}); + +/** + * TCaptchaValidator verifies if the input data is the same as + * the token shown in the associated CAPTCHA control. + * + * @class Prado.WebUI.TCaptchaValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TCaptchaValidator = Class.extend(Prado.WebUI.TBaseValidator, +{ + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if value matches captcha text + */ + evaluateIsValid : function() + { + var a = this.getValidationValue(); + var h = 0; + if (this.options.CaseSensitive==false) + a = a.toUpperCase(); + for(var i = a.length-1; i >= 0; --i) + h += a.charCodeAt(i); + return h == this.options.TokenHash; + }, + + crc32 : function(str) + { + function Utf8Encode(string) + { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) + { + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + + return utftext; + }; + + str = Utf8Encode(str); + + var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; + var crc = 0; + var x = 0; + var y = 0; + + crc = crc ^ (-1); + for( var i = 0, iTop = str.length; i < iTop; i++ ) + { + y = ( crc ^ str.charCodeAt( i ) ) & 0xFF; + x = "0x" + table.substr( y * 9, 8 ); + crc = ( crc >>> 8 ) ^ x; + } + return crc ^ (-1); + } +}); + + +/** + * TReCaptchaValidator client-side control. + * + * @class Prado.WebUI.TReCaptchaValidator + * @extends Prado.WebUI.TBaseValidator + */ +Prado.WebUI.TReCaptchaValidator = Class.create(Prado.WebUI.TBaseValidator, +{ + onInit : function() + { + var obj = this; + var elements = document.getElementsByName(this.options.ResponseFieldName); + if (elements) + if (elements.length>=1) + { + this.observe(elements[0],'change',function() { obj.responseChanged() }); + this.observe(elements[0],'keydown',function() { obj.responseChanged() }); + } + }, + + responseChanged: function() + { + var field = $(this.options.ID+'_1'); + if (field.value=='1') return; + field.value = '1'; + Prado.Validation.validateControl(this.options.ID); + }, + + /** + * Evaluate validation state + * @function {boolean} ? + * @return True if the captcha has validate, False otherwise. + */ + evaluateIsValid : function() + { + return ($(this.options.ID+'_1').value=='1'); + } +}); + diff --git a/gui/baculum/framework/Web/Javascripts/source/prototype-1.7/prototype.js b/gui/baculum/framework/Web/Javascripts/source/prototype-1.7/prototype.js new file mode 100644 index 0000000000..de5fff4a6f --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/prototype-1.7/prototype.js @@ -0,0 +1,7039 @@ +/* Prototype JavaScript framework, version 1.7.1 + * (c) 2005-2010 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + + Version: '1.7.1', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + + SelectorsAPI: !!document.querySelector, + + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'), + form = document.createElement('form'), + isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script\\s*>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0, length = properties.length; i < length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); + + if (IS_DONTENUM_BUGGY) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = (function(method) { + return function() { return method.valueOf.call(method); }; + })(method); + + value.toString = (function(method) { + return function() { return method.toString.call(method); }; + })(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString, + _hasOwnProperty = Object.prototype.hasOwnProperty, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]', + NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && + typeof JSON.stringify === 'function' && + JSON.stringify(0) === '0' && + typeof JSON.stringify(Prototype.K) === 'undefined'; + + + + var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', + 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key]; + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + var _class = _toString.call(value); + + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + var type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { + throw new TypeError("Cyclic reference to '" + value + "' in object"); + } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; + } + } + + function stringify(object) { + return JSON.stringify(object); + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } + var results = []; + for (var property in object) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + + if (IS_DONTENUM_BUGGY) { + for (var i = 0; property = DONT_ENUMS[i]; i++) { + if (_hasOwnProperty.call(object, property)) + results.push(property); + } + } + + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) === ARRAY_CLASS; + } + + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return _toString.call(object) === FUNCTION_CLASS; + } + + function isString(object) { + return _toString.call(object) === STRING_CLASS; + } + + function isNumber(object) { + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: Object.keys || keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isDate: isDate, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) + return this; + + if (!Object.isFunction(this)) + throw new TypeError("The object is not callable."); + + var nop = function() {}; + var __method = this, args = slice.call(arguments, 1); + + var bound = function() { + var a = merge(args, arguments), c = context; + var c = this instanceof bound ? this : context; + return __method.apply(c, a); + }; + + nop.prototype = this.prototype; + bound.prototype = new nop(); + + return bound; + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + var extensions = { + argumentNames: argumentNames, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + }; + + if (!Function.prototype.bind) + extensions.bind = bind; + + return extensions; +})()); + + + +(function(proto) { + + + function toISOString() { + return this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z'; + } + + + function toJSON() { + return this.toISOString(); + } + + if (!proto.toISOString) proto.toISOString = toISOString; + if (!proto.toJSON) proto.toJSON = toJSON; + +})(Date.prototype); + + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, (function() { + var NATIVE_JSON_PARSE_SUPPORT = window.JSON && + typeof JSON.parse === 'function' && + JSON.parse('{"test": true}').test; + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || pattern.source)) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + replacement; + } + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), + matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script); }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()), + value = pair.length > 1 ? pair.join('=') : pair[0]; + + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + var str = this; + if (str.blank()) return false; + str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); + str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); + str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); + return (/^[\],:{}\s]*$/).test(str); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(), + cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + if (cx.test(json)) { + json = json.replace(cx, function (a) { + return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function parseJSON() { + var json = this.unfilterJSON(); + return JSON.parse(json); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern) { + return this.lastIndexOf(pattern, 0) === 0; + } + + function endsWith(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.indexOf(pattern, d) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim || strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, + include: include, + startsWith: startsWith, + endsWith: endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3], + pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = (function() { + function each(iterator, context) { + try { + this._each(iterator, context); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(number, iterator, context) { + var index = -number, slices = [], array = this.toArray(); + if (number < 1) return array; + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + } + + function all(iterator, context) { + iterator = iterator || Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator.call(context, value, index, this); + if (!result) throw $break; + }, this); + return result; + } + + function any(iterator, context) { + iterator = iterator || Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator.call(context, value, index, this)) + throw $break; + }, this); + return result; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) { + result = value; + throw $break; + } + }, this); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index, this)); + }, this); + return results; + } + + function include(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index, this); + }, this); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value >= result) + result = value; + }, this); + return result; + } + + function min(iterator, context) { + iterator = iterator || Prototype.K; + var result; + this.each(function(value, index) { + value = iterator.call(context, value, index, this); + if (result == null || value < result) + result = value; + }, this); + return result; + } + + function partition(iterator, context) { + iterator = iterator || Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator.call(context, value, index, this) ? + trues : falses).push(value); + }, this); + return [trues, falses]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index, this)) + results.push(value); + }, this); + return results; + } + + function sortBy(iterator, context) { + return this.map(function(value, index) { + return { + value: value, + criteria: iterator.call(context, value, index, this) + }; + }, this).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + } + + function toArray() { + return this.map(); + } + + function zip() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); + +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator, context) { + for (var i = 0, length = this.length >>> 0; i < length; i++) { + if (i in this) iterator.call(context, this[i], i, this); + } + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline === false ? this.toArray() : this)._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.indexOf(item) !== -1; + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function indexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + + if (i > length) return -1; + + var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0); + for (; k < length; k++) + if (k in array && array[k] === item) return k; + return -1; + } + + + function lastIndexOf(item, i) { + if (this == null) throw new TypeError(); + + var array = Object(this), length = array.length >>> 0; + if (length === 0) return -1; + + if (!Object.isUndefined(i)) { + i = Number(i); + if (isNaN(i)) { + i = 0; + } else if (i !== 0 && isFinite(i)) { + i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); + } + } else { + i = length; + } + + var k = i >= 0 ? Math.min(i, length - 1) : + length - Math.abs(i); + + for (; k >= 0; k--) + if (k in array && array[k] === item) return k; + return -1; + } + + function concat(_) { + var array = [], items = slice.call(arguments, 0), item, n = 0; + items.unshift(this); + for (var i = 0, length = items.length; i < length; i++) { + item = items[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) { + if (j in item) array[n] = item[j]; + n++; + } + } else { + array[n++] = item; + } + } + array.length = n; + return array; + } + + + function wrapNative(method) { + return function() { + if (arguments.length === 0) { + return method.call(this, Prototype.K); + } else if (arguments[0] === undefined) { + var args = slice.call(arguments, 1); + args.unshift(Prototype.K); + return method.apply(this, args); + } else { + return method.apply(this, arguments); + } + }; + } + + + function map(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + + var object = Object(this); + var results = [], context = arguments[1], n = 0; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + results[n] = iterator.call(context, object[i], i, object); + } + n++; + } + results.length = n; + return results; + } + + if (arrayProto.map) { + map = wrapNative(Array.prototype.map); + } + + function filter(iterator) { + if (this == null || !Object.isFunction(iterator)) + throw new TypeError(); + + var object = Object(this); + var results = [], context = arguments[1], value; + + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object) { + value = object[i]; + if (iterator.call(context, value, i, object)) { + results.push(value); + } + } + } + return results; + } + + if (arrayProto.filter) { + filter = Array.prototype.filter; + } + + function some(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && iterator.call(context, object[i], i, object)) { + return true; + } + } + + return false; + } + + if (arrayProto.some) { + var some = wrapNative(Array.prototype.some); + } + + + function every(iterator) { + if (this == null) throw new TypeError(); + iterator = iterator || Prototype.K; + var context = arguments[1]; + + var object = Object(this); + for (var i = 0, length = object.length >>> 0; i < length; i++) { + if (i in object && !iterator.call(context, object[i], i, object)) { + return false; + } + } + + return true; + } + + if (arrayProto.every) { + var every = wrapNative(Array.prototype.every); + } + + var _reduce = arrayProto.reduce; + function inject(memo, iterator) { + iterator = iterator || Prototype.K; + var context = arguments[2]; + return _reduce.call(this, iterator.bind(context), memo); + } + + if (!arrayProto.reduce) { + var inject = Enumerable.inject; + } + + Object.extend(arrayProto, Enumerable); + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + + map: map, + collect: map, + select: filter, + filter: filter, + findAll: filter, + some: some, + any: some, + every: every, + all: every, + inject: inject, + + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2); + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + + function _each(iterator, context) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator.call(context, pair); + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + + var value = String.interpret(value); + + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return key + '=' + value; + } + + function toQueryString() { + return this.inject([], function(results, pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) { + var queryValues = []; + for (var i = 0, len = values.length, value; i < len; i++) { + value = values[i]; + queryValues.push(toQueryPair(key, value)); + } + return results.concat(queryValues); + } + } else results.push(toQueryPair(key, values)); + return results; + }).join('&'); + } + + function inspect() { + return '#'; + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toObject, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator, context) { + var value = this.start; + while (this.include(value)) { + iterator.call(context, value); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +var Abstract = { }; + + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator, context) { + this.responders._each(iterator, context); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.isString(this.options.parameters) ? + this.options.parameters : + Object.toQueryString(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + params += (params ? '&' : '') + "_method=" + this.method; + this.method = 'post'; + } + + if (params && this.method === 'get') { + this.url += (this.url.include('?') ? '&' : '?') + params; + } + + this.parameters = params.toQueryParams(); + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300) || status == 304; + }, + + getStatus: function() { + try { + if (this.transport.status === 1223) return 204; + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null; } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + + + + + + + + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if (readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + + try { + json = decodeURIComponent(escape(json)); + } catch(e) { + } + + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); + +(function(GLOBAL) { + + var UNDEFINED; + var SLICE = Array.prototype.slice; + + var DIV = document.createElement('div'); + + + function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); + } + + GLOBAL.$ = $; + + + if (!GLOBAL.Node) GLOBAL.Node = {}; + + if (!GLOBAL.Node.ELEMENT_NODE) { + Object.extend(GLOBAL.Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); + } + + var ELEMENT_CACHE = {}; + + function shouldUseCreationCache(tagName, attributes) { + if (tagName === 'select') return false; + if ('type' in attributes) return false; + return true; + } + + var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ + try { + var el = document.createElement(''); + return el.tagName.toLowerCase() === 'input' && el.name === 'x'; + } + catch(err) { + return false; + } + })(); + + + var oldElement = GLOBAL.Element; + function Element(tagName, attributes) { + attributes = attributes || {}; + tagName = tagName.toLowerCase(); + + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + + if (!ELEMENT_CACHE[tagName]) + ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName)); + + var node = shouldUseCreationCache(tagName, attributes) ? + ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName); + + return Element.writeAttribute(node, attributes); + } + + GLOBAL.Element = Element; + + Object.extend(GLOBAL.Element, oldElement || {}); + if (oldElement) GLOBAL.Element.prototype = oldElement.prototype; + + Element.Methods = { ByTag: {}, Simulated: {} }; + + var methods = {}; + + var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' }; + function inspect(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + + var attribute, value; + for (var property in INSPECT_ATTRIBUTES) { + attribute = INSPECT_ATTRIBUTES[property]; + value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + } + + return result + '>'; + } + + methods.inspect = inspect; + + + function visible(element) { + return $(element).style.display !== 'none'; + } + + function toggle(element, bool) { + element = $(element); + if (Object.isUndefined(bool)) + bool = !Element.visible(element); + Element[bool ? 'show' : 'hide'](element); + + return element; + } + + function hide(element) { + element = $(element); + element.style.display = 'none'; + return element; + } + + function show(element) { + element = $(element); + element.style.display = ''; + return element; + } + + + Object.extend(methods, { + visible: visible, + toggle: toggle, + hide: hide, + show: show + }); + + + function remove(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + } + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var LINK_ELEMENT_INNERHTML_BUGGY = (function() { + try { + var el = document.createElement('div'); + el.innerHTML = ""; + var isBuggy = (el.childNodes.length === 0); + el = null; + return isBuggy; + } catch(e) { + return true; + } + })(); + + var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || + TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(element, content) { + element = $(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + while (i--) purgeElement(descendants[i]); + + if (content && content.toElement) + content = content.toElement(); + + if (Object.isElement(content)) + return element.update().insert(content); + + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (ANY_INNERHTML_BUGGY) { + if (tagName in INSERTION_TRANSLATIONS.tags) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + + } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { + while (element.firstChild) + element.removeChild(element.firstChild); + + var nodes = getContentFromAnonymousElement(tagName, + content.stripScripts(), true); + + for (var i = 0, node; node = nodes[i]; i++) + element.appendChild(node); + } else { + element.innerHTML = content.stripScripts(); + } + } else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + function replace(element, content) { + element = $(element); + + if (content && content.toElement) { + content = content.toElement(); + } else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + + element.parentNode.replaceChild(content, element); + return element; + } + + var INSERTION_TRANSLATIONS = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + + tags: { + TABLE: ['', '
    ', 1], + TBODY: ['', '
    ', 2], + TR: ['', '
    ', 3], + TD: ['
    ', '
    ', 4], + SELECT: ['', 1] + } + }; + + var tags = INSERTION_TRANSLATIONS.tags; + + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); + + function replace_IE(element, content) { + element = $(element); + if (content && content.toElement) + content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (tagName in INSERTION_TRANSLATIONS.tags) { + var nextSibling = Element.next(element); + var fragments = getContentFromAnonymousElement( + tagName, content.stripScripts()); + + parent.removeChild(element); + + var iterator; + if (nextSibling) + iterator = function(node) { parent.insertBefore(node, nextSibling) }; + else + iterator = function(node) { parent.appendChild(node); } + + fragments.each(iterator); + } else { + element.outerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + if ('outerHTML' in document.documentElement) + replace = replace_IE; + + function isContent(content) { + if (Object.isUndefined(content) || content === null) return false; + + if (Object.isString(content) || Object.isNumber(content)) return true; + if (Object.isElement(content)) return true; + if (content.toElement || content.toHTML) return true; + + return false; + } + + function insertContentAt(element, content, position) { + position = position.toLowerCase(); + var method = INSERTION_TRANSLATIONS[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + method(element, content); + return element; + } + + content = Object.toHTML(content); + var tagName = ((position === 'before' || position === 'after') ? + element.parentNode : element).tagName.toUpperCase(); + + var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position === 'top' || position === 'after') childNodes.reverse(); + + for (var i = 0, node; node = childNodes[i]; i++) + method(element, node); + + content.evalScripts.bind(content).defer(); + } + + function insert(element, insertions) { + element = $(element); + + if (isContent(insertions)) + insertions = { bottom: insertions }; + + for (var position in insertions) + insertContentAt(element, insertions[position], position); + + return element; + } + + function wrap(element, wrapper, attributes) { + element = $(element); + + if (Object.isElement(wrapper)) { + $(wrapper).writeAttribute(attributes || {}); + } else if (Object.isString(wrapper)) { + wrapper = new Element(wrapper, attributes); + } else { + wrapper = new Element('div', wrapper); + } + + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + + wrapper.appendChild(element); + + return wrapper; + } + + function cleanWhitespace(element) { + element = $(element); + var node = element.firstChild; + + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + } + + function empty(element) { + return $(element).innerHTML.blank(); + } + + function getContentFromAnonymousElement(tagName, html, force) { + var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV; + + var workaround = !!t; + if (!workaround && force) { + workaround = true; + t = ['', '', 0]; + } + + if (workaround) { + div.innerHTML = ' ' + t[0] + html + t[1]; + div.removeChild(div.firstChild); + for (var i = t[2]; i--; ) + div = div.firstChild; + } else { + div.innerHTML = html; + } + + return $A(div.childNodes); + } + + function clone(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + if (!HAS_UNIQUE_ID_PROPERTY) { + clone._prototypeUID = UNDEFINED; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) + descendants[i]._prototypeUID = UNDEFINED; + } + } + return Element.extend(clone); + } + + function purgeElement(element) { + var uid = getUniqueElementID(element); + if (uid) { + Element.stopObserving(element); + if (!HAS_UNIQUE_ID_PROPERTY) + element._prototypeUID = UNDEFINED; + delete Element.Storage[uid]; + } + } + + function purgeCollection(elements) { + var i = elements.length; + while (i--) + purgeElement(elements[i]); + } + + function purgeCollection_IE(elements) { + var i = elements.length, element, uid; + while (i--) { + element = elements[i]; + uid = getUniqueElementID(element); + delete Element.Storage[uid]; + delete Event.cache[uid]; + } + } + + if (HAS_UNIQUE_ID_PROPERTY) { + purgeCollection = purgeCollection_IE; + } + + + function purge(element) { + if (!(element = $(element))) return; + purgeElement(element); + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + + while (i--) purgeElement(descendants[i]); + + return null; + } + + Object.extend(methods, { + remove: remove, + update: update, + replace: replace, + insert: insert, + wrap: wrap, + cleanWhitespace: cleanWhitespace, + empty: empty, + clone: clone, + purge: purge + }); + + + + function recursivelyCollect(element, property, maximumLength) { + element = $(element); + maximumLength = maximumLength || -1; + var elements = []; + + while (element = element[property]) { + if (element.nodeType === Node.ELEMENT_NODE) + elements.push(Element.extend(element)); + + if (elements.length === maximumLength) break; + } + + return elements; + } + + + function ancestors(element) { + return recursivelyCollect(element, 'parentNode'); + } + + function descendants(element) { + return Element.select(element, '*'); + } + + function firstDescendant(element) { + element = $(element).firstChild; + while (element && element.nodeType !== Node.ELEMENT_NODE) + element = element.nextSibling; + + return $(element); + } + + function immediateDescendants(element) { + var results = [], child = $(element).firstChild; + + while (child) { + if (child.nodeType === Node.ELEMENT_NODE) + results.push(Element.extend(child)); + + child = child.nextSibling; + } + + return results; + } + + function previousSiblings(element) { + return recursivelyCollect(element, 'previousSibling'); + } + + function nextSiblings(element) { + return recursivelyCollect(element, 'nextSibling'); + } + + function siblings(element) { + element = $(element); + var previous = previousSiblings(element), + next = nextSiblings(element); + return previous.reverse().concat(next); + } + + function match(element, selector) { + element = $(element); + + if (Object.isString(selector)) + return Prototype.Selector.match(element, selector); + + return selector.match(element); + } + + + function _recursivelyFind(element, property, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + if (Object.isNumber(expression)) { + index = expression, expression = null; + } + + while (element = element[property]) { + if (element.nodeType !== 1) continue; + if (expression && !Prototype.Selector.match(element, expression)) + continue; + if (--index >= 0) continue; + + return Element.extend(element); + } + } + + + function up(element, expression, index) { + element = $(element); + + if (arguments.length === 1) return $(element.parentNode); + return _recursivelyFind(element, 'parentNode', expression, index); + } + + function down(element, expression, index) { + element = $(element), expression = expression || 0, index = index || 0; + + if (Object.isNumber(expression)) + index = expression, expression = '*'; + + var node = Prototype.Selector.select(expression, element)[index]; + return Element.extend(node); + } + + function previous(element, expression, index) { + return _recursivelyFind(element, 'previousSibling', expression, index); + } + + function next(element, expression, index) { + return _recursivelyFind(element, 'nextSibling', expression, index); + } + + function select(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element); + } + + function adjacent(element) { + element = $(element); + var expressions = SLICE.call(arguments, 1).join(', '); + var siblings = Element.siblings(element), results = []; + for (var i = 0, sibling; sibling = siblings[i]; i++) { + if (Prototype.Selector.match(sibling, expressions)) + results.push(sibling); + } + + return results; + } + + function descendantOf_DOM(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element === ancestor) return true; + return false; + } + + function descendantOf_contains(element, ancestor) { + element = $(element), ancestor = $(ancestor); + if (!ancestor.contains) return descendantOf_DOM(element, ancestor); + return ancestor.contains(element) && ancestor !== element; + } + + function descendantOf_compareDocumentPosition(element, ancestor) { + element = $(element), ancestor = $(ancestor); + return (element.compareDocumentPosition(ancestor) & 8) === 8; + } + + var descendantOf; + if (DIV.compareDocumentPosition) { + descendantOf = descendantOf_compareDocumentPosition; + } else if (DIV.contains) { + descendantOf = descendantOf_contains; + } else { + descendantOf = descendantOf_DOM; + } + + + Object.extend(methods, { + recursivelyCollect: recursivelyCollect, + ancestors: ancestors, + descendants: descendants, + firstDescendant: firstDescendant, + immediateDescendants: immediateDescendants, + previousSiblings: previousSiblings, + nextSiblings: nextSiblings, + siblings: siblings, + match: match, + up: up, + down: down, + previous: previous, + next: next, + select: select, + adjacent: adjacent, + descendantOf: descendantOf, + + getElementsBySelector: select, + + childElements: immediateDescendants + }); + + + var idCounter = 1; + function identify(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + + do { id = 'anonymous_element_' + idCounter++ } while ($(id)); + + Element.writeAttribute(element, 'id', id); + return id; + } + + + function readAttribute(element, name) { + return $(element).getAttribute(name); + } + + function readAttribute_IE(element, name) { + element = $(element); + + var table = ATTRIBUTE_TRANSLATIONS.read; + if (table.values[name]) + return table.values[name](element, name); + + if (table.names[name]) name = table.names[name]; + + if (name.include(':')) { + if (!element.attributes || !element.attributes[name]) return null; + return element.attributes[name].value; + } + + return element.getAttribute(name); + } + + function readAttribute_Opera(element, name) { + if (name === 'title') return element.title; + return element.getAttribute(name); + } + + var PROBLEMATIC_ATTRIBUTE_READING = (function() { + // This test used to set 'onclick' to `Prototype.emptyFunction`, but that + // caused an (uncatchable) error in IE 10. For some reason, switching to + // an empty array prevents this issue. + DIV.setAttribute('onclick', []); + var value = DIV.getAttribute('onclick'); + var isFunction = Object.isArray(value); + DIV.removeAttribute('onclick'); + return isFunction; + })(); + + if (PROBLEMATIC_ATTRIBUTE_READING) { + readAttribute = readAttribute_IE; + } else if (Prototype.Browser.Opera) { + readAttribute = readAttribute_Opera; + } + + + function writeAttribute(element, name, value) { + element = $(element); + var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write; + + if (typeof name === 'object') { + attributes = name; + } else { + attributes[name] = Object.isUndefined(value) ? true : value; + } + + for (var attr in attributes) { + name = table.names[attr] || attr; + value = attributes[attr]; + if (table.values[attr]) + name = table.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + + return element; + } + + function hasAttribute(element, attribute) { + attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return !!(node && node.specified); + } + + GLOBAL.Element.Methods.Simulated.hasAttribute = hasAttribute; + + function classNames(element) { + return new Element.ClassNames(element); + } + + var regExpCache = {}; + function getRegExpForClassName(className) { + if (regExpCache[className]) return regExpCache[className]; + + var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); + regExpCache[className] = re; + return re; + } + + function hasClassName(element, className) { + if (!(element = $(element))) return; + + var elementClassName = element.className; + + if (elementClassName.length === 0) return false; + if (elementClassName === className) return true; + + return getRegExpForClassName(className).test(elementClassName); + } + + function addClassName(element, className) { + if (!(element = $(element))) return; + + if (!hasClassName(element, className)) + element.className += (element.className ? ' ' : '') + className; + + return element; + } + + function removeClassName(element, className) { + if (!(element = $(element))) return; + + element.className = element.className.replace( + getRegExpForClassName(className), ' ').strip(); + + return element; + } + + function toggleClassName(element, className, bool) { + if (!(element = $(element))) return; + + if (Object.isUndefined(bool)) + bool = !hasClassName(element, className); + + var method = Element[bool ? 'addClassName' : 'removeClassName']; + return method(element, className); + } + + var ATTRIBUTE_TRANSLATIONS = {}; + + var classProp = 'className', forProp = 'for'; + + DIV.setAttribute(classProp, 'x'); + if (DIV.className !== 'x') { + DIV.setAttribute('class', 'x'); + if (DIV.className === 'x') + classProp = 'class'; + } + + var LABEL = document.createElement('label'); + LABEL.setAttribute(forProp, 'x'); + if (LABEL.htmlFor !== 'x') { + LABEL.setAttribute('htmlFor', 'x'); + if (LABEL.htmlFor === 'x') + forProp = 'htmlFor'; + } + LABEL = null; + + function _getAttr(element, attribute) { + return element.getAttribute(attribute); + } + + function _getAttr2(element, attribute) { + return element.getAttribute(attribute, 2); + } + + function _getAttrNode(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ''; + } + + function _getFlag(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + } + + DIV.onclick = Prototype.emptyFunction; + var onclickValue = DIV.getAttribute('onclick'); + + var _getEv; + + if (String(onclickValue).indexOf('{') > -1) { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + value = value.toString(); + value = value.split('{')[1]; + value = value.split('}')[0]; + return value.strip(); + }; + } + else if (onclickValue === '') { + _getEv = function(element, attribute) { + var value = element.getAttribute(attribute); + if (!value) return null; + return value.strip(); + }; + } + + ATTRIBUTE_TRANSLATIONS.read = { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + + values: { + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.write = { + names: { + className: 'class', + htmlFor: 'for', + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, + + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + ATTRIBUTE_TRANSLATIONS.has = { names: {} }; + + Object.extend(ATTRIBUTE_TRANSLATIONS.write.names, + ATTRIBUTE_TRANSLATIONS.read.names); + + var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' + + 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'); + + for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) { + ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr; + ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr; + } + + Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, { + href: _getAttr2, + src: _getAttr2, + type: _getAttr, + action: _getAttrNode, + disabled: _getFlag, + checked: _getFlag, + readonly: _getFlag, + multiple: _getFlag, + onload: _getEv, + onunload: _getEv, + onclick: _getEv, + ondblclick: _getEv, + onmousedown: _getEv, + onmouseup: _getEv, + onmouseover: _getEv, + onmousemove: _getEv, + onmouseout: _getEv, + onfocus: _getEv, + onblur: _getEv, + onkeypress: _getEv, + onkeydown: _getEv, + onkeyup: _getEv, + onsubmit: _getEv, + onreset: _getEv, + onselect: _getEv, + onchange: _getEv + }); + + + Object.extend(methods, { + identify: identify, + readAttribute: readAttribute, + writeAttribute: writeAttribute, + classNames: classNames, + hasClassName: hasClassName, + addClassName: addClassName, + removeClassName: removeClassName, + toggleClassName: toggleClassName + }); + + + function normalizeStyleName(style) { + if (style === 'float' || style === 'styleFloat') + return 'cssFloat'; + return style.camelize(); + } + + function normalizeStyleName_IE(style) { + if (style === 'float' || style === 'cssFloat') + return 'styleFloat'; + return style.camelize(); + } + + function setStyle(element, styles) { + element = $(element); + var elementStyle = element.style, match; + + if (Object.isString(styles)) { + elementStyle.cssText += ';' + styles; + if (styles.include('opacity')) { + var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1]; + Element.setOpacity(element, opacity); + } + return element; + } + + for (var property in styles) { + if (property === 'opacity') { + Element.setOpacity(element, styles[property]); + } else { + var value = styles[property]; + if (property === 'float' || property === 'cssFloat') { + property = Object.isUndefined(elementStyle.styleFloat) ? + 'cssFloat' : 'styleFloat'; + } + elementStyle[property] = value; + } + } + + return element; + } + + + function getStyle(element, style) { + element = $(element); + style = normalizeStyleName(style); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getStyle_Opera(element, style) { + switch (style) { + case 'height': case 'width': + if (!Element.visible(element)) return null; + + var dim = parseInt(getStyle(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + return Element.measure(element, style); + + default: return getStyle(element, style); + } + } + + function getStyle_IE(element, style) { + element = $(element); + style = normalizeStyleName_IE(style); + + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + + if (style === 'opacity' && !STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity_IE(element); + + if (value === 'auto') { + if ((style === 'width' || style === 'height') && Element.visible(element)) + return Element.measure(element, style) + 'px'; + return null; + } + + return value; + } + + function stripAlphaFromFilter_IE(filter) { + return (filter || '').replace(/alpha\([^\)]*\)/gi, ''); + } + + function hasLayout_IE(element) { + if (!element.currentStyle.hasLayout) + element.style.zoom = 1; + return element; + } + + var STANDARD_CSS_OPACITY_SUPPORTED = (function() { + DIV.style.cssText = "opacity:.55"; + return /^0.55/.test(DIV.style.opacity); + })(); + + function setOpacity(element, value) { + element = $(element); + if (value == 1 || value === '') value = ''; + else if (value < 0.00001) value = 0; + element.style.opacity = value; + return element; + } + + function setOpacity_IE(element, value) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return setOpacity(element, value); + + element = hasLayout_IE($(element)); + var filter = Element.getStyle(element, 'filter'), + style = element.style; + + if (value == 1 || value === '') { + filter = stripAlphaFromFilter_IE(filter); + if (filter) style.filter = filter; + else style.removeAttribute('filter'); + return element; + } + + if (value < 0.00001) value = 0; + + style.filter = stripAlphaFromFilter_IE(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + + return element; + } + + + function getOpacity(element) { + return Element.getStyle(element, 'opacity'); + } + + function getOpacity_IE(element) { + if (STANDARD_CSS_OPACITY_SUPPORTED) + return getOpacity(element); + + var filter = Element.getStyle(element, 'filter'); + if (filter.length === 0) return 1.0; + var match = (filter || '').match(/alpha\(opacity=(.*)\)/); + if (match[1]) return parseFloat(match[1]) / 100; + return 1.0; + } + + + Object.extend(methods, { + setStyle: setStyle, + getStyle: getStyle, + setOpacity: setOpacity, + getOpacity: getOpacity + }); + + if ('styleFloat' in DIV.style) { + methods.getStyle = getStyle_IE; + methods.setOpacity = setOpacity_IE; + methods.getOpacity = getOpacity_IE; + } + + var UID = 0; + + GLOBAL.Element.Storage = { UID: 1 }; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV); + if (HAS_UNIQUE_ID_PROPERTY) + getUniqueElementID = getUniqueElementID_IE; + + function getStorage(element) { + if (!(element = $(element))) return; + + var uid = getUniqueElementID(element); + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + } + + function store(element, key, value) { + if (!(element = $(element))) return; + var storage = getStorage(element); + if (arguments.length === 2) { + storage.update(key); + } else { + storage.set(key, value); + } + return element; + } + + function retrieve(element, key, defaultValue) { + if (!(element = $(element))) return; + var storage = getStorage(element), value = storage.get(key); + + if (Object.isUndefined(value)) { + storage.set(key, defaultValue); + value = defaultValue; + } + + return value; + } + + + Object.extend(methods, { + getStorage: getStorage, + store: store, + retrieve: retrieve + }); + + + var Methods = {}, ByTag = Element.Methods.ByTag, + F = Prototype.BrowserFeatures; + + if (!F.ElementExtensions && ('__proto__' in DIV)) { + GLOBAL.HTMLElement = {}; + GLOBAL.HTMLElement.prototype = DIV['__proto__']; + F.ElementExtensions = true; + } + + function checkElementPrototypeDeficiency(tagName) { + if (typeof window.Element === 'undefined') return false; + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random() + '').slice(2), + el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + + return false; + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = + checkElementPrototypeDeficiency('object'); + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var EXTENDED = {}; + function elementIsExtended(element) { + var uid = getUniqueElementID(element); + return (uid in EXTENDED); + } + + function extend(element) { + if (!element || elementIsExtended(element)) return element; + if (element.nodeType !== Node.ELEMENT_NODE || element == window) + return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + EXTENDED[getUniqueElementID(element)] = true; + return element; + } + + function extend_IE8(element) { + if (!element || elementIsExtended(element)) return element; + + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + + return element; + } + + if (F.SpecificElementExtensions) { + extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K; + } + + function addMethodsToTagName(tagName, methods) { + tagName = tagName.toUpperCase(); + if (!ByTag[tagName]) ByTag[tagName] = {}; + Object.extend(ByTag[tagName], methods); + } + + function mergeMethods(destination, methods, onlyIfAbsent) { + if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + var element = document.createElement(tagName), + proto = element['__proto__'] || element.constructor.prototype; + + element = null; + return proto; + } + + function addMethods(methods) { + if (arguments.length === 0) addFormMethods(); + + if (arguments.length === 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) { + Object.extend(Element.Methods, methods || {}); + } else { + if (Object.isArray(tagName)) { + for (var i = 0, tag; tag = tagName[i]; i++) + addMethodsToTagName(tag, methods); + } else { + addMethodsToTagName(tagName, methods); + } + } + + var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods); + mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + mergeMethods(klass.prototype, ByTag[tag]); + } + } + + Object.extend(Element, Element.Methods); + Object.extend(Element, Element.Methods.Simulated); + delete Element.ByTag; + delete Element.Simulated; + + Element.extend.refresh(); + + ELEMENT_CACHE = {}; + } + + Object.extend(GLOBAL.Element, { + extend: extend, + addMethods: addMethods + }); + + if (extend === Prototype.K) { + GLOBAL.Element.extend.refresh = Prototype.emptyFunction; + } else { + GLOBAL.Element.extend.refresh = function() { + if (Prototype.BrowserFeatures.ElementExtensions) return; + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + + EXTENDED = {}; + }; + } + + function addFormMethods() { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods), + "BUTTON": Object.clone(Form.Element.Methods) + }); + } + + Element.addMethods(methods); + +})(this); +(function() { + + function toDecimal(pctString) { + var match = pctString.match(/^(\d+)%?$/i); + if (!match) return null; + return (Number(match[1]) / 100); + } + + function getRawStyle(element, style) { + element = $(element); + + var value = element.style[style]; + if (!value || value === 'auto') { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + + if (style === 'opacity') return value ? parseFloat(value) : 1.0; + return value === 'auto' ? null : value; + } + + function getRawStyle_IE(element, style) { + var value = element.style[style]; + if (!value && element.currentStyle) { + value = element.currentStyle[style]; + } + return value; + } + + function getContentWidth(element, context) { + var boxWidth = element.offsetWidth; + + var bl = getPixelValue(element, 'borderLeftWidth', context) || 0; + var br = getPixelValue(element, 'borderRightWidth', context) || 0; + var pl = getPixelValue(element, 'paddingLeft', context) || 0; + var pr = getPixelValue(element, 'paddingRight', context) || 0; + + return boxWidth - bl - br - pl - pr; + } + + if ('currentStyle' in document.documentElement) { + getRawStyle = getRawStyle_IE; + } + + + function getPixelValue(value, property, context) { + var element = null; + if (Object.isElement(value)) { + element = value; + value = getRawStyle(element, property); + } + + if (value === null || Object.isUndefined(value)) { + return null; + } + + if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { + return window.parseFloat(value); + } + + var isPercentage = value.include('%'), isViewport = (context === document.viewport); + + if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { + var style = element.style.left, rStyle = element.runtimeStyle.left; + element.runtimeStyle.left = element.currentStyle.left; + element.style.left = value || 0; + value = element.style.pixelLeft; + element.style.left = style; + element.runtimeStyle.left = rStyle; + + return value; + } + + if (element && isPercentage) { + context = context || element.parentNode; + var decimal = toDecimal(value), whole = null; + + var isHorizontal = property.include('left') || property.include('right') || + property.include('width'); + + var isVertical = property.include('top') || property.include('bottom') || + property.include('height'); + + if (context === document.viewport) { + if (isHorizontal) { + whole = document.viewport.getWidth(); + } else if (isVertical) { + whole = document.viewport.getHeight(); + } + } else { + if (isHorizontal) { + whole = $(context).measure('width'); + } else if (isVertical) { + whole = $(context).measure('height'); + } + } + + return (whole === null) ? 0 : whole * decimal; + } + + return 0; + } + + function toCSSPixels(number) { + if (Object.isString(number) && number.endsWith('px')) + return number; + return number + 'px'; + } + + function isDisplayed(element) { + while (element && element.parentNode) { + var display = element.getStyle('display'); + if (display === 'none') { + return false; + } + element = $(element.parentNode); + } + return true; + } + + var hasLayout = Prototype.K; + if ('currentStyle' in document.documentElement) { + hasLayout = function(element) { + if (!element.currentStyle.hasLayout) { + element.style.zoom = 1; + } + return element; + }; + } + + function cssNameFor(key) { + if (key.include('border')) key = key + '-width'; + return key.camelize(); + } + + Element.Layout = Class.create(Hash, { + initialize: function($super, element, preCompute) { + $super(); + this.element = $(element); + + Element.Layout.PROPERTIES.each( function(property) { + this._set(property, null); + }, this); + + if (preCompute) { + this._preComputing = true; + this._begin(); + Element.Layout.PROPERTIES.each( this._compute, this ); + this._end(); + this._preComputing = false; + } + }, + + _set: function(property, value) { + return Hash.prototype.set.call(this, property, value); + }, + + set: function(property, value) { + throw "Properties of Element.Layout are read-only."; + }, + + get: function($super, property) { + var value = $super(property); + return value === null ? this._compute(property) : value; + }, + + _begin: function() { + if (this._isPrepared()) return; + + var element = this.element; + if (isDisplayed(element)) { + this._setPrepared(true); + return; + } + + + var originalStyles = { + position: element.style.position || '', + width: element.style.width || '', + visibility: element.style.visibility || '', + display: element.style.display || '' + }; + + element.store('prototype_original_styles', originalStyles); + + var position = getRawStyle(element, 'position'), width = element.offsetWidth; + + if (width === 0 || width === null) { + element.style.display = 'block'; + width = element.offsetWidth; + } + + var context = (position === 'fixed') ? document.viewport : + element.parentNode; + + var tempStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (position !== 'fixed') tempStyles.position = 'absolute'; + + element.setStyle(tempStyles); + + var positionedWidth = element.offsetWidth, newWidth; + if (width && (positionedWidth === width)) { + newWidth = getContentWidth(element, context); + } else if (position === 'absolute' || position === 'fixed') { + newWidth = getContentWidth(element, context); + } else { + var parent = element.parentNode, pLayout = $(parent).getLayout(); + + newWidth = pLayout.get('width') - + this.get('margin-left') - + this.get('border-left') - + this.get('padding-left') - + this.get('padding-right') - + this.get('border-right') - + this.get('margin-right'); + } + + element.setStyle({ width: newWidth + 'px' }); + + this._setPrepared(true); + }, + + _end: function() { + var element = this.element; + var originalStyles = element.retrieve('prototype_original_styles'); + element.store('prototype_original_styles', null); + element.setStyle(originalStyles); + this._setPrepared(false); + }, + + _compute: function(property) { + var COMPUTATIONS = Element.Layout.COMPUTATIONS; + if (!(property in COMPUTATIONS)) { + throw "Property not found."; + } + + return this._set(property, COMPUTATIONS[property].call(this, this.element)); + }, + + _isPrepared: function() { + return this.element.retrieve('prototype_element_layout_prepared', false); + }, + + _setPrepared: function(bool) { + return this.element.store('prototype_element_layout_prepared', bool); + }, + + toObject: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var obj = {}; + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + var value = this.get(key); + if (value != null) obj[key] = value; + }, this); + return obj; + }, + + toHash: function() { + var obj = this.toObject.apply(this, arguments); + return new Hash(obj); + }, + + toCSS: function() { + var args = $A(arguments); + var keys = (args.length === 0) ? Element.Layout.PROPERTIES : + args.join(' ').split(' '); + var css = {}; + + keys.each( function(key) { + if (!Element.Layout.PROPERTIES.include(key)) return; + if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; + + var value = this.get(key); + if (value != null) css[cssNameFor(key)] = value + 'px'; + }, this); + return css; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Element.Layout, { + PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), + + COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), + + COMPUTATIONS: { + 'height': function(element) { + if (!this._preComputing) this._begin(); + + var bHeight = this.get('border-box-height'); + if (bHeight <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bTop = this.get('border-top'), + bBottom = this.get('border-bottom'); + + var pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + if (!this._preComputing) this._end(); + + return bHeight - bTop - bBottom - pTop - pBottom; + }, + + 'width': function(element) { + if (!this._preComputing) this._begin(); + + var bWidth = this.get('border-box-width'); + if (bWidth <= 0) { + if (!this._preComputing) this._end(); + return 0; + } + + var bLeft = this.get('border-left'), + bRight = this.get('border-right'); + + var pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + if (!this._preComputing) this._end(); + return bWidth - bLeft - bRight - pLeft - pRight; + }, + + 'padding-box-height': function(element) { + var height = this.get('height'), + pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + return height + pTop + pBottom; + }, + + 'padding-box-width': function(element) { + var width = this.get('width'), + pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + return width + pLeft + pRight; + }, + + 'border-box-height': function(element) { + if (!this._preComputing) this._begin(); + var height = element.offsetHeight; + if (!this._preComputing) this._end(); + return height; + }, + + 'border-box-width': function(element) { + if (!this._preComputing) this._begin(); + var width = element.offsetWidth; + if (!this._preComputing) this._end(); + return width; + }, + + 'margin-box-height': function(element) { + var bHeight = this.get('border-box-height'), + mTop = this.get('margin-top'), + mBottom = this.get('margin-bottom'); + + if (bHeight <= 0) return 0; + + return bHeight + mTop + mBottom; + }, + + 'margin-box-width': function(element) { + var bWidth = this.get('border-box-width'), + mLeft = this.get('margin-left'), + mRight = this.get('margin-right'); + + if (bWidth <= 0) return 0; + + return bWidth + mLeft + mRight; + }, + + 'top': function(element) { + var offset = element.positionedOffset(); + return offset.top; + }, + + 'bottom': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pHeight = parent.measure('height'); + + var mHeight = this.get('border-box-height'); + + return pHeight - mHeight - offset.top; + }, + + 'left': function(element) { + var offset = element.positionedOffset(); + return offset.left; + }, + + 'right': function(element) { + var offset = element.positionedOffset(), + parent = element.getOffsetParent(), + pWidth = parent.measure('width'); + + var mWidth = this.get('border-box-width'); + + return pWidth - mWidth - offset.left; + }, + + 'padding-top': function(element) { + return getPixelValue(element, 'paddingTop'); + }, + + 'padding-bottom': function(element) { + return getPixelValue(element, 'paddingBottom'); + }, + + 'padding-left': function(element) { + return getPixelValue(element, 'paddingLeft'); + }, + + 'padding-right': function(element) { + return getPixelValue(element, 'paddingRight'); + }, + + 'border-top': function(element) { + return getPixelValue(element, 'borderTopWidth'); + }, + + 'border-bottom': function(element) { + return getPixelValue(element, 'borderBottomWidth'); + }, + + 'border-left': function(element) { + return getPixelValue(element, 'borderLeftWidth'); + }, + + 'border-right': function(element) { + return getPixelValue(element, 'borderRightWidth'); + }, + + 'margin-top': function(element) { + return getPixelValue(element, 'marginTop'); + }, + + 'margin-bottom': function(element) { + return getPixelValue(element, 'marginBottom'); + }, + + 'margin-left': function(element) { + return getPixelValue(element, 'marginLeft'); + }, + + 'margin-right': function(element) { + return getPixelValue(element, 'marginRight'); + } + } + }); + + if ('getBoundingClientRect' in document.documentElement) { + Object.extend(Element.Layout.COMPUTATIONS, { + 'right': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.right - rect.right).round(); + }, + + 'bottom': function(element) { + var parent = hasLayout(element.getOffsetParent()); + var rect = element.getBoundingClientRect(), + pRect = parent.getBoundingClientRect(); + + return (pRect.bottom - rect.bottom).round(); + } + }); + } + + Element.Offset = Class.create({ + initialize: function(left, top) { + this.left = left.round(); + this.top = top.round(); + + this[0] = this.left; + this[1] = this.top; + }, + + relativeTo: function(offset) { + return new Element.Offset( + this.left - offset.left, + this.top - offset.top + ); + }, + + inspect: function() { + return "#".interpolate(this); + }, + + toString: function() { + return "[#{left}, #{top}]".interpolate(this); + }, + + toArray: function() { + return [this.left, this.top]; + } + }); + + function getLayout(element, preCompute) { + return new Element.Layout(element, preCompute); + } + + function measure(element, property) { + return $(element).getLayout().get(property); + } + + function getHeight(element) { + return Element.getDimensions(element).height; + } + + function getWidth(element) { + return Element.getDimensions(element).width; + } + + function getDimensions(element) { + element = $(element); + var display = Element.getStyle(element, 'display'); + + if (display && display !== 'none') { + return { width: element.offsetWidth, height: element.offsetHeight }; + } + + var style = element.style; + var originalStyles = { + visibility: style.visibility, + position: style.position, + display: style.display + }; + + var newStyles = { + visibility: 'hidden', + display: 'block' + }; + + if (originalStyles.position !== 'fixed') + newStyles.position = 'absolute'; + + Element.setStyle(element, newStyles); + + var dimensions = { + width: element.offsetWidth, + height: element.offsetHeight + }; + + Element.setStyle(element, originalStyles); + + return dimensions; + } + + function getOffsetParent(element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var isInline = (Element.getStyle(element, 'display') === 'inline'); + if (!isInline && element.offsetParent) return $(element.offsetParent); + + while ((element = element.parentNode) && element !== document.body) { + if (Element.getStyle(element, 'position') !== 'static') { + return isHtml(element) ? $(document.body) : $(element); + } + } + + return $(document.body); + } + + + function cumulativeOffset(element) { + element = $(element); + var valueT = 0, valueL = 0; + if (element.parentNode) { + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + } + return new Element.Offset(valueL, valueT); + } + + function positionedOffset(element) { + element = $(element); + + var layout = element.getLayout(); + + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (isBody(element)) break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + + valueL -= layout.get('margin-top'); + valueT -= layout.get('margin-left'); + + return new Element.Offset(valueL, valueT); + } + + function cumulativeScrollOffset(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return new Element.Offset(valueL, valueT); + } + + function viewportOffset(forElement) { + var valueT = 0, valueL = 0, docBody = document.body; + + var element = $(forElement); + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == docBody && + Element.getStyle(element, 'position') == 'absolute') break; + } while (element = element.offsetParent); + + element = forElement; + do { + if (element != docBody) { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + return new Element.Offset(valueL, valueT); + } + + function absolutize(element) { + element = $(element); + + if (Element.getStyle(element, 'position') === 'absolute') { + return element; + } + + var offsetParent = getOffsetParent(element); + var eOffset = element.viewportOffset(), + pOffset = offsetParent.viewportOffset(); + + var offset = eOffset.relativeTo(pOffset); + var layout = element.getLayout(); + + element.store('prototype_absolutize_original_styles', { + left: element.getStyle('left'), + top: element.getStyle('top'), + width: element.getStyle('width'), + height: element.getStyle('height') + }); + + element.setStyle({ + position: 'absolute', + top: offset.top + 'px', + left: offset.left + 'px', + width: layout.get('width') + 'px', + height: layout.get('height') + 'px' + }); + + return element; + } + + function relativize(element) { + element = $(element); + if (Element.getStyle(element, 'position') === 'relative') { + return element; + } + + var originalStyles = + element.retrieve('prototype_absolutize_original_styles'); + + if (originalStyles) element.setStyle(originalStyles); + return element; + } + + + function scrollTo(element) { + element = $(element); + var pos = Element.cumulativeOffset(element); + window.scrollTo(pos.left, pos.top); + return element; + } + + + function makePositioned(element) { + element = $(element); + var position = Element.getStyle(element, 'position'), styles = {}; + if (position === 'static' || !position) { + styles.position = 'relative'; + if (Prototype.Browser.Opera) { + styles.top = 0; + styles.left = 0; + } + Element.setStyle(element, styles); + Element.store(element, 'prototype_made_positioned', true); + } + return element; + } + + function undoPositioned(element) { + element = $(element); + var storage = Element.getStorage(element), + madePositioned = storage.get('prototype_made_positioned'); + + if (madePositioned) { + storage.unset('prototype_made_positioned'); + Element.setStyle(element, { + position: '', + top: '', + bottom: '', + left: '', + right: '' + }); + } + return element; + } + + function makeClipping(element) { + element = $(element); + + var storage = Element.getStorage(element), + madeClipping = storage.get('prototype_made_clipping'); + + if (Object.isUndefined(madeClipping)) { + var overflow = Element.getStyle(element, 'overflow'); + storage.set('prototype_made_clipping', overflow); + if (overflow !== 'hidden') + element.style.overflow = 'hidden'; + } + + return element; + } + + function undoClipping(element) { + element = $(element); + var storage = Element.getStorage(element), + overflow = storage.get('prototype_made_clipping'); + + if (!Object.isUndefined(overflow)) { + storage.unset('prototype_made_clipping'); + element.style.overflow = overflow || ''; + } + + return element; + } + + function clonePosition(element, source, options) { + options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, options || {}); + + source = $(source); + element = $(element); + var p, delta, layout, styles = {}; + + if (options.setLeft || options.setTop) { + p = Element.viewportOffset(source); + delta = [0, 0]; + if (Element.getStyle(element, 'position') === 'absolute') { + var parent = Element.getOffsetParent(element); + if (parent !== document.body) delta = Element.viewportOffset(parent); + } + } + + if (options.setWidth || options.setHeight) { + layout = Element.getLayout(source); + } + + if (options.setLeft) + styles.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) + styles.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + + if (options.setWidth) + styles.width = layout.get('border-box-width') + 'px'; + if (options.setHeight) + styles.height = layout.get('border-box-height') + 'px'; + + return Element.setStyle(element, styles); + } + + + if (Prototype.Browser.IE) { + getOffsetParent = getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); + + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + positionedOffset = positionedOffset.wrap(function(proceed, element) { + element = $(element); + if (!element.parentNode) return new Element.Offset(0, 0); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + hasLayout(offsetParent); + + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + }); + } else if (Prototype.Browser.Webkit) { + cumulativeOffset = function(element) { + element = $(element); + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) { + if (Element.getStyle(element, 'position') == 'absolute') break; + } + + element = element.offsetParent; + } while (element); + + return new Element.Offset(valueL, valueT); + }; + } + + + Element.addMethods({ + getLayout: getLayout, + measure: measure, + getWidth: getWidth, + getHeight: getHeight, + getDimensions: getDimensions, + getOffsetParent: getOffsetParent, + cumulativeOffset: cumulativeOffset, + positionedOffset: positionedOffset, + cumulativeScrollOffset: cumulativeScrollOffset, + viewportOffset: viewportOffset, + absolutize: absolutize, + relativize: relativize, + scrollTo: scrollTo, + makePositioned: makePositioned, + undoPositioned: undoPositioned, + makeClipping: makeClipping, + undoClipping: undoClipping, + clonePosition: clonePosition + }); + + function isBody(element) { + return element.nodeName.toUpperCase() === 'BODY'; + } + + function isHtml(element) { + return element.nodeName.toUpperCase() === 'HTML'; + } + + function isDocument(element) { + return element.nodeType === Node.DOCUMENT_NODE; + } + + function isDetached(element) { + return element !== document.body && + !Element.descendantOf(element, document.body); + } + + if ('getBoundingClientRect' in document.documentElement) { + Element.addMethods({ + viewportOffset: function(element) { + element = $(element); + if (isDetached(element)) return new Element.Offset(0, 0); + + var rect = element.getBoundingClientRect(), + docEl = document.documentElement; + return new Element.Offset(rect.left - docEl.clientLeft, + rect.top - docEl.clientTop); + } + }); + } + + +})(); + +(function() { + + var IS_OLD_OPERA = Prototype.Browser.Opera && + (window.parseFloat(window.opera.version()) < 9.5); + var ROOT = null; + function getRootElement() { + if (ROOT) return ROOT; + ROOT = IS_OLD_OPERA ? document.body : document.documentElement; + return ROOT; + } + + function getDimensions() { + return { width: this.getWidth(), height: this.getHeight() }; + } + + function getWidth() { + return getRootElement().clientWidth; + } + + function getHeight() { + return getRootElement().clientHeight; + } + + function getScrollOffsets() { + var x = window.pageXOffset || document.documentElement.scrollLeft || + document.body.scrollLeft; + var y = window.pageYOffset || document.documentElement.scrollTop || + document.body.scrollTop; + + return new Element.Offset(x, y); + } + + document.viewport = { + getDimensions: getDimensions, + getWidth: getWidth, + getHeight: getHeight, + getScrollOffsets: getScrollOffsets + }; + +})(); +window.$$ = function() { + var expression = $A(arguments).join(', '); + return Prototype.Selector.select(expression, document); +}; + +Prototype.Selector = (function() { + + function select() { + throw new Error('Method "Prototype.Selector.select" must be defined.'); + } + + function match() { + throw new Error('Method "Prototype.Selector.match" must be defined.'); + } + + function find(elements, expression, index) { + index = index || 0; + var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; + + for (i = 0; i < length; i++) { + if (match(elements[i], expression) && index == matchIndex++) { + return Element.extend(elements[i]); + } + } + } + + function extendElements(elements) { + for (var i = 0, length = elements.length; i < length; i++) { + Element.extend(elements[i]); + } + return elements; + } + + + var K = Prototype.K; + + return { + select: select, + match: match, + find: find, + extendElements: (Element.extend === K) ? K : extendElements, + extendElement: Element.extend + }; +})(); +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + if ( aup === bup ) { + return siblingCheck( a, b ); + + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +(function(){ + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + root.insertBefore( form, root.firstChild ); + + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + root = form = null; +})(); + +(function(){ + + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

    "; + + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + if ( !seed && !Sizzle.isXML(context) ) { + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + if ( elem && elem.parentNode ) { + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + if ( ret || !disconnectedMatch || + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + + +window.Sizzle = Sizzle; + +})(); + +Prototype._original_property = window.Sizzle; + +;(function(engine) { + var extendElements = Prototype.Selector.extendElements; + + function select(selector, scope) { + return extendElements(engine(selector, scope || document)); + } + + function match(element, selector) { + return engine.matches(selector, [element]).length == 1; + } + + Prototype.Selector.engine = engine; + Prototype.Selector.select = select; + Prototype.Selector.match = match; +})(Sizzle); + +window.Sizzle = Prototype._original_property; +delete Prototype._original_property; + +var Form = { + reset: function(form) { + form = $(form); + form.reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit, accumulator, initial; + + if (options.hash) { + initial = {}; + accumulator = function(result, key, value) { + if (key in result) { + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } else result[key] = value; + return result; + }; + } else { + initial = ''; + accumulator = function(result, key, value) { + value = value.gsub(/(\r)?\n/, '\r\n'); + value = encodeURIComponent(value); + value = value.gsub(/%20/, '+'); + return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; + } + } + + return elements.inject(initial, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + result = accumulator(result, key, value); + } + } + return result; + }); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + + getElements: function(form) { + var elements = $(form).getElementsByTagName('*'); + var element, results = [], serializers = Form.Element.Serializers; + + for (var i = 0; element = elements[i]; i++) { + if (serializers[element.tagName.toLowerCase()]) + results.push(Element.extend(element)); + } + return results; + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return /^(?:input|select|textarea)$/i.test(element.tagName); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + var element = form.findFirstElement(); + if (element) element.activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !(/^(?:button|reset|submit)$/i.test(element.type)))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; + +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = (function() { + function input(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return inputSelector(element, value); + default: + return valueSelector(element, value); + } + } + + function inputSelector(element, value) { + if (Object.isUndefined(value)) + return element.checked ? element.value : null; + else element.checked = !!value; + } + + function valueSelector(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + } + + function select(element, value) { + if (Object.isUndefined(value)) + return (element.type === 'select-one' ? selectOne : selectMany)(element); + + var opt, currentValue, single = !Object.isArray(value); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + currentValue = this.optionValue(opt); + if (single) { + if (currentValue == value) { + opt.selected = true; + return; + } + } + else opt.selected = value.include(currentValue); + } + } + + function selectOne(element) { + var index = element.selectedIndex; + return index >= 0 ? optionValue(element.options[index]) : null; + } + + function selectMany(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(optionValue(opt)); + } + return values; + } + + function optionValue(opt) { + return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; + } + + return { + input: input, + inputSelector: inputSelector, + textarea: valueSelector, + select: select, + selectOne: selectOne, + selectMany: selectMany, + optionValue: optionValue, + button: valueSelector + }; +})(); + +/*--------------------------------------------------------------------------*/ + + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +(function(GLOBAL) { + var DIV = document.createElement('div'); + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + var Event = { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45 + }; + + + var isIELegacyEvent = function(event) { return false; }; + + if (window.attachEvent) { + if (window.addEventListener) { + isIELegacyEvent = function(event) { + return !(event instanceof window.Event); + }; + } else { + isIELegacyEvent = function(event) { return true; }; + } + } + + var _isButton; + + function _isButtonForDOMEvents(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + } + + var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; + function _isButtonForLegacyEvents(event, code) { + return event.button === legacyButtonMap[code]; + } + + function _isButtonForWebKit(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 2 || (event.which == 1 && event.metaKey); + case 2: return event.which == 3; + default: return false; + } + } + + if (window.attachEvent) { + if (!window.addEventListener) { + _isButton = _isButtonForLegacyEvents; + } else { + _isButton = function(event, code) { + return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : + _isButtonForDOMEvents(event, code); + } + } + } else if (Prototype.Browser.WebKit) { + _isButton = _isButtonForWebKit; + } else { + _isButton = _isButtonForDOMEvents; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + return Element.extend(_element(event)); + } + + function _element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; + } + + if (node.nodeType == Node.TEXT_NODE) + node = node.parentNode; + + return Element.extend(node); + } + + function findElement(event, expression) { + var element = _element(event), match = Prototype.Selector.match; + if (!expression) return Element.extend(element); + while (element) { + if (Object.isElement(element) && match(element, expression)) + return Element.extend(element); + element = element.parentNode; + } + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (window.attachEvent) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': + case 'mouseenter': + element = event.fromElement; + break; + case 'mouseout': + case 'mouseleave': + element = event.toElement; + break; + default: + return null; + } + return Element.extend(element); + } + + var additionalMethods = { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }; + + Event.extend = function(event, element) { + if (!event) return false; + + if (!isIELegacyEvent(event)) return event; + + if (event._extendedByPrototype) return event; + event._extendedByPrototype = Prototype.emptyFunction; + + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + Object.extend(event, methods); + Object.extend(event, additionalMethods); + + return event; + }; + } else { + Event.extend = Prototype.K; + } + + if (window.addEventListener) { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + } + + var EVENT_TRANSLATIONS = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + + function getDOMEventName(eventName) { + return EVENT_TRANSLATIONS[eventName] || eventName; + } + + if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) + getDOMEventName = Prototype.K; + + function getUniqueElementID(element) { + if (element === window) return 0; + + if (typeof element._prototypeUID === 'undefined') + element._prototypeUID = Element.Storage.UID++; + return element._prototypeUID; + } + + function getUniqueElementID_IE(element) { + if (element === window) return 0; + if (element == document) return 1; + return element.uniqueID; + } + + if ('uniqueID' in DIV) + getUniqueElementID = getUniqueElementID_IE; + + function isCustomEvent(eventName) { + return eventName.include(':'); + } + + Event._isCustomEvent = isCustomEvent; + + function getRegistryForElement(element, uid) { + var CACHE = GLOBAL.Event.cache; + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + if (!CACHE[uid]) CACHE[uid] = { element: element }; + return CACHE[uid]; + } + + function destroyRegistryForElement(element, uid) { + if (Object.isUndefined(uid)) + uid = getUniqueElementID(element); + delete GLOBAL.Event.cache[uid]; + } + + + function register(element, eventName, handler) { + var registry = getRegistryForElement(element); + if (!registry[eventName]) registry[eventName] = []; + var entries = registry[eventName]; + + var i = entries.length; + while (i--) + if (entries[i].handler === handler) return null; + + var uid = getUniqueElementID(element); + var responder = GLOBAL.Event._createResponder(uid, eventName, handler); + var entry = { + responder: responder, + handler: handler + }; + + entries.push(entry); + return entry; + } + + function unregister(element, eventName, handler) { + var registry = getRegistryForElement(element); + var entries = registry[eventName]; + if (!entries) return; + + var i = entries.length, entry; + while (i--) { + if (entries[i].handler === handler) { + entry = entries[i]; + break; + } + } + + if (!entry) return; + + var index = entries.indexOf(entry); + entries.splice(index, 1); + + return entry; + } + + + function observe(element, eventName, handler) { + element = $(element); + var entry = register(element, eventName, handler); + + if (entry === null) return element; + + var responder = entry.responder; + if (isCustomEvent(eventName)) + observeCustomEvent(element, eventName, responder); + else + observeStandardEvent(element, eventName, responder); + + return element; + } + + function observeStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.addEventListener) { + element.addEventListener(actualEventName, responder, false); + } else { + element.attachEvent('on' + actualEventName, responder); + } + } + + function observeCustomEvent(element, eventName, responder) { + if (element.addEventListener) { + element.addEventListener('dataavailable', responder, false); + } else { + element.attachEvent('ondataavailable', responder); + element.attachEvent('onlosecapture', responder); + } + } + + function stopObserving(element, eventName, handler) { + element = $(element); + var handlerGiven = !Object.isUndefined(handler), + eventNameGiven = !Object.isUndefined(eventName); + + if (!eventNameGiven && !handlerGiven) { + stopObservingElement(element); + return element; + } + + if (!handlerGiven) { + stopObservingEventName(element, eventName); + return element; + } + + var entry = unregister(element, eventName, handler); + + if (!entry) return element; + removeEvent(element, eventName, entry.responder); + return element; + } + + function stopObservingStandardEvent(element, eventName, responder) { + var actualEventName = getDOMEventName(eventName); + if (element.removeEventListener) { + element.removeEventListener(actualEventName, responder, false); + } else { + element.detachEvent('on' + actualEventName, responder); + } + } + + function stopObservingCustomEvent(element, eventName, responder) { + if (element.removeEventListener) { + element.removeEventListener('dataavailable', responder, false); + } else { + element.detachEvent('ondataavailable', responder); + element.detachEvent('onlosecapture', responder); + } + } + + + + function stopObservingElement(element) { + var uid = getUniqueElementID(element), + registry = getRegistryForElement(element, uid); + + destroyRegistryForElement(element, uid); + + var entries, i; + for (var eventName in registry) { + if (eventName === 'element') continue; + + entries = registry[eventName]; + i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + } + + function stopObservingEventName(element, eventName) { + var registry = getRegistryForElement(element); + var entries = registry[eventName]; + if (!entries) return; + delete registry[eventName]; + + var i = entries.length; + while (i--) + removeEvent(element, eventName, entries[i].responder); + } + + + function removeEvent(element, eventName, handler) { + if (isCustomEvent(eventName)) + stopObservingCustomEvent(element, eventName, handler); + else + stopObservingStandardEvent(element, eventName, handler); + } + + + + function getFireTarget(element) { + if (element !== document) return element; + if (document.createEvent && !element.dispatchEvent) + return document.documentElement; + return element; + } + + function fire(element, eventName, memo, bubble) { + element = getFireTarget($(element)); + if (Object.isUndefined(bubble)) bubble = true; + memo = memo || {}; + + var event = fireEvent(element, eventName, memo, bubble); + return Event.extend(event); + } + + function fireEvent_DOM(element, eventName, memo, bubble) { + var event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', bubble, true); + + event.eventName = eventName; + event.memo = memo; + + element.dispatchEvent(event); + return event; + } + + function fireEvent_IE(element, eventName, memo, bubble) { + var event = document.createEventObject(); + event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; + + event.eventName = eventName; + event.memo = memo; + + element.fireEvent(event.eventType, event); + return event; + } + + var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE; + + + + Event.Handler = Class.create({ + initialize: function(element, eventName, selector, callback) { + this.element = $(element); + this.eventName = eventName; + this.selector = selector; + this.callback = callback; + this.handler = this.handleEvent.bind(this); + }, + + + start: function() { + Event.observe(this.element, this.eventName, this.handler); + return this; + }, + + stop: function() { + Event.stopObserving(this.element, this.eventName, this.handler); + return this; + }, + + handleEvent: function(event) { + var element = Event.findElement(event, this.selector); + if (element) this.callback.call(this.element, event, element); + } + }); + + function on(element, eventName, selector, callback) { + element = $(element); + if (Object.isFunction(selector) && Object.isUndefined(callback)) { + callback = selector, selector = null; + } + + return new Event.Handler(element, eventName, selector, callback).start(); + } + + Object.extend(Event, Event.Methods); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving, + on: on + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving, + + on: on + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + on: on.methodize(), + + loaded: false + }); + + if (GLOBAL.Event) Object.extend(window.Event, Event); + else GLOBAL.Event = Event; + + GLOBAL.Event.cache = {}; + + function destroyCache_IE() { + GLOBAL.Event.cache = null; + } + + if (window.attachEvent) + window.attachEvent('onunload', destroyCache_IE); + + DIV = null; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Code for creating leak-free event responders is based on work by + John-David Dalton. */ + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + function isSimulatedMouseEnterLeaveEvent(eventName) { + return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === 'mouseenter' || eventName === 'mouseleave'); + } + + function createResponder(uid, eventName, handler) { + if (Event._isCustomEvent(eventName)) + return createResponderForCustomEvent(uid, eventName, handler); + if (isSimulatedMouseEnterLeaveEvent(eventName)) + return createMouseEnterLeaveResponder(uid, eventName, handler); + + return function(event) { + var cacheEntry = Event.cache[uid]; + var element = cacheEntry.element; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createResponderForCustomEvent(uid, eventName, handler) { + return function(event) { + var cacheEntry = Event.cache[uid], element = cacheEntry.element; + + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } + + function createMouseEnterLeaveResponder(uid, eventName, handler) { + return function(event) { + var cacheEntry = Event.cache[uid], element = cacheEntry.element; + + Event.extend(event, element); + var parent = event.relatedTarget; + + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + handler.call(element, event); + } + } + + GLOBAL.Event._createResponder = createResponder; + docEl = null; +})(this); + +(function(GLOBAL) { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var TIMER; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (TIMER) window.clearTimeout(TIMER); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.detachEvent('onreadystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { + document.documentElement.doScroll('left'); + } catch (e) { + TIMER = pollDoScroll.defer(); + return; + } + + fireContentLoadedEvent(); + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.attachEvent('onreadystatechange', checkReadyState); + if (window == top) TIMER = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(this); + + +Element.addMethods(); +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator, context) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator, context); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); + +/*--------------------------------------------------------------------------*/ + +(function() { + window.Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + }, + + findElements: function(rootElement) { + return Prototype.Selector.select(this.expression, rootElement); + }, + + match: function(element) { + return Prototype.Selector.match(element, this.expression); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } + }); + + Object.extend(Selector, { + matchElements: function(elements, expression) { + var match = Prototype.Selector.match, + results = []; + + for (var i = 0, length = elements.length; i < length; i++) { + var element = elements[i]; + if (match(element, expression)) { + results.push(Element.extend(element)); + } + } + return results; + }, + + findElement: function(elements, expression, index) { + index = index || 0; + var matchIndex = 0, element; + for (var i = 0, length = elements.length; i < length; i++) { + element = elements[i]; + if (Prototype.Selector.match(element, expression) && index === matchIndex++) { + return Element.extend(element); + } + } + }, + + findChildElements: function(element, expressions) { + var selector = expressions.toArray().join(', '); + return Prototype.Selector.select(selector, element || document); + } + }); +})(); diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/CHANGELOG b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/CHANGELOG new file mode 100644 index 0000000000..3925f1dffa --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/CHANGELOG @@ -0,0 +1,1205 @@ +*V1.9.0* (December 23, 2010) + +* Update to Prototype 1.7 + +* Search for all script[src] occurrences rather than just within the head. [pgib] + +* Fixed scrolling Sortables for Prototype 1.7 [fragility] + +*V1.8.3* (October 8, 2009) + +* Update to Prototype 1.6.1 + +* Effect.toggle to return effect (to be able to do Effect.toggle(element, 'appear', {sync: true});) [RStankov] + +* Use element.identify() for fetching element.id in Sortable.create [RStankov] + +* Fix deprecated usage of Position.cumulativeOffset. [#182 state:resolved] [James Wheare] + +* Make loader work for application/xhtml+xml served documents. Closes #95. [Pavel Sedek] + +* Check for Windows Media plugin and RealPlayer plugin in Firefox on Windows to allow sound playback. Closes #36, #86. [Alexander Gavazov et al.] + +* Remove dead code in effects.js. Closes #125. [Confusioner] + +*V1.8.2* (November 18, 2008) + +* Update to Prototype 1.6.0.3 + +* Make sure InPlaceEditor converts HTML entities to text. [Sean Kirby] + +* Fix that Builder.node did not return extended elements on IE. Closes #71 and #77. + +* Fix a bug in Sortable.destroy to make sure it's called on the referenced Sortable only, which allows for the correct intialization of nested Sortables. Closes Trac #8615. [Leon Chevalier] + +* Change Effect.Base#render not to use eval(), so certain JavaScript runtime environments (like Adobe AIR) that do not support eval() work. [King Maxemilian, John-David Dalton] + +* Fixed a calculation error in Effect.Transitions.pulse that could lead to flickering, add easing and change it to be a normal 0 to 1 transition that can be used with any effects; Effect.Pulsate now uses its own implementation. [Thomas Fuchs] + +* Fixed Effect.ScrollTo. Changeset 8686 had a typo, document.viewport.getScrollOffsets[0] is always undefined. Removed the max check as it is not a cross-browser way to get scroll height and breaks the effect. Depending on scrollTo to do the right thing. Closes #11306. [Nick Stakenburg] + +* Update version check so all Prototype versions can be required, not just x.x.x. Closes #10966. [Nick Stakenburg] + +* Using $$ in the loader instead of getElementsByTagName to prevent limitations. Closes #9032. [Nick Stakenburg] + +* Fix some missing semicolons. [jdalton] + +* Fix an issue with Effect.ScrollTo that caused Firefox to scroll to the wrong offset in some situations. Closes #10245. [nik.wakelin] + +* Fixes an issue with IE ghosting on non-absolute elements. Closes #10423. [Tanrikut, tdd] + +*V1.8.1* (January 3, 2008) + +* Fix Element#getStyles in IE. Closes #10563. [Tobie Langel] + +* Update to Prototype 1.6.0.1 as of 8551. + +* Fix a regression with autocompleters not responding correctly to cursor keys on both Safari and IE. Closes #10107. [thx Zman771] + +*V1.8.0* (November 6, 2007) + +* Update to Prototype 1.6.0 final + +* Ajax.InPlaceEditor now can deal with callbacks that return an object. Closes #10064. [tdd] + +* Fix a potential problem with the loader and Firefox 2.0 on the Mac. Closes #9951. [awaters] + +* Add duration and distance options to Effect.Shake. Closes #8637. [amiel, rmm5t] + +* Update code to use new Hash implemention in Prototype 1.6. Update InPlaceEditor to use new Class.create syntax. [tdd] + +*V1.8.0 preview 0* (October 12, 2007) + +* Update to new Class.create syntax in Prototype 1.6; update to latest Prototype 1.6 trunk. + +* Fix bottom CSS property reassignment and initialization in queues for Effect#SlideUp. Closes #7412, #7761. + +* Fix an issue with String#parseStyle that could sometimes cause errors on Safari 2. + +* Refactoring to use Prototype 1.6. Remove dependency of Effect.tagifyText from builder. Use Prototype code style rules more consistently. + +* Remove the deprecated Effect2 namespace. + +* Update to Prototype 1.6.0_rc0. + +* Some effect code refactoring to take advantage of new Prototype 1.6 features. + +* Effect.ScrollTo is now no longer a core effect (returns a Effect.Tween instance now). + +* Add Effect.Tween, a new core effect. This effect tweens between two values and sets a property or calls a method on an object (including DOM elements); or allows for a callback method, which will be automatically bound to the object. + Examples: + var whatever = { blech: 0 }; + new Effect.Tween(whatever, 5, 0, 'blech'); // sets property on the object + new Effect.Tween('foo', 10, 20, 'innerHTML'); // sets property on the 'foo' DOM element + new Effect.Tween('foo', 10, 20, 'update'); // method call on 'foo' DOM element + new Effect.Tween('foo', 50, 0, { duration: 2.0 }, function(p){ + this.setStyle({border:p.toFixed(3)+'px solid red'}); + }); + new Effect.Tween(null, 0, 100, function(p){ scrollTo(0,p) }); // scrolls the window + +* Revert the workaround for the Firefox issue that makes autocompleter input field values be remembered correctly when the back button is hit to return to a page that contains autocompleted values, as this can cause "Are you sure you want to navigate away?" popups. Closes #8411. [okada] + +* Improve the way deactivating the active droppable is handled. Closes #9072. [Karsten Sperling] + +* Next version will be 1.8.0. Update to Prototype 1.6.0_pre1. + +* Make draggable initialization faster. Closes #8697. [ssinghi] + +* Make BDD-style testing compatible with IE6 and IE7. Closes #8972. [steveluscher] + +* Add BDD methods to booleans. Closes #9147. [steveluscher] + +* Add support for full CSS inheritance in Effect.Morph. Closes #9054. [Tobie] + IMPORTANT: Note that this could potentially change the outcome of classname-based morphs, so be sure to check your morph effects if you use this feature. + For this feature, the new method Element#getStyles is introduced, which returns an objects which properties correspond to the CSS properties found in Element.CSS_PROPERTIES. Note that this method doesn't work seemlessly across browsers for certain non-measurable CSS properties, such as float. + +* Add the externalControlOnly option to the in-place editors to allow for external controls to exclusively trigger the in-place editing. Closes #9024. [tdd] + +* Complete rewrite of Ajax.InPlaceEditor and Ajax.InPlaceCollectionEditor. [tdd] + THIS UPDATE MIGHT AFFECT BACKWARDS COMPATIBILITY. + Be sure to properly test, especially if you're using multi-line editing and/or monkeypatching. + Read more about all the added goodness in this article: + http://mir.aculo.us/2007/7/17/in-place-editing-the-summer-2007-rewrite + +* Update to Prototype 1.5.2_rc0 r7076: + - Works around bugs in Safari 1 and Safari 2 Regexp engines that could cause crashes + - Fixes a rendering issue with opacity-based effects and floating elements on Safari + +* Mild refactoring of sound.js to take advantage of new Prototype features. + +* Add highlight element method as a shortcut to Effect.Highlight. + Example: + $('message').highlight({startcolor:'#ff0000'}).update('KTHXBYE'); + +* Improve performance of String.prototype.parseStyle by caching the dummy element used. + +* Add combined effects as element methods. + Examples: + $('message').appear().update('Oh noes!'); + $('login').shake({ duration: 2 }); + +* Add Effect.Transitions.spring, which simulates a spring which oscillates while coming to rest. This transition is specifically targeted towards use with Effect.Move, for example as a reverteffect when using Draggables, as it will have return values >1 and cause temporary "overshooting" of the effect. Closes #8474. [edg2s] + +* Fix effects initialization if no options are given. Fixes #7809. [thx Kroc Camen] + +* Fix incorrect placement on drop for absolute ghosting elements. Update to Prototype 1.5.2_rc0 r6955 (required for #8581). Closes #8581. [tdd] + +* Make sure the autocompletion happens on the token where the cursor is, not the last token when using tokenized autocompleting text fields. Closes #8588. [tdd] + +* Fix that non-Mozilla browsers would display messages with the autocompleter back-button fix introduced in 1.7.1 beta 2. Fixes #7752, #8411. [josh, stetz] + +*V1.7.1 beta 3* (May 19, 2007) + +* Update to Prototype 1.5.1 final + +* New elements and handles options for Sortable.create which take arrays of elements or element ids. These overrule the normal handle and elements finding options on initialization of the sortable, and can yield huge performance increases in situations where these elements or ids are known at call time. [Thomas Fuchs] + +* Major performance improvement of Sortable.create when using the handle option. [Tobie Langel] + +* Fix Builder.node double quote escaping in attributes, fixes #8125. [Aa!] + +*V1.7.1 beta 2* (April 28, 2007) + +* Update to Prototype 1.5.1_rc4 + - Various optimizations and fixes + - http://dev.rubyonrails.org/browser/spinoffs/prototype/trunk/CHANGELOG?rev=6604 + +* Fix autocompleter indicator not appearing when calling activate() directly. Don't show indicator for local autocompleting. Fixes #7770. [pyro8019] + +* Don't clear out autocompleting fields in Mozilla-based browsers when navigating back to a page with the Back button, fixes #7752. [seanc] + +*V1.7.1 beta 1* (March 12, 2007) + +* Update to Prototype 1.5.1 r6386 + - Fixes SlideUp/SlideDown on IE + - Fixes an opacity problem with IE + - Performance optimizations + - See http://dev.rubyonrails.org/browser/spinoffs/prototype/trunk/CHANGELOG?rev=6381 for more information + +* Make builder unit tests work on IE + +* Fix id assignment for sounds on Firefox/Windows, fixes #7709 [Robert Wallis] + +* Add Sound.enable() and Sound.disable() to globally turn off Sound.play() + +* Added Sound.play(url,options) in new sound.js file. scriptaculous.js automatically includes this file. + Based on code by Jules Gravinese, used with permission. + + The sound player uses native sound support on IE, and falls back to using on other browsers, + which means it uses QuickTime for most cases. The recommended format to use is MP3. + + Examples: + Sound.play('blah.mp3'); + // --> plays sound immediately in 'global' track + Sound.play('blah.mp3',{replace:true}); + // --> stop playing all sounds in 'global' track, and play new sound + Sound.play('blah.mp3',{track:'mytrack'}); + // --> places the sound in the 'mytrack' track + Sound.play('blah.mp3',{track:'mytrack',replace:true}); + // --> stop playing all sounds in 'mytrack' track, and play new sound + + The sound effect used in the functional test is "Sword being drawn" by James Greever, released as freeware. + +* Various effects engine optimizations [Tobie Langel, Thomas Fuchs] + +* Make Ajax.InPlaceEditor more customizable: [thx godlie] + - Add an okLink option to Ajax.InPlaceEditor so you can have a link instead of a button. Uses the 'editor_ok_link' CSS class for styling, fixes #6455 + - Add an cancelButton option to Ajax.InPlaceEditor, uses the 'editor_cancel_button' CSS class + - Add textBefore-, textBetween- and textAfterControls (which default to empty strings) + - For consistency, cancel link now also gets 'editor_cancel_link' CSS class + +* Add an onDropped callback to Draggables, that fires at the end of a drag when the Draggable was dropped on a Droppable + +* Add revert: 'failure' option to Draggables to only revert if not dropped on a valid drop target, fixes #6909 [davidw] + +* Update to new Prototype Browser detection + +* Fix a memory leak with Effect.Morph in Firefox, fixes #7558 [Malard] + +* Add ability to nest single nodes in Builder.node in addition to arrays of nodes. + Examples: + var element = Builder.node('div', Builder.node('span','blah')); + + Builder.dump(); + var element = DIV(SPAN('blah')); + +*V1.7.0* (January 19, 2007) + +* Cleanups for Effect.Morph + +* Monkeypatch Prototype 1.5.0 to incorporate [6002], fixes setStyle({opacity:0}) for IE + +* Fix Effect.inspect() for Prototype 1.5.0 final hash changes + +* Update to Prototype 1.5.0 final + +* New option keepBackgroundImage: true for Effect.Highlight, fixes #5037 [docwhat, tomg] + +* Minor tweaks for issues with application/xhtml+xml documents on Firefox, fixes #6836 [sjinks] + +* Fix a possible exception with Sortables, fixes #6828 [craiggwilson] + +* Update to Prototype 1.5.0_rc2 revision [5844] (as of Rails 1.2 RC 2) + +*V1.7.0 beta 2* (December 18, 2006) + +* Change the default setting for effects to support up to 60fps, if renderable by the browser. Add performance info to effects unit test. + +* Change get/setOpacity to use Prototype's new support for cross-browser opacity. + +* Update to Prototype 1.5.0_rc2 revision [5741], which fixes some of the reported issues with beta 1 (see Prototype's CHANGELOG for more): + * Opera 9, while not officially supported, should work now with Prototype and script.aculo.us + * Fixes issue with Safari when using Prototype's Ajax helpers with HTTP authorization + +* Add hash and CSS className support to Effect.Morph, fixes #6674 [Tobie] + Examples: + $(element).morph('myClass') + // will morph to all the properties specified + // in .className (in your external stylesheet). + // All properties which cannot be morphed (such as font-style) + // will be applied AfterFinish + $(element).morph('font-size: 10px') // or + $(element).morph({fontSize: '10px'}) // will morph the font-size to 10px + +*V1.7.0 beta 1* (November 21, 2006) + +* Add Element.morph() and Element.Methods.morph() as a shortcut to Effect.Morph + Example: + // basic Effect.Morph + $('error_message').morph('color:#f00;font-size:17px'); + // fade out after a while + $('error_message').show().morph('font-size:17px').morph('opacity:0',{delay:4}); + +* Update to Prototype 1.5.0_rc2 revision [5580] + +* Add a paramName option to the inplace editor for overriding the default parameter name of "value" + +* Add Effect.Transform that generates parallel executing Effect.Morph sets + Example: + // set up transformation + var transformation = new Effect.Transform([ + { 'div.morphing': 'font-size:20px;padding-left:40em' }, + { 'blah' : 'width:480px;border-width:10px;border-right-width:20px;margin:200px;margin-bottom:-20px;font-size:30px' } + ],{ duration: 0.5 }); + // play transformation (can be called more than once) + transformation.play(); + +* Add Effect.Morph core effect that morphs to a given CSS style rule. Effect.Morph does take orginal styles given by CSS style rules or the style attribute into consideration when calculating the transforms. It works with all length and color based CSS properties, including margins, paddings, borders, opacity and text/background colors. + Example: + new Effect.Morph('mydiv',{ + style: 'font-size:3em;color:#f00;border-width:2em', + duration: 2.0 + }); + +*V1.6.5* (November 8, 2006) + +* Update to Prototype 1.5.0_rc1 revision [5462] + +* Support the HTML 'for' attribute in Builder by using 'htmlFor', fixes #6472 [gjones, tdd] + + var node = Builder.node('label', { htmlFor: 'myinput' }); + +* Add support to run a specific failing unit test by clicking on the corresponding test result, fixes #6290 [leeo] + +* Add modifier key support to Event.simulateMouse, fixes #6391 [savetheclocktower] + +* Add rails-trunk update task, clean up references to MIT license + +* Add new 'with-last' queue position option to queue effects to occur in parallel with the last effect to start in the queue + +* Add new special core effect Effect.Event for one-shot events that follow timelines defined by effect queues + + new Effect.Event({ afterFinish:function(){ + // do some code here + }, position: 'end' }); + +* Do some refactoring to make use of Prototype 1.5 functionalities and save LOC + +* Fix an possible crash of IE on Effect.SlideUp, fixes #3192 [thx nel] + +* Add Builder.build() to create nodes from strings containing HTML, [DHH] + + var node = Builder.build("

    this is neat!

    "); + +* Add a pulses parameter to Effect.Pulsate to control the amount of pulses, fixes #6245 [leeo] + + For example, Effect.Pulsate('d8', {pulses: 2}) would pulsate twice. If the option is not given, it defaults to five pulses. + +* Fix an issue with clicking on a slider span resulting in an exception, fixes #4707 [thx sergeykojin] + +* Fix an issue with Draggables when no options are supplied, fixes #6045 [thx tdd] + +*V1.6.4* (September 6, 2006) + +* Hotfix IE issues with patched Prototype V1.5.0_rc1 + +*V1.6.3* (September 5, 2006) + +* Update Prototype to V1.5.0_rc1 + +* Merge assertElementsMatch and assertElementMatches from Prototype's [4986] unittest.js [Sam Stephenson] + +* Update Prototype to revision [4930] + +* Fix various issues with IE detection and Opera, and setOpacity, fixes #3886, #5973 + +* Make Sortable.serialize handle DOM IDs like "some_element_1" correctly, fixes #5324 + +* Add assertRespondsTo and shouldRespondTo assertions + +* Add experimental alternate syntax for unit tests (Behaviour Driven Development-style) + +* Add support for onStart, onDrag and onEnd events directly on Draggables (invoked from the Draggables.notify), fixes #4747 [thx scriptkitchen] + +* Add element shortcuts to Builder that can be activated by calling Builder.dump() (see the unit test), fixes #4260 [thx napalm] + +* Fix selection of correct option in SELECT element generated by InPlaceCollectionEditor for indexed option arrays, fixes #4789 [thx steve] + +* Add autoSelect option to Autocompleters to auto select an entry if only one is returned, fixes #5183 [thx cassiano dandrea] + +* Added delay option to Draggables and Sortables, see test/functional/dragdrop_delay_test.html for usage, implements #3325 [thx lsimon, tomg] + +* Remove revert cache code obsoleted by #4706, fixes #3436 (again) [thx tomg] + +* Fix autoscrolling inside scrollable containers when window is scrolled too, fixes #5200 [thx wseitz] + +* Make Effect.Puff work correctly for floating elements, fixes #3777 [thx michael hartl] + +* Add version and timestamp to indvidual library files for easier identification (the files are preprocessed by the Rake fresh_scriptaculous task), fixes #3015 [thx Tobie] + +* Add assertIndentical and assertNotIdentical unit test assertions, which test for equality and common type, fixes #5822 [thx glazedginger] + +* Add integration test for Ajax autocompleter for results with no linebreaks, #4149 + +* Fix an issue with redrawing ghosted draggables that are inside a scrolled container, fixes #3860 [thx gkupps, tsukue] + +* Added a custom exception to all base effects when used on non-existing DOM elements, added a assertRaise method to unit tests + +* Fix autoscrolling when dragging an element unto a scrollable container, fixes #5017 [thx tomg] + +* Fix a condition where overriding the endeffect on Draggables without overriding the starteffect too leads to a Javascript error [thx Javier Martinez] + +* Fix a possible error with the drag/drop logic (affects the solution to #4706) + +*V1.6.2* + +* Fix a problem in the drag and drop logic if an reverting/drag ending draggable was initialized for a new drag (for example by clicking repeatedly) for all cases where the default start/revert/end-effects are used, fixes #4706 [thx tecM0] + +* Fix possible memory leaks with Draggables, fixes #3436 [thx aal] + +* Throw nicer errors when requires script.aculo.us libraries are not loaded, fixes #5339 + +* Make slider handles work when not displayed initially by using CSS width/height, fixes #4011 [thx foysavas] + +* Update sortable functional test with onUpdate counter + +* Make more Element extensions unit tests work on Safari + +* Add the assertMatch unit test assertion for asserts with RegExps [thx Ian Tyndall] + +* Fix a problem with Effect.Move causing "jumping" elements because of very low float numbers in some situations + +* Fix a missing semicolon in dragdrop.js, fixes #5569 [thx mackalicious] + +* Fix a slight inaccuracy with Effect.Scale that could lead the scaling to be one pixel off + +* Be more prototypish with Effect.Transitions.linear + +* Make Effect.Scale recognize font sizes that use the pt unit, fixes #4136 [thx aljoscha] + +* Fix IE hack in Effect.Opacity, fixes #5444 [thx nicholas] + +* Fix IFRAME layout fix for IE and Autocompleter, fixes #5192 [thx tommy skaue] + +* Fix only option in onEmptyHover, fiex #5348 [thx glenn nilsson] + +* Fix Effect.BlindDown and SwitchOff handling of supplied callbacks, fixes #5089 [thx martinstrom] + +* Fix a problem with field focus on Ajax.InPlaceEditor and loading external text, fixes #4988, #5244 [thx rob] + +* Do not attempt to scroll if scrollspeed is 0/0, fixes #5035 [thx tomg] + +* Fix a problem with Sortable Tree serialization, fixes #4939, #4688, #4767 [thx Sammi Williams] + +* Fix an endless loop with sliders, fixes #3226, #4051, #4765 [thx jeff] + +* Make autocompleter work with update DIVs that have scrollbars, fixes #4782 [thx Tommy Skaue] + +* Corrected options parsing on switchoff effect, fixes #4710 [thx haldini] + +*V1.6.1* + +* Update to Prototype 1.5.0_rc0 + +* Add Draggable object as third parameter to snap, fixes #4074 [thx mdaines] + +* Fix an IE flicker with SlideUp/SlideDown, fixes #3774, [thx sbbowers] + +* Add parsing/setting of any currently set opacity CSS rule to default opacity effect on draggables, fixes #3682 [thx Mike A. Owens] + +* Added prototype $() performance patch from #4477 and updated effects.js to new Element.addMethods() syntax + +* Sortable trees [thx Sammi Williams, sammi@oriontransfer.co.nz] + - Added functional test (test/functional/sortable_tree_test.html) + - removed greedy option + - modified the way Droppables.show works - added affected list + - added Droppables.findDeepestChild + - modified Sortable.options to work for trees + - modified Sortable.onEmptyHover with additional logic to insert + the element at a certain place according to the overlap + - modified Sortable.onHover to avoid DOM Exceptions + - modified Sortable.create to support the creation of Sortable trees + - added two convenience functions - Sortable.findElements and + Sortable.findTreeElements + - Added Sortable.tree (and a number of private functions to facilitate it) + Returns a tree structure: + var root = { + id: null, + parent: null, + children: , + container: , + } + var child = { + parent: , + children: , + id: (as per options.format), + element: , + container: , + position: + } + This is intended to become part of the public API, and can be used to manipulate + the tree programatically. + - Modified Sortable.serialize to use Sortable.tree when set in the options. + +* Fix a problem with window scrolling on draggables [thx Gregory Hill] + +* Let the benchmark method return the time taken in ms, so it can be used for time-based assertions + +* Fix problem with id-based handle names and draggables + +* More Element.childrenWithClassName optimization + +* Added benchmark method to unittest.js; some cleaning up of unit tests + +* Add fix for IE memory leaks in included prototype.js from [4094] + +*V1.6.0* + +* Major speedup for sortable with handles initialization [thx Jamis Buck] + +* Update to latest Prototype 1.5.0_pre1 trunk + +* Add functional "random effects" test, also for browser compatibility testing + +* Fix two typos introduced with the 1.5 Methods syntax update + +* Add functional test for $$-triggered effects with .visualEffect + +* Fix shift-tab for autocompleter in Safari, fixes #4423 [thx matt] + +* Prepare for script.aculo.us 1.6, add Prototype 1.5 requirement and check that Element.Methods is available in the scriptaculous loader + +* Refactor effects.js to use the new Prototype 1.5 Element Methods syntax + +* Update to latest Prototype 1.5.0_pre0 trunk + +* Fix a problem with the draggable window scrolling code and scrolling inside overflow containers + +* Add passing through of scroll options from Sortable to Draggable [thx Gregory Hill] + +* Make it possible to scroll window on dragging, #3921 [thx rdmiller] + +* Make Element.forceRerendering give up on any exception (this fixes various problems with IE) + +* added visualEffect method for the Element Mixin, fixed so you can chain multiple calls. [Rick Olson] + +* Fix only option on Sortable.create to accept multiple class names, fixes #3427 [thx glenn nilsson] + +* Added workaround for a rendering bug in Safari when using floating elements with Effect.Appear + +* Update lib/prototype.js to Prototype 1.5.0_pre0 + +*V1.5.3* + +* Change version number to V1.5.3, prepare Rakefile + +* Remove unneeded height restoring in Effect.BlindDown as that is handled internally by the restoreAfterFinish option to Effect.Scale, fixes #3667 [thx Ross Lawley] + +* Added Ajax.InPlaceCollectionEditor which uses a SELECT element instead of a text field (see test/functional/ajax_inplacecollectioneditor_test.html for usage), #3491 [thx anna] + +* Enable in place editor to use RJS (implements a new evalScripts option for the in place editor), #3490 [thx Richard Livsey] + +* Added Sortable.setSequence to programmatically reorder a Sortable, #3711 [thx Mike A. Owens] + +* Added unit test for #3732 (currently fails due to Prototype #3877) [thx michal] + +* Fix span positioning for sliders with ranges not starting at 0, fixes #3731 [thx michal] + +* Make name option on Sortable.serialize work again, fixes #3873 + +* Make dragging cancel only on ESC key, not on any key, fixes #3817 + +* Fix Rakefile for V1.5.2 + +*V1.5.2* + +* Change version number to V1.5.2 + +* Fix a wrong parameter in dragdrop.js, fixes #3555 + +* Fix two typos in effects.js, fixes #3481 [thx jtolds] + +* Add assertEnumEqual for unit testing (from Prototype SVN trunk) [Sam Stephenson] + +* Add Sortable.sequence method to extract the current sequence of a Sortable as an array, fixes #3675 [thx sphivo] + +* Add limit option to effect queues to limit the maximum number of effects in a queue, new unit tests for scoped effect queues, fixes #3498 [thx Abdur-Rahman Advany] + +* Fix Element.collectTextNodesIgnoreClass to correctly filter child nodes, fixes #3380 [thx lam] + +* Fix a condition where OPTION elements could cause unwanted dragging on Draggables, fixes #3441 [thx tom] + +* Fix a crash because of an IE bug in Effect.SlideDown, fixes #3192 [thx Rob Mayhew] + +* Added experimental auto-scroll option to Draggables and Sortables, activate with scroll: 'id_of_scroll_container_element'. + Note: needs testing, call syntax might change + See test/functional/dragdrop3_test.html for usage/demo + +* Added activate method to Autocompleter that allows you to trigger the suggestions through other means than user input [DHH] + +*V1.5.1* + +* Add select option to Autocompleter to optionally use textnodes from elements with a specific CSS class (see test/functional/ajax_autocompleter_test.html for usage) + +* In-place editor: add ok/cancel visibility options and onblur() submission, fixes #3233 [thx pulsation] + Note: currently, blur form submission breaks the cancel link functionality, if enabled + +* Added Effect.toggle for slide, blind and appear/fade effects, fixes #2704 [thx Azad] + +* Added selective component loading to scriptaculous.js, see test/unit/loading_test.html for usage [thx David Zülke] + +* Added local/scoped effect queues [thx David Zülke] + +* New core effect Effect.Move that can do absolute/relative movement [thx David Zülke] + +* Make default effects options modifyable globally by setting Effect.DefaultOptions + +* Make Draggables recognize top/left CSS properties defined in an external stylesheet, fixes #3205 [thx ansonparker] + +* Fixed draggables starting to drag even if mouse button was released with no movement, [thx topfunky] + +* Updated the README to reflect final status + +*V1.5.0* + +* Prepared for V1.5.0 final + +* Update lib/prototype.js to Prototype 1.4.0 final + +*V1.5.0_rc6* + +* Update lib/prototype.js to Prototype 1.4.0_rc4 + +* Fix Effect.SlideDown/SlideUp on Internet Explorer, includes a change to Effect.Scale, (should) fix #2962 and others, [thx wyldeone] + +* Make Draggables that are reverting cancel the default revert effect when they are dragged again while reverting, fixes #3188, [thx maverick] + +* Fix a problem with a wrong reference in Effect.Pulsate, fixes #3184, [thx cyx_] + +* Fix Sortable.findElements for XHTML in Gecko, fixes #3081, [thx fgnass] + +* Fix a problem with the slider getting NaN results when clicking directly on the track, fixes #3020, [thx rectalogic] + +* Fix a problem with Safari not doing sub-pixel rendering when setting opacity to 1 with Element.setOpacity + +* Fix slider to make handle width/height count for track length, fixes #3040, fixes #3017, [thx buggedcom] + +* Added Basic unit test for Effect.inspect + +* Fix Effect.multiple to deal correctly with delays + +* Safeguard Effect.Base.render to only do updates when Effect is in running state, fixes #2912 + +* Added inspect method to Effect.Base + +* Added functional test for DOM-based UL sortables #3016 + +* Added offset option to Effect.ScrollTo + +* More effects.js/dragdrop.js refactoring + +* Refactoring and optimizations for effects (not complete yet) + +* Refactoring and optimizations for Draggables/Droppables/Sortables (not complete yet) + +*V1.5.0_rc5* + +* Make Droppables.remove accept DOM IDs in addition to elements + +* Added some unit tests for drag and drop + +* Update lib/prototype.js to Prototype 1.4.0_rc3 + +* Make 'contents' a synonym for 'content' on Effect.Scale scaleMode option + +* Fix some possible exceptions in slider.js + +* Support for various snapping options on Draggables, adresses #2826, [thx saimonmoore] + +* Support horizontal ghostable Sortables, fixes #2794, [thx johnm] + +* Prevent an exception on IE with a functional test, see #2706 + +* Add functional testing of hoverclasses for sortables + +* Add an assert for correct UTF-8 return chars in Autocompleter + +* Correct IE detection in Autocompleter for Opera, fixes #2558 [thx gary] + +* Add onDrag callback to Draggable observers, #2827 [thx saimonmoore] + +* Added Form.Element.DelayedObserver to controls.js for live-search fields + +* Remove Element.Class in favour of Prototype Element.ClassNames, new function Element.childrenWithClassName() + +* Update Copyright notice in slider.js + +* Fix slider firing onChange events to eagerly on dragging a handle [thx wombert] + +* Fix a problem with Start/End spans and single handles + +* Add event property to Slider object so that callbacks can determine if setValue originated from a UI event or from script + +* Fix Builder.node() throwing unresolved errors in IE6, #2707 [thx flwombat] + +* Give currently active handle on slider the "selected" CSS class + +* Add start and end spans to slider + +* Make track clickable for sliders (moves last active handle [or first handle] to nearest possible position) + +* Make initial values on slider work for single and multiple handle sliders + +*V1.5.0_rc4* + +* Abort Effect.Highlight on elements which have display:none set (prevents problem with Safari) + +* Make Effect.Queue an Enumerable, fix a problem with Effect.Grow, more unit tests + +* Added restricted option to prevent moved of handles prior/after adjacent handles on Sliders with multiple handles + +* Update lib/prototype.js to Prototype 1.4.0_rc2 + +* Fix a bug with wrongly scrolling to the page top in Ajax.InPlaceEditor (circumvents various browser bugs) [Jon Tirsen], #2616 + +* major slider refactoring, changed slider range to default to [0,1]. Slider Range can be set with range:$R(from,to) option. The increment option is not active for now. + +* Added spans support for Control.Slider to mark spans bordered by slider handles, see unit test [thx to www.bartenbach.de for sponsoring this functionality] + +* Added preliminary support for optional multiple handles to Control.Slider [thx to www.bartenbach.de for sponsoring this functionality] + +* Fixed wrong inclusion of libs in inplace editor functional test + +* Fixed Effect.SlideDown/SlideUp to honor refactoring of Effect.Scale, fixes #2523 [thx obiwanki] + +* Reset the zIndex on Draggables correctly, #2483 [thx Jon Whitcraft] + +* Fix onChange callback on Sortables when dragging between sortables, #2488 [thx Dylan Greene] + +* Removed Builder code from effects.js, removed Object.debug (implemented as Test.Unit.inspect) + +* Added slider unit tests, fixed handling of values to autocalculate min/max, fixed upper/lower boundaries on setting the value programmatically + +* Synced to Rails 1.0 release candidate, update to Prototype 1.4.0_rc1, removed util.js, merged rests of util.js into effects.js to prepare for refactoring + +* Give Builder it's own file + +* Fix a logic issue with Autocompleter [thx tshinnic], #2457 + +*V1.5.0_rc3* + +* Make Effect position available to callbacks + +* Droppables.fire: send event to onDrop callback [François Beausoleil], #2389 + +* InPlaceEditor: Add disabling the field while loadTextURL loads and add a class while loading, plus fix various bugs with Internet Explorer and InPlaceEditor, [Jon Tirsen] #2302, #2303 + +* Made Droppables.remove work again [thx Mindaugas Pelionis], #2409 + +* Fixed that IE6 would incorrectly render the "fix-windowed-elements-overlapping" IFRAME on autocompletion [thx tshinnic], #2403 + +* Fixed Element.getOpacity throwing an error on Safari in some situations (this caused the autocompleter not to work) + +* Added format option to Sortable.create and Sortable.serialize to allow custom id formats. The format option takes a regular expression where the first grouping that matches is used for building the parameters. The default format is /^[^_]*_(.*)$/ which matches the string_identifier format. If you want to use the full id of the elements, use "format: /(.*)/". More examples are available in the sortable unit test file. + +* Started refactorings to use the new Prototype features and general code-cleanup + +* Update lib/prototype.js to Prototype 1.4.0_pre11 + +* Fixed a typo breaking the up arrow key for autocompletion [thx tshinnic], #2406 + +* Changed the handle option on Draggbles to accept classnames, or ids or elements [thx to Andrew West], #2274 + +* Force indicator to be hidden on hiding autocompletion update element, #2342 + +* Make Draggables honor external CSS positioning [thx to Mark Shawkey], #2359 + +* Make zindex handling for Draggables honor external CSS styles + +* Fix two Sortable.serialize issues, [thx Avi, Gorou], #2339, #2357 + +* Make Element.getOpacity work with IE, added unit tests [thx to Greg Hill] + +* Make Element.setOpacity honor non-alpha filters on IE (it now works with filters for alpha PNGs) + +* Fixed that Element.class.remove wrongly deleted spaces between class names, fixes #2311, #2313 + +* Fixed Builder for OPTION and OPTGROUP tags on Firefox < 1.5 and Internet Explorer 6, completely fixes #2325 + +* Improved Builder implementation to deal with Firefox-specific requirements and innerHTML parsing, partly fixes #2325 + +*V1.5.0_rc2* + +* Update lib/prototype.js to corrected Prototype 1.4.0_pre7 (fixed a problem with IE) + +* Update lib/prototype.js to Prototype 1.4.0_pre7 + +* Reverted to patched version of Prototype 1.4.0_pre4 for the time being (getResponseHeader problem on Mozilla) + +* Attempt to fix crashes in Safari 2.0.1, probably related to the event registering und unregistering in Draggables, possibly fixes #2310 + +* Update lib/prototype.js to Prototype 1.4.0_pre6 + +* Changed effects_test.html functional test to incoporate Firefox anti-flicker overflow:hidden CSS property + +*V1.5.0_rc1* + +* Prepared Rakefile and README for V1.5.0_rc1 release + +* Droppables w/greedy and hoverclass are now reverted when dragged item is no longer over them, fixes #2184 + +* Let Effect.Highlight correctly parse IE colors, add String.prototype.parseColor() for this, fixes #2037 + +* Fix Effect.Highlight to restore background image only if there was one + +* Made Effect.tagifyText work again on IE + +* Added String.prototype.toArray because Strings aren't iterable on IE + +* Fixed Element.getOpacity falsely returning 0 on IE + +* Some cleaning up in util.js + +* Cleaned up Effect.Opacity + +* Removed useless line in Position.page + +* Make scriptaculous.js work if a query strings are used when calling it + +* Fixed typos in slider.js and the functional test + +* Fixed a bug with Safari and the InPlaceEditor with form submission. Add support for interpreting simple
    s into linebreaks. [Jon Tirsen] + +* New Control.Slider() for horizontal and vertical sliders [Marty Haught] + +* Fixed autoapplying a 'layout' on IE with Effect.Opacity not working for elements given as id string + +* Make Effect.Parallel render final frames and call beforeFinish/afterFinish on included effects + +* Make Element.setOpacity work correctly again (browser-specific extensions, except for IE, are disabled at this time) + +* Added focusing the text field or text area created when using Ajax.InPlaceEditor #2126 [thx to Lee Mallabone] + +* Fixed Element.Class.childrenWith not returning the correct elements #2120 [cmees AT rogers DOT com] + +* Added MIT-LICENSE header to scriptaculous.js, cut back on license info in other libs to keep files smaller + +* Fix issues with form elements inside draggables not clickable with Firefox #2129 + +* Fixed an error in dragdrop.js with Draggables when no Droppables are defined + +* Fixed an issue with Element.makePositioned causing Effect.SlideDown to fail on Safari + +* Make effects "stateless" by cleaning up element.style after finish of effect #2133 [agr30 AT uni-koeln DOT de] + +* Add "internal" events to effects, to make them more easily extendable [Martin Bialasinski] + +* Set container element height to auto on finishing Effect.BlindDown und Effect.SlideDown #2123 [Tony Arnold] + +* Fixed a weird char in dragdrop.js + +* Controls.js Autocompleter.Base.updateElement() hook #2116 [Rob Wills] + +* Refactoring to use the Prototype enumerable extensions + +* Update to Prototype 1.4.0_pre4 + +*V1.5.0_pre4* + +* Added a queue option to effects for easy stacking and simple timeline building. Valid values are "front", "end" and "parallel" (default), see for test/functional/effects_queue_test.html for usage + +* Added a setup function to the core effects that gets called by Effect.Base when first entering into running state, which allows them to query the current state of an element or other page properties when queuing and/or delays are used + +* Added a state instance variable to Effect.Base that indicates whether a effect that uses the queue is idle, running or finished + +* Fixed a flicker with Effect.Puff + +* General code cleaning to get rid of (legitimate) Mozilla strict javascript warnings + +* Changes to assertVisible in unittest.js + +* Slight refactoring of Effect.Text, now known as Effect.multiple(), change first parameter to also accept an array of strings or elements, or a NodeList of elements (if a single element or a string is given, defaults to $('element').childNodes) + +* Made tagifyText work with IE. defaults to a style of "position: relative; zoom:1;" for the created SPAN wrappers. It also replaces spaces with non-breakable spaces. These sit-ups are required to work around problems with rendering elements with the proprietary IE "layout" and non-quirksmode documents. + +* Add a break to scriptaculous.js when the includes are done [thx to Danilo Celic] + +* Fixed a problem with the Autocompleter when hitting ESC would select the entry in IE and redisplay the entries box in Safari + +* Fixed that the onDrop callback on Droppables didn't receive the dopped element as second parameter + +* Make check for correct Prototype version on loading script.aculo.us + +* Changed implementation of Builder to an innerHTML-based approach to make it work with IE6 (note: removed special "style" handling for now) + +* Changed non-sync Effects to use a common setInterval call for better timing and page update speeds. See the new Effect.Queue object. + +* Fixed a problem with setting opacity in Effect.Opacity to very small values + +* Changed the implemention of Effect.Base to use setInterval + +* Set version number to 1.5_pre4_trunk + +* Added experimental Effect.Text function that allows to start delayed text effects, see test/functional/texteffects_test.html. + +* Added experimental Effect.tagifyText(element) which wraps an elements text nodes characters in relatively positioned span elements + +* Added "delay" option to Effect.Base to start an effect only after a specified delay + +* Finetuning unittest.js + even more unit tests + +* Added support for Firefox and Konqueror automatic unit testing on Linux [Michael Schuerig] + +* Added basic unit test files for Effects, updated unit testing tests + +* Fix typo in lib/prototype.js fix in R2069 (whoops) + +* Added unit test for Position.clone + +* Made the Postition.cumulative override recognize KHTML and AppleWebKit + +* Fix the "hide form controls" iframe fix on the Autocompleter on Internet Explorer + +*V1.5.0_pre3* + +* More positioning fixes, expanded Element.clone [thx to Michael Schuerig] + +* Fixed some JavaScript warnings about redeclared vars [Courtenay] + +* Fixed a problem with autocompleting update divs not correctly positioned inside positioned elements [thx to Jonathan Feinberg] + +* Workaround for a Safari bug on absolutely positioned children of the BODY element + +* Added test/run_functional_test.html to more easily find your way around in the functional tests + +* Fixed some missing var declarations in effects.js + +* Support for automatic unit testing on IE/Win [Jon Tirsen] + +* Added loading the text for the Ajax.InPlaceEditor from the server, more CSS support [Richard Livsey, Jon Tirsen] + +* Made builder_test.html unit test work with Safari, fixed (one of two) caching issues with javascripttest.rb, added ajax_autocompleter_test.html to Rakefile unittests + +* Fixed Element.Class getting false matches on class names like classA-foobar, added unit tests [Martin Bialasinski] + +* Added a test to evluate the Fade/Appear effects across browsers and elements (test/functional/effects6_test.html) + +* Framework for completely self-running in-browser unit tests on Mac OS X (try "rake unittest") [Jon Tirsen] + +* Updates to Ajax.InPlaceEditor (Highlighting, Ajax options, more) [Jon Tirsen] + +* Made event registering on Draggables happen per drag for improved performance [thx to Cathy] + +* Moved Element.collectTextNodesIgnoreClass to util.js (preparation for refactoring) + +* Made sortable onChange option work again + +* Fixed a bug with Sortables resulting in wrong result when using Sortable.serialize with onUpdate on ghostly Sortables [thx Richard Livsey, Caleb Buxton] + +* Changed version number in scriptaculous.js to 1.5_pre3 + +* Moved setting focus in Autocompleter to updateElement function to make it overridable [Brian Palmer] + +* Added special handling for style attributes to Builder [Michael Schuerig] + +* Changed opacity=1.0 check back to be inline with its original (pre 1.5) implementation to prevent flickering in Firefox < 1.1 on opacity-based effects (this also fixes flickering with drag-and-drop, sortable lists and autocompleting text fields). Note that this potentially breaks correct colors in some situations. [thx to David Zülke] + +* Automatically deactivate browser built-in autocompletion on autocompleting textfields, fixes part of Rails #1824. + +* Fixed a problem with Ajax.InPlaceEditor causing a JavaScript exception on Safari [thx to Patrick Lenz] + +*V1.5.0_pre2* + +* Fixed a weird character in dragdrop.js, added check to allow empty containers on Sortables (useful with dropOnEmpty option) [thx to Dave Strus] + +* Added unit test runner file test/run_unit_tests.html, new unit test files must be listed in test/unit/index.html + +* Added unit tests for Prototype string functions + +*V1.5.0_pre1* + +* Prepared README for V1.5.0_pre1 + +* Added a main scriptaculous.js file to load in the other libraries transparently: + + + + + in the HEAD section of an HTML file is now all that's needed to include all of the script.aculo.us libraries, + given that they all can be accessed by the same path as scriptaculous.js is. + +* Fixed possible problem with greedy Droppables when using the dropOnEmpty option on Sortable.create + +* Added new CSS/background color features to Ajax.InPlaceEditor [Jon Tirsen] + +* Added unit test for Autocompleter, first Version + +* Added Event.simulateKey/s to unittest.js. Note that due to a bug in Firefox this will only work with Firefox up to v1.0.4 (see https://bugzilla.mozilla.org/show_bug.cgi?id=289940) + +* Fixed a condition where standard a href=xxx links wouldn't work in autocomplete results. These do now, the onclick event is not cancelled. [thx to Jasper Slits] + +* Fix showing ghosting marker only on ghosting Sortables + +* Some more Builder stuff, expanded builder unit tests + +* Moved stuff that didn't belong into specific libs into util.js; util.js is now required for all script.aculo.us libs + +* Corrected weirdness in unittest.js + +* Added dropOnEmpty option to Sortables to allow dropping on empty lists (see test/functional/sortable3_test.html) + +* Changed algoritm for drag/drop position setting + +* Changed workaround for class attributes (fixes a Firefox 1.0 error) from klass to className to be more Javascriptesque [thx to Martin Bialasinski] + +* Fixed a typo in ajax_inplaceeditor_test.html + +* Updated Rakefile to add unittest.js and util.js to the package + +* Added util.js which contains various utlity functions and extensions to Prototype + +* Major restructuring + +* Added TEXTAREA support to Ajax.InPlaceEditor [Sanford Barr] + +* Added Ghost Train alpha 0.1.0. See ghosttrain/test.html. + +* More features for Ajax.InPlaceEditor [Jon Tirsen] + +* Tweaks to unittest.css [Thomas Fuchs] + +* Refactoring and new features in unittest.js [Thomas Fuchs] [Jon Tirsen] + +* Fixed a wrong variable reference in Effect.Scale + +* Fix Element.makePositioned to recognize the 'static' position + +* Allow to choose the parameter name for Ajax.Autocompleter [Cameron Braid] + +* Changed resolving indicator DOM element more flexibility [Cameron Braid] + +* Fixed a reference in Ajax.InPlaceEditor + +* Added contributors to unittest.js + +* Souped-up Ajax.InPlaceEditor, added support for a form id and specifying all the texts through options [Jon Tirsen] + +* Make unit testing look good [Michael Schuerig] + +* Changed default revert duration for Draggables to distance-dependent algorithm [suggested by San] + +* Fix double unescaping in Autocompleter + +* Refactoring auf Autocompleter classes to use camelCase (note: changes the syntax of some of the options) + +* Add updateElement option to Autocompleter [Rob Sharp] + +* Updated Ajax.InPlaceEditor, refactoring of unit testing [Jon Tirsen] + +* Added preliminary version of Ajax.InPlaceEditor to controls.js; added experimental Firefox only functional testing for it (inplaceeditor_result.html) [Jon Tirsen] + +* Added some addtional test files. + +* Fixes a bug with Droppables not recognizing a drop if no hoverclass is given [thanks drewie] + +* Fixes to ghosting marker, allow a predefined marker (element with id "dropmarker"). + +* Changed Effect.Opacity to better handle 100% opacity + +* Various fixes to ghosting, improves compatiblity with Safari and IE. [thanks to David Zülke] + +* Added experimental ghosting option to Sortables. See sortable_functional_ghosting.html for usage. + +* Renamed the clone option on Draggables to "ghosting". + +* Added experimental "clone" option to Draggable to so that a "clone" stays in place while dragging a translucent version of the draggable element. Currently, this requires relatively positioned elements, and doesn't work with sortable lists (but should after some tweaking). See dragdrop_function_4.html test file for details. + +* Added Element.getStyle to find computed CSS styles of an element. Note: doesn't work in all cases in Safari, see my bug report at http://bugzilla.opendarwin.org/show_bug.cgi?id=4125 + +*1.1beta1* + +* Fixed rendering of last frame of animation when from/to is not 0.0/1.0. [thanks to David Zülke] + +* Updated internal Prototype JavaScript framework to version 1.4.0_pre2 (patched w/ workaround for Ajax.Updater init bug) + +* Some refactoring of controls.js to get rid of "dirty" implementation detail (dont' ask) + +* Added returning the generated combined effects to allow for .cancel() on all effects + +* Updated internal Prototype JavaScript framework to version 1.4.0_pre2 + +*Rails 0.13.1* + +* Updated Ajax.Autocompleter to deal with parameters options correctly [Martin Marinschek] + +* Updated controls.js to allow multple matches in local localcompletion [Ivan Krstic] + +* Make version of prototype.js in lib have a nicer Event object [David Zülke] + +* Added incremental and local autocompleting and loads of documentation to controls.js [Ivan Krstic] + +* Experimental: make version of prototype.js in lib destroy events on page unload to prevent browser memory leaks + +* Fixed a possible memory leak in dragdrop.js + +* Make version of prototype.js in lib compatible with some 3rd-party JavaScript libraries (like IE7) by refactoring to use Object.extend() for testing [David Zülke] + +* Make effects.js, dragdrop.js and controls.js compatible with some 3rd-party JavaScript libraries (like IE7) by refactoring to use Object.extend() [David Zülke] + +* Changed some tests to work better + +* Always reset background color on Effect.Highlight; this make change backwards-compatibility, to be sure include style="background-color:(target-color)" on your elements or else elements will fall back to their CSS rules (which is a good thing in most circumstances) + +* Removed a possible memory leaks with IE with sortables and droppables (removed references from elements) + +* Changes to class extension in effects.js + +* Make Effect.Highlight restore any previously set background color when finishing (makes effect work with set CSS classes) + +* Added capability to remove draggables/droppables and redeclare sortables in dragdrop.js + +* Added Effect.ScrollTo to smoothly scroll the page to an element + +* Better Firefox flickering handling on SlideUp/SlideDown + +* Some cleaning up in effects.js + +* Removed a possible memory leak in IE with draggables + +* Added support for cancelling dragging my hitting ESC + +* Changed logic of drag-and-drop to only include the last referenced droppable when firing a drop event. This change also offers slight performance gains. [Dominik Wagner] + +* Added addtional effects test page, added tests for scriptfragment matching + +*1.0.0* + +* Changed auto-hiding update div in Ajax.Autocompleter + +* Changed default serialization on Ajax.Autocompleter to use Form.Element.serialize + +* Added test file for drag/drop inside scrolled elements + +* Fixed dragging of unpositioned elements in Internet Explorer + +* Change default behaviour of Ajax.Autocompleter to do automatic overlapping, sizing and appear/fade effects + +* Fixed Internet Explorer hide-windowed-controls iframe handling + +* Changed Ajax.Autocompleter event handling + +* Added onShow/onHide callbacks to Ajax.Autocompleter to allow for customized handling/effects + +* Fixed SlideUp/SlideDown to restore the overflow CSS property (note: Firefox 1.0.X is buggy, set overflow:hidden as a workaround) + +* Fixed BlindUp/BlindDown to restore the overflow CSS property (note: Firefox 1.0.X is buggy, set overflow:hidden as a workaround) + +* Fixed draggables with revert:false on repeated drags behaving badly + +* Expanded the revert option on draggables to optionally take a function and revert only if it returns true + +* Added the dragged element as a parameter to the Draggables.notify callbacks [Michael Sokolov] + +* Removed a deprecated reference to Effect2 om Effect.Fold + +* Make the percentage on Element.setContentZoom absolute + +* Corrected rendering of Ajax.AutoCompleter when focus is lost while updating + +* Added (crude) functional tests + +* Some slight refactoring in controls.js + +* Changed dragdrop.js to use the Effect namespace for its effects + +* Updated to Prototype 1.3.0: removal of prototype-ext.js; refactoring. + +* Fixed behaviour of cursor keys in Safari on autocomplete script + +* Fixed Position.within_including_scrolloffsets + +* Fixed sortables that are absolutely positioned + +* Fixed unhandled whitespace in Ajax.Autocompleter + +* Updated prototype-ext.js to include additions for Ajax.Autocompleter + +* Added controls.js, contains AJAX autocompleting text fields from #960 + +* Refactored Event object + +* Renamed effects2.js to effects.js + +* Fixed draggables on pages with no droppables + +* Moved Event, Position and Element.Class to prototype-ext.js in preparation to Rails 0.13 + +* Added Effect.Transitions.pulse + +* Added Effect.Pulsate and Effect.Fold effect by Justin Palmer + +* Added transitions by Mark Pilgrim: .full, .none + +* Added effects by Mark Pilgrim: Effect.Grow, Effect.Shrink + +* Changed effects namespace to Effect. (Effect2 is deprecated, but works too) + +* Changed old Effect.ContentZoom class to Element.setContentZoom() function + +* Greatly expanded Effect.Highlight to have user-defined colors and autodetecting the background color + +* Converted remaining effects (Squish, Highlight) to new format + +* Sortable.create now passes the zindex, starteffect, reverteffect and endeffect options to the underlying Draggables + +* Sortable.serialize now honors the only option on Sortable.create + +* New overridable options on Draggables: zindex, starteffect, reverteffect, endeffect + +* Fix a Gecko engine flicker on Sortables in dragdrop.js + +* Fixed event.isLeftClick + +* Some small changes in effects2.js + +* Refactoring of dragdrop.js + +* Added an Object.prototype.inspect() and more verbose messages for js unit testing + +* Added test/unittest.js and initial tests in test/html. + +* Cleaning up of effects2.js (convert tabs to spaces) + +* Added Rakefile for distribution packaging (default task = make distribution, rake clean for cleaning up) + +* Initial check-in and directory layout for the script.aculo.us JavaScripts diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/MIT-LICENSE b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/MIT-LICENSE new file mode 100644 index 0000000000..da26ff0a24 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/builder.js b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/builder.js new file mode 100644 index 0000000000..7325038078 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/builder.js @@ -0,0 +1,136 @@ +// script.aculo.us builder.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Builder = { + NODEMAP: { + AREA: 'map', + CAPTION: 'table', + COL: 'table', + COLGROUP: 'table', + LEGEND: 'fieldset', + OPTGROUP: 'select', + OPTION: 'select', + PARAM: 'object', + TBODY: 'table', + TD: 'table', + TFOOT: 'table', + TH: 'table', + THEAD: 'table', + TR: 'table' + }, + // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, + // due to a Firefox bug + node: function(elementName) { + elementName = elementName.toUpperCase(); + + // try innerHTML approach + var parentTag = this.NODEMAP[elementName] || 'div'; + var parentElement = document.createElement(parentTag); + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" + elementName + ">"; + } catch(e) {} + var element = parentElement.firstChild || null; + + // see if browser added wrapping tags + if(element && (element.tagName.toUpperCase() != elementName)) + element = element.getElementsByTagName(elementName)[0]; + + // fallback to createElement approach + if(!element) element = document.createElement(elementName); + + // abort if nothing could be created + if(!element) return; + + // attributes (or text) + if(arguments[1]) + if(this._isStringOrNumber(arguments[1]) || + (arguments[1] instanceof Array) || + arguments[1].tagName) { + this._children(element, arguments[1]); + } else { + var attrs = this._attributes(arguments[1]); + if(attrs.length) { + try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707 + parentElement.innerHTML = "<" +elementName + " " + + attrs + ">"; + } catch(e) {} + element = parentElement.firstChild || null; + // workaround firefox 1.0.X bug + if(!element) { + element = document.createElement(elementName); + for(attr in arguments[1]) + element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; + } + if(element.tagName.toUpperCase() != elementName) + element = parentElement.getElementsByTagName(elementName)[0]; + } + } + + // text, or array of children + if(arguments[2]) + this._children(element, arguments[2]); + + return $(element); + }, + _text: function(text) { + return document.createTextNode(text); + }, + + ATTR_MAP: { + 'className': 'class', + 'htmlFor': 'for' + }, + + _attributes: function(attributes) { + var attrs = []; + for(attribute in attributes) + attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + + '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); + return attrs.join(" "); + }, + _children: function(element, children) { + if(children.tagName) { + element.appendChild(children); + return; + } + if(typeof children=='object') { // array can hold nodes and text + children.flatten().each( function(e) { + if(typeof e=='object') + element.appendChild(e); + else + if(Builder._isStringOrNumber(e)) + element.appendChild(Builder._text(e)); + }); + } else + if(Builder._isStringOrNumber(children)) + element.appendChild(Builder._text(children)); + }, + _isStringOrNumber: function(param) { + return(typeof param=='string' || typeof param=='number'); + }, + build: function(html) { + var element = this.node('div'); + $(element).update(html.strip()); + return element.down(); + }, + dump: function(scope) { + if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope + + var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " + + "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " + + "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+ + "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+ + "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+ + "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/); + + tags.each( function(tag){ + scope[tag] = function() { + return Builder.node.apply(Builder, [tag].concat($A(arguments))); + }; + }); + } +}; \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/controls.js b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/controls.js new file mode 100644 index 0000000000..5137ab510c --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/controls.js @@ -0,0 +1,965 @@ +// script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005-2010 Jon Tirsen (http://www.tirsen.com) +// Contributors: +// Richard Livsey +// Rahul Bhargava +// Rob Wills +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// Autocompleter.Base handles all the autocompletion functionality +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least, +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most +// useful when one of the tokens is \n (a newline), as it +// allows smart autocompletion after linebreaks. + +if(typeof Effect == 'undefined') + throw("controls.js requires including script.aculo.us' effects.js library"); + +var Autocompleter = { }; +Autocompleter.Base = Class.create({ + baseInitialize: function(element, update, options) { + element = $(element); + this.element = element; + this.update = $(update); + this.hasFocus = false; + this.changed = false; + this.active = false; + this.index = 0; + this.entryCount = 0; + this.oldElementValue = this.element.value; + + if(this.setOptions) + this.setOptions(options); + else + this.options = options || { }; + + this.options.paramName = this.options.paramName || this.element.name; + this.options.tokens = this.options.tokens || []; + this.options.frequency = this.options.frequency || 0.4; + this.options.minChars = this.options.minChars || 1; + this.options.onShow = this.options.onShow || + function(element, update){ + if(!update.style.position || update.style.position=='absolute') { + update.style.position = 'absolute'; + Position.clone(element, update, { + setHeight: false, + offsetTop: element.offsetHeight + }); + } + Effect.Appear(update,{duration:0.15}); + }; + this.options.onHide = this.options.onHide || + function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + + if(typeof(this.options.tokens) == 'string') + this.options.tokens = new Array(this.options.tokens); + // Force carriage returns as token delimiters anyway + if (!this.options.tokens.include('\n')) + this.options.tokens.push('\n'); + + this.observer = null; + + this.element.setAttribute('autocomplete','off'); + + Element.hide(this.update); + + Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); + Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this)); + }, + + show: function() { + if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); + if(!this.iefix && + (Prototype.Browser.IE) && + (Element.getStyle(this.update, 'position')=='absolute')) { + new Insertion.After(this.update, + ''); + this.iefix = $(this.update.id+'_iefix'); + } + if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); + }, + + fixIEOverlapping: function() { + Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); + this.iefix.style.zIndex = 1; + this.update.style.zIndex = 2; + Element.show(this.iefix); + }, + + hide: function() { + this.stopIndicator(); + if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); + if(this.iefix) Element.hide(this.iefix); + }, + + startIndicator: function() { + if(this.options.indicator) Element.show(this.options.indicator); + }, + + stopIndicator: function() { + if(this.options.indicator) Element.hide(this.options.indicator); + }, + + onKeyPress: function(event) { + if(this.active) + switch(event.keyCode) { + case Event.KEY_TAB: + case Event.KEY_RETURN: + this.selectEntry(); + Event.stop(event); + case Event.KEY_ESC: + this.hide(); + this.active = false; + Event.stop(event); + return; + case Event.KEY_LEFT: + case Event.KEY_RIGHT: + return; + case Event.KEY_UP: + this.markPrevious(); + this.render(); + Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || + (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; + + this.changed = true; + this.hasFocus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + activate: function() { + this.changed = false; + this.hasFocus = true; + this.getUpdatedChoices(); + }, + + onHover: function(event) { + var element = Event.findElement(event, 'LI'); + if(this.index != element.autocompleteIndex) + { + this.index = element.autocompleteIndex; + this.render(); + } + Event.stop(event); + }, + + onClick: function(event) { + var element = Event.findElement(event, 'LI'); + this.index = element.autocompleteIndex; + this.selectEntry(); + this.hide(); + }, + + onBlur: function(event) { + // needed to make click events working + setTimeout(this.hide.bind(this), 250); + this.hasFocus = false; + this.active = false; + }, + + render: function() { + if(this.entryCount > 0) { + for (var i = 0; i < this.entryCount; i++) + this.index==i ? + Element.addClassName(this.getEntry(i),"selected") : + Element.removeClassName(this.getEntry(i),"selected"); + if(this.hasFocus) { + this.show(); + this.active = true; + } + } else { + this.active = false; + this.hide(); + } + }, + + markPrevious: function() { + if(this.index > 0) this.index--; + else this.index = this.entryCount-1; + this.getEntry(this.index).scrollIntoView(true); + }, + + markNext: function() { + if(this.index < this.entryCount-1) this.index++; + else this.index = 0; + this.getEntry(this.index).scrollIntoView(false); + }, + + getEntry: function(index) { + return this.update.firstChild.childNodes[index]; + }, + + getCurrentEntry: function() { + return this.getEntry(this.index); + }, + + selectEntry: function() { + this.active = false; + this.updateElement(this.getCurrentEntry()); + }, + + updateElement: function(selectedElement) { + if (this.options.updateElement) { + this.options.updateElement(selectedElement); + return; + } + var value = ''; + if (this.options.select) { + var nodes = $(selectedElement).select('.' + this.options.select) || []; + if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); + } else + value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); + + var bounds = this.getTokenBounds(); + if (bounds[0] != -1) { + var newValue = this.element.value.substr(0, bounds[0]); + var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); + if (whitespace) + newValue += whitespace[0]; + this.element.value = newValue + value + this.element.value.substr(bounds[1]); + } else { + this.element.value = value; + } + this.oldElementValue = this.element.value; + this.element.focus(); + + if (this.options.afterUpdateElement) + this.options.afterUpdateElement(this.element, selectedElement); + }, + + updateChoices: function(choices) { + if(!this.changed && this.hasFocus) { + this.update.innerHTML = choices; + Element.cleanWhitespace(this.update); + Element.cleanWhitespace(this.update.down()); + + if(this.update.firstChild && this.update.down().childNodes) { + this.entryCount = + this.update.down().childNodes.length; + for (var i = 0; i < this.entryCount; i++) { + var entry = this.getEntry(i); + entry.autocompleteIndex = i; + this.addObservers(entry); + } + } else { + this.entryCount = 0; + } + + this.stopIndicator(); + this.index = 0; + + if(this.entryCount==1 && this.options.autoSelect) { + this.selectEntry(); + this.hide(); + } else { + this.render(); + } + } + }, + + addObservers: function(element) { + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); + }, + + onObserverEvent: function() { + this.changed = false; + this.tokenBounds = null; + if(this.getToken().length>=this.options.minChars) { + this.getUpdatedChoices(); + } else { + this.active = false; + this.hide(); + } + this.oldElementValue = this.element.value; + }, + + getToken: function() { + var bounds = this.getTokenBounds(); + return this.element.value.substring(bounds[0], bounds[1]).strip(); + }, + + getTokenBounds: function() { + if (null != this.tokenBounds) return this.tokenBounds; + var value = this.element.value; + if (value.strip().empty()) return [-1, 0]; + var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); + var offset = (diff == this.oldElementValue.length ? 1 : 0); + var prevTokenPos = -1, nextTokenPos = value.length; + var tp; + for (var index = 0, l = this.options.tokens.length; index < l; ++index) { + tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); + if (tp > prevTokenPos) prevTokenPos = tp; + tp = value.indexOf(this.options.tokens[index], diff + offset); + if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; + } + return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); + } +}); + +Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { + var boundary = Math.min(newS.length, oldS.length); + for (var index = 0; index < boundary; ++index) + if (newS[index] != oldS[index]) + return index; + return boundary; +}; + +Ajax.Autocompleter = Class.create(Autocompleter.Base, { + initialize: function(element, update, url, options) { + this.baseInitialize(element, update, options); + this.options.asynchronous = true; + this.options.onComplete = this.onComplete.bind(this); + this.options.defaultParams = this.options.parameters || null; + this.url = url; + }, + + getUpdatedChoices: function() { + this.startIndicator(); + + var entry = encodeURIComponent(this.options.paramName) + '=' + + encodeURIComponent(this.getToken()); + + this.options.parameters = this.options.callback ? + this.options.callback(this.element, entry) : entry; + + if(this.options.defaultParams) + this.options.parameters += '&' + this.options.defaultParams; + + new Ajax.Request(this.url, this.options); + }, + + onComplete: function(request) { + this.updateChoices(request.responseText); + } +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +// text only at the beginning of strings in the +// autocomplete array. Defaults to true, which will +// match text at the beginning of any *word* in the +// strings in the autocomplete array. If you want to +// search anywhere in the string, additionally set +// the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +// a partial match (unlike minChars, which defines +// how many characters are required to do any match +// at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +// Defaults to true. +// +// It's possible to pass in a custom function as the 'selector' +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(Autocompleter.Base, { + initialize: function(element, update, array, options) { + this.baseInitialize(element, update, options); + this.options.array = array; + }, + + getUpdatedChoices: function() { + this.updateChoices(this.options.selector(this)); + }, + + setOptions: function(options) { + this.options = Object.extend({ + choices: 10, + partialSearch: true, + partialChars: 2, + ignoreCase: true, + fullSearch: false, + selector: function(instance) { + var ret = []; // Beginning matches + var partial = []; // Inside matches + var entry = instance.getToken(); + var count = 0; + + for (var i = 0; i < instance.options.array.length && + ret.length < instance.options.choices ; i++) { + + var elem = instance.options.array[i]; + var foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase()) : + elem.indexOf(entry); + + while (foundPos != -1) { + if (foundPos == 0 && elem.length != entry.length) { + ret.push("
  • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  • "); + break; + } else if (entry.length >= instance.options.partialChars && + instance.options.partialSearch && foundPos != -1) { + if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { + partial.push("
  • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  • "); + break; + } + } + + foundPos = instance.options.ignoreCase ? + elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : + elem.indexOf(entry, foundPos + 1); + + } + } + if (partial.length) + ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)); + return "
      " + ret.join('') + "
    "; + } + }, options || { }); + } +}); + +// AJAX in-place editor and collection editor +// Full rewrite by Christophe Porteneuve (April 2007). + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { + setTimeout(function() { + Field.activate(field); + }, 1); +}; + +Ajax.InPlaceEditor = Class.create({ + initialize: function(element, url, options) { + this.url = url; + this.element = element = $(element); + this.prepareOptions(); + this._controls = { }; + arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! + Object.extend(this.options, options || { }); + if (!this.options.formId && this.element.id) { + this.options.formId = this.element.id + '-inplaceeditor'; + if ($(this.options.formId)) + this.options.formId = ''; + } + if (this.options.externalControl) + this.options.externalControl = $(this.options.externalControl); + if (!this.options.externalControl) + this.options.externalControlOnly = false; + this._originalBackground = this.element.getStyle('background-color') || 'transparent'; + this.element.title = this.options.clickToEditText; + this._boundCancelHandler = this.handleFormCancellation.bind(this); + this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); + this._boundFailureHandler = this.handleAJAXFailure.bind(this); + this._boundSubmitHandler = this.handleFormSubmission.bind(this); + this._boundWrapperHandler = this.wrapUp.bind(this); + this.registerListeners(); + }, + checkForEscapeOrReturn: function(e) { + if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; + if (Event.KEY_ESC == e.keyCode) + this.handleFormCancellation(e); + else if (Event.KEY_RETURN == e.keyCode) + this.handleFormSubmission(e); + }, + createControl: function(mode, handler, extraClasses) { + var control = this.options[mode + 'Control']; + var text = this.options[mode + 'Text']; + if ('button' == control) { + var btn = document.createElement('input'); + btn.type = 'submit'; + btn.value = text; + btn.className = 'editor_' + mode + '_button'; + if ('cancel' == mode) + btn.onclick = this._boundCancelHandler; + this._form.appendChild(btn); + this._controls[mode] = btn; + } else if ('link' == control) { + var link = document.createElement('a'); + link.href = '#'; + link.appendChild(document.createTextNode(text)); + link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; + link.className = 'editor_' + mode + '_link'; + if (extraClasses) + link.className += ' ' + extraClasses; + this._form.appendChild(link); + this._controls[mode] = link; + } + }, + createEditField: function() { + var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); + var fld; + if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { + fld = document.createElement('input'); + fld.type = 'text'; + var size = this.options.size || this.options.cols || 0; + if (0 < size) fld.size = size; + } else { + fld = document.createElement('textarea'); + fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); + fld.cols = this.options.cols || 40; + } + fld.name = this.options.paramName; + fld.value = text; // No HTML breaks conversion anymore + fld.className = 'editor_field'; + if (this.options.submitOnBlur) + fld.onblur = this._boundSubmitHandler; + this._controls.editor = fld; + if (this.options.loadTextURL) + this.loadExternalText(); + this._form.appendChild(this._controls.editor); + }, + createForm: function() { + var ipe = this; + function addText(mode, condition) { + var text = ipe.options['text' + mode + 'Controls']; + if (!text || condition === false) return; + ipe._form.appendChild(document.createTextNode(text)); + }; + this._form = $(document.createElement('form')); + this._form.id = this.options.formId; + this._form.addClassName(this.options.formClassName); + this._form.onsubmit = this._boundSubmitHandler; + this.createEditField(); + if ('textarea' == this._controls.editor.tagName.toLowerCase()) + this._form.appendChild(document.createElement('br')); + if (this.options.onFormCustomization) + this.options.onFormCustomization(this, this._form); + addText('Before', this.options.okControl || this.options.cancelControl); + this.createControl('ok', this._boundSubmitHandler); + addText('Between', this.options.okControl && this.options.cancelControl); + this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); + addText('After', this.options.okControl || this.options.cancelControl); + }, + destroy: function() { + if (this._oldInnerHTML) + this.element.innerHTML = this._oldInnerHTML; + this.leaveEditMode(); + this.unregisterListeners(); + }, + enterEditMode: function(e) { + if (this._saving || this._editing) return; + this._editing = true; + this.triggerCallback('onEnterEditMode'); + if (this.options.externalControl) + this.options.externalControl.hide(); + this.element.hide(); + this.createForm(); + this.element.parentNode.insertBefore(this._form, this.element); + if (!this.options.loadTextURL) + this.postProcessEditField(); + if (e) Event.stop(e); + }, + enterHover: function(e) { + if (this.options.hoverClassName) + this.element.addClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onEnterHover'); + }, + getText: function() { + return this.element.innerHTML.unescapeHTML(); + }, + handleAJAXFailure: function(transport) { + this.triggerCallback('onFailure', transport); + if (this._oldInnerHTML) { + this.element.innerHTML = this._oldInnerHTML; + this._oldInnerHTML = null; + } + }, + handleFormCancellation: function(e) { + this.wrapUp(); + if (e) Event.stop(e); + }, + handleFormSubmission: function(e) { + var form = this._form; + var value = $F(this._controls.editor); + this.prepareSubmission(); + var params = this.options.callback(form, value) || ''; + if (Object.isString(params)) + params = params.toQueryParams(); + params.editorId = this.element.id; + if (this.options.htmlResponse) { + var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Updater({ success: this.element }, this.url, options); + } else { + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: params, + onComplete: this._boundWrapperHandler, + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.url, options); + } + if (e) Event.stop(e); + }, + leaveEditMode: function() { + this.element.removeClassName(this.options.savingClassName); + this.removeForm(); + this.leaveHover(); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + if (this.options.externalControl) + this.options.externalControl.show(); + this._saving = false; + this._editing = false; + this._oldInnerHTML = null; + this.triggerCallback('onLeaveEditMode'); + }, + leaveHover: function(e) { + if (this.options.hoverClassName) + this.element.removeClassName(this.options.hoverClassName); + if (this._saving) return; + this.triggerCallback('onLeaveHover'); + }, + loadExternalText: function() { + this._form.addClassName(this.options.loadingClassName); + this._controls.editor.disabled = true; + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._form.removeClassName(this.options.loadingClassName); + var text = transport.responseText; + if (this.options.stripLoadedTextTags) + text = text.stripTags(); + this._controls.editor.value = text; + this._controls.editor.disabled = false; + this.postProcessEditField(); + }.bind(this), + onFailure: this._boundFailureHandler + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + postProcessEditField: function() { + var fpc = this.options.fieldPostCreation; + if (fpc) + $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); + }, + prepareOptions: function() { + this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); + Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); + [this._extraDefaultOptions].flatten().compact().each(function(defs) { + Object.extend(this.options, defs); + }.bind(this)); + }, + prepareSubmission: function() { + this._saving = true; + this.removeForm(); + this.leaveHover(); + this.showSaving(); + }, + registerListeners: function() { + this._listeners = { }; + var listener; + $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { + listener = this[pair.value].bind(this); + this._listeners[pair.key] = listener; + if (!this.options.externalControlOnly) + this.element.observe(pair.key, listener); + if (this.options.externalControl) + this.options.externalControl.observe(pair.key, listener); + }.bind(this)); + }, + removeForm: function() { + if (!this._form) return; + this._form.remove(); + this._form = null; + this._controls = { }; + }, + showSaving: function() { + this._oldInnerHTML = this.element.innerHTML; + this.element.innerHTML = this.options.savingText; + this.element.addClassName(this.options.savingClassName); + this.element.style.backgroundColor = this._originalBackground; + this.element.show(); + }, + triggerCallback: function(cbName, arg) { + if ('function' == typeof this.options[cbName]) { + this.options[cbName](this, arg); + } + }, + unregisterListeners: function() { + $H(this._listeners).each(function(pair) { + if (!this.options.externalControlOnly) + this.element.stopObserving(pair.key, pair.value); + if (this.options.externalControl) + this.options.externalControl.stopObserving(pair.key, pair.value); + }.bind(this)); + }, + wrapUp: function(transport) { + this.leaveEditMode(); + // Can't use triggerCallback due to backward compatibility: requires + // binding + direct element + this._boundComplete(transport, this.element); + } +}); + +Object.extend(Ajax.InPlaceEditor.prototype, { + dispose: Ajax.InPlaceEditor.prototype.destroy +}); + +Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { + initialize: function($super, element, url, options) { + this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; + $super(element, url, options); + }, + + createEditField: function() { + var list = document.createElement('select'); + list.name = this.options.paramName; + list.size = 1; + this._controls.editor = list; + this._collection = this.options.collection || []; + if (this.options.loadCollectionURL) + this.loadCollection(); + else + this.checkForExternalText(); + this._form.appendChild(this._controls.editor); + }, + + loadCollection: function() { + this._form.addClassName(this.options.loadingClassName); + this.showLoadingText(this.options.loadingCollectionText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + var js = transport.responseText.strip(); + if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check + throw('Server returned an invalid collection representation.'); + this._collection = eval(js); + this.checkForExternalText(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadCollectionURL, options); + }, + + showLoadingText: function(text) { + this._controls.editor.disabled = true; + var tempOption = this._controls.editor.firstChild; + if (!tempOption) { + tempOption = document.createElement('option'); + tempOption.value = ''; + this._controls.editor.appendChild(tempOption); + tempOption.selected = true; + } + tempOption.update((text || '').stripScripts().stripTags()); + }, + + checkForExternalText: function() { + this._text = this.getText(); + if (this.options.loadTextURL) + this.loadExternalText(); + else + this.buildOptionList(); + }, + + loadExternalText: function() { + this.showLoadingText(this.options.loadingText); + var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); + Object.extend(options, { + parameters: 'editorId=' + encodeURIComponent(this.element.id), + onComplete: Prototype.emptyFunction, + onSuccess: function(transport) { + this._text = transport.responseText.strip(); + this.buildOptionList(); + }.bind(this), + onFailure: this.onFailure + }); + new Ajax.Request(this.options.loadTextURL, options); + }, + + buildOptionList: function() { + this._form.removeClassName(this.options.loadingClassName); + this._collection = this._collection.map(function(entry) { + return 2 === entry.length ? entry : [entry, entry].flatten(); + }); + var marker = ('value' in this.options) ? this.options.value : this._text; + var textFound = this._collection.any(function(entry) { + return entry[0] == marker; + }.bind(this)); + this._controls.editor.update(''); + var option; + this._collection.each(function(entry, index) { + option = document.createElement('option'); + option.value = entry[0]; + option.selected = textFound ? entry[0] == marker : 0 == index; + option.appendChild(document.createTextNode(entry[1])); + this._controls.editor.appendChild(option); + }.bind(this)); + this._controls.editor.disabled = false; + Field.scrollFreeActivate(this._controls.editor); + } +}); + +//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** +//**** This only exists for a while, in order to let **** +//**** users adapt to the new API. Read up on the new **** +//**** API and convert your code to it ASAP! **** + +Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { + if (!options) return; + function fallback(name, expr) { + if (name in options || expr === undefined) return; + options[name] = expr; + }; + fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : + options.cancelLink == options.cancelButton == false ? false : undefined))); + fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : + options.okLink == options.okButton == false ? false : undefined))); + fallback('highlightColor', options.highlightcolor); + fallback('highlightEndColor', options.highlightendcolor); +}; + +Object.extend(Ajax.InPlaceEditor, { + DefaultOptions: { + ajaxOptions: { }, + autoRows: 3, // Use when multi-line w/ rows == 1 + cancelControl: 'link', // 'link'|'button'|false + cancelText: 'cancel', + clickToEditText: 'Click to edit', + externalControl: null, // id|elt + externalControlOnly: false, + fieldPostCreation: 'activate', // 'activate'|'focus'|false + formClassName: 'inplaceeditor-form', + formId: null, // id|elt + highlightColor: '#ffff99', + highlightEndColor: '#ffffff', + hoverClassName: '', + htmlResponse: true, + loadingClassName: 'inplaceeditor-loading', + loadingText: 'Loading...', + okControl: 'button', // 'link'|'button'|false + okText: 'ok', + paramName: 'value', + rows: 1, // If 1 and multi-line, uses autoRows + savingClassName: 'inplaceeditor-saving', + savingText: 'Saving...', + size: 0, + stripLoadedTextTags: false, + submitOnBlur: false, + textAfterControls: '', + textBeforeControls: '', + textBetweenControls: '' + }, + DefaultCallbacks: { + callback: function(form) { + return Form.serialize(form); + }, + onComplete: function(transport, element) { + // For backward compatibility, this one is bound to the IPE, and passes + // the element directly. It was too often customized, so we don't break it. + new Effect.Highlight(element, { + startcolor: this.options.highlightColor, keepBackgroundImage: true }); + }, + onEnterEditMode: null, + onEnterHover: function(ipe) { + ipe.element.style.backgroundColor = ipe.options.highlightColor; + if (ipe._effect) + ipe._effect.cancel(); + }, + onFailure: function(transport, ipe) { + alert('Error communication with the server: ' + transport.responseText.stripTags()); + }, + onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. + onLeaveEditMode: null, + onLeaveHover: function(ipe) { + ipe._effect = new Effect.Highlight(ipe.element, { + startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, + restorecolor: ipe._originalBackground, keepBackgroundImage: true + }); + } + }, + Listeners: { + click: 'enterEditMode', + keydown: 'checkForEscapeOrReturn', + mouseover: 'enterHover', + mouseout: 'leaveHover' + } +}); + +Ajax.InPlaceCollectionEditor.DefaultOptions = { + loadingCollectionText: 'Loading options...' +}; + +// Delayed observer, like Form.Element.Observer, +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create({ + initialize: function(element, delay, callback) { + this.delay = delay || 0.5; + this.element = $(element); + this.callback = callback; + this.timer = null; + this.lastValue = $F(this.element); + Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); + }, + delayedListener: function(event) { + if(this.lastValue == $F(this.element)) return; + if(this.timer) clearTimeout(this.timer); + this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); + this.lastValue = $F(this.element); + }, + onTimerEvent: function() { + this.timer = null; + this.callback(this.element, $F(this.element)); + } +}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/dragdrop.js b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/dragdrop.js new file mode 100644 index 0000000000..9ebfe24d3c --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/dragdrop.js @@ -0,0 +1,974 @@ +// script.aculo.us dragdrop.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +if(Object.isUndefined(Effect)) + throw("dragdrop.js requires including script.aculo.us' effects.js library"); + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null, + tree: false + }, arguments[1] || { }); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if(Object.isArray(containment)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + if(options.accept) options.accept = [options.accept].flatten(); + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + findDeepestChild: function(drops) { + deepest = drops[0]; + + for (i = 1; i < drops.length; ++i) + if (Element.isParent(drops[i].element, deepest.element)) + deepest = drops[i]; + + return deepest; + }, + + isContained: function(element, drop) { + var containmentNode; + if(drop.tree) { + containmentNode = element.treeNode; + } else { + containmentNode = element.parentNode; + } + return drop._containers.detect(function(c) { return containmentNode == c }); + }, + + isAffected: function(point, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && + Position.within(drop.element, point[0], point[1]) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.removeClassName(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(drop.hoverclass) + Element.addClassName(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(point, element) { + if(!this.drops.length) return; + var drop, affected = []; + + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) + drop = Droppables.findDeepestChild(affected); + + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); + if (drop) { + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + if (drop != this.last_active) Droppables.activate(drop); + } + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) + if (this.last_active.onDrop) { + this.last_active.onDrop(element, this.last_active.element, event); + return true; + } + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +}; + +var Draggables = { + drags: [], + observers: [], + + register: function(draggable) { + if(this.drags.length == 0) { + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + } + this.drags.push(draggable); + }, + + unregister: function(draggable) { + this.drags = this.drags.reject(function(d) { return d==draggable }); + if(this.drags.length == 0) { + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + } + }, + + activate: function(draggable) { + if(draggable.options.delay) { + this._timeout = setTimeout(function() { + Draggables._timeout = null; + window.focus(); + Draggables.activeDraggable = draggable; + }.bind(this), draggable.options.delay); + } else { + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari + this.activeDraggable = draggable; + } + }, + + deactivate: function() { + this.activeDraggable = null; + }, + + updateDrag: function(event) { + if(!this.activeDraggable) return; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + // Mozilla-based browsers fire successive mousemove events with + // the same coordinates, prevent needless redrawing (moz bug?) + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; + this._lastPointer = pointer; + + this.activeDraggable.updateDrag(event, pointer); + }, + + endDrag: function(event) { + if(this._timeout) { + clearTimeout(this._timeout); + this._timeout = null; + } + if(!this.activeDraggable) return; + this._lastPointer = null; + this.activeDraggable.endDrag(event); + this.activeDraggable = null; + }, + + keyPress: function(event) { + if(this.activeDraggable) + this.activeDraggable.keyPress(event); + }, + + addObserver: function(observer) { + this.observers.push(observer); + this._cacheObserverCallbacks(); + }, + + removeObserver: function(element) { // element instead of observer fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + this._cacheObserverCallbacks(); + }, + + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' + if(this[eventName+'Count'] > 0) + this.observers.each( function(o) { + if(o[eventName]) o[eventName](eventName, draggable, event); + }); + if(draggable.options[eventName]) draggable.options[eventName](draggable, event); + }, + + _cacheObserverCallbacks: function() { + ['onStart','onEnd','onDrag'].each( function(eventName) { + Draggables[eventName+'Count'] = Draggables.observers.select( + function(o) { return o[eventName]; } + ).length; + }); + } +}; + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create({ + initialize: function(element) { + var defaults = { + handle: false, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, + queue: {scope:'_draggable', position:'end'} + }); + }, + endeffect: function(element) { + var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + queue: {scope:'_draggable', position:'end'}, + afterFinish: function(){ + Draggable._dragging[element] = false + } + }); + }, + zindex: 1000, + revert: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } + delay: 0 + }; + + if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) + Object.extend(defaults, { + starteffect: function(element) { + element._opacity = Element.getOpacity(element); + Draggable._dragging[element] = true; + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); + } + }); + + var options = Object.extend(defaults, arguments[1] || { }); + + this.element = $(element); + + if(options.handle && Object.isString(options.handle)) + this.handle = this.element.down('.'+options.handle, 0); + + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { + options.scroll = $(options.scroll); + this._isScrollChild = Element.childOf(this.element, options.scroll); + } + + Element.makePositioned(this.element); // fix IE + + this.options = options; + this.dragging = false; + + this.eventMouseDown = this.initDrag.bindAsEventListener(this); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + + Draggables.register(this); + }, + + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Draggables.unregister(this); + }, + + currentDelta: function() { + return([ + parseInt(Element.getStyle(this.element,'left') || '0'), + parseInt(Element.getStyle(this.element,'top') || '0')]); + }, + + initDrag: function(event) { + if(!Object.isUndefined(Draggable._dragging[this.element]) && + Draggable._dragging[this.element]) return; + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if((tag_name = src.tagName.toUpperCase()) && ( + tag_name=='INPUT' || + tag_name=='SELECT' || + tag_name=='OPTION' || + tag_name=='BUTTON' || + tag_name=='TEXTAREA')) return; + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = this.element.cumulativeOffset(); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } + }, + + startDrag: function(event) { + this.dragging = true; + if(!this.delta) + this.delta = this.currentDelta(); + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + if(this.options.scroll) { + if (this.options.scroll == window) { + var where = this._getWindowScroll(this.options.scroll); + this.originalScrollLeft = where.left; + this.originalScrollTop = where.top; + } else { + this.originalScrollLeft = this.options.scroll.scrollLeft; + this.originalScrollTop = this.options.scroll.scrollTop; + } + } + + Draggables.notify('onStart', this, event); + + if(this.options.starteffect) this.options.starteffect(this.element); + }, + + updateDrag: function(event, pointer) { + if(!this.dragging) this.startDrag(event); + + if(!this.options.quiet){ + Position.prepare(); + Droppables.show(pointer, this.element); + } + + Draggables.notify('onDrag', this, event); + + this.draw(pointer); + if(this.options.change) this.options.change(this); + + if(this.options.scroll) { + this.stopScrolling(); + + var p; + if (this.options.scroll == window) { + with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } + } else { + p = Position.page(this.options.scroll).toArray(); + p[0] += this.options.scroll.scrollLeft + Position.deltaX; + p[1] += this.options.scroll.scrollTop + Position.deltaY; + p.push(p[0]+this.options.scroll.offsetWidth); + p.push(p[1]+this.options.scroll.offsetHeight); + } + var speed = [0,0]; + if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); + if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); + if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); + if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); + this.startScrolling(speed); + } + + // fix AppleWebKit rendering + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + + Event.stop(event); + }, + + finishDrag: function(event, success) { + this.dragging = false; + + if(this.options.quiet){ + Position.prepare(); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + Droppables.show(pointer, this.element); + } + + if(this.options.ghosting) { + if (!this._originallyAbsolute) + Position.relativize(this.element); + delete this._originallyAbsolute; + Element.remove(this._clone); + this._clone = null; + } + + var dropped = false; + if(success) { + dropped = Droppables.fire(event, this.element); + if (!dropped) dropped = false; + } + if(dropped && this.options.onDropped) this.options.onDropped(this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && Object.isFunction(revert)) revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + if (dropped == 0 || revert != 'failure') + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + Draggables.deactivate(this); + Droppables.reset(); + }, + + keyPress: function(event) { + if(event.keyCode!=Event.KEY_ESC) return; + this.finishDrag(event, false); + Event.stop(event); + }, + + endDrag: function(event) { + if(!this.dragging) return; + this.stopScrolling(); + this.finishDrag(event, true); + Event.stop(event); + }, + + draw: function(point) { + var pos = this.element.cumulativeOffset(); + if(this.options.ghosting) { + var r = Position.realOffset(this.element); + pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; + } + + var d = this.currentDelta(); + pos[0] -= d[0]; pos[1] -= d[1]; + + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; + } + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) + }.bind(this)); + + if(this.options.snap) { + if(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*this.options.snap }.bind(this)); + } + }} + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + }, + + stopScrolling: function() { + if(this.scrollInterval) { + clearInterval(this.scrollInterval); + this.scrollInterval = null; + Draggables._lastScrollPointer = null; + } + }, + + startScrolling: function(speed) { + if(!(speed[0] || speed[1])) return; + this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; + this.lastScrolled = new Date(); + this.scrollInterval = setInterval(this.scroll.bind(this), 10); + }, + + scroll: function() { + var current = new Date(); + var delta = current - this.lastScrolled; + this.lastScrolled = current; + if(this.options.scroll == window) { + with (this._getWindowScroll(this.options.scroll)) { + if (this.scrollSpeed[0] || this.scrollSpeed[1]) { + var d = delta / 1000; + this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); + } + } + } else { + this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; + this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; + } + + Position.prepare(); + Droppables.show(Draggables._lastPointer, this.element); + Draggables.notify('onDrag', this); + if (this._isScrollChild) { + Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); + Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; + Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; + if (Draggables._lastScrollPointer[0] < 0) + Draggables._lastScrollPointer[0] = 0; + if (Draggables._lastScrollPointer[1] < 0) + Draggables._lastScrollPointer[1] = 0; + this.draw(Draggables._lastScrollPointer); + } + + if(this.options.change) this.options.change(this); + }, + + _getWindowScroll: function(w) { + var T, L, W, H; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight; + } + } + return { top: T, left: L, width: W, height: H }; + } +}); + +Draggable._dragging = { }; + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create({ + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +}); + +var Sortable = { + SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, + + sortables: { }, + + _findRootElement: function(element) { + while (element.tagName.toUpperCase() != "BODY") { + if(element.id && Sortable.sortables[element.id]) return element; + element = element.parentNode; + } + }, + + options: function(element) { + element = Sortable._findRootElement($(element)); + if(!element) return; + return Sortable.sortables[element.id]; + }, + + destroy: function(element){ + element = $(element); + var s = Sortable.sortables[element.id]; + + if(s) { + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + + delete Sortable.sortables[s.element.id]; + } + }, + + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, + treeTag: 'ul', + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + delay: 0, + hoverclass: null, + ghosting: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + format: this.SERIALIZE_RULE, + + // these take arrays of elements or ids and can be + // used for better initialization performance + elements: false, + handles: false, + + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction + }, arguments[1] || { }); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + quiet: options.quiet, + scroll: options.scroll, + scrollSpeed: options.scrollSpeed, + scrollSensitivity: options.scrollSensitivity, + delay: options.delay, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + tree: options.tree, + hoverclass: options.hoverclass, + onHover: Sortable.onHover + }; + + var options_for_tree = { + onHover: Sortable.onEmptyHover, + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass + }; + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // drop on empty handling + if(options.dropOnEmpty || options.tree) { + Droppables.add(element, options_for_tree); + options.droppables.push(element); + } + + (options.elements || this.findElements(element, options) || []).each( function(e,i) { + var handle = options.handles ? $(options.handles[i]) : + (options.handle ? $(e).select('.' + options.handle)[0] : e); + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + if(options.tree) e.treeNode = element; + options.droppables.push(e); + }); + + if(options.tree) { + (Sortable.findTreeElements(element, options) || []).each( function(e) { + Droppables.add(e, options_for_tree); + e.treeNode = element; + options.droppables.push(e); + }); + } + + // keep reference + this.sortables[element.identify()] = options; + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.tag); + }, + + findTreeElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.treeTag); + }, + + onHover: function(element, dropon, overlap) { + if(Element.isParent(dropon, element)) return; + + if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { + return; + } else if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon, overlap) { + var oldParentNode = element.parentNode; + var droponOptions = Sortable.options(dropon); + + if(!Element.isParent(dropon, element)) { + var index; + + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); + var child = null; + + if(children) { + var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); + + for (index = 0; index < children.length; index += 1) { + if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { + offset -= Element.offsetSize (children[index], droponOptions.overlap); + } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { + child = index + 1 < children.length ? children[index + 1] : null; + break; + } else { + child = children[index]; + break; + } + } + } + + dropon.insertBefore(element, child); + + Sortable.options(oldParentNode).onChange(element); + droponOptions.onChange(element); + } + }, + + unmark: function() { + if(Sortable._marker) Sortable._marker.hide(); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = + ($('dropmarker') || Element.extend(document.createElement('DIV'))). + hide().addClassName('dropmarker').setStyle({position:'absolute'}); + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = dropon.cumulativeOffset(); + Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); + + if(position=='after') + if(sortable.overlap == 'horizontal') + Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); + else + Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); + + Sortable._marker.show(); + }, + + _tree: function(element, options, parent) { + var children = Sortable.findElements(element, options) || []; + + for (var i = 0; i < children.length; ++i) { + var match = children[i].id.match(options.format); + + if (!match) continue; + + var child = { + id: encodeURIComponent(match ? match[1] : null), + element: element, + parent: parent, + children: [], + position: parent.children.length, + container: $(children[i]).down(options.treeTag) + }; + + /* Get the element containing the children and recurse over it */ + if (child.container) + this._tree(child.container, options, child); + + parent.children.push (child); + } + + return parent; + }, + + tree: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + treeTag: sortableOptions.treeTag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format + }, arguments[1] || { }); + + var root = { + id: null, + parent: null, + children: [], + container: element, + position: 0 + }; + + return Sortable._tree(element, options, root); + }, + + /* Construct a [i] index for a particular node */ + _constructIndex: function(node) { + var index = ''; + do { + if (node.id) index = '[' + node.position + ']' + index; + } while ((node = node.parent) != null); + return index; + }, + + sequence: function(element) { + element = $(element); + var options = Object.extend(this.options(element), arguments[1] || { }); + + return $(this.findElements(element, options) || []).map( function(item) { + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; + }); + }, + + setSequence: function(element, new_sequence) { + element = $(element); + var options = Object.extend(this.options(element), arguments[2] || { }); + + var nodeMap = { }; + this.findElements(element, options).each( function(n) { + if (n.id.match(options.format)) + nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; + n.parentNode.removeChild(n); + }); + + new_sequence.each(function(ident) { + var n = nodeMap[ident]; + if (n) { + n[1].appendChild(n[0]); + delete nodeMap[ident]; + } + }); + }, + + serialize: function(element) { + element = $(element); + var options = Object.extend(Sortable.options(element), arguments[1] || { }); + var name = encodeURIComponent( + (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); + + if (options.tree) { + return Sortable.tree(element, arguments[1]).children.map( function (item) { + return [name + Sortable._constructIndex(item) + "[id]=" + + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); + }).flatten().join('&'); + } else { + return Sortable.sequence(element, arguments[1]).map( function(item) { + return name + "[]=" + encodeURIComponent(item); + }).join('&'); + } + } +}; + +// Returns true if child is contained within element +Element.isParent = function(child, element) { + if (!child.parentNode || child == element) return false; + if (child.parentNode == element) return true; + return Element.isParent(child.parentNode, element); +}; + +Element.findChildren = function(element, only, recursive, tagName) { + if(!element.hasChildNodes()) return null; + tagName = tagName.toUpperCase(); + if(only) only = [only].flatten(); + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName.toUpperCase()==tagName && + (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) + elements.push(e); + if(recursive) { + var grandchildren = Element.findChildren(e, only, recursive, tagName); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : []); +}; + +Element.offsetSize = function (element, type) { + return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; +}; \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/effects.js b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/effects.js new file mode 100644 index 0000000000..860ddc0932 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/effects.js @@ -0,0 +1,1123 @@ +// script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + var color = '#'; + if (this.slice(0,4) == 'rgb(') { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if (this.slice(0,1) == '#') { + if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if (this.length==7) color = this.toLowerCase(); + } + } + return (color.length==7 ? color : (arguments[0] || this)); +}; + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +}; + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + Element.collectTextNodesIgnoreClass(node, className) : '')); + }).flatten().join(''); +}; + +Element.setContentZoom = function(element, percent) { + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); + if (Prototype.Browser.WebKit) window.scrollBy(0,0); + return element; +}; + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +}; + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + .5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5; + }, + pulse: function(pos, pulses) { + return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, + tagifyText: function(element) { + var tagifyStyle = 'position:relative'; + if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; + + element = $(element); + $A(element.childNodes).each( function(child) { + if (child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + new Element('span', {style: tagifyStyle}).update( + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if (((typeof element == 'object') || + Object.isFunction(element)) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || { }); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect, options) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + + return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, options || {})); + } +}; + +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(Enumerable, { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = Object.isString(effect.options.queue) ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) + this.effects.push(effect); + + if (!this.interval) + this.interval = setInterval(this.loop.bind(this), 15); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if (this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + for(var i=0, len=this.effects.length;i= this.startOn) { + if (timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if (this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / this.totalTime, + frame = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if (this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + var data = $H(); + for(property in this) + if (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#'; + } +}); + +Effect.Parallel = Class.create(Effect.Base, { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if (effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + // make this work on IE on elements without 'layout' + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || { }); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || { }); + this.start(options); + }, + setup: function() { + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if (this.options.mode == 'absolute') { + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); +}; + +Effect.Scale = Class.create(Effect.Base, { + initialize: function(element, percent) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or { } with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || { }); + this.start(options); + }, + setup: function() { + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = this.element.getStyle('position'); + + this.originalStyle = { }; + ['top','left','width','height','fontSize'].each( function(k) { + this.originalStyle[k] = this.element.style[k]; + }.bind(this)); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = this.element.getStyle('font-size') || '100%'; + ['em','px','%','pt'].each( function(fontSizeType) { + if (fontSize.indexOf(fontSizeType)>0) { + this.fontSize = parseFloat(fontSize); + this.fontSizeType = fontSizeType; + } + }.bind(this)); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if (this.options.scaleMode=='box') + this.dims = [this.element.offsetHeight, this.element.offsetWidth]; + if (/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if (!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if (this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = { }; + if (this.options.scaleX) d.width = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + 'px'; + if (this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if (this.elementPositioning == 'absolute') { + if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if (this.options.scaleY) d.top = -topd + 'px'; + if (this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if (this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = { }; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if (!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if (!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()); } + ); +}; + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if (effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + } + }, arguments[1] || { }); + return new Effect.Opacity(element,options); +}; + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + effect.element.setOpacity(effect.options.from).show(); + }}, arguments[1] || { }); + return new Effect.Opacity(element,options); +}; + +Effect.Puff = function(element) { + element = $(element); + var oldStyle = { + opacity: element.getInlineOpacity(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height + }; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) { + Position.absolutize(effect.effects[0].element); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || { }) + ); +}; + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }, arguments[1] || { }) + ); +}; + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || { })); +}; + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, Object.extend({ + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); + } + }); + } + }, arguments[1] || { })); +}; + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + effect.effects[0].element.makePositioned(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); + } + }, arguments[1] || { })); +}; + +Effect.Shake = function(element) { + element = $(element); + var options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); + }}); }}); }}); }}); }}); }}); +}; + +Effect.SlideDown = function(element) { + element = $(element).cleanWhitespace(); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || { }) + ); +}; + +Effect.SlideUp = function(element) { + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, arguments[1] || { }) + ); +}; + +// Bug in opera makes the TD containing this element expand for a instance after finish +Effect.Squish = function(element) { + return new Effect.Scale(element, window.opera ? 1 : 0, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }); +}; + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || { }); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide().makeClipping().makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), + new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.setStyle({height: '0px'}).show(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + } + }, options) + ); + } + }); +}; + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || { }); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + case 'center': + moveX = dims.width / 2; + moveY = dims.height / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), + new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } + }, options) + ); +}; + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || { }, + oldOpacity = element.getInlineOpacity(), + transition = options.transition || Effect.Transitions.linear, + reverser = function(pos){ + return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5); + }; + + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 2.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +}; + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + element.makeClipping(); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().setStyle(oldStyle); + } }); + }}, arguments[1] || { })); +}; + +Effect.Morph = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.style] = ''; + }); + }; + } + } + this.start(options); + }, + + setup: function(){ + function parseColor(color){ + if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; + color = color.parseColor(); + return $R(0,2).map(function(i){ + return parseInt( color.slice(i*2+1,i*2+3), 16 ); + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if (value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if (property == 'opacity') { + value = parseFloat(value); + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if (Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ); + }); + }, + update: function(position) { + var style = { }, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + (transform.originalValue + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: track.keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + + 'right textIndent top width wordSpacing zIndex'); + +Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; + +String.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '
    '; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); + + return styleRules; +}; + +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { + results[property] = css[property]; + return results; + }); + if (!styles.opacity) styles.opacity = element.getOpacity(); + return styles; + }; +} + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element); + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + }; + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/scriptaculous.js b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/scriptaculous.js new file mode 100644 index 0000000000..0ea5c44570 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/scriptaculous-1.9.0/scriptaculous.js @@ -0,0 +1,68 @@ +// script.aculo.us scriptaculous.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010 + +// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Scriptaculous = { + Version: '1.9.0', + require: function(libraryName) { + try{ + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write(''); + tinymce.ScriptLoader.markDone(u); + } + } + }, + + /** + * Executes a color picker on the specified element id. When the user + * then selects a color it will be set as the value of the specified element. + * + * @method pickColor + * @param {DOMEvent} e DOM event object. + * @param {string} element_id Element id to be filled with the color value from the picker. + */ + pickColor : function(e, element_id) { + this.execCommand('mceColorPicker', true, { + color : document.getElementById(element_id).value, + func : function(c) { + document.getElementById(element_id).value = c; + + try { + document.getElementById(element_id).onchange(); + } catch (ex) { + // Try fire event, ignore errors + } + } + }); + }, + + /** + * Opens a filebrowser/imagebrowser this will set the output value from + * the browser as a value on the specified element. + * + * @method openBrowser + * @param {string} element_id Id of the element to set value in. + * @param {string} type Type of browser to open image/file/flash. + * @param {string} option Option name to get the file_broswer_callback function name from. + */ + openBrowser : function(element_id, type, option) { + tinyMCEPopup.restoreSelection(); + this.editor.execCallback('file_browser_callback', element_id, document.getElementById(element_id).value, type, window); + }, + + /** + * Creates a confirm dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method confirm + * @param {String} t Title for the new confirm dialog. + * @param {function} cb Callback function to be executed after the user has selected ok or cancel. + * @param {Object} s Optional scope to execute the callback in. + */ + confirm : function(t, cb, s) { + this.editor.windowManager.confirm(t, cb, s, window); + }, + + /** + * Creates a alert dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method alert + * @param {String} t Title for the new alert dialog. + * @param {function} cb Callback function to be executed after the user has selected ok. + * @param {Object} s Optional scope to execute the callback in. + */ + alert : function(tx, cb, s) { + this.editor.windowManager.alert(tx, cb, s, window); + }, + + /** + * Closes the current window. + * + * @method close + */ + close : function() { + var t = this; + + // To avoid domain relaxing issue in Opera + function close() { + t.editor.windowManager.close(window); + tinymce = tinyMCE = t.editor = t.params = t.dom = t.dom.doc = null; // Cleanup + }; + + if (tinymce.isOpera) + t.getWin().setTimeout(close, 0); + else + close(); + }, + + // Internal functions + + _restoreSelection : function() { + var e = window.event.srcElement; + + if (e.nodeName == 'INPUT' && (e.type == 'submit' || e.type == 'button')) + tinyMCEPopup.restoreSelection(); + }, + +/* _restoreSelection : function() { + var e = window.event.srcElement; + + // If user focus a non text input or textarea + if ((e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA') || e.type != 'text') + tinyMCEPopup.restoreSelection(); + },*/ + + _onDOMLoaded : function() { + var t = tinyMCEPopup, ti = document.title, bm, h, nv; + + // Translate page + if (t.features.translate_i18n !== false) { + h = document.body.innerHTML; + + // Replace a=x with a="x" in IE + if (tinymce.isIE) + h = h.replace(/ (value|title|alt)=([^"][^\s>]+)/gi, ' $1="$2"') + + document.dir = t.editor.getParam('directionality',''); + + if ((nv = t.editor.translate(h)) && nv != h) + document.body.innerHTML = nv; + + if ((nv = t.editor.translate(ti)) && nv != ti) + document.title = ti = nv; + } + + if (!t.editor.getParam('browser_preferred_colors', false) || !t.isWindow) + t.dom.addClass(document.body, 'forceColors'); + + document.body.style.display = ''; + + // Restore selection in IE when focus is placed on a non textarea or input element of the type text + if (tinymce.isIE) { + document.attachEvent('onmouseup', tinyMCEPopup._restoreSelection); + + // Add base target element for it since it would fail with modal dialogs + t.dom.add(t.dom.select('head')[0], 'base', {target : '_self'}); + } + + t.restoreSelection(); + t.resizeToInnerSize(); + + // Set inline title + if (!t.isWindow) + t.editor.windowManager.setTitle(window, ti); + else + window.focus(); + + if (!tinymce.isIE && !t.isWindow) { + t.dom.bind(document, 'focus', function() { + t.editor.windowManager.focus(t.id); + }); + } + + // Patch for accessibility + tinymce.each(t.dom.select('select'), function(e) { + e.onkeydown = tinyMCEPopup._accessHandler; + }); + + // Call onInit + // Init must be called before focus so the selection won't get lost by the focus call + tinymce.each(t.listeners, function(o) { + o.func.call(o.scope, t.editor); + }); + + // Move focus to window + if (t.getWindowArg('mce_auto_focus', true)) { + window.focus(); + + // Focus element with mceFocus class + tinymce.each(document.forms, function(f) { + tinymce.each(f.elements, function(e) { + if (t.dom.hasClass(e, 'mceFocus') && !e.disabled) { + e.focus(); + return false; // Break loop + } + }); + }); + } + + document.onkeyup = tinyMCEPopup._closeWinKeyHandler; + }, + + _accessHandler : function(e) { + e = e || window.event; + + if (e.keyCode == 13 || e.keyCode == 32) { + var elm = e.target || e.srcElement; + + if (elm.onchange) + elm.onchange(); + + return tinymce.dom.Event.cancel(e); + } + }, + + _closeWinKeyHandler : function(e) { + e = e || window.event; + + if (e.keyCode == 27) + tinyMCEPopup.close(); + }, + + _eventProxy: function(id) { + return function(evt) { + tinyMCEPopup.dom.events.callNativeHandler(id, evt); + }; + } +}; + +tinyMCEPopup.init(); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/compat3x/validate.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/compat3x/validate.js new file mode 100755 index 0000000000..d13aaa1baa --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/compat3x/validate.js @@ -0,0 +1,252 @@ +/** + * validate.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + // String validation: + + if (!Validator.isEmail('myemail')) + alert('Invalid email.'); + + // Form validation: + + var f = document.forms['myform']; + + if (!Validator.isEmail(f.myemail)) + alert('Invalid email.'); +*/ + +var Validator = { + isEmail : function(s) { + return this.test(s, '^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+@[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$'); + }, + + isAbsUrl : function(s) { + return this.test(s, '^(news|telnet|nttp|file|http|ftp|https)://[-A-Za-z0-9\\.]+\\/?.*$'); + }, + + isSize : function(s) { + return this.test(s, '^[0-9.]+(%|in|cm|mm|em|ex|pt|pc|px)?$'); + }, + + isId : function(s) { + return this.test(s, '^[A-Za-z_]([A-Za-z0-9_])*$'); + }, + + isEmpty : function(s) { + var nl, i; + + if (s.nodeName == 'SELECT' && s.selectedIndex < 1) + return true; + + if (s.type == 'checkbox' && !s.checked) + return true; + + if (s.type == 'radio') { + for (i=0, nl = s.form.elements; i parseInt(v)) + st = this.mark(f, n); + } + } + + return st; + }, + + hasClass : function(n, c, d) { + return new RegExp('\\b' + c + (d ? '[0-9]+' : '') + '\\b', 'g').test(n.className); + }, + + getNum : function(n, c) { + c = n.className.match(new RegExp('\\b' + c + '([0-9]+)\\b', 'g'))[0]; + c = c.replace(/[^0-9]/g, ''); + + return c; + }, + + addClass : function(n, c, b) { + var o = this.removeClass(n, c); + n.className = b ? c + (o != '' ? (' ' + o) : '') : (o != '' ? (o + ' ') : '') + c; + }, + + removeClass : function(n, c) { + c = n.className.replace(new RegExp("(^|\\s+)" + c + "(\\s+|$)"), ' '); + return n.className = c != ' ' ? c : ''; + }, + + tags : function(f, s) { + return f.getElementsByTagName(s); + }, + + mark : function(f, n) { + var s = this.settings; + + this.addClass(n, s.invalid_cls); + n.setAttribute('aria-invalid', 'true'); + this.markLabels(f, n, s.invalid_cls); + + return false; + }, + + markLabels : function(f, n, ic) { + var nl, i; + + nl = this.tags(f, "label"); + for (i=0; i'}),e+=""}),e+=""}var i=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];e.addButton("emoticons",{type:"panelbutton",popoverAlign:"bc-tl",panel:{autohide:!0,html:n,onclick:function(t){var n=e.dom.getParent(t.target,"a");n&&(e.insertContent(''),this.hide())}},tooltip:"Emoticons"})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/example/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/example/plugin.min.js new file mode 100755 index 0000000000..24a64c8ea6 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/example/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("example",function(e){e.addButton("example",{text:"My button",icon:!1,onclick:function(){e.windowManager.open({title:"Example plugin",body:[{type:"textbox",name:"title",label:"Title"}],onsubmit:function(t){e.insertContent("Title: "+t.data.title)}})}}),e.addMenuItem("example",{text:"Example plugin",context:"tools",onclick:function(){e.windowManager.open({title:"TinyMCE site",url:"http://www.tinymce.com",width:800,height:600,buttons:[{text:"Close",onclick:"close"}]})}})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/example_dependency/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/example_dependency/plugin.min.js new file mode 100755 index 0000000000..e61bf473ad --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/example_dependency/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("example_dependency",function(){},["example"]); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/fullpage/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/fullpage/plugin.min.js new file mode 100755 index 0000000000..0f15de4645 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/fullpage/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("fullpage",function(e){function t(){var t=n();e.windowManager.open({title:"Document properties",data:t,defaults:{type:"textbox",size:40},body:[{name:"title",label:"Title"},{name:"keywords",label:"Keywords"},{name:"description",label:"Description"},{name:"robots",label:"Robots"},{name:"author",label:"Author"},{name:"docencoding",label:"Encoding"}],onSubmit:function(e){i(tinymce.extend(t,e.data))}})}function n(){function t(e,t){var n=e.attr(t);return n||""}var n,i,r=a(),o={};return o.fontface=e.getParam("fullpage_default_fontface",""),o.fontsize=e.getParam("fullpage_default_fontsize",""),n=r.firstChild,7==n.type&&(o.xml_pi=!0,i=/encoding="([^"]+)"/.exec(n.value),i&&(o.docencoding=i[1])),n=r.getAll("#doctype")[0],n&&(o.doctype=""),n=r.getAll("title")[0],n&&n.firstChild&&(o.title=n.firstChild.value),u(r.getAll("meta"),function(e){var t,n=e.attr("name"),i=e.attr("http-equiv");n?o[n.toLowerCase()]=e.attr("content"):"Content-Type"==i&&(t=/charset\s*=\s*(.*)\s*/gi.exec(e.attr("content")),t&&(o.docencoding=t[1]))}),n=r.getAll("html")[0],n&&(o.langcode=t(n,"lang")||t(n,"xml:lang")),n=r.getAll("link")[0],n&&"stylesheet"==n.attr("rel")&&(o.stylesheet=n.attr("href")),n=r.getAll("body")[0],n&&(o.langdir=t(n,"dir"),o.style=t(n,"style"),o.visited_color=t(n,"vlink"),o.link_color=t(n,"link"),o.active_color=t(n,"alink")),o}function i(t){function n(e,t,n){e.attr(t,n?n:void 0)}function i(e){o.firstChild?o.insert(e,o.firstChild):o.append(e)}var r,o,l,c,m,f=e.dom;r=a(),o=r.getAll("head")[0],o||(c=r.getAll("html")[0],o=new d("head",1),c.firstChild?c.insert(o,c.firstChild,!0):c.append(o)),c=r.firstChild,t.xml_pi?(m='version="1.0"',t.docencoding&&(m+=' encoding="'+t.docencoding+'"'),7!=c.type&&(c=new d("xml",7),r.insert(c,r.firstChild,!0)),c.value=m):c&&7==c.type&&c.remove(),c=r.getAll("#doctype")[0],t.doctype?(c||(c=new d("#doctype",10),t.xml_pi?r.insert(c,r.firstChild):i(c)),c.value=t.doctype.substring(9,t.doctype.length-1)):c&&c.remove(),t.docencoding&&(c=null,u(r.getAll("meta"),function(e){"Content-Type"==e.attr("http-equiv")&&(c=e)}),c||(c=new d("meta",1),c.attr("http-equiv","Content-Type"),c.shortEnded=!0,i(c)),c.attr("content","text/html; charset="+t.docencoding)),c=r.getAll("title")[0],t.title?c||(c=new d("title",1),c.append(new d("#text",3)).value=t.title,i(c)):c&&c.remove(),u("keywords,description,author,copyright,robots".split(","),function(e){var n,a,o=r.getAll("meta"),l=t[e];for(n=0;n"))}function a(){return new tinymce.html.DomParser({validate:!1,root_name:"#document"}).parse(s)}function r(t){function n(e){return e.replace(/<\/?[A-Z]+/g,function(e){return e.toLowerCase()})}var i,r,l,d,m=t.content,f="",g=e.dom;t.selection||"raw"==t.format&&s||t.source_view&&e.getParam("fullpage_hide_in_source_view")||(m=m.replace(/<(\/?)BODY/gi,"<$1body"),i=m.indexOf("",i),s=n(m.substring(0,i+1)),r=m.indexOf("\n"),l=a(),u(l.getAll("style"),function(e){e.firstChild&&(f+=e.firstChild.value)}),d=l.getAll("body")[0],d&&g.setAttribs(e.getBody(),{style:d.attr("style")||"",dir:d.attr("dir")||"",vLink:d.attr("vlink")||"",link:d.attr("link")||"",aLink:d.attr("alink")||""}),g.remove("fullpage_styles"),f&&(g.add(e.getDoc().getElementsByTagName("head")[0],"style",{id:"fullpage_styles"},f),d=g.get("fullpage_styles"),d.styleSheet&&(d.styleSheet.cssText=f)))}function o(){var t,n="",i="";return e.getParam("fullpage_default_xml_pi")&&(n+='\n'),n+=e.getParam("fullpage_default_doctype",""),n+="\n\n\n",(t=e.getParam("fullpage_default_title"))&&(n+=""+t+"\n"),(t=e.getParam("fullpage_default_encoding"))&&(n+='\n'),(t=e.getParam("fullpage_default_font_family"))&&(i+="font-family: "+t+";"),(t=e.getParam("fullpage_default_font_size"))&&(i+="font-size: "+t+";"),(t=e.getParam("fullpage_default_text_color"))&&(i+="color: "+t+";"),n+="\n\n"}function l(t){t.selection||t.source_view&&e.getParam("fullpage_hide_in_source_view")||(t.content=tinymce.trim(s)+"\n"+tinymce.trim(t.content)+"\n"+tinymce.trim(c))}var s,c,u=tinymce.each,d=tinymce.html.Node;e.addCommand("mceFullPageProperties",t),e.addButton("fullpage",{title:"Document properties",cmd:"mceFullPageProperties"}),e.addMenuItem("fullpage",{text:"Document properties",cmd:"mceFullPageProperties",context:"file"}),e.on("BeforeSetContent",r),e.on("GetContent",l)}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/fullscreen/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/fullscreen/plugin.min.js new file mode 100755 index 0000000000..92a3b7033c --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/fullscreen/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("fullscreen",function(e){function t(){var e,t,n=window,i=document,a=i.body;return a.offsetWidth&&(e=a.offsetWidth,t=a.offsetHeight),n.innerWidth&&n.innerHeight&&(e=n.innerWidth,t=n.innerHeight),{w:e,h:t}}function n(){function n(){l.setStyle(c,"height",t().h-(s.clientHeight-c.clientHeight))}var s,c,u,d=document.body,m=document.documentElement;o=!o,s=e.getContainer().firstChild,c=e.getContentAreaContainer().firstChild,u=c.style,o?(i=u.width,a=u.height,u.width=u.height="100%",l.addClass(d,"mce-fullscreen"),l.addClass(m,"mce-fullscreen"),l.addClass(s,"mce-fullscreen"),l.bind(window,"resize",n),n(),r=n):(u.width=i,u.height=a,l.removeClass(d,"mce-fullscreen"),l.removeClass(m,"mce-fullscreen"),l.removeClass(s,"mce-fullscreen"),l.unbind(window,"resize",r)),e.fire("FullscreenStateChanged",{state:o})}var i,a,r,o=!1,l=tinymce.DOM;if(!e.settings.inline)return e.on("init",function(){e.addShortcut("Ctrl+Alt+F","",n)}),e.on("remove",function(){r&&l.unbind(window,"resize",r)}),e.addCommand("mceFullScreen",n),e.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Alt+F",selectable:!0,onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})},context:"view"}),e.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Alt+F",onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})}}),{isFullscreen:function(){return o}}}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/hr/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/hr/plugin.min.js new file mode 100755 index 0000000000..ca36c92751 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/hr/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("hr",function(e){e.addCommand("InsertHorizontalRule",function(){e.execCommand("mceInsertContent",!1,"
    ")}),e.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),e.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/image/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/image/plugin.min.js new file mode 100755 index 0000000000..417dc1bd89 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/image/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("image",function(e){function t(e,t){function n(e,n){i.parentNode.removeChild(i),t({width:e,height:n})}var i=document.createElement("img");i.onload=function(){n(i.clientWidth,i.clientHeight)},i.onerror=function(){n()},i.src=e;var a=i.style;a.visibility="hidden",a.position="fixed",a.bottom=a.left=0,a.width=a.height="auto",document.body.appendChild(i)}function n(t){return function(){var n=e.settings.image_list;"string"==typeof n?tinymce.util.XHR.send({url:n,success:function(e){t(tinymce.util.JSON.parse(e))}}):t(n)}}function i(n){function i(){var e=[{text:"None",value:""}];return tinymce.each(n,function(t){e.push({text:t.text||t.title,value:t.value||t.url,menu:t.menu})}),e}function a(e){var t,n,i,a;t=c.find("#width")[0],n=c.find("#height")[0],i=t.value(),a=n.value(),c.find("#constrain")[0].checked()&&d&&m&&i&&a&&(e.control==t?(a=Math.round(i/d*a),n.value(a)):(i=Math.round(a/m*i),t.value(i))),d=i,m=a}function o(){function t(t){function i(){t.onload=t.onerror=null,e.selection.select(t),e.nodeChanged()}t.onload=function(){n.width||n.height||h.setAttribs(t,{width:t.clientWidth,height:t.clientHeight}),i()},t.onerror=i}var n=c.toJSON();""===n.width&&(n.width=null),""===n.height&&(n.height=null),""===n.style&&(n.style=null),n={src:n.src,alt:n.alt,width:n.width,height:n.height,style:n.style},e.undoManager.transact(function(){return n.src?(g?h.setAttribs(g,n):(n.id="__mcenew",e.selection.setContent(h.createHTML("img",n)),g=h.get("__mcenew"),h.setAttrib(g,"id",null)),t(g),void 0):(g&&(h.remove(g),e.nodeChanged()),void 0)})}function r(e){return e&&(e=e.replace(/px$/,"")),e}function s(){t(this.value(),function(e){e.width&&e.height&&(d=e.width,m=e.height,c.find("#width").value(d),c.find("#height").value(m))})}function l(){function e(e){return e.length>0&&/^[0-9]+$/.test(e)&&(e+="px"),e}var t=c.toJSON(),n=h.parseStyle(t.style);h.setAttrib(g,"style",""),delete n.margin,n["margin-top"]=n["margin-bottom"]=e(t.vspace),n["margin-left"]=n["margin-right"]=e(t.hspace),n["border-width"]=e(t.border),c.find("#style").value(h.serializeStyle(h.parseStyle(h.serializeStyle(n))))}var c,u,d,m,f,h=e.dom,g=e.selection.getNode();d=h.getAttrib(g,"width"),m=h.getAttrib(g,"height"),"IMG"!=g.nodeName||g.getAttribute("data-mce-object")?g=null:u={src:h.getAttrib(g,"src"),alt:h.getAttrib(g,"alt"),width:d,height:m},n&&(f={name:"target",type:"listbox",label:"Image list",values:i(),onselect:function(e){var t=c.find("#alt");(!t.value()||e.lastControl&&t.value()==e.lastControl.text())&&t.value(e.control.text()),c.find("#src").value(e.control.value())}});var p=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:s},f,{name:"alt",type:"textbox",label:"Image description"},{type:"container",label:"Dimensions",layout:"flex",direction:"row",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:3,size:3,onchange:a},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:3,size:3,onchange:a},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}];e.settings.image_advtab?(g&&(u.hspace=r(g.style.marginLeft||g.style.marginRight),u.vspace=r(g.style.marginTop||g.style.marginBottom),u.border=r(g.style.borderWidth),u.style=e.dom.serializeStyle(e.dom.parseStyle(e.dom.getAttrib(g,"style")))),c=e.windowManager.open({title:"Insert/edit image",data:u,bodyType:"tabpanel",body:[{title:"General",type:"form",items:p},{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox"},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,alignH:["left","right"],defaults:{type:"textbox",maxWidth:50,onchange:l},items:[{label:"Vertical space",name:"vspace"},{label:"Horizontal space",name:"hspace"},{label:"Border",name:"border"}]}]}],onSubmit:o})):c=e.windowManager.open({title:"Edit image",data:u,body:p,onSubmit:o})}e.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:n(i),stateSelector:"img:not([data-mce-object])"}),e.addMenuItem("image",{icon:"image",text:"Insert image",onclick:n(i),context:"insert",prependToContext:!0})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/importcss/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/importcss/plugin.min.js new file mode 100755 index 0000000000..0a2b235b80 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/importcss/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("importcss",function(e){function t(t){function n(e,t){(t||o[e.href])&&(i(e.imports,function(e){n(e,!0)}),i(e.cssRules||e.rules,function(e){e.styleSheet?n(e.styleSheet,!0):e.selectorText&&i(e.selectorText.split(","),function(e){a.push(tinymce.trim(e))})}))}var a=[],o={};i(e.contentCSS,function(e){o[e]=!0});try{i(t.styleSheets,n)}catch(r){}return a}function n(t){var n,i=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(t);if(i){var a=i[1],o=i[2].substr(1).split(".").join(" ");return i[1]?(n={title:t},e.schema.getTextBlockElements()[a]?n.block=a:e.schema.getBlockElements()[a]?n.selector=a:n.inline=a):i[2]&&(n={inline:"span",title:t.substr(1),classes:o}),e.settings.importcss_merge_classes!==!1?n.classes=o:n.attributes={"class":o},n}}var i=tinymce.each;e.settings.style_formats||e.on("renderFormatsMenu",function(a){var o=e.settings.importcss_selector_converter||n,r={};e.settings.importcss_append||a.control.items().remove(),i(t(e.getDoc()),function(t){if(-1===t.indexOf(".mce-")&&!r[t]){var n=o(t);if(n){var i=n.name||tinymce.DOM.uniqueId();e.formatter.register(i,n),a.control.append(tinymce.extend({},a.control.settings.itemDefaults,{text:n.title,format:i}))}r[t]=!0}})})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/insertdatetime/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/insertdatetime/plugin.min.js new file mode 100755 index 0000000000..08e2833796 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/insertdatetime/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("insertdatetime",function(e){function t(t,n){function i(e,t){if(e=""+e,e.length'+i+"";var r=e.dom.getParent(e.selection.getStart(),"time");if(r)return e.dom.setOuterHTML(r,i),void 0}e.insertContent(i)}var i,a="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),r="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),o="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),s="January February March April May June July August September October November December".split(" "),l=[];e.addCommand("mceInsertDate",function(){n(e.getParam("insertdatetime_dateformat",e.translate("%Y-%m-%d")))}),e.addCommand("mceInsertTime",function(){n(e.getParam("insertdatetime_timeformat",e.translate("%H:%M:%S")))}),e.addButton("inserttime",{type:"splitbutton",title:"Insert time",onclick:function(){n(i||"%H:%M:%S")},menu:l}),tinymce.each(e.settings.insertdatetime_formats||["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"],function(e){l.push({text:t(e),onclick:function(){i=e,n(e)}})}),e.addMenuItem("insertdatetime",{icon:"date",text:"Insert date/time",menu:l,context:"insert"})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/layer/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/layer/plugin.min.js new file mode 100755 index 0000000000..eb1ad4b68d --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/layer/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("layer",function(e){function t(e){do if(e.className&&-1!=e.className.indexOf("mceItemLayer"))return e;while(e=e.parentNode)}function n(t){var n=e.dom;tinymce.each(n.select("div,p",t),function(e){/^(absolute|relative|fixed)$/i.test(e.style.position)&&(e.hasVisual?n.addClass(e,"mceItemVisualAid"):n.removeClass(e,"mceItemVisualAid"),n.addClass(e,"mceItemLayer"))})}function i(n){var i,o,a=[],r=t(e.selection.getNode()),l=-1,s=-1;for(o=[],tinymce.walk(e.getBody(),function(e){1==e.nodeType&&/^(absolute|relative|static)$/i.test(e.style.position)&&o.push(e)},"childNodes"),i=0;il&&o[i]==r&&(l=i);if(0>n){for(i=0;i-1?(o[l].style.zIndex=a[s],o[s].style.zIndex=a[l]):a[l]>0&&(o[l].style.zIndex=a[l]-1)}else{for(i=0;ia[l]){s=i;break}s>-1?(o[l].style.zIndex=a[s],o[s].style.zIndex=a[l]):o[l].style.zIndex=a[l]+1}e.execCommand("mceRepaint")}function o(){var t=e.dom,n=t.getPos(t.getParent(e.selection.getNode(),"*")),i=e.getBody();e.dom.add(i,"div",{style:{position:"absolute",left:n.x,top:n.y>20?n.y:20,width:100,height:100},"class":"mceItemVisualAid mceItemLayer"},e.selection.getContent()||e.getLang("layer.content")),tinymce.Env.ie&&t.setHTML(i,i.innerHTML)}function a(){var n=t(e.selection.getNode());n||(n=e.dom.getParent(e.selection.getNode(),"DIV,P,IMG")),n&&("absolute"==n.style.position.toLowerCase()?(e.dom.setStyles(n,{position:"",left:"",top:"",width:"",height:""}),e.dom.removeClass(n,"mceItemVisualAid"),e.dom.removeClass(n,"mceItemLayer")):(n.style.left||(n.style.left="20px"),n.style.top||(n.style.top="20px"),n.style.width||(n.style.width=n.width?n.width+"px":"100px"),n.style.height||(n.style.height=n.height?n.height+"px":"100px"),n.style.position="absolute",e.dom.setAttrib(n,"data-mce-style",""),e.addVisual(e.getBody())),e.execCommand("mceRepaint"),e.nodeChanged())}e.addCommand("mceInsertLayer",o),e.addCommand("mceMoveForward",function(){i(1)}),e.addCommand("mceMoveBackward",function(){i(-1)}),e.addCommand("mceMakeAbsolute",function(){a()}),e.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"}),e.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"}),e.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"}),e.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"}),e.on("init",function(){tinymce.Env.ie&&e.getDoc().execCommand("2D-Position",!1,!0)}),e.on("mouseup",function(n){var i=t(n.target);i&&e.dom.setAttrib(i,"data-mce-style","")}),e.on("mousedown",function(n){var i,o=n.target,a=e.getDoc();tinymce.Env.gecko&&(t(o)?"on"!==a.designMode&&(a.designMode="on",o=a.body,i=o.parentNode,i.removeChild(o),i.appendChild(o)):"on"==a.designMode&&(a.designMode="off"))}),e.on("NodeChange",n)}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/legacyoutput/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/legacyoutput/plugin.min.js new file mode 100755 index 0000000000..4f6f7c1aa3 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/legacyoutput/plugin.min.js @@ -0,0 +1 @@ +!function(e){e.on("AddEditor",function(e){e.editor.settings.inline_styles=!1}),e.PluginManager.add("legacyoutput",function(t){t.on("init",function(){var n="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",i=e.explode(t.settings.font_size_style_values),o=t.schema;t.formatter.register({alignleft:{selector:n,attributes:{align:"left"}},aligncenter:{selector:n,attributes:{align:"center"}},alignright:{selector:n,attributes:{align:"right"}},alignjustify:{selector:n,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:!0}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:!0}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(t){return e.inArray(i,t.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}}),e.each("b,i,u,strike".split(","),function(e){o.addValidElements(e+"[*]")}),o.getElementRule("font")||o.addValidElements("font[face|size|color|style]"),e.each(n.split(","),function(e){var t=o.getElementRule(e);t&&(t.attributes.align||(t.attributes.align={},t.attributesOrder.push("align")))})})})}(tinymce); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/link/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/link/plugin.min.js new file mode 100755 index 0000000000..9cd7616de6 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/link/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("link",function(e){function t(t){return function(){var n=e.settings.link_list;"string"==typeof n?tinymce.util.XHR.send({url:n,success:function(e){t(tinymce.util.JSON.parse(e))}}):t(n)}}function n(t){function n(e){var t=d.find("#text");(!t.value()||e.lastControl&&t.value()==e.lastControl.text())&&t.value(e.control.text()),d.find("#href").value(e.control.value())}function i(){var e=[{text:"None",value:""}];return tinymce.each(t,function(t){e.push({text:t.text||t.title,value:t.value||t.url,menu:t.menu})}),e}function a(t){var n=[{text:"None",value:""}];return tinymce.each(e.settings.rel_list,function(e){n.push({text:e.text||e.title,value:e.value,selected:t===e.value})}),n}function o(t){var n=[{text:"None",value:""}];return e.settings.target_list||n.push({text:"New window",value:"_blank"}),tinymce.each(e.settings.target_list,function(e){n.push({text:e.text||e.title,value:e.value,selected:t===e.value})}),n}function r(t){var i=[];return tinymce.each(e.dom.select("a:not([href])"),function(e){var n=e.name||e.id;n&&i.push({text:n,value:"#"+n,selected:-1!=t.indexOf("#"+n)})}),i.length?(i.unshift({text:"None",value:""}),{name:"anchor",type:"listbox",label:"Anchors",values:i,onselect:n}):void 0}function s(){c||0!==g.text.length||this.parent().parent().find("#text")[0].value(this.value())}var l,u,c,d,m,f,h,g={},p=e.selection,v=e.dom;l=p.getNode(),u=v.getParent(l,"a[href]"),g.text=c=u?u.innerText||u.textContent:p.getContent({format:"text"}),g.href=u?v.getAttrib(u,"href"):"",g.target=u?v.getAttrib(u,"target"):"",g.rel=u?v.getAttrib(u,"rel"):"","IMG"==l.nodeName&&(g.text=c=" "),t&&(m={type:"listbox",label:"Link list",values:i(),onselect:n}),e.settings.target_list!==!1&&(h={name:"target",type:"listbox",label:"Target",values:o(g.target)}),e.settings.rel_list&&(f={name:"rel",type:"listbox",label:"Rel",values:a(g.rel)}),d=e.windowManager.open({title:"Insert link",data:g,body:[{name:"href",type:"filepicker",filetype:"file",size:40,autofocus:!0,label:"Url",onchange:s,onkeyup:s},{name:"text",type:"textbox",size:40,label:"Text to display",onchange:function(){g.text=this.value()}},r(g.href),m,f,h],onSubmit:function(t){function n(t,n){window.setTimeout(function(){e.windowManager.confirm(t,n)},0)}function i(){a.text!=c?u?(e.focus(),u.innerHTML=a.text,v.setAttribs(u,{href:o,target:a.target?a.target:null,rel:a.rel?a.rel:null}),p.select(u)):e.insertContent(v.createHTML("a",{href:o,target:a.target?a.target:null,rel:a.rel?a.rel:null},a.text)):e.execCommand("mceInsertLink",!1,{href:o,target:a.target,rel:a.rel?a.rel:null})}var a=t.data,o=a.href;return o?o.indexOf("@")>0&&-1==o.indexOf("//")&&-1==o.indexOf("mailto:")?(n("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",function(e){e&&(o="mailto:"+o),i()}),void 0):/^\s*www\./i.test(o)?(n("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(e){e&&(o="http://"+o),i()}),void 0):(i(),void 0):(e.execCommand("unlink"),void 0)}})}e.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Ctrl+K",onclick:t(n),stateSelector:"a[href]"}),e.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",stateSelector:"a[href]"}),e.addShortcut("Ctrl+K","",t(n)),this.showDialog=n,e.addMenuItem("link",{icon:"link",text:"Insert link",shortcut:"Ctrl+K",onclick:t(n),stateSelector:"a[href]",context:"insert",prependToContext:!0})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/lists/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/lists/plugin.min.js new file mode 100755 index 0000000000..811c46dd1e --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/lists/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("lists",function(e){var t=this;e.on("init",function(){function n(e){function t(t){var i,r,a;r=e[t?"startContainer":"endContainer"],a=e[t?"startOffset":"endOffset"],1==r.nodeType&&(i=b.create("span",{"data-mce-type":"bookmark"}),r.hasChildNodes()?(a=Math.min(a,r.childNodes.length-1),r.insertBefore(i,r.childNodes[a])):r.appendChild(i),r=i,a=0),n[t?"startContainer":"endContainer"]=r,n[t?"startOffset":"endOffset"]=a}var n={};return t(!0),e.collapsed||t(),n}function i(t){function n(n){function i(e){for(var t=e.parentNode.firstChild,n=0;t;){if(t==e)return n;(1!=t.nodeType||"bookmark"!=t.getAttribute("data-mce-type"))&&n++,t=t.nextSibling}return-1}var r,a,o;if(r=o=t[n?"startContainer":"endContainer"],a=t[n?"startOffset":"endOffset"],r){if(1==r.nodeType){if(r.parentNode==e.getBody()){var s,l=e.settings.forced_root_block;l?(s=b.create(l),(!tinymce.Env.ie||tinymce.Env.ie>10)&&s.appendChild(b.create("br",{"data-mce-bogus":"true"})),r.parentNode.insertBefore(s,r),r=s,a=0):(s=b.create("br"),r.parentNode.insertBefore(s,r),r=r.parentNode,a=i(s))}else n?(a=i(r),r=r.parentNode):(a=i(r),r=r.parentNode);b.remove(o)}t[n?"startContainer":"endContainer"]=r,t[n?"startOffset":"endOffset"]=a}}n(!0),n();var i=b.createRng();i.setStart(t.startContainer,t.startOffset),t.endContainer&&i.setEnd(t.endContainer,t.endOffset),C.setRng(i)}function r(e){return e&&/^(OL|UL)$/.test(e.nodeName)}function a(e){return e.parentNode.firstChild==e}function o(e){return e.parentNode.lastChild==e}function s(t){return t&&!!e.schema.getTextBlockElements()[t.nodeName]}function l(t,n){var i,r;if(e.settings.forced_root_block&&(n=n||e.settings.forced_root_block),r=n?b.create(n):b.createFragment(),t)for(;i=t.firstChild;)r.appendChild(i);return e.settings.forced_root_block||r.appendChild(b.create("br")),r.hasChildNodes()||tinymce.Env.ie&&!(tinymce.Env.ie>10)||(r.innerHTML='
    '),r}function c(){return tinymce.grep(C.getSelectedBlocks(),function(e){return"LI"==e.nodeName})}function d(){return tinymce.grep(C.getSelectedBlocks(),s)}function u(e,t,n){var i,r,a=b.select('span[data-mce-type="bookmark"]',e);n=n||l(t),i=b.createRng(),i.setStartAfter(t),i.setEndAfter(e),r=i.extractContents(),b.isEmpty(r)||b.insertAfter(r,e),b.isEmpty(n)||b.insertAfter(n,e),b.isEmpty(t.parentNode)&&(tinymce.each(a,function(e){t.parentNode.parentNode.insertBefore(e,t.parentNode)}),b.remove(t.parentNode)),b.remove(t)}function m(e){var t,n;if(t=e.nextSibling,t&&r(t)&&t.nodeName==e.nodeName){for(;n=t.firstChild;)e.appendChild(n);b.remove(t)}if(t=e.previousSibling,t&&r(t)&&t.nodeName==e.nodeName){for(;n=t.firstChild;)e.insertBefore(n,e.firstChild);b.remove(t)}}function f(e){tinymce.each(tinymce.grep(b.select("ol,ul",e)),function(e){var t,n=e.parentNode;"LI"==n.nodeName&&n.firstChild==e&&(t=n.previousSibling,t&&"LI"==t.nodeName&&(t.appendChild(e),b.isEmpty(n)&&b.remove(n))),r(n)&&(t=n.previousSibling,t&&"LI"==t.nodeName&&t.appendChild(e))})}function p(){var e,t=n(C.getRng(!0));return tinymce.each(c(),function(t){var n,i;return n=t.previousSibling,n&&"UL"==n.nodeName?(n.appendChild(t),void 0):n&&"LI"==n.nodeName&&r(n.lastChild)?(n.lastChild.appendChild(t),void 0):(n=t.nextSibling,n&&"UL"==n.nodeName?(n.insertBefore(t,n.firstChild),void 0):(n&&"LI"==n.nodeName&&r(t.lastChild)||(n=t.previousSibling,n&&"LI"==n.nodeName&&(i=b.create(t.parentNode.nodeName),n.appendChild(i),i.appendChild(t)),e=!0),void 0))}),i(t),e}function h(){function e(e){b.isEmpty(e)&&b.remove(e)}var t,s=n(C.getRng(!0));return tinymce.each(c(),function(n){var i,s=n.parentNode,c=s.parentNode;if(a(n)&&o(n))if("LI"==c.nodeName)b.insertAfter(n,c),e(c);else{if(!r(c))return;b.remove(s,!0)}else if(a(n))if("LI"==c.nodeName)b.insertAfter(n,c),i=b.create("LI"),i.appendChild(s),b.insertAfter(i,n),e(c);else{if(!r(c))return;c.insertBefore(n,s)}else if(o(n))if("LI"==c.nodeName)b.insertAfter(n,c);else{if(!r(c))return;b.insertAfter(n,s)}else{if("LI"==c.nodeName)s=c,i=l(n,"LI");else{if(!r(c))return;i=l(n,"LI")}u(s,n,i),f(s.parentNode)}t=!0}),i(s),t}function g(t){function a(){function t(t){var n,i,r=e.getBody();for(n=o[t?"startContainer":"endContainer"],i=o[t?"startOffset":"endOffset"],1==n.nodeType&&(n=n.childNodes[Math.min(i,n.childNodes.length-1)]||n);n.parentNode!=r;){if(s(n))return n;if(/^(TD|TH)$/.test(n.parentNode.nodeName))return n;n=n.parentNode}return n}function n(e,t){var n,i=[];if(!s(e)){for(;e&&(n=e[t?"previousSibling":"nextSibling"],!b.isBlock(n)&&n);)e=n;for(;e;)i.push(e),e=e[t?"nextSibling":"previousSibling"]}return i}var i,r,a=t(!0),l=t();r=n(a,!0),a!=l&&(r=r.concat(n(l).reverse())),tinymce.each(r,function(e){if(!b.isBlock(e)||"BR"==e.nodeName){if(!i||"BR"==e.nodeName){if("BR"==e.nodeName&&(!e.nextSibling||b.isBlock(e.nextSibling)&&"BR"!=e.nextSibling.nodeName))return b.remove(e),!1;i=b.create("p"),c.push(i),e.parentNode.insertBefore(i,e)}return"BR"!=e.nodeName?i.appendChild(e):b.remove(e),e==l?!1:void 0}})}var o=C.getRng(!0),l=n(o),c=d();a(),tinymce.each(c,function(e){var n,i;i=e.previousSibling,i&&r(i)&&i.nodeName==t?(n=i,e=b.rename(e,"LI"),i.appendChild(e)):(n=b.create(t),e.parentNode.insertBefore(n,e),n.appendChild(e),e=b.rename(e,"LI")),m(n)}),i(l)}function v(){var e=n(C.getRng(!0));tinymce.each(c(),function(e){var t,n;for(t=e;t;t=t.parentNode)r(t)&&(n=t);u(n,e)}),i(e)}function y(e){var t=b.getParent(C.getStart(),"OL,UL");if(t)if(t.nodeName==e)v(e);else{var r=n(C.getRng(!0));m(b.rename(t,e)),i(r)}else g(e)}var b=e.dom,C=e.selection;t.backspaceDelete=function(e){function t(e,t){var n=e.startContainer,i=e.startOffset;if(3==n.nodeType&&(t?i0))return n;for(var r=new tinymce.dom.TreeWalker(e.startContainer);n=r[t?"next":"prev"]();)if(3==n.nodeType&&n.data.length>0)return n}function a(e,t){var n,i,a=e.parentNode;for(r(t.lastChild)&&(i=t.lastChild),n=t.lastChild,n&&"BR"==n.nodeName&&e.hasChildNodes()&&b.remove(n);n=e.firstChild;)t.appendChild(n);i&&t.appendChild(i),b.remove(e),b.isEmpty(a)&&b.remove(a)}if(C.isCollapsed()){var o=b.getParent(C.getStart(),"LI");if(o){var s=C.getRng(!0),l=b.getParent(t(s,e),"LI");if(l&&l!=o){var c=n(s);return e?a(l,o):a(o,l),i(c),!0}if(!l&&!e&&v(o.parentNode.nodeName))return!0}}},e.addCommand("Indent",function(){return p()?void 0:!0}),e.addCommand("Outdent",function(){return h()?void 0:!0}),e.addCommand("InsertUnorderedList",function(){y("UL")}),e.addCommand("InsertOrderedList",function(){y("OL")})}),e.on("keydown",function(e){e.keyCode==tinymce.util.VK.BACKSPACE?t.backspaceDelete()&&e.preventDefault():e.keyCode==tinymce.util.VK.DELETE&&t.backspaceDelete(!0)&&e.preventDefault()})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/media/moxieplayer.swf b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/media/moxieplayer.swf new file mode 100755 index 0000000000..19c771bea5 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/media/moxieplayer.swf differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/media/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/media/plugin.min.js new file mode 100755 index 0000000000..ce8cd0ee5f --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/media/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("media",function(e,t){function n(e){return-1!=e.indexOf(".mp3")?"audio/mpeg":-1!=e.indexOf(".wav")?"audio/wav":-1!=e.indexOf(".mp4")?"video/mp4":-1!=e.indexOf(".webm")?"video/webm":-1!=e.indexOf(".ogg")?"video/ogg":-1!=e.indexOf(".swf")?"application/x-shockwave-flash":""}function i(){function t(e){var t,r,a,o;t=n.find("#width")[0],r=n.find("#height")[0],a=t.value(),o=r.value(),n.find("#constrain")[0].checked()&&i&&c&&a&&o&&(e.control==t?(o=Math.round(a/i*o),r.value(o)):(a=Math.round(o/c*a),t.value(a))),i=a,c=o}var n,i,c,l;l=s(e.selection.getNode()),i=l.width,c=l.height,n=e.windowManager.open({title:"Insert/edit video",data:l,bodyType:"tabpanel",body:[{title:"General",type:"form",onShowTab:function(){this.fromJSON(o(this.next().find("#embed").value()))},items:[{name:"source1",type:"filepicker",filetype:"media",size:40,autofocus:!0,label:"Source"},{name:"source2",type:"filepicker",filetype:"media",size:40,label:"Alternative source"},{name:"poster",type:"filepicker",filetype:"image",size:40,label:"Poster"},{type:"container",label:"Dimensions",layout:"flex",direction:"row",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:3,size:3,onchange:t},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:3,size:3,onchange:t},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}]},{title:"Embed",type:"panel",layout:"flex",direction:"column",align:"stretch",padding:10,spacing:10,onShowTab:function(){this.find("#embed").value(a(this.parent().toJSON()))},items:[{type:"label",text:"Paste your embed code below:"},{type:"textbox",flex:1,name:"embed",value:r(),multiline:!0,label:"Source"}]}],onSubmit:function(){e.insertContent(a(this.toJSON()))}})}function r(){var t=e.selection.getNode();return t.getAttribute("data-mce-object")?e.selection.getContent():void 0}function a(i){var r="";return i.source1||(tinymce.extend(i,o(i.embed)),i.source1)?(i.source1=e.convertURL(i.source1,"source"),i.source2=e.convertURL(i.source2,"source"),i.source1mime=n(i.source1),i.source2mime=n(i.source2),i.poster=e.convertURL(i.poster,"poster"),i.flashPlayerUrl=e.convertURL(t+"/moxieplayer.swf","movie"),i.embed?r=c(i.embed,i,!0):(tinymce.each(l,function(e){var t,n,r;if(t=e.regex.exec(i.source1)){for(r=e.url,n=0;t[n];n++)r=r.replace("$"+n,function(){return t[n]});i.source1=r,i.type=e.type,i.width=e.w,i.height=e.h}}),i.width=i.width||300,i.height=i.height||150,tinymce.each(i,function(t,n){i[n]=e.dom.encode(t)}),"iframe"==i.type?r+='':"application/x-shockwave-flash"==i.source1mime?(r+='',i.poster&&(r+=''),r+=""):-1!=i.source1mime.indexOf("audio")?e.settings.audio_template_callback?r=e.settings.audio_template_callback(i):r+='":r=e.settings.video_template_callback?e.settings.video_template_callback(i):'"),r):""}function o(e){var t={};return new tinymce.html.SaxParser({validate:!1,special:"script,noscript",start:function(e,n){t.source1||"param"!=e||(t.source1=n.map.movie),("iframe"==e||"object"==e||"embed"==e||"video"==e||"audio"==e)&&(t=tinymce.extend(n.map,t)),"source"==e&&(t.source1?t.source2||(t.source2=n.map.src):t.source1=n.map.src),"img"!=e||t.poster||(t.poster=n.map.src)}}).parse(e),t.source1=t.source1||t.src||t.data,t.source2=t.source2||"",t.poster=t.poster||"",t}function s(t){return t.getAttribute("data-mce-object")?o(e.serializer.serialize(t,{selection:!0})):{}}function c(e,t,n){function i(e,t){var n,i,r,a;for(n in t)if(r=""+t[n],e.map[n])for(i=e.length;i--;)a=e[i],a.name==n&&(r?(e.map[n]=r,a.value=r):(delete e.map[n],e.splice(i,1)));else r&&(e.push({name:n,value:r}),e.map[n]=r)}var r,a=new tinymce.html.Writer,o=0;return new tinymce.html.SaxParser({validate:!1,special:"script,noscript",comment:function(e){a.comment(e)},cdata:function(e){a.cdata(e)},text:function(e,t){a.text(e,t)},start:function(e,s,c){switch(e){case"video":case"object":case"img":case"iframe":i(s,{width:t.width,height:t.height})}if(n)switch(e){case"video":i(s,{poster:t.poster,src:""}),t.source2&&i(s,{src:""});break;case"iframe":i(s,{src:t.source1});break;case"source":if(o++,2>=o&&(i(s,{src:t["source"+o],type:t["source"+o+"mime"]}),!t["source"+o]))return;break;case"img":if(!t.poster)return;r=!0}a.start(e,s,c)},end:function(e){if("video"==e&&n)for(var s=1;2>=s;s++)if(t["source"+s]){var c=[];c.map={},s>o&&(i(c,{src:t["source"+s],type:t["source"+s+"mime"]}),a.start("source",c,!0))}if(t.poster&&"object"==e&&n&&!r){var l=[];l.map={},i(l,{src:t.poster,width:t.width,height:t.height}),a.start("img",l,!0)}a.end(e)}},new tinymce.html.Schema({})).parse(e),a.getContent()}var l=[{regex:/youtu\.be\/([a-z1-9.-_]+)/,type:"iframe",w:425,h:350,url:"http://www.youtube.com/embed/$1"},{regex:/youtube\.com(.+)v=([^&]+)/,type:"iframe",w:425,h:350,url:"http://www.youtube.com/embed/$2"},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"http://player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc"},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'http://maps.google.com/maps/ms?msid=$2&output=embed"'}];e.on("ResolveName",function(e){var t;1==e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}),e.on("preInit",function(){var t=e.schema.getSpecialElements();tinymce.each("video audio iframe object".split(" "),function(e){t[e]=new RegExp("]*>","gi")}),e.schema.addValidElements("object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]");var n=e.schema.getBoolAttrs();tinymce.each("webkitallowfullscreen mozallowfullscreen allowfullscreen".split(" "),function(e){n[e]={}}),e.parser.addNodeFilter("iframe,video,audio,object,embed",function(t,n){for(var i,r,a,o,s,c,l,d=t.length;d--;){for(r=t[d],a=new tinymce.html.Node("img",1),a.shortEnded=!0,c=r.attributes,i=c.length;i--;)o=c[i].name,s=c[i].value,"width"!==o&&"height"!==o&&"style"!==o&&(("data"==o||"src"==o)&&(s=e.convertURL(s,o)),a.attr("data-mce-p-"+o,s));l=r.firstChild&&r.firstChild.value,l&&(a.attr("data-mce-html",escape(l)),a.firstChild=null),a.attr({width:r.attr("width")||"300",height:r.attr("height")||("audio"==n?"30":"150"),style:r.attr("style"),src:tinymce.Env.transparentSrc,"data-mce-object":n,"class":"mce-object mce-object-"+n}),r.replace(a)}}),e.serializer.addAttributeFilter("data-mce-object",function(e,t){for(var n,i,r,a,o,s,c=e.length;c--;){for(n=e[c],i=new tinymce.html.Node(n.attr(t),1),"audio"!=n.attr(t)&&i.attr({width:n.attr("width"),height:n.attr("height")}),i.attr({style:n.attr("style")}),a=n.attributes,r=a.length;r--;){var l=a[r].name;0===l.indexOf("data-mce-p-")&&i.attr(l.substr(11),a[r].value)}o=n.attr("data-mce-html"),o&&(s=new tinymce.html.Node("#text",3),s.raw=!0,s.value=unescape(o),i.append(s)),n.replace(i)}})}),e.on("ObjectSelected",function(e){"audio"==e.target.getAttribute("data-mce-object")&&e.preventDefault()}),e.on("objectResized",function(e){var t,n=e.target;n.getAttribute("data-mce-object")&&(t=n.getAttribute("data-mce-html"),t&&(t=unescape(t),n.setAttribute("data-mce-html",escape(c(t,{width:e.width,height:e.height})))))}),e.addButton("media",{tooltip:"Insert/edit video",onclick:i,stateSelector:"img[data-mce-object=video]"}),e.addMenuItem("media",{icon:"media",text:"Insert video",onclick:i,context:"insert",prependToContext:!0})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/nonbreaking/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/nonbreaking/plugin.min.js new file mode 100755 index 0000000000..866339c7dc --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/nonbreaking/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("nonbreaking",function(e){var t=e.getParam("nonbreaking_force_tab");if(e.addCommand("mceNonBreaking",function(){e.insertContent(e.plugins.visualchars&&e.plugins.visualchars.state?' ':" ")}),e.addButton("nonbreaking",{title:"Insert nonbreaking space",cmd:"mceNonBreaking"}),e.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),t){var n=+t>1?+t:3;e.on("keydown",function(t){if(9==t.keyCode){if(t.shiftKey)return;t.preventDefault();for(var i=0;n>i;i++)e.execCommand("mceNonBreaking")}})}}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/noneditable/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/noneditable/plugin.min.js new file mode 100755 index 0000000000..dd15d59ee0 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/noneditable/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("noneditable",function(e){function t(){function t(e){var t;if(1===e.nodeType){if(t=e.getAttribute(s),t&&"inherit"!==t)return t;if(t=e.contentEditable,"inherit"!==t)return t}return null}function n(e){for(var n;e;){if(n=t(e))return"false"===n?e:null;e=e.parentNode}}function i(e){for(;e;){if(e.id===g)return e;e=e.parentNode}}function o(e){var t;if(e)for(t=new r(e,e),e=t.current();e;e=t.next())if(3===e.nodeType)return e}function a(n,i){var o,a;return"false"===t(n)&&m.isBlock(n)?(f.select(n),void 0):(a=m.createRng(),"true"===t(n)&&(n.firstChild||n.appendChild(e.getDoc().createTextNode(" ")),n=n.firstChild,i=!0),o=m.create("span",{id:g,"data-mce-bogus":!0},p),i?n.parentNode.insertBefore(o,n):m.insertAfter(o,n),a.setStart(o.firstChild,1),a.collapse(!0),f.setRng(a),o)}function l(e){var t,n,a,r;if(e)t=f.getRng(!0),t.setStartBefore(e),t.setEndBefore(e),n=o(e),n&&n.nodeValue.charAt(0)==p&&(n=n.deleteData(0,1)),m.remove(e,!0),f.setRng(t);else for(a=i(f.getStart());(e=m.get(g))&&e!==r;)a!==e&&(n=o(e),n&&n.nodeValue.charAt(0)==p&&(n=n.deleteData(0,1)),m.remove(e,!0)),r=e}function d(){function e(e,n){var i,o,a,l,s;if(i=c.startContainer,o=c.startOffset,3==i.nodeType){if(s=i.nodeValue.length,o>0&&s>o||(n?o==s:0===o))return}else{if(!(o0?o-1:o;i=i.childNodes[d],i.hasChildNodes()&&(i=i.firstChild)}for(a=new r(i,e);l=a[n?"prev":"next"]();){if(3===l.nodeType&&l.nodeValue.length>0)return;if("true"===t(l))return l}return e}var i,o,s,c,d;l(),s=f.isCollapsed(),i=n(f.getStart()),o=n(f.getEnd()),(i||o)&&(c=f.getRng(!0),s?(i=i||o,(d=e(i,!0))?a(d,!0):(d=e(i,!1))?a(d,!1):f.select(i)):(c=f.getRng(!0),i&&c.setStartBefore(i),o&&c.setEndAfter(o),f.setRng(c)))}function u(o){function a(e,t){for(;e=e[t?"previousSibling":"nextSibling"];)if(3!==e.nodeType||e.nodeValue.length>0)return e}function s(e,t){f.select(e),f.collapse(t)}function u(o){function a(e){for(var t=s;t;){if(t===e)return;t=t.parentNode}m.remove(e),d()}function r(){var i,r,l=e.schema.getNonEmptyElements();for(r=new tinymce.dom.TreeWalker(s,e.getBody());(i=o?r.prev():r.next())&&!l[i.nodeName.toLowerCase()]&&!(3===i.nodeType&&tinymce.trim(i.nodeValue).length>0);)if("false"===t(i))return a(i),!0;return n(i)?!0:!1}var l,s,c,u;if(f.isCollapsed()){if(l=f.getRng(!0),s=l.startContainer,c=l.startOffset,s=i(s)||s,u=n(s))return a(u),!1;if(3==s.nodeType&&(o?c>0:cv||v>124)&&v!=c.DELETE&&v!=c.BACKSPACE){if((tinymce.isMac?o.metaKey:o.ctrlKey)&&(67==v||88==v||86==v))return;if(o.preventDefault(),v==c.LEFT||v==c.RIGHT){var b=v==c.LEFT;if(e.dom.isBlock(g)){var x=b?g.previousSibling:g.nextSibling,w=new r(x,x),C=b?w.prev():w.next();s(C,!b)}else s(g,b)}}else if(v==c.LEFT||v==c.RIGHT||v==c.BACKSPACE||v==c.DELETE){if(p=i(h)){if(v==c.LEFT||v==c.BACKSPACE)if(g=a(p,!0),g&&"false"===t(g)){if(o.preventDefault(),v!=c.LEFT)return m.remove(g),void 0;s(g,!0)}else l(p);if(v==c.RIGHT||v==c.DELETE)if(g=a(p),g&&"false"===t(g)){if(o.preventDefault(),v!=c.RIGHT)return m.remove(g),void 0;s(g,!1)}else l(p)}if((v==c.BACKSPACE||v==c.DELETE)&&!u(v==c.BACKSPACE))return o.preventDefault(),!1}}var m=e.dom,f=e.selection,g="mce_noneditablecaret",p="";e.on("mousedown",function(n){var i=e.selection.getNode();"false"===t(i)&&i==n.target&&d()}),e.on("mouseup keyup",d),e.on("keydown",u)}function n(t){var n=a.length,i=t.content,r=tinymce.trim(o);if("raw"!=t.format){for(;n--;)i=i.replace(a[n],function(t){var n=arguments,o=n[n.length-2];return o>0&&'"'==i.charAt(o-1)?t:''+e.dom.encode("string"==typeof n[1]?n[1]:n[0])+""});t.content=i}}var i,o,a,r=tinymce.dom.TreeWalker,l="contenteditable",s="data-mce-"+l,c=tinymce.util.VK;i=" "+tinymce.trim(e.getParam("noneditable_editable_class","mceEditable"))+" ",o=" "+tinymce.trim(e.getParam("noneditable_noneditable_class","mceNonEditable"))+" ",a=e.getParam("noneditable_regexp"),a&&!a.length&&(a=[a]),e.on("PreInit",function(){t(),a&&e.on("BeforeSetContent",n),e.parser.addAttributeFilter("class",function(e){for(var t,n,a=e.length;a--;)n=e[a],t=" "+n.attr("class")+" ",-1!==t.indexOf(i)?n.attr(s,"true"):-1!==t.indexOf(o)&&n.attr(s,"false")}),e.serializer.addAttributeFilter(s,function(e){for(var t,n=e.length;n--;)t=e[n],a&&t.attr("data-mce-content")?(t.name="#text",t.type=3,t.raw=!0,t.value=t.attr("data-mce-content")):(t.attr(l,null),t.attr(s,null))}),e.parser.addAttributeFilter(l,function(e){for(var t,n=e.length;n--;)t=e[n],t.attr(s,t.attr(l)),t.attr(l,null)})})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/pagebreak/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/pagebreak/plugin.min.js new file mode 100755 index 0000000000..8f535fa1fb --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/pagebreak/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("pagebreak",function(e){var t,n="mce-pagebreak",i=e.getParam("pagebreak_separator",""),a='';t=new RegExp(i.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(e){return"\\"+e}),"gi"),e.addCommand("mcePageBreak",function(){e.execCommand("mceInsertContent",0,a)}),e.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),e.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),e.on("ResolveName",function(t){"IMG"==t.target.nodeName&&e.dom.hasClass(t.target,n)&&(t.name="pagebreak")}),e.on("click",function(t){t=t.target,"IMG"===t.nodeName&&e.dom.hasClass(t,n)&&e.selection.select(t)}),e.on("BeforeSetContent",function(e){e.content=e.content.replace(t,a)}),e.on("PreInit",function(){e.serializer.addNodeFilter("img",function(e){for(var t,n,a=e.length;a--;)t=e[a],n=t.attr("class"),n&&-1!==n.indexOf("mce-pagebreak")&&(t.type=3,t.value=i,t.raw=!0)})})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/paste/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/paste/plugin.min.js new file mode 100755 index 0000000000..68851b2e4d --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/paste/plugin.min.js @@ -0,0 +1 @@ +!function(e,t){"use strict";function n(e,t){for(var n,r=[],i=0;i]+>/g,"")),(i.settings.paste_remove_styles||i.settings.paste_remove_styles_if_webkit!==!1&&e.webkit)&&(t=t.replace(/ style=\"[^\"]+\"/g,"")),n.isDefaultPrevented()||i.insertContent(t)}function d(e){e=i.dom.encode(e).replace(/\r\n/g,"\n");var t=i.dom.getParent(i.selection.getStart(),i.dom.isBlock);e=t&&/^(PRE|DIV)$/.test(t.nodeName)||!i.settings.forced_root_block?c(e,[[/\n/g,"
    "]]):c(e,[[/\n\n/g,"

    "],[/^(.*<\/p>)(

    )$/,"

    $1"],[/\n/g,"
    "]]);var n=i.fire("PastePreProcess",{content:e});n.isDefaultPrevented()||i.insertContent(n.content)}function f(){var e=i.dom.getViewPort().y,t=i.dom.add(i.getBody(),"div",{contentEditable:!1,"data-mce-bogus":"1",style:"position: absolute; top: "+e+"px; left: 0; width: 1px; height: 1px; overflow: hidden"},'

    X
    ');return i.dom.bind(t,"beforedeactivate focusin focusout",function(e){e.stopPropagation()}),t}function p(e){i.dom.unbind(e),i.dom.remove(e)}var m=this,h;if(i.on("keydown",function(e){n.metaKeyPressed(e)&&e.shiftKey&&86==e.keyCode&&(h=o())}),r())i.on("paste",function(e){function t(e,t){for(var r=0;r100){var n,r=f();t.preventDefault(),e.bind(r,"paste",function(e){e.stopPropagation(),n=!0});var a=i.selection.getRng(),c=e.doc.body.createTextRange();if(c.moveToElementText(r.firstChild),c.execCommand("Paste"),p(r),!n)return i.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents."),void 0;i.selection.setRng(a),l()?d(s(r.firstChild)):u(r.firstChild.innerHTML)}})})}else i.on("init",function(){i.dom.bind(i.getBody(),"paste",function(e){e.preventDefault(),i.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.")})}),i.on("keydown",function(e){if(a(e)&&!e.isDefaultPrevented()){e.stopImmediatePropagation();var t=f(),n=i.selection.getRng();i.selection.select(t,!0),i.dom.bind(t,"paste",function(e){e.stopPropagation(),setTimeout(function(){p(t),i.lastRng=n,i.selection.setRng(n);var e=t.firstChild;e.lastChild&&"BR"==e.lastChild.nodeName&&e.removeChild(e.lastChild),l()?d(s(e)):u(e.innerHTML)},0)})}});i.settings.paste_data_images||i.on("drop",function(e){var t=e.dataTransfer;t&&t.files&&t.files.length>0&&e.preventDefault()})}i.paste_block_drop&&i.on("dragend dragover draggesture dragdrop drop drag",function(e){e.preventDefault(),e.stopPropagation()}),this.paste=u,this.pasteText=d}}),r(f,[u,p,m,h,g],function(e,t,n,r,i){return function(o){var a=e.each;o.on("PastePreProcess",function(s){function l(e){a(e,function(e){d=e.constructor==RegExp?d.replace(e,""):d.replace(e[0],e[1])})}function c(e){function t(e,t,a,s){var l=e._listLevel||o;l!=o&&(o>l?n&&(n=n.parent.parent):(r=n,n=null)),n&&n.name==a?n.append(e):(r=r||n,n=new i(a,1),s>1&&n.attr("start",""+s),e.wrap(n)),e.name="li",t.value="";var c=t.next;c&&3==c.type&&(c.value=c.value.replace(/^\u00a0+/,"")),l>o&&r&&r.lastChild.append(n),o=l}for(var n,r,o=1,a=e.getAll("p"),s=0;s/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/ /gi,"\xa0"],[/([\s\u00a0]*)<\/span>/gi,function(e,t){return t.length>0?t.replace(/./," ").slice(Math.floor(t.length/2)).split("").join("\xa0"):""}]]);var m=new n({valid_elements:"@[style],-strong/b,-em/i,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-table,-tr,-td[colspan|rowspan],-th,-thead,-tfoot,-tbody,-a[!href],sub,sup,strike"}),h=new t({},m);h.addAttributeFilter("style",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("style",u(n,n.attr("style"))),"span"!=n.name||n.attributes.length||n.unwrap()});var g=h.parse(d);c(g),s.content=new r({},m).serialize(g)}})}}),r(v,[c,u],function(e,t){return function(n){function r(e){n.on("PastePreProcess",function(t){t.content=e(t.content)})}function i(e,n){return t.each(n,function(t){e=t.constructor==RegExp?e.replace(t,""):e.replace(t[0],t[1])}),e}function o(e){return e=i(e,[/^[\s\S]*|[\s\S]*$/g,[/\u00a0<\/span>/g,"\xa0"],/
    $/])}function a(e){if(!s){var r=[];t.each(n.schema.getBlockElements(),function(e,t){r.push(t)}),s=new RegExp("(?:
     [\\s\\r\\n]+|
    )*(<\\/?("+r.join("|")+")[^>]*>)(?:
     [\\s\\r\\n]+|
    )*","g")}return e=i(e,[[s,"$1"]]),e=i(e,[[/

    /g,"

    "],[/
    /g," "],[/

    /g,"
    "]])}var s;e.webkit&&r(o),e.ie&&r(a)}}),r(y,[b,l,f,v],function(e,t,n,r){var i;e.add("paste",function(e){function o(){"text"==s.pasteFormat?(this.active(!1),s.pasteFormat="html"):(s.pasteFormat="text",this.active(!0),i||(e.windowManager.alert("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),i=!0))}var a=this,s;a.clipboard=s=new t(e),a.quirks=new r(e),a.wordFilter=new n(e),e.settings.paste_as_text&&(a.clipboard.pasteFormat="text"),e.addCommand("mceInsertClipboardContent",function(e,t){t.content&&a.clipboard.paste(t.content),t.text&&a.clipboard.pasteText(t.text)}),e.addButton("pastetext",{icon:"pastetext",tooltip:"Paste as text",onclick:o,active:"text"==a.clipboard.pasteFormat}),e.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:s.pasteFormat,onclick:o})})}),a([l,f,v,y])}(this); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/preview/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/preview/plugin.min.js new file mode 100755 index 0000000000..b8430c648c --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/preview/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("preview",function(e){var t=e.settings;e.addCommand("mcePreview",function(){e.windowManager.open({title:"Preview",width:parseInt(e.getParam("plugin_preview_width","650"),10),height:parseInt(e.getParam("plugin_preview_height","500"),10),html:'',buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var n,i=this.getEl("body").firstChild.contentWindow.document,a="";tinymce.each(e.contentCSS,function(t){a+=''});var r=t.body_id||"tinymce";-1!=r.indexOf("=")&&(r=e.getParam("body_id","","hash"),r=r[e.id]||r);var o=t.body_class||"";-1!=o.indexOf("=")&&(o=e.getParam("body_class","","hash"),o=o[e.id]||""),n=""+a+""+''+e.getContent()+""+"",i.open(),i.write(n),i.close()}})}),e.addButton("preview",{title:"Preview",cmd:"mcePreview"}),e.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/print/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/print/plugin.min.js new file mode 100755 index 0000000000..abc37b5fd4 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/print/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("print",function(t){t.addCommand("mcePrint",function(){t.getWin().print()}),t.addButton("print",{title:"Print",cmd:"mcePrint"}),t.addShortcut("Ctrl+P","","mcePrint"),t.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print",shortcut:"Ctrl+P",context:"file"})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/save/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/save/plugin.min.js new file mode 100755 index 0000000000..bd50cec41e --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/save/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("save",function(e){function t(){var t,n;return t=tinymce.DOM.getParent(e.id,"form"),!e.getParam("save_enablewhendirty",!0)||e.isDirty()?(tinymce.triggerSave(),(n=e.getParam("save_onsavecallback"))?(e.execCallback("save_onsavecallback",e)&&(e.startContent=tinymce.trim(e.getContent({format:"raw"})),e.nodeChanged()),void 0):(t?(e.isNotDirty=!0,(!t.onsubmit||t.onsubmit())&&("function"==typeof t.submit?t.submit():e.windowManager.alert("Error: Form submit field collision.")),e.nodeChanged()):e.windowManager.alert("Error: No form element found."),void 0)):void 0}function n(){var t,n=tinymce.trim(e.startContent);return(t=e.getParam("save_oncancelcallback"))?(e.execCallback("save_oncancelcallback",e),void 0):(e.setContent(n),e.undoManager.clear(),e.nodeChanged(),void 0)}function i(){var t=this;e.on("nodeChange",function(){t.disabled(e.getParam("save_enablewhendirty",!0)&&!e.isDirty())})}e.addCommand("mceSave",t),e.addCommand("mceCancel",n),e.addButton("save",{icon:"save",text:"Save",cmd:"mceSave",disabled:!0,onPostRender:i}),e.addButton("cancel",{text:"Cancel",icon:!1,cmd:"mceCancel",disabled:!0,onPostRender:i}),e.addShortcut("ctrl+s","","mceSave")}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/searchreplace/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/searchreplace/plugin.min.js new file mode 100755 index 0000000000..b9c4fccfc9 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/searchreplace/plugin.min.js @@ -0,0 +1 @@ +!function(){function e(e,t,n,i,a){function r(e,t){if(t=t||0,!e[0])throw"findAndReplaceDOMText cannot handle zero-length matches";var n=e.index;if(t>0){var i=e[t];if(!i)throw"Invalid capture group";n+=e[0].indexOf(i),e[0]=i}return[n,n+e[0].length,[e[0]]]}function o(e){var t;if(3===e.nodeType)return e.data;if(f[e.nodeName])return"";if(t="",(m[e.nodeName]||h[e.nodeName])&&(t+="\n"),e=e.firstChild)do t+=o(e);while(e=e.nextSibling);return t}function c(e,t,n){var i,a,r,o,c=[],s=0,d=e,l=t.shift(),u=0;e:for(;;){if((m[d.nodeName]||h[d.nodeName])&&s++,3===d.nodeType&&(!a&&d.length+s>=l[1]?(a=d,o=l[1]-s):i&&c.push(d),!i&&d.length+s>l[0]&&(i=d,r=l[0]-s),s+=d.length),i&&a){if(d=n({startNode:i,startNodeIndex:r,endNode:a,endNodeIndex:o,innerNodes:c,match:l[2],matchIndex:u}),s-=a.length-o,i=null,a=null,c=[],l=t.shift(),u++,!l)break}else{if(!f[d.nodeName]&&d.firstChild){d=d.firstChild;continue}if(d.nextSibling){d=d.nextSibling;continue}}for(;;){if(d.nextSibling){d=d.nextSibling;break}if(d.parentNode===e)break e;d=d.parentNode}}}function s(e){var t;if("function"!=typeof e){var n=e.nodeType?e:u.createElement(e);t=function(e,t){var i=n.cloneNode(!1);return i.setAttribute("data-mce-index",t),e&&i.appendChild(u.createTextNode(e)),i}}else t=e;return function(e){var n,i,a,r=e.startNode,o=e.endNode,c=e.matchIndex;if(r===o){var s=r;a=s.parentNode,e.startNodeIndex>0&&(n=u.createTextNode(s.data.substring(0,e.startNodeIndex)),a.insertBefore(n,s));var d=t(e.match[0],c);return a.insertBefore(d,s),e.endNodeIndexf;++f){var p=e.innerNodes[f],g=t(p.data,c);p.parentNode.replaceChild(g,p),m.push(g)}var v=t(o.data.substring(0,e.endNodeIndex),c);return a=r.parentNode,a.insertBefore(n,r),a.insertBefore(l,r),a.removeChild(r),a=o.parentNode,a.insertBefore(v,o),a.insertBefore(i,o),a.removeChild(o),v}}var d,l,u,m,f,h,p=[],g=0;if(u=t.ownerDocument,m=a.getBlockElements(),f=a.getWhiteSpaceElements(),h=a.getShortEndedElements(),l=o(t)){if(e.global)for(;d=e.exec(l);)p.push(r(d,i));else d=l.match(e),p.push(r(d,i));return p.length&&(g=p.length,c(t,p,s(n))),g}}function t(t){function n(){var e=tinymce.ui.Factory.create({type:"window",layout:"flex",pack:"center",align:"center",onClose:function(){t.focus(),o=!1,c.unmarkAllMatches()},buttons:[{text:"Find",onclick:function(){e.find("form")[0].submit()}},{text:"Replace",disabled:!0,onclick:function(){c.replace(e.find("#replace").value())||e.statusbar.items().slice(1).disabled(!0)}},{text:"Replace all",disabled:!0,onclick:function(){c.replaceAll(e.find("#replace").value()),e.statusbar.items().slice(1).disabled(!0)}},{type:"spacer",flex:1},{text:"Prev",disabled:!0,onclick:function(){c.prev()}},{text:"Next",disabled:!0,onclick:function(){c.next()}}],title:"Find and replace",items:{type:"form",padding:20,labelGap:30,spacing:10,onsubmit:function(t){var n,i,a,r,o;return t.preventDefault(),a=e.find("#case").checked(),o=e.find("#words").checked(),r=e.find("#find").value(),r.length?(r=r.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),r=o?"\\b"+r+"\\b":r,i=new RegExp(r,a?"g":"gi"),n=c.markAllMatches(i),n?c.first():tinymce.ui.MessageBox.alert("Could not find the specified string."),e.statusbar.items().slice(1).disabled(0===n),void 0):(c.unmarkAllMatches(),e.statusbar.items().slice(1).disabled(!0),void 0)},items:[{type:"textbox",name:"find",size:40,label:"Find",value:t.selection.getNode().src},{type:"textbox",name:"replace",size:40,label:"Replace with"},{type:"checkbox",name:"case",text:"Match case",label:" "},{type:"checkbox",name:"words",text:"Whole words",label:" "}]}}).renderTo().reflow();o=!0}function i(e){var t=e.parentNode;t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function a(e,n){function i(){var i,o;for(i=n?t.getBody()[e?"firstChild":"lastChild"]:c[e?"endContainer":"startContainer"],o=new tinymce.dom.TreeWalker(i,t.getBody());i=o.current();){if(1==i.nodeType&&"SPAN"==i.nodeName&&null!==i.getAttribute("data-mce-index"))for(s=i.getAttribute("data-mce-index"),a=i.firstChild;i=o.current();){if(1==i.nodeType&&"SPAN"==i.nodeName&&null!==i.getAttribute("data-mce-index")){if(i.getAttribute("data-mce-index")!==s)return;r=i.firstChild}o[e?"next":"prev"]()}o[e?"next":"prev"]()}}var a,r,o=t.selection,c=o.getRng(!0),s=-1;return e=e!==!1,i(),a&&r&&(t.focus(),e?(c.setStart(a,0),c.setEnd(r,r.length)):(c.setStart(r,0),c.setEnd(a,a.length)),o.scrollIntoView(a.parentNode),o.setRng(c)),s}function r(e){e.parentNode.removeChild(e)}var o,c=this,s=-1;c.init=function(e){e.addMenuItem("searchreplace",{text:"Find and replace",shortcut:"Ctrl+F",onclick:n,separator:"before",context:"edit"}),e.addButton("searchreplace",{tooltip:"Find and replace",shortcut:"Ctrl+F",onclick:n}),e.addCommand("SearchReplace",n),e.shortcuts.add("Ctrl+F","",n)},c.markAllMatches=function(n){var i,a;return a=t.dom.create("span",{"class":"mce-match-marker","data-mce-bogus":1}),i=t.getBody(),c.unmarkAllMatches(i),e(n,i,a,!1,t.schema)},c.first=function(){return s=a(!0,!0),-1!==s},c.next=function(){return s=a(!0),-1!==s},c.prev=function(){return s=a(!1),-1!==s},c.replace=function(e,n,o){var c,d,l,u,m,f;if(-1===s&&(s=a(n)),f=a(n),l=t.getBody(),d=tinymce.toArray(l.getElementsByTagName("span")),d.length)for(c=0;c=d[1]?(o=c,a=d[1]-l):r&&s.push(c),!r&&c.length+l>d[0]&&(r=c,i=d[0]-l),l+=c.length),r&&o){if(c=n({startNode:r,startNodeIndex:i,endNode:o,endNodeIndex:a,innerNodes:s,match:d[2],matchIndex:u}),l-=o.length-a,r=null,o=null,s=[],d=t.shift(),u++,!d)break}else{if(!g[c.nodeName]&&c.firstChild){c=c.firstChild;continue}if(c.nextSibling){c=c.nextSibling;continue}}for(;;){if(c.nextSibling){c=c.nextSibling;break}if(c.parentNode===e)break e;c=c.parentNode}}}function a(e){var t;if("function"!=typeof e){var n=e.nodeType?e:m.createElement(e);t=function(e,t){var r=n.cloneNode(!1);return r.setAttribute("data-mce-index",t),e&&r.appendChild(m.createTextNode(e)),r}}else t=e;return function r(e){var n,r,o,i=e.startNode,a=e.endNode,s=e.matchIndex;if(i===a){var l=i;o=l.parentNode,e.startNodeIndex>0&&(n=m.createTextNode(l.data.substring(0,e.startNodeIndex)),o.insertBefore(n,l));var c=t(e.match[0],s);return o.insertBefore(c,l),e.endNodeIndexf;++f){var h=e.innerNodes[f],g=t(h.data,s);h.parentNode.replaceChild(g,h),u.push(g)}var v=t(a.data.substring(0,e.endNodeIndex),s);return o=i.parentNode,o.insertBefore(n,i),o.insertBefore(d,i),o.removeChild(i),o=a.parentNode,o.insertBefore(v,a),o.insertBefore(r,a),o.removeChild(a),v}}function s(e){var t=[];return l(function(n,r){e(n,r)&&t.push(n)}),u=t,this}function l(e){for(var t=0,n=u.length;n>t&&e(u[t],t)!==!1;t++);return this}function c(e){return u.length&&(p=u.length,i(t,u,a(e))),this}var d,u=[],f,p=0,m,h,g,v;if(m=t.ownerDocument,h=n.getBlockElements(),g=n.getWhiteSpaceElements(),v=n.getShortEndedElements(),f=o(t),f&&e.global)for(;d=e.exec(f);)u.push(r(d));return{text:f,count:p,matches:u,each:l,filter:s,mark:c}}}),r(c,[l,d,u,f,p,m,h],function(e,t,n,r,o,i,a){t.add("spellchecker",function(t,s){function l(e){for(var t in e)return!1;return!0}function c(e,i){var a=[],s=g[i];n.each(s,function(e){a.push({text:e,onclick:function(){t.insertContent(e),u()}})}),a.push.apply(a,[{text:"-"},{text:"Ignore",onclick:function(){p(e,i)}},{text:"Ignore all",onclick:function(){p(e,i,!0)}},{text:"Finish",onclick:m}]),y=new r({items:a,context:"contextmenu",onautohide:function(e){-1!=e.target.className.indexOf("spellchecker")&&e.preventDefault()},onhide:function(){y.remove(),y=null}}),y.renderTo(document.body);var l=o.DOM.getPos(t.getContentAreaContainer()),c=t.dom.getPos(e);l.x+=c.x,l.y+=c.y,y.moveTo(l.x,l.y+e.offsetHeight)}function d(){function n(e){return t.setProgressState(!1),l(e)?(t.windowManager.alert("No misspellings found"),v=!1,void 0):(g=e,o.filter(function(t){return!!e[t[2][0]]}).mark(t.dom.create("span",{"class":"mce-spellchecker-word","data-mce-bogus":1})),o=null,t.fire("SpellcheckStart"),void 0)}function r(e,n,r){i.sendRPC({url:new a(s).toAbsolute(b.spellchecker_rpc_url),method:e,params:{lang:b.spellchecker_language||"en",words:n},success:function(e){r(e)},error:function(e,n){e="JSON Parse error."==e?"Non JSON response:"+n.responseText:"Error: "+e,t.windowManager.alert(e),t.setProgressState(!1),o=null,v=!1}})}var o,c=[],d={};if(v)return m(),void 0;v=!0;var u=t.getParam("spellchecker_wordchar_pattern")||new RegExp('[^\\s!"#$%&()*+,-./:;<=>?@[\\]^_{|}`\xa7\xa9\xab\xae\xb1\xb6\xb7\xb8\xbb\xbc\xbd\xbe\xbf\xd7\xf7\xa4\u201d\u201c\u201e]+',"g");o=new e(u,t.getBody(),t.schema).each(function(e){var t=e[2][0];if(!d[t]){if(/^\d+$/.test(t)||1==t.length)return;c.push(t),d[t]=!0}}),t.setProgressState(!0);var f=b.spellchecker_callback||r;f("spellcheck",c,n)}function u(){t.dom.select("span.mce-spellchecker-word").length||m()}function f(e){var t=e.parentNode;t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function p(e,r,o){o?n.each(t.dom.select("span.mce-spellchecker-word"),function(e){var t=e.innerText||e.textContent;t==r&&f(e)}):f(e),u()}function m(){var e,n,r;for(v=!1,r=t.getBody(),n=r.getElementsByTagName("span"),e=n.length;e--;)r=n[e],r.getAttribute("data-mce-index")&&f(r);t.fire("SpellcheckEnd")}function h(e){var n,r,o,i=-1,a,s;for(e=""+e,n=t.getBody().getElementsByTagName("span"),r=0;r0){for(c=u+1;c=0;c--)if(a(d[c]))return d[c];return null}var u,d,a,c;if(9===n.keyCode&&(a=r(e.getParam("tab_focus",e.getParam("tabfocus_elements",":prev,:next"))),1==a.length&&(a[1]=a[0],a[0]=":prev"),d=n.shiftKey?":prev"==a[0]?t(-1):i.get(a[0]):":next"==a[1]?t(1):i.get(a[1]))){var f=tinymce.get(d.id||d.name);d.id&&f?f.focus():window.setTimeout(function(){tinymce.Env.webkit||window.focus(),d.focus()},10),n.preventDefault()}}var i=tinymce.DOM,o=tinymce.each,r=tinymce.explode;e.on("init",function(){e.inline&&tinymce.DOM.setAttrib(e.getBody(),"tabIndex",null)}),e.on("keyup",n),tinymce.Env.gecko?e.on("keypress keydown",t):e.on("keydown",t)}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/table/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/table/plugin.min.js new file mode 100755 index 0000000000..c7c2e8ed48 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/table/plugin.min.js @@ -0,0 +1 @@ +!function(e,t){"use strict";function n(e,t){for(var n,r=[],i=0;i "+t+" tr",o);r(i,function(i,o){o+=e,r(H.select("> td, > th",i),function(e,r){var i,a,s,l;if(R[o])for(;R[o][r];)r++;for(s=n(e,"rowspan"),l=n(e,"colspan"),a=o;o+s>a;a++)for(R[a]||(R[a]=[]),i=r;r+l>i;i++)R[a][i]={part:t,real:a==o&&i==r,elm:e,rowspan:s,colspan:l}})}),e+=i.length})}function s(e,t){return e=e.cloneNode(t),e.removeAttribute("id"),e}function l(e,t){var n;return n=R[t],n?n[e]:void 0}function c(e,t,n){e&&(n=parseInt(n,10),1===n?e.removeAttribute(t,1):e.setAttribute(t,n,1))}function d(e){return e&&(H.hasClass(e.elm,"mce-item-selected")||e==P)}function u(){var e=[];return r(o.rows,function(t){r(t.cells,function(n){return H.hasClass(n,"mce-item-selected")||n==P.elm?(e.push(t),!1):void 0})}),e}function f(){var e=H.createRng();e.setStartAfter(o),e.setEndAfter(o),M.setRng(e),H.remove(o)}function p(n){var o,a={};return i.settings.table_clone_elements!==!1&&(a=e.makeMap((i.settings.table_clone_elements||"strong em b i span font h1 h2 h3 h4 h5 h6 p div").toUpperCase(),/[ ,]/)),e.walk(n,function(e){var i;return 3==e.nodeType?(r(H.getParents(e.parentNode,null,n).reverse(),function(e){a[e.nodeName]&&(e=s(e,!1),o?i&&i.appendChild(e):o=i=e,i=e)}),i&&(i.innerHTML=t.ie?" ":'
    '),!1):void 0},"childNodes"),n=s(n,!1),c(n,"rowSpan",1),c(n,"colSpan",1),o?n.appendChild(o):t.ie||(n.innerHTML='
    '),n}function m(){var e=H.createRng(),t;return r(H.select("tr",o),function(e){0===e.cells.length&&H.remove(e)}),0===H.select("tr",o).length?(e.setStartBefore(o),e.setEndBefore(o),M.setRng(e),H.remove(o),void 0):(r(H.select("thead,tbody,tfoot",o),function(e){0===e.rows.length&&H.remove(e)}),a(),t=R[Math.min(R.length-1,A.y)],t&&(M.select(t[Math.min(t.length-1,A.x)].elm,!0),M.collapse(!0)),void 0)}function h(e,t,n,r){var i,o,a,s,l;for(i=R[t][e].elm.parentNode,a=1;n>=a;a++)if(i=H.getNext(i,"tr")){for(o=e;o>=0;o--)if(l=R[t+a][o].elm,l.parentNode==i){for(s=1;r>=s;s++)H.insertAfter(p(l),l);break}if(-1==o)for(s=1;r>=s;s++)i.insertBefore(p(i.cells[0]),i.cells[0])}}function g(){r(R,function(e,t){r(e,function(e,r){var i,o,a;if(d(e)&&(e=e.elm,i=n(e,"colspan"),o=n(e,"rowspan"),i>1||o>1)){for(c(e,"rowSpan",1),c(e,"colSpan",1),a=0;i-1>a;a++)H.insertAfter(p(e),e);h(r,t,o-1,i)}})})}function v(t,n,i){var o,s,u,f,p,h,v,y,b,C,x;if(t?(o=S(t),s=o.x,u=o.y,f=s+(n-1),p=u+(i-1)):(A=B=null,r(R,function(e,t){r(e,function(e,n){d(e)&&(A||(A={x:n,y:t}),B={x:n,y:t})})}),s=A.x,u=A.y,f=B.x,p=B.y),y=l(s,u),b=l(f,p),y&&b&&y.part==b.part){for(g(),a(),y=l(s,u).elm,c(y,"colSpan",f-s+1),c(y,"rowSpan",p-u+1),v=u;p>=v;v++)for(h=s;f>=h;h++)R[v]&&R[v][h]&&(t=R[v][h].elm,t!=y&&(C=e.grep(t.childNodes),r(C,function(e){y.appendChild(e)}),C.length&&(C=e.grep(y.childNodes),x=0,r(C,function(e){"BR"==e.nodeName&&H.getAttrib(e,"data-mce-bogus")&&x++0&&R[t-1][a]&&(m=R[t-1][a].elm,h=n(m,"rowSpan"),h>1)){c(m,"rowSpan",h+1);continue}}else if(h=n(i,"rowspan"),h>1){c(i,"rowSpan",h+1);continue}f=p(i),c(f,"colSpan",i.colSpan),u.appendChild(f),o=i}u.hasChildNodes()&&(e?l.parentNode.insertBefore(u,l):H.insertAfter(u,l))}function b(e){var t,i;r(R,function(n){return r(n,function(n,r){return d(n)&&(t=r,e)?!1:void 0}),e?!t:void 0}),r(R,function(r,o){var a,s,l;r[t]&&(a=r[t].elm,a!=i&&(l=n(a,"colspan"),s=n(a,"rowspan"),1==l?e?(a.parentNode.insertBefore(p(a),a),h(t,o,s-1,l)):(H.insertAfter(p(a),a),h(t,o,s-1,l)):c(a,"colSpan",a.colSpan+1),i=a))})}function C(){var t=[];r(R,function(i){r(i,function(i,o){d(i)&&-1===e.inArray(t,o)&&(r(R,function(e){var t=e[o].elm,r;r=n(t,"colSpan"),r>1?c(t,"colSpan",r-1):H.remove(t)}),t.push(o))})}),m()}function x(){function e(e){var t,i,o;t=H.getNext(e,"tr"),r(e.cells,function(e){var t=n(e,"rowSpan");t>1&&(c(e,"rowSpan",t-1),i=S(e),h(i.x,i.y,1,1))}),i=S(e.cells[0]),r(R[i.y],function(e){var t;e=e.elm,e!=o&&(t=n(e,"rowSpan"),1>=t?H.remove(e):c(e,"rowSpan",t-1),o=e)})}var t;t=u(),r(t.reverse(),function(t){e(t)}),m()}function w(){var e=u();return H.remove(e),m(),e}function _(){var e=u();return r(e,function(t,n){e[n]=s(t,!0)}),e}function N(e,t){var n=u(),i=n[t?0:n.length-1],o=i.cells.length;e&&(r(R,function(e){var t;return o=0,r(e,function(e){e.real&&(o+=e.colspan),e.elm.parentNode==i&&(t=1)}),t?!1:void 0}),t||e.reverse(),r(e,function(e){var n,r=e.cells.length,a;for(n=0;r>n;n++)a=e.cells[n],c(a,"colSpan",1),c(a,"rowSpan",1);for(n=r;o>n;n++)e.appendChild(p(e.cells[r-1]));for(n=o;r>n;n++)H.remove(e.cells[n]);t?i.parentNode.insertBefore(e,i):H.insertAfter(e,i)}),H.removeClass(H.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"))}function S(e){var t;return r(R,function(n,i){return r(n,function(n,r){return n.elm==e?(t={x:r,y:i},!1):void 0}),!t}),t}function E(e){A=S(e)}function k(){var e,t;return e=t=0,r(R,function(n,i){r(n,function(n,r){var o,a;d(n)&&(n=R[i][r],r>e&&(e=r),i>t&&(t=i),n.real&&(o=n.colspan-1,a=n.rowspan-1,o&&r+o>e&&(e=r+o),a&&i+a>t&&(t=i+a)))})}),{x:e,y:t}}function T(e){var t,n,r,i,o,a,s,l,c,d;if(B=S(e),A&&B){for(t=Math.min(A.x,B.x),n=Math.min(A.y,B.y),r=Math.max(A.x,B.x),i=Math.max(A.y,B.y),o=r,a=i,d=n;a>=d;d++)e=R[d][t],e.real||t-(e.colspan-1)=c;c++)e=R[n][c],e.real||n-(e.rowspan-1)=d;d++)for(c=t;r>=c;c++)e=R[d][c],e.real&&(s=e.colspan-1,l=e.rowspan-1,s&&c+s>o&&(o=c+s),l&&d+l>a&&(a=d+l));for(H.removeClass(H.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=n;a>=d;d++)for(c=t;o>=c;c++)R[d][c]&&H.addClass(R[d][c].elm,"mce-item-selected")}}var R,A,B,P,M=i.selection,H=M.dom;o=o||H.getParent(M.getStart(),"table"),a(),P=H.getParent(M.getStart(),"th,td"),P&&(A=S(P),B=k(),P=l(A.x,A.y)),e.extend(this,{deleteTable:f,split:g,merge:v,insertRow:y,insertCol:b,deleteCols:C,deleteRows:x,cutRows:w,copyRows:_,pasteRows:N,getPos:S,setStartCell:E,setEndCell:T})}}),r(u,[f,d,c],function(e,t,n){function r(e,t){return parseInt(e.getAttribute(t)||1,10)}var i=n.each;return function(n){function o(){function t(t){function o(e,r){var i=e?"previousSibling":"nextSibling",o=n.dom.getParent(r,"tr"),s=o[i];if(s)return g(n,r,s,e),t.preventDefault(),!0;var d=n.dom.getParent(o,"table"),u=o.parentNode,f=u.nodeName.toLowerCase();if("tbody"===f||f===(e?"tfoot":"thead")){var p=a(e,d,u,"tbody");if(null!==p)return l(e,p,r)}return c(e,o,i,d)}function a(e,t,r,i){var o=n.dom.select(">"+i,t),a=o.indexOf(r);if(e&&0===a||!e&&a===o.length-1)return s(e,t);if(-1===a){var l="thead"===r.tagName.toLowerCase()?0:o.length-1;return o[l]}return o[a+(e?-1:1)]}function s(e,t){var r=e?"thead":"tfoot",i=n.dom.select(">"+r,t);return 0!==i.length?i[0]:null}function l(e,r,i){var o=d(r,e);return o&&g(n,i,o,e),t.preventDefault(),!0}function c(e,r,i,a){var s=a[i];if(s)return u(s),!0;var l=n.dom.getParent(a,"td,th");if(l)return o(e,l,t);var c=d(r,!e);return u(c),t.preventDefault(),!1}function d(e,t){var r=e&&e[t?"lastChild":"firstChild"];return r&&"BR"===r.nodeName?n.dom.getParent(r,"td,th"):r}function u(e){n.selection.setCursorLocation(e,0)}function f(){return b==e.UP||b==e.DOWN}function p(e){var t=e.selection.getNode(),n=e.dom.getParent(t,"tr");return null!==n}function m(e){for(var t=0,n=e;n.previousSibling;)n=n.previousSibling,t+=r(n,"colspan");return t}function h(e,t){var n=0,o=0;return i(e.children,function(e,i){return n+=r(e,"colspan"),o=i,n>t?!1:void 0}),o}function g(e,t,r,i){var o=m(n.dom.getParent(t,"td,th")),a=h(r,o),s=r.childNodes[a],l=d(s,i);u(l||s)}function v(e){var t=n.selection.getNode(),r=n.dom.getParent(t,"td,th"),i=n.dom.getParent(e,"td,th");return r&&r!==i&&y(r,i)}function y(e,t){return n.dom.getParent(e,"TABLE")===n.dom.getParent(t,"TABLE")}var b=t.keyCode;if(f()&&p(n)){var C=n.selection.getNode();setTimeout(function(){v(C)&&o(!t.shiftKey&&b===e.UP,C,t)},0)}}n.on("KeyDown",function(e){t(e)})}function a(){function e(e,t){var n=t.ownerDocument,r=n.createRange(),i;return r.setStartBefore(t),r.setEnd(e.endContainer,e.endOffset),i=n.createElement("body"),i.appendChild(r.cloneContents()),0===i.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length}n.on("KeyDown",function(t){var r,i,o=n.dom;(37==t.keyCode||38==t.keyCode)&&(r=n.selection.getRng(),i=o.getParent(r.startContainer,"table"),i&&n.getBody().firstChild==i&&e(r,i)&&(r=o.createRng(),r.setStartBefore(i),r.setEndBefore(i),n.selection.setRng(r),t.preventDefault()))})}function s(){n.on("KeyDown SetContent VisualAid",function(){var e;for(e=n.getBody().lastChild;e;e=e.previousSibling)if(3==e.nodeType){if(e.nodeValue.length>0)break}else if(1==e.nodeType&&!e.getAttribute("data-mce-bogus"))break;e&&"TABLE"==e.nodeName&&(n.settings.forced_root_block?n.dom.add(n.getBody(),n.settings.forced_root_block,null,t.ie?" ":'
    '):n.dom.add(n.getBody(),"br",{"data-mce-bogus":"1"}))}),n.on("PreProcess",function(e){var t=e.node.lastChild;t&&("BR"==t.nodeName||1==t.childNodes.length&&("BR"==t.firstChild.nodeName||"\xa0"==t.firstChild.nodeValue))&&t.previousSibling&&"TABLE"==t.previousSibling.nodeName&&n.dom.remove(t)})}function l(){function e(e,t,n,r){var i=3,o=e.dom.getParent(t.startContainer,"TABLE"),a,s,l;return o&&(a=o.parentNode),s=t.startContainer.nodeType==i&&0===t.startOffset&&0===t.endOffset&&r&&("TR"==n.nodeName||n==a),l=("TD"==n.nodeName||"TH"==n.nodeName)&&!r,s||l}function t(){var t=n.selection.getRng(),r=n.selection.getNode(),i=n.dom.getParent(t.startContainer,"TD,TH");if(e(n,t,r,i)){i||(i=r);for(var o=i.lastChild;o.lastChild;)o=o.lastChild;t.setEnd(o,o.nodeValue.length),n.selection.setRng(t)}}n.on("KeyDown",function(){t()}),n.on("MouseDown",function(e){2!=e.button&&t()})}t.webkit&&(o(),l()),t.gecko&&(a(),s())}}),r(p,[l,m,c],function(e,t,n){return function(r){function i(){r.getBody().style.webkitUserSelect="",c&&(r.dom.removeClass(r.dom.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),c=!1)}var o=r.dom,a,s,l,c=!0;return r.on("MouseDown",function(e){2!=e.button&&(i(),s=o.getParent(e.target,"td,th"),l=o.getParent(s,"table"))}),o.bind(r.getDoc(),"mouseover",function(t){var n,i,d=t.target;if(s&&(a||d!=s)&&("TD"==d.nodeName||"TH"==d.nodeName)){i=o.getParent(d,"table"),i==l&&(a||(a=new e(r,i),a.setStartCell(s),r.getBody().style.webkitUserSelect="none"),a.setEndCell(d),c=!0),n=r.selection.getSel();try{n.removeAllRanges?n.removeAllRanges():n.empty()}catch(u){}t.preventDefault()}}),r.on("MouseUp",function(){function e(e,r){var o=new t(e,e);do{if(3==e.nodeType&&0!==n.trim(e.nodeValue).length)return r?i.setStart(e,0):i.setEnd(e,e.nodeValue.length),void 0;if("BR"==e.nodeName)return r?i.setStartBefore(e):i.setEndBefore(e),void 0}while(e=r?o.next():o.prev())}var i,c=r.selection,d,u,f,p,m;if(s){if(a&&(r.getBody().style.webkitUserSelect=""),d=o.select("td.mce-item-selected,th.mce-item-selected"),d.length>0){i=o.createRng(),f=d[0],m=d[d.length-1],i.setStartBefore(f),i.setEndAfter(f),e(f,1),u=new t(f,o.getParent(d[0],"table"));do if("TD"==f.nodeName||"TH"==f.nodeName){if(!o.hasClass(f,"mce-item-selected"))break;p=f}while(f=u.next());e(p),c.setRng(i)}r.nodeChanged(),s=a=l=null}}),r.on("KeyUp",function(){i()}),{clear:i}}}),r(h,[l,u,p,c,m,d,g],function(e,t,n,r,i,o,a){function s(r){function i(e){return e?e.replace(/px$/,""):""}function a(e){return/^[0-9]+$/.test(e)&&(e+="px"),e}function s(e){l("left center right".split(" "),function(t){r.formatter.remove("align"+t,{},e)})}function c(){var e=r.dom,t,n,c;t=r.dom.getParent(r.selection.getStart(),"table"),c=!1,n={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),cellspacing:e.getAttrib(t,"cellspacing"),cellpadding:e.getAttrib(t,"cellpadding"),border:e.getAttrib(t,"border"),caption:!!e.select("caption",t)[0]},l("left center right".split(" "),function(e){r.formatter.matchNode(t,"align"+e)&&(n.align=e)}),r.windowManager.open({title:"Table properties",items:{type:"form",layout:"grid",columns:2,data:n,defaults:{type:"textbox",maxWidth:50},items:[c?{label:"Cols",name:"cols",disabled:!0}:null,c?{label:"Rows",name:"rows",disabled:!0}:null,{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell spacing",name:"cellspacing"},{label:"Cell padding",name:"cellpadding"},{label:"Border",name:"border"},{label:"Caption",name:"caption",type:"checkbox"},{label:"Alignment",minWidth:90,name:"align",type:"listbox",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]}]},onsubmit:function(){var n=this.toJSON(),i;r.undoManager.transact(function(){r.dom.setAttribs(t,{cellspacing:n.cellspacing,cellpadding:n.cellpadding,border:n.border}),r.dom.setStyles(t,{width:a(n.width),height:a(n.height)}),i=e.select("caption",t)[0],i&&!n.caption&&e.remove(i),!i&&n.caption&&(i=e.create("caption"),o.ie||(i.innerHTML='
    '),t.insertBefore(i,t.firstChild)),s(t),n.align&&r.formatter.apply("align"+n.align,{},t),r.focus(),r.addVisual()})}})}function d(e,t){r.windowManager.open({title:"Merge cells",body:[{label:"Cols",name:"cols",type:"textbox",size:10},{label:"Rows",name:"rows",type:"textbox",size:10}],onsubmit:function(){var n=this.toJSON();r.undoManager.transact(function(){e.merge(t,n.cols,n.rows)})}})}function u(){var e=r.dom,t,n,o=[];o=r.dom.select("td.mce-item-selected,th.mce-item-selected"),t=r.dom.getParent(r.selection.getStart(),"td,th"),!o.length&&t&&o.push(t),t=t||o[0],n={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),scope:e.getAttrib(t,"scope")},n.type=t.nodeName.toLowerCase(),l("left center right".split(" "),function(e){r.formatter.matchNode(t,"align"+e)&&(n.align=e)}),r.windowManager.open({title:"Cell properties",items:{type:"form",data:n,layout:"grid",columns:2,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell type",name:"type",type:"listbox",text:"None",minWidth:90,maxWidth:null,menu:[{text:"Cell",value:"td"},{text:"Header cell",value:"th"}]},{label:"Scope",name:"scope",type:"listbox",text:"None",minWidth:90,maxWidth:null,menu:[{text:"None",value:""},{text:"Row",value:"row"},{text:"Column",value:"col"},{text:"Row group",value:"rowgroup"},{text:"Column group",value:"colgroup"}]},{label:"Alignment",name:"align",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]}]},onsubmit:function(){var t=this.toJSON();r.undoManager.transact(function(){l(o,function(n){r.dom.setAttrib(n,"scope",t.scope),r.dom.setStyles(n,{width:a(t.width),height:a(t.height)}),t.type&&n.nodeName.toLowerCase()!=t.type&&(n=e.rename(n,t.type)),s(n),t.align&&r.formatter.apply("align"+t.align,{},n)}),r.focus()})}})}function f(){var e=r.dom,t,n,o,c,d=[];t=r.dom.getParent(r.selection.getStart(),"table"),n=r.dom.getParent(r.selection.getStart(),"td,th"),l(t.rows,function(t){l(t.cells,function(r){return e.hasClass(r,"mce-item-selected")||r==n?(d.push(t),!1):void 0})}),o=d[0],c={height:i(e.getStyle(o,"height")||e.getAttrib(o,"height")),scope:e.getAttrib(o,"scope")},c.type=o.parentNode.nodeName.toLowerCase(),l("left center right".split(" "),function(e){r.formatter.matchNode(o,"align"+e)&&(c.align=e)}),r.windowManager.open({title:"Row properties",items:{type:"form",data:c,columns:2,defaults:{type:"textbox"},items:[{type:"listbox",name:"type",label:"Row type",text:"None",maxWidth:null,menu:[{text:"Header",value:"thead"},{text:"Body",value:"tbody"},{text:"Footer",value:"tfoot"}]},{type:"listbox",name:"align",label:"Alignment",text:"None",maxWidth:null,menu:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"Height",name:"height"}]},onsubmit:function(){var t=this.toJSON(),n,i,o;r.undoManager.transact(function(){var c=t.type;l(d,function(l){r.dom.setAttrib(l,"scope",t.scope),r.dom.setStyles(l,{height:a(t.height)}),c!=l.parentNode.nodeName.toLowerCase()&&(n=e.getParent(l,"table"),i=l.parentNode,o=e.select(c,n)[0],o||(o=e.create(c),n.firstChild?n.insertBefore(o,n.firstChild):n.appendChild(o)),o.appendChild(l),i.hasChildNodes()||e.remove(i)),s(l),t.align&&r.formatter.apply("align"+t.align,{},l)}),r.focus()})}})}function p(e){return function(){r.execCommand(e)}}function m(e,t){var n,i,a;for(a="",n=0;t>n;n++){for(a+="",i=0;e>i;i++)a+="";a+=""}a+="
    "+(o.ie?" ":"
    ")+"
    ",r.insertContent(a)}function h(e,t){function n(){e.disabled(!r.dom.getParent(r.selection.getStart(),t)),r.selection.selectorChanged(t,function(t){e.disabled(!t)})}r.initialized?n():r.on("init",n)}function g(){h(this,"table")}function v(){h(this,"td,th")}function y(){var e="";e='';for(var t=0;10>t;t++){e+="";for(var n=0;10>n;n++)e+='';e+=""}return e+="",e+='
    0 x 0
    '}var b,C,x=this;r.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",onhide:function(){r.dom.removeClass(this.menu.items()[0].getEl().getElementsByTagName("a"),"mce-active")},menu:[{type:"container",html:y(),onmousemove:function(e){var t=e.target;if("A"==t.nodeName){var n=r.dom.getParent(t,"table"),i=t.getAttribute("data-mce-index");if(i!=this.lastPos){i=i.split(","),i[0]=parseInt(i[0],10),i[1]=parseInt(i[1],10);for(var o=0;10>o;o++)for(var a=0;10>a;a++)r.dom.toggleClass(n.rows[o].childNodes[a].firstChild,"mce-active",a<=i[0]&&o<=i[1]);n.nextSibling.innerHTML=i[0]+1+" x "+(i[1]+1),this.lastPos=i}}},onclick:function(e){"A"==e.target.nodeName&&this.lastPos&&(e.preventDefault(),m(this.lastPos[0]+1,this.lastPos[1]+1),this.parent().cancel())}}]}),r.addMenuItem("tableprops",{text:"Table properties",context:"table",onPostRender:g,onclick:c}),r.addMenuItem("deletetable",{text:"Delete table",context:"table",onPostRender:g,cmd:"mceTableDelete"}),r.addMenuItem("cell",{separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:p("mceTableCellProps"),onPostRender:v},{text:"Merge cells",onclick:p("mceTableMergeCells"),onPostRender:v},{text:"Split cell",onclick:p("mceTableSplitCells"),onPostRender:v}]}),r.addMenuItem("row",{text:"Row",context:"table",menu:[{text:"Insert row before",onclick:p("mceTableInsertRowBefore"),onPostRender:v},{text:"Insert row after",onclick:p("mceTableInsertRowAfter"),onPostRender:v},{text:"Delete row",onclick:p("mceTableDeleteRow"),onPostRender:v},{text:"Row properties",onclick:p("mceTableRowProps"),onPostRender:v},{text:"-"},{text:"Cut row",onclick:p("mceTableCutRow"),onPostRender:v},{text:"Copy row",onclick:p("mceTableCopyRow"),onPostRender:v},{text:"Paste row before",onclick:p("mceTablePasteRowBefore"),onPostRender:v},{text:"Paste row after",onclick:p("mceTablePasteRowAfter"),onPostRender:v}]}),r.addMenuItem("column",{text:"Column",context:"table",menu:[{text:"Insert column before",onclick:p("mceTableInsertColBefore"),onPostRender:v},{text:"Insert column after",onclick:p("mceTableInsertColAfter"),onPostRender:v},{text:"Delete column",onclick:p("mceTableDeleteCol"),onPostRender:v}]});var w=[];l("inserttable tableprops deletetable | cell row column".split(" "),function(e){"|"==e?w.push({text:"-"}):w.push(r.menuItems[e])}),r.addButton("table",{type:"menubutton",title:"Table",menu:w}),o.isIE||r.on("click",function(e){e=e.target,"TABLE"===e.nodeName&&(r.selection.select(e),r.nodeChanged())}),x.quirks=new t(r),r.on("Init",function(){b=r.windowManager,x.cellSelection=new n(r)}),l({mceTableSplitCells:function(e){e.split()},mceTableMergeCells:function(e){var t,n,i;i=r.dom.getParent(r.selection.getStart(),"th,td"),i&&(t=i.rowSpan,n=i.colSpan),r.dom.select("td.mce-item-selected,th.mce-item-selected").length?e.merge():d(e,i)},mceTableInsertRowBefore:function(e){e.insertRow(!0)},mceTableInsertRowAfter:function(e){e.insertRow()},mceTableInsertColBefore:function(e){e.insertCol(!0)},mceTableInsertColAfter:function(e){e.insertCol()},mceTableDeleteCol:function(e){e.deleteCols()},mceTableDeleteRow:function(e){e.deleteRows()},mceTableCutRow:function(e){C=e.cutRows()},mceTableCopyRow:function(e){C=e.copyRows()},mceTablePasteRowBefore:function(e){e.pasteRows(C,!0)},mceTablePasteRowAfter:function(e){e.pasteRows(C)},mceTableDelete:function(e){e.deleteTable()}},function(t,n){r.addCommand(n,function(){var n=new e(r);n&&(t(n),r.execCommand("mceRepaint"),x.cellSelection.clear())})}),l({mceInsertTable:function(){c()},mceTableRowProps:f,mceTableCellProps:u},function(e,t){r.addCommand(t,function(t,n){e(n)})})}var l=r.each;a.add("table",s)}),a([l,u,p,h])}(this); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/template/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/template/plugin.min.js new file mode 100755 index 0000000000..eacf712dc4 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/template/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("template",function(e){function t(){function t(e){var t=e.control.value();t.url?tinymce.util.XHR.send({url:t.url,success:function(e){i=e,n.find("iframe")[0].html(e)}}):(i=t.content,n.find("iframe")[0].html(t.content)),n.find("#description")[0].text(e.control.value().description)}var n,i,o=[];return e.settings.templates?(tinymce.each(e.settings.templates,function(e){o.push({selected:!o.length,text:e.title,value:{url:e.url,content:e.content,description:e.description}})}),n=e.windowManager.open({title:"Insert template",body:[{type:"container",label:"Templates",items:{type:"listbox",name:"template",values:o,onselect:t}},{type:"label",name:"description",label:"Description",text:" "},{type:"iframe",minWidth:600,minHeight:400,border:1}],onsubmit:function(){a(!1,i)}}),n.find("listbox")[0].fire("select"),void 0):(e.windowManager.alert("No templates defined"),void 0)}function n(t,n){function i(e,t){if(e=""+e,e.length0&&(l=s.create("div",null),l.appendChild(c[0].cloneNode(!0))),o(s.select("*",l),function(t){r(t,e.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_cdate_format",e.getLang("template.cdate_format")))),r(t,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_mdate_format",e.getLang("template.mdate_format")))),r(t,e.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))&&(t.innerHTML=u)}),i(l),e.execCommand("mceInsertContent",!1,l.innerHTML),e.addVisual()}var o=tinymce.each;e.addCommand("mceInsertTemplate",a),e.addButton("template",{title:"Insert template",onclick:t}),e.addMenuItem("template",{text:"Insert template",onclick:t,context:"insert"}),e.on("PreProcess",function(t){var a=e.dom;o(a.select("div",t.node),function(t){a.hasClass(t,"mceTmpl")&&(o(a.select("*",t),function(t){a.hasClass(t,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_mdate_format",e.getLang("template.mdate_format"))))}),i(t))})})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/textcolor/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/textcolor/plugin.min.js new file mode 100755 index 0000000000..9f2524fde4 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/textcolor/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("textcolor",function(e){function t(){var t,n,i=[];for(n=e.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Brown","C0C0C0","Silver","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum","FFFFFF","White"],t=0;t',o=n.length-1,r=e.settings.textcolor_rows||5,l=e.settings.textcolor_cols||8,s=0;r>s;s++){for(a+="",c=0;l>c;c++)u=s*l+c,u>o?a+="":(i=n[u],a+='
    '+"
    "+"");a+=""}return a+=""}function i(t){var n,i=this.parent();(n=t.target.getAttribute("data-mce-color"))&&(i.hidePanel(),n="#"+n,i.color(n),e.execCommand(i.settings.selectcmd,!1,n))}function a(){var t=this;t._color&&e.execCommand(t.settings.selectcmd,!1,t._color)}e.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",popoverAlign:"bc-tl",selectcmd:"ForeColor",panel:{html:n,onclick:i},onclick:a}),e.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",popoverAlign:"bc-tl",selectcmd:"HiliteColor",panel:{html:n,onclick:i},onclick:a})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualblocks/css/visualblocks.css b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualblocks/css/visualblocks.css new file mode 100755 index 0000000000..30fa34ef07 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualblocks/css/visualblocks.css @@ -0,0 +1,136 @@ +.mce-visualblocks p { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7); +} + +.mce-visualblocks h1 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==); +} + +.mce-visualblocks h2 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==); +} + +.mce-visualblocks h3 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7); +} + +.mce-visualblocks h4 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==); +} + +.mce-visualblocks h5 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==); +} + +.mce-visualblocks h6 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==); +} + +.mce-visualblocks div { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7); +} + +.mce-visualblocks section { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=); +} + +.mce-visualblocks article { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7); +} + +.mce-visualblocks blockquote { + padding-top: 10px; + border: 1px dashed #BBB; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7); +} + +.mce-visualblocks address { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=); +} + +.mce-visualblocks pre { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==); +} + +.mce-visualblocks figure { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7); +} + +.mce-visualblocks hgroup { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7); +} + +.mce-visualblocks aside { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=); +} + +.mce-visualblocks figcaption { + border: 1px dashed #BBB; +} + +.mce-visualblocks ul { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAPcAAAAAALu7uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAOAAoAAAgsAP8JHEhwYICCCAUe/LewIUGHDBUmhLiwIMWEDCsG2CgR40OPHxVyFLkxQEAAOw==); +} + +.mce-visualblocks ol { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAPcAALu7u////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAADgAKAAAILQADCBxIcCCAgggFHlS4MEBDhwojQkS4sGJCixMpNgTAUWJCgg8/ZuS4kSSAgAA7); +} + +.mce-visualblocks br { + content:""; + padding-left:6px; + background: transparent bottom right no-repeat url(data:image/gif;base64,R0lGODlhBgAIAPcAAAAAALu7uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAGAAgAAAgYAP8FGDjwn0GDAQ4iVCiQYUKFDw9GPBgQADs=); +} + +.mce-visualblocks br:after { content:"\A"; white-space:pre; } diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualblocks/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualblocks/plugin.min.js new file mode 100755 index 0000000000..cafa418736 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualblocks/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("visualblocks",function(e,t){function n(){var t=this;t.active(r),e.on("VisualBlocks",function(){t.active(e.dom.hasClass(e.getBody(),"mce-visualblocks"))})}var i,a,r;window.NodeList&&(e.addCommand("mceVisualBlocks",function(){var n,o=e.dom;i||(i=o.uniqueId(),n=o.create("link",{id:i,rel:"stylesheet",href:t+"/css/visualblocks.css"}),e.getDoc().getElementsByTagName("head")[0].appendChild(n)),e.on("PreviewFormats AfterPreviewFormats",function(t){r&&o.toggleClass(e.getBody(),"mce-visualblocks","afterpreviewformats"==t.type)}),o.toggleClass(e.getBody(),"mce-visualblocks"),r=e.dom.hasClass(e.getBody(),"mce-visualblocks"),a&&a.active(o.hasClass(e.getBody(),"mce-visualblocks")),e.fire("VisualBlocks")}),e.addButton("visualblocks",{title:"Show blocks",cmd:"mceVisualBlocks",onPostRender:n}),e.addMenuItem("visualblocks",{text:"Show blocks",cmd:"mceVisualBlocks",onPostRender:n,selectable:!0,context:"view",prependToContext:!0}),e.on("init",function(){e.settings.visualblocks_default_state&&e.execCommand("mceVisualBlocks",!1,null,{skip_focus:!0})}),e.on("remove",function(){e.dom.removeClass(e.getBody(),"mce-visualblocks")}))}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualchars/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualchars/plugin.min.js new file mode 100755 index 0000000000..447423884e --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/visualchars/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("visualchars",function(e){function t(t){var n,a,r,o,l,s,c=e.getBody(),d=e.selection;if(i=!i,e.fire("VisualChars",{state:i}),t&&(s=d.getBookmark()),i)for(a=[],tinymce.walk(c,function(e){3==e.nodeType&&e.nodeValue&&-1!=e.nodeValue.indexOf(" ")&&a.push(e)},"childNodes"),r=0;r$1
    '),l=e.dom.create("div",null,o);n=l.lastChild;)e.dom.insertAfter(n,a[r]);e.dom.remove(a[r])}else for(a=e.dom.select("span.mce-nbsp",c),r=a.length-1;r>=0;r--)e.dom.remove(a[r],1);d.moveToBookmark(s)}function n(){var t=this;e.on("VisualChars",function(e){t.active(e.state)})}var i;e.addCommand("mceVisualChars",t),e.addButton("visualchars",{title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:n}),e.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:n,selectable:!0,context:"view",prependToContext:!0}),e.on("beforegetcontent",function(e){i&&"raw"!=e.format&&!e.draft&&(i=!0,t(!1))})}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/wordcount/plugin.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/wordcount/plugin.min.js new file mode 100755 index 0000000000..8c419801b2 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/plugins/wordcount/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wordcount",function(e){function t(){e.theme.panel.find("#wordcount").text(["Words: {0}",a.getCount()])}var n,i,a=this;n=e.getParam("wordcount_countregex",/[\w\u2019\x27\-]+/g),i=e.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\x27\x22_+=\\\/\-]*/g),e.on("init",function(){var n=e.theme.panel&&e.theme.panel.find("#statusbar")[0];n&&window.setTimeout(function(){n.insert({type:"label",name:"wordcount",text:["Words: {0}",a.getCount()],classes:"wordcount"},0),e.on("setcontent beforeaddundo",t),e.on("keyup",function(e){32==e.keyCode&&t()})},0)}),a.getCount=function(){var t=e.getContent({format:"raw"}),a=0;if(t){t=t.replace(/\.\.\./g," "),t=t.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," "),t=t.replace(/(\w+)(&.+?;)+(\w+)/,"$1$3").replace(/&.+?;/g," "),t=t.replace(i,"");var o=t.match(n);o&&(a=o.length)}return a}}); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/content.inline.min.css b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/content.inline.min.css new file mode 100755 index 0000000000..771b83e553 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/content.inline.min.css @@ -0,0 +1 @@ +.mce-object{border:1px dotted #3a3a3a;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px!important;height:9px!important;border:1px dotted #3a3a3a;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp{background:#AAA}hr{cursor:default}.mce-match-marker{background:green;color:#fff}.mce-spellchecker-word{background:url(img/wline.gif) repeat-x bottom left;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td.mce-item-selected,th.mce-item-selected{background-color:#39f!important}.mce-edit-focus{outline:1px dotted #333} \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/content.min.css b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/content.min.css new file mode 100755 index 0000000000..b9bbab143f --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/content.min.css @@ -0,0 +1 @@ +body{background-color:#fff;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px;scrollbar-3dlight-color:#f0f0ee;scrollbar-arrow-color:#676662;scrollbar-base-color:#f0f0ee;scrollbar-darkshadow-color:#ddd;scrollbar-face-color:#e0e0dd;scrollbar-highlight-color:#f0f0ee;scrollbar-shadow-color:#f0f0ee;scrollbar-track-color:#f5f5f5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px}.mce-object{border:1px dotted #3a3a3a;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px!important;height:9px!important;border:1px dotted #3a3a3a;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp{background:#AAA}hr{cursor:default}.mce-match-marker{background:green;color:#fff}.mce-spellchecker-word{background:url(img/wline.gif) repeat-x bottom left;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td.mce-item-selected,th.mce-item-selected{background-color:#39f!important}.mce-edit-focus{outline:1px dotted #333} \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.eot b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.eot new file mode 100755 index 0000000000..43a30f9925 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.eot differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.svg b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.svg new file mode 100755 index 0000000000..d338114f07 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.svg @@ -0,0 +1,175 @@ + + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.ttf b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.ttf new file mode 100755 index 0000000000..841c79c182 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.ttf differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.woff b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.woff new file mode 100755 index 0000000000..ad14a2406e Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon-small.woff differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.eot b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.eot new file mode 100755 index 0000000000..eed4f8149a Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.eot differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.svg b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.svg new file mode 100755 index 0000000000..727f61af13 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.svg @@ -0,0 +1,153 @@ + + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.ttf b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.ttf new file mode 100755 index 0000000000..dea6e458f9 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.ttf differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.woff b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.woff new file mode 100755 index 0000000000..f17657986c Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/icomoon.woff differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/readme.md b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/readme.md new file mode 100755 index 0000000000..fa5d63946c --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/fonts/readme.md @@ -0,0 +1 @@ +Icons are generated and provided by the http://icomoon.io service. diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/anchor.gif b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/anchor.gif new file mode 100755 index 0000000000..606348c7f5 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/anchor.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/loader.gif b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/loader.gif new file mode 100755 index 0000000000..c69e937232 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/loader.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/object.gif b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/object.gif new file mode 100755 index 0000000000..cccd7f023f Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/object.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/trans.gif b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/trans.gif new file mode 100755 index 0000000000..388486517f Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/trans.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/wline.gif b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/wline.gif new file mode 100755 index 0000000000..7d0a4dbca0 Binary files /dev/null and b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/img/wline.gif differ diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/skin.ie7.min.css b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/skin.ie7.min.css new file mode 100755 index 0000000000..0d29f3a81f --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/skin.ie7.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#000;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-container ::-webkit-scrollbar{width:8px;height:8px;-webkit-border-radius:4px}.mce-container ::-webkit-scrollbar-track,.mce-container ::-webkit-scrollbar-track-piece{background-color:transparent}.mce-container ::-webkit-scrollbar-thumb{background-color:rgba(53,57,71,0.3);width:6px;height:6px;-webkit-border-radius:4px}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:visible!important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;border-radius:2px}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #c5c5c5;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td div{border:1px solid #808080;width:12px;height:12px;margin:2px;cursor:pointer}.mce-grid td div:hover{border-color:black}.mce-grid td div:focus{border-color:#59a5e1;outline:1px solid rgba(82,168,236,0.8);border-color:rgba(82,168,236,0.8)}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover{border-color:#c5c5c5}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#e8e8e8;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#c4daff;background:#deeafa}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:#ccc 5px 5px 5px;-moz-box-shadow:#ccc 5px 5px 5px;box-shadow:#ccc 5px 5px 5px}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{top:0;left:0;background:#fff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#FFF;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #EEE;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#CCC;text-shadow:0 1px 0 white;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#AAA}.mce-window-head .mce-title{display:inline-block;*display:inline;*zoom:1;line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:whiteSmoke;border-top:1px solid #DDD;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000;box-shadow:0 0 5px #000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:0;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #c5c5c5;position:relative;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-btn:hover,.mce-btn:focus{text-decoration:none;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn.mce-disabled,.mce-btn.mce-disabled:hover{cursor:default;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{min-width:50px;color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);zoom:1;border-color:#04c #04c #002b80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary:hover,.mce-primary:focus{color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#005fb3;background-image:-moz-linear-gradient(top,#0077b3,#003cb3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#0077b3),to(#003cb3));background-image:-webkit-linear-gradient(top,#0077b3,#003cb3);background-image:-o-linear-gradient(top,#0077b3,#003cb3);background-image:linear-gradient(to bottom,#0077b3,#003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3',endColorstr='#ff003cb3',GradientType=0);zoom:1;border-color:#003cb3 #003cb3 #026;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary button{color:#fff}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:3px 5px;font-size:12px;line-height:15px}.mce-btn-small i{margin-top:0}.mce-btn .mce-caret{margin-top:8px;*margin-top:6px;margin-left:0}.mce-btn-small .mce-caret{margin-top:6px;*margin-top:4px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #444;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#999}.mce-caret.mce-up{border-bottom:4px solid #444;border-top:0}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-btn:hover,.mce-btn-group .mce-btn:focus{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-disabled,.mce-btn-group .mce-btn.mce-disabled:hover{-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-active,.mce-btn-group .mce-btn.mce-active:hover,.mce-btn-group .mce-btn:active{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn-group .mce-btn.mce-disabled button{opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn-group .mce-first{border-left:1px solid #c5c5c5;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #c5c5c5;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0}.mce-checked i.mce-i-checkbox{color:#000;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox{border:1px solid #59a5e1;border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-colorbutton .mce-ico{position:relative}.mce-colorpicker{background:#FFF}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-14px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-17px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;border-left:1px solid transparent;border-right:1px solid transparent}.mce-colorbutton:hover .mce-open{border-left-color:#c5c5c5;border-right-color:#c5c5c5}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;width:100px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:rgba(0,0,0,0.15);height:28px}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox *:focus{border-color:#59a5e1;border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#000}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:gray;color:white}.mce-path .mce-divider{display:inline}.mce-fieldset{border:0 solid #9e9e9e;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-iframe{border:0 solid #c5c5c5;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);border:0 solid #c5c5c5;overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label-disabled .mce-text{color:#999}.mce-label.mce-multiline{white-space:pre-wrap}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #ddd}.mce-menubar .mce-menubtn button{color:#000}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:transparent;background:#ddd;filter:none}.mce-menubtn.mce-disabled span{color:#999}.mce-menubtn span{margin-right:2px;line-height:20px;*line-height:16px}.mce-menubtn.mce-btn-small span{font-size:12px;line-height:15px;*line-height:16px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item.mce-disabled .mce-text{color:#999}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0);zoom:1}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:white}.mce-menu-item.mce-disabled:hover{background:#CCC}.mce-menu-shortcut{display:inline-block;color:#999}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #666}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret{border-left-color:#FFF}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item-sep,.mce-menu-item-sep:hover{padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#e5e5e5;border-bottom:1px solid white;cursor:default;filter:none}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item.mce-active{background-color:#c8def4;outline:1px solid #c5c5c5}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa;background-color:transparent;outline:0}.mce-menu-item-checkbox.mce-active{background-color:#FFF;outline:0}.mce-menu{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#FFF;border:1px solid #CCC;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline;*zoom:1}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}i.mce-radio{padding:1px;margin:0 3px 0 0;background-color:#fafafa;border:1px solid #cacece;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}i.mce-radio:after{font-family:Arial;font-size:12px;color:#000;content:'\25cf'}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#000}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#c5c5c5;border-right-color:#c5c5c5}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #ccc}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #ccc;border-width:1px 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-textbox{background:#FFF;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);display:inline-block;-webkit-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#000}.mce-textbox:focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}@font-face{font-family:'icomoon';src:url('fonts/icomoon.eot');src:url('fonts/icomoon.eot?#iefix') format('embedded-opentype'),url('fonts/icomoon.svg#icomoon') format('svg'),url('fonts/icomoon.woff') format('woff'),url('fonts/icomoon.ttf') format('truetype');font-weight:normal;font-style:normal}@font-face{font-family:'icomoon-small';src:url('fonts/icomoon-small.eot');src:url('fonts/icomoon-small.eot?#iefix') format('embedded-opentype'),url('fonts/icomoon-small.svg#icomoon') format('svg'),url('fonts/icomoon-small.woff') format('woff'),url('fonts/icomoon-small.ttf') format('truetype');font-weight:normal;font-style:normal}.mce-ico{font-family:'icomoon';font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333;-ie7-icon:' '}.mce-btn-small .mce-ico{font-family:'icomoon-small'}.mce-ico,i.mce-i-checkbox{zoom:expression(this.runtimeStyle['zoom'] = '1',this.innerHTML = this.currentStyle['-ie7-icon'].substr(1,1)+' ')}.mce-i-save{-ie7-icon:"\e000"}.mce-i-newdocument{-ie7-icon:"\e001"}.mce-i-fullpage{-ie7-icon:"\e002"}.mce-i-alignleft{-ie7-icon:"\e003"}.mce-i-aligncenter{-ie7-icon:"\e004"}.mce-i-alignright{-ie7-icon:"\e005"}.mce-i-alignjustify{-ie7-icon:"\e006"}.mce-i-cut{-ie7-icon:"\e007"}.mce-i-paste{-ie7-icon:"\e008"}.mce-i-searchreplace{-ie7-icon:"\e009"}.mce-i-bullist{-ie7-icon:"\e00a"}.mce-i-numlist{-ie7-icon:"\e00b"}.mce-i-indent{-ie7-icon:"\e00c"}.mce-i-outdent{-ie7-icon:"\e00d"}.mce-i-blockquote{-ie7-icon:"\e00e"}.mce-i-undo{-ie7-icon:"\e00f"}.mce-i-redo{-ie7-icon:"\e010"}.mce-i-link{-ie7-icon:"\e011"}.mce-i-unlink{-ie7-icon:"\e012"}.mce-i-anchor{-ie7-icon:"\e013"}.mce-i-image{-ie7-icon:"\e014"}.mce-i-media{-ie7-icon:"\e015"}.mce-i-help{-ie7-icon:"\e016"}.mce-i-code{-ie7-icon:"\e017"}.mce-i-inserttime{-ie7-icon:"\e018"}.mce-i-preview{-ie7-icon:"\e019"}.mce-i-forecolor{-ie7-icon:"\e01a"}.mce-i-backcolor{-ie7-icon:"\e01a"}.mce-i-table{-ie7-icon:"\e01b"}.mce-i-hr{-ie7-icon:"\e01c"}.mce-i-removeformat{-ie7-icon:"\e01d"}.mce-i-subscript{-ie7-icon:"\e01e"}.mce-i-superscript{-ie7-icon:"\e01f"}.mce-i-charmap{-ie7-icon:"\e020"}.mce-i-emoticons{-ie7-icon:"\e021"}.mce-i-print{-ie7-icon:"\e022"}.mce-i-fullscreen{-ie7-icon:"\e023"}.mce-i-spellchecker{-ie7-icon:"\e024"}.mce-i-nonbreaking{-ie7-icon:"\e025"}.mce-i-template{-ie7-icon:"\e026"}.mce-i-pagebreak{-ie7-icon:"\e027"}.mce-i-restoredraft{-ie7-icon:"\e028"}.mce-i-untitled{-ie7-icon:"\e029"}.mce-i-bold{-ie7-icon:"\e02a"}.mce-i-italic{-ie7-icon:"\e02b"}.mce-i-underline{-ie7-icon:"\e02c"}.mce-i-strikethrough{-ie7-icon:"\e02d"}.mce-i-visualchars{-ie7-icon:"\e02e"}.mce-i-ltr{-ie7-icon:"\e02f"}.mce-i-rtl{-ie7-icon:"\e030"}.mce-i-copy{-ie7-icon:"\e031"}.mce-i-resize{-ie7-icon:"\e032"}.mce-i-browse{-ie7-icon:"\e034"}.mce-i-checkbox,.mce-i-selected{-ie7-icon:"\e033"}.mce-i-selected{visibility:hidden}.mce-i-backcolor{background:#BBB} \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/skin.min.css b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/skin.min.css new file mode 100755 index 0000000000..23a89fdfc1 --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/skins/lightgray/skin.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#000;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-container ::-webkit-scrollbar{width:8px;height:8px;-webkit-border-radius:4px}.mce-container ::-webkit-scrollbar-track,.mce-container ::-webkit-scrollbar-track-piece{background-color:transparent}.mce-container ::-webkit-scrollbar-thumb{background-color:rgba(53,57,71,0.3);width:6px;height:6px;-webkit-border-radius:4px}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:visible!important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;border-radius:2px}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #c5c5c5;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td div{border:1px solid #808080;width:12px;height:12px;margin:2px;cursor:pointer}.mce-grid td div:hover{border-color:black}.mce-grid td div:focus{border-color:#59a5e1;outline:1px solid rgba(82,168,236,0.8);border-color:rgba(82,168,236,0.8)}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover{border-color:#c5c5c5}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#e8e8e8;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#c4daff;background:#deeafa}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:#ccc 5px 5px 5px;-moz-box-shadow:#ccc 5px 5px 5px;box-shadow:#ccc 5px 5px 5px}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{top:0;left:0;background:#fff;-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#FFF;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#FFF;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #EEE;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#CCC;text-shadow:0 1px 0 white;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#AAA}.mce-window-head .mce-title{display:inline-block;*display:inline;*zoom:1;line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:whiteSmoke;border-top:1px solid #DDD;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000;box-shadow:0 0 5px #000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:0;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:0;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #c5c5c5;position:relative;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-btn:hover,.mce-btn:focus{text-decoration:none;color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn.mce-disabled,.mce-btn.mce-disabled:hover{cursor:default;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{min-width:50px;color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);zoom:1;border-color:#04c #04c #002b80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary:hover,.mce-primary:focus{color:#fff;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#005fb3;background-image:-moz-linear-gradient(top,#0077b3,#003cb3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#0077b3),to(#003cb3));background-image:-webkit-linear-gradient(top,#0077b3,#003cb3);background-image:-o-linear-gradient(top,#0077b3,#003cb3);background-image:linear-gradient(to bottom,#0077b3,#003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3',endColorstr='#ff003cb3',GradientType=0);zoom:1;border-color:#003cb3 #003cb3 #026;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-primary button{color:#fff}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:3px 5px;font-size:12px;line-height:15px}.mce-btn-small i{margin-top:0}.mce-btn .mce-caret{margin-top:8px;*margin-top:6px;margin-left:0}.mce-btn-small .mce-caret{margin-top:6px;*margin-top:4px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #444;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#999}.mce-caret.mce-up{border-bottom:4px solid #444;border-top:0}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-btn:hover,.mce-btn-group .mce-btn:focus{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffcccccc',GradientType=0);zoom:1;border-color:#ccc #ccc #a6a6a6;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-disabled,.mce-btn-group .mce-btn.mce-disabled:hover{-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffd9d9d9',GradientType=0);zoom:1;border-color:#d9d9d9 #d9d9d9 #b3b3b3;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.mce-btn-group .mce-btn.mce-active,.mce-btn-group .mce-btn.mce-active:hover,.mce-btn-group .mce-btn:active{color:#333;text-shadow:0 1px 1px rgba(255,255,255,0.75);background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(#bfbfbf));background-image:-webkit-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:-o-linear-gradient(top,#e6e6e6,#bfbfbf);background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-btn-group .mce-btn.mce-disabled button{opacity:.65;filter:alpha(opacity=65);zoom:1}.mce-btn-group .mce-first{border-left:1px solid #c5c5c5;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #c5c5c5;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0}.mce-checked i.mce-i-checkbox{color:#000;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox{border:1px solid #59a5e1;border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-colorbutton .mce-ico{position:relative}.mce-colorpicker{background:#FFF}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-14px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-17px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;border-left:1px solid transparent;border-right:1px solid transparent}.mce-colorbutton:hover .mce-open{border-left-color:#c5c5c5;border-right-color:#c5c5c5}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;width:100px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:rgba(0,0,0,0.15);height:28px}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox *:focus{border-color:#59a5e1;border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#000}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:gray;color:white}.mce-path .mce-divider{display:inline}.mce-fieldset{border:0 solid #9e9e9e;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-iframe{border:0 solid #c5c5c5;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);border:0 solid #c5c5c5;overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label-disabled .mce-text{color:#999}.mce-label.mce-multiline{white-space:pre-wrap}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #ddd}.mce-menubar .mce-menubtn button{color:#000}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:transparent;background:#ddd;filter:none}.mce-menubtn.mce-disabled span{color:#999}.mce-menubtn span{margin-right:2px;line-height:20px;*line-height:16px}.mce-menubtn.mce-btn-small span{font-size:12px;line-height:15px;*line-height:16px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item.mce-disabled .mce-text{color:#999}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0);zoom:1}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:white}.mce-menu-item.mce-disabled:hover{background:#CCC}.mce-menu-shortcut{display:inline-block;color:#999}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #666}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret{border-left-color:#FFF}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item-sep,.mce-menu-item-sep:hover{padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#e5e5e5;border-bottom:1px solid white;cursor:default;filter:none}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item.mce-active{background-color:#c8def4;outline:1px solid #c5c5c5}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa;background-color:transparent;outline:0}.mce-menu-item-checkbox.mce-active{background-color:#FFF;outline:0}.mce-menu{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#FFF;border:1px solid #CCC;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline;*zoom:1}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}i.mce-radio{padding:1px;margin:0 3px 0 0;background-color:#fafafa;border:1px solid #cacece;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd',endColorstr='#ffdddddd',GradientType=0);zoom:1}i.mce-radio:after{font-family:Arial;font-size:12px;color:#000;content:'\25cf'}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#000}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#c5c5c5;border-right-color:#c5c5c5}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #ccc}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #ccc;border-width:1px 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-textbox{background:#FFF;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);display:inline-block;-webkit-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#000}.mce-textbox:focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}@font-face{font-family:'tinymce';src:url('fonts/icomoon.eot');src:url('fonts/icomoon.eot?#iefix') format('embedded-opentype'),url('fonts/icomoon.svg#icomoon') format('svg'),url('fonts/icomoon.woff') format('woff'),url('fonts/icomoon.ttf') format('truetype');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/icomoon-small.eot');src:url('fonts/icomoon-small.eot?#iefix') format('embedded-opentype'),url('fonts/icomoon-small.svg#icomoon') format('svg'),url('fonts/icomoon-small.woff') format('woff'),url('fonts/icomoon-small.ttf') format('truetype');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333}.mce-btn-small .mce-ico{font-family:'tinymce-small',Arial}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-inserttime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-untitled:before{content:"\e029"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e02e"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-pastetext:before{content:"\e035"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#BBB} \ No newline at end of file diff --git a/gui/baculum/framework/Web/Javascripts/source/tinymce-405/themes/modern/theme.min.js b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/themes/modern/theme.min.js new file mode 100755 index 0000000000..4a6368710a --- /dev/null +++ b/gui/baculum/framework/Web/Javascripts/source/tinymce-405/themes/modern/theme.min.js @@ -0,0 +1 @@ +tinymce.ThemeManager.add("modern",function(e){function t(){function t(t){var i,r=[];if(t)return m(t.split(/[ ,]/),function(t){function n(){var n=e.selection;"bullist"==a&&n.selectorChanged("ul > li",function(e,n){for(var i,r=n.parents.length;r--&&(i=n.parents[r].nodeName,"OL"!=i&&"UL"!=i););t.active("UL"==i)}),"numlist"==a&&n.selectorChanged("ol > li",function(e,n){for(var i,r=n.parents.length;r--&&(i=n.parents[r].nodeName,"OL"!=i&&"UL"!=i););t.active("OL"==i)}),t.settings.stateSelector&&n.selectorChanged(t.settings.stateSelector,function(e){t.active(e)},!0),t.settings.disabledStateSelector&&n.selectorChanged(t.settings.disabledStateSelector,function(e){t.disabled(e)})}var a;"|"==t?i=null:u.has(t)?(t={type:t},c.toolbar_items_size&&(t.size=c.toolbar_items_size),r.push(t),i=null):(i||(i={type:"buttongroup",items:[]},r.push(i)),e.buttons[t]&&(a=t,t=e.buttons[a],"function"==typeof t&&(t=t()),t.type=t.type||"button",c.toolbar_items_size&&(t.size=c.toolbar_items_size),t=u.create(t),i.items.push(t),e.initialized?n():e.on("init",n)))}),n.push({type:"toolbar",layout:"flow",items:r}),!0}for(var n=[],i=1;10>i&&t(c["toolbar"+i]);i++);return n.length||t(c.toolbar||h),n}function n(){function t(t){var n;return"|"==t?{text:"|"}:n=e.menuItems[t]}function n(n){var i,r,a,o,s;if(s=tinymce.makeMap((c.removed_menuitems||"").split(/[ ,]/)),c.menu?(r=c.menu[n],o=!0):r=f[n],r){i={text:r.title},a=[],m((r.items||"").split(/[ ,]/),function(e){var n=t(e);n&&!s[e]&&a.push(t(e))}),o||m(e.menuItems,function(e){e.context==n&&("before"==e.separator&&a.push({text:"|"}),e.prependToContext?a.unshift(e):a.push(e),"after"==e.separator&&a.push({text:"|"}))});for(var l=0;lr;r++)if(o=n[r],o&&o.func.call(o.scope,e)===!1&&e.preventDefault(),e.isImmediatePropagationStopped())return}var a=this,s={},l,c,u,d,f;c=o+(+new Date).toString(32),d="onmouseenter"in document.documentElement,u="onfocusin"in document.documentElement,f={mouseenter:"mouseover",mouseleave:"mouseout"},l=1,a.domLoaded=!1,a.events=s,a.bind=function(t,o,p,h){function m(e){i(n(e||_.event),g)}var g,v,y,b,C,x,w,_=window;if(t&&3!==t.nodeType&&8!==t.nodeType){for(t[c]?g=t[c]:(g=l++,t[c]=g,s[g]={}),h=h||t,o=o.split(" "),y=o.length;y--;)b=o[y],x=m,C=w=!1,"DOMContentLoaded"===b&&(b="ready"),a.domLoaded&&"ready"===b&&"complete"==t.readyState?p.call(h,n({type:b})):(d||(C=f[b],C&&(x=function(e){var t,r;if(t=e.currentTarget,r=e.relatedTarget,r&&t.contains)r=t.contains(r);else for(;r&&r!==t;)r=r.parentNode;r||(e=n(e||_.event),e.type="mouseout"===e.type?"mouseleave":"mouseenter",e.target=t,i(e,g))})),u||"focusin"!==b&&"focusout"!==b||(w=!0,C="focusin"===b?"focus":"blur",x=function(e){e=n(e||_.event),e.type="focus"===e.type?"focusin":"focusout",i(e,g)}),v=s[g][b],v?"ready"===b&&a.domLoaded?p({type:b}):v.push({func:p,scope:h}):(s[g][b]=v=[{func:p,scope:h}],v.fakeName=C,v.capture=w,v.nativeHandler=x,"ready"===b?r(t,x,a):e(t,C||b,x,w)));return t=v=0,p}},a.unbind=function(e,n,r){var i,o,l,u,d,f;if(!e||3===e.nodeType||8===e.nodeType)return a;if(i=e[c]){if(f=s[i],n){for(n=n.split(" "),l=n.length;l--;)if(d=n[l],o=f[d]){if(r)for(u=o.length;u--;)o[u].func===r&&o.splice(u,1);r&&0!==o.length||(delete f[d],t(e,o.fakeName||d,o.nativeHandler,o.capture))}}else{for(d in f)o=f[d],t(e,o.fakeName||d,o.nativeHandler,o.capture);f={}}for(d in f)return a;delete s[i];try{delete e[c]}catch(p){e[c]=null}}return a},a.fire=function(e,t,r){var o;if(!e||3===e.nodeType||8===e.nodeType)return a;r=n(null,r),r.type=t,r.target=e;do o=e[c],o&&i(r,o),e=e.parentNode||e.ownerDocument||e.defaultView||e.parentWindow;while(e&&!r.isPropagationStopped());return a},a.clean=function(e){var t,n,r=a.unbind;if(!e||3===e.nodeType||8===e.nodeType)return a;if(e[c]&&r(e),e.getElementsByTagName||(e=e.document),e&&e.getElementsByTagName)for(r(e),n=e.getElementsByTagName("*"),t=n.length;t--;)e=n[t],e[c]&&r(e);return a},a.destroy=function(){s={}},a.cancel=function(e){return e&&(e.preventDefault(),e.stopImmediatePropagation()),!1}}var o="mce-data-",a=/^(?:mouse|contextmenu)|click/,s={keyLocation:1,layerX:1,layerY:1};return i.Event=new i,i.Event.bind(window,"ready",function(){}),i}),r(c,[],function(){function e(e){return gt.test(e+"")}function n(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>N.cacheLength&&delete e[t.shift()],e[n]=r,r}}function i(e){return e[F]=!0,e}function o(e){var t=D.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t=null}}function a(e,t,n,r){var i,o,a,s,l,c,u,p,h,m;if((t?t.ownerDocument||t:W)!==D&&B(t),t=t||D,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(M&&!r){if(i=vt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&I(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return et.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&z.getElementsByClassName&&t.getElementsByClassName)return et.apply(n,t.getElementsByClassName(a)),n}if(z.qsa&&!H.test(e)){if(u=!0,p=F,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(c=d(e),(u=t.getAttribute("id"))?p=u.replace(Ct,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=c.length;l--;)c[l]=p+f(c[l]);h=mt.test(e)&&t.parentNode||t,m=c.join(",")}if(m)try{return et.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{u||t.removeAttribute("id")}}}return C(e.replace(ct,"$1"),t,n,r)}function s(e,t){var n=t&&e,r=n&&(~t.sourceIndex||X)-(~e.sourceIndex||X);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function l(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function c(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return i(function(t){return t=+t,i(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function d(e,t){var n,r,i,o,s,l,c,u=j[e+" "];if(u)return t?0:u.slice(0);for(s=e,l=[],c=N.preFilter;s;){(!n||(r=ut.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=dt.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(ct," ")}),s=s.slice(n.length));for(o in N.filter)!(r=ht[o].exec(s))||c[o]&&!(r=c[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?a.error(e):j(e,l).slice(0)}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=U++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,c,u=V+" "+o;if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i)if(c=t[F]||(t[F]={}),(l=c[r])&&l[0]===u){if((s=l[1])===!0||s===_)return s===!0}else if(l=c[r]=[u],l[1]=e(t,n,a)||_,l[1]===!0)return!0}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,c=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),c&&t.push(s));return a}function g(e,t,n,r,o,a){return r&&!r[F]&&(r=g(r)),o&&!o[F]&&(o=g(o,a)),i(function(i,a,s,l){var c,u,d,f=[],p=[],h=a.length,g=i||b(t||"*",s.nodeType?[s]:s,[]),v=!e||!i&&t?g:m(g,f,e,s,l),y=n?o||(i?e:h||r)?[]:a:v;if(n&&n(v,y,s,l),r)for(c=m(y,p),r(c,[],s,l),u=c.length;u--;)(d=c[u])&&(y[p[u]]=!(v[p[u]]=d));if(i){if(o||e){if(o){for(c=[],u=y.length;u--;)(d=y[u])&&c.push(v[u]=d);o(null,y=[],c,l)}for(u=y.length;u--;)(d=y[u])&&(c=o?nt.call(i,d):f[u])>-1&&(i[c]=!(a[c]=d))}}else y=m(y===a?y.splice(h,y.length):y),o?o(null,a,y,l):et.apply(a,y)})}function v(e){for(var t,n,r,i=e.length,o=N.relative[e[0].type],a=o||N.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),c=p(function(e){return nt.call(t,e)>-1},a,!0),u=[function(e,n,r){return!o&&(r||n!==T)||((t=n).nodeType?l(e,n,r):c(e,n,r))}];i>s;s++)if(n=N.relative[e[s].type])u=[p(h(u),n)];else{if(n=N.filter[e[s].type].apply(null,e[s].matches),n[F]){for(r=++s;i>r&&!N.relative[e[r].type];r++);return g(s>1&&h(u),s>1&&f(e.slice(0,s-1)).replace(ct,"$1"),n,r>s&&v(e.slice(s,r)),i>r&&v(e=e.slice(r)),i>r&&f(e))}u.push(n)}return h(u)}function y(e,t){var n=0,r=t.length>0,o=e.length>0,s=function(i,s,l,c,u){var d,f,p,h=[],g=0,v="0",y=i&&[],b=null!=u,C=T,x=i||o&&N.find.TAG("*",u&&s.parentNode||s),w=V+=null==C?1:Math.random()||.1;for(b&&(T=s!==D&&s,_=n);null!=(d=x[v]);v++){if(o&&d){for(f=0;p=e[f++];)if(p(d,s,l)){c.push(d);break}b&&(V=w,_=++n)}r&&((d=!p&&d)&&g--,i&&y.push(d))}if(g+=v,r&&v!==g){for(f=0;p=t[f++];)p(y,h,s,l);if(i){if(g>0)for(;v--;)y[v]||h[v]||(h[v]=Q.call(c));h=m(h)}et.apply(c,h),b&&!i&&h.length>0&&g+t.length>1&&a.uniqueSort(c)}return b&&(V=w,T=C),y};return r?i(s):s}function b(e,t,n){for(var r=0,i=t.length;i>r;r++)a(e,t[r],n);return n}function C(e,t,n,r){var i,o,a,s,l,c=d(e);if(!r&&1===c.length){if(o=c[0]=c[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&9===t.nodeType&&M&&N.relative[o[1].type]){if(t=(N.find.ID(a.matches[0].replace(wt,_t),t)||[])[0],!t)return n;e=e.slice(o.shift().value.length)}for(i=ht.needsContext.test(e)?0:o.length;i--&&(a=o[i],!N.relative[s=a.type]);)if((l=N.find[s])&&(r=l(a.matches[0].replace(wt,_t),mt.test(o[0].type)&&t.parentNode||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return et.apply(n,r),n;break}}return S(e,c)(r,t,!M,n,mt.test(e)),n}function x(){}var w,_,N,E,k,S,T,R,A,B,D,L,M,H,P,O,I,F="sizzle"+-new Date,W=window.document,z={},V=0,U=0,q=n(),j=n(),$=n(),K=!1,G=function(){return 0},Y=typeof t,X=1<<31,J=[],Q=J.pop,Z=J.push,et=J.push,tt=J.slice,nt=J.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},rt="[\\x20\\t\\r\\n\\f]",it="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ot=it.replace("w","w#"),at="([*^$|!~]?=)",st="\\["+rt+"*("+it+")"+rt+"*(?:"+at+rt+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+ot+")|)|)"+rt+"*\\]",lt=":("+it+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+st.replace(3,8)+")*)|.*)\\)|)",ct=new RegExp("^"+rt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+rt+"+$","g"),ut=new RegExp("^"+rt+"*,"+rt+"*"),dt=new RegExp("^"+rt+"*([\\x20\\t\\r\\n\\f>+~])"+rt+"*"),ft=new RegExp(lt),pt=new RegExp("^"+ot+"$"),ht={ID:new RegExp("^#("+it+")"),CLASS:new RegExp("^\\.("+it+")"),NAME:new RegExp("^\\[name=['\"]?("+it+")['\"]?\\]"),TAG:new RegExp("^("+it.replace("w","w*")+")"),ATTR:new RegExp("^"+st),PSEUDO:new RegExp("^"+lt),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+rt+"*(even|odd|(([+-]|)(\\d*)n|)"+rt+"*(?:([+-]|)"+rt+"*(\\d+)|))"+rt+"*\\)|)","i"),needsContext:new RegExp("^"+rt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+rt+"*((?:-\\d)?\\d*)"+rt+"*\\)|)(?=[^-]|$)","i")},mt=/[\x20\t\r\n\f]*[+~]/,gt=/^[^{]+\{\s*\[native code/,vt=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,yt=/^(?:input|select|textarea|button)$/i,bt=/^h\d$/i,Ct=/'|\\/g,xt=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,wt=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,_t=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{et.apply(J=tt.call(W.childNodes),W.childNodes),J[W.childNodes.length].nodeType}catch(Nt){et={apply:J.length?function(e,t){Z.apply(e,tt.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}k=a.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},B=a.setDocument=function(n){var r=n?n.ownerDocument||n:W;return r!==D&&9===r.nodeType&&r.documentElement?(D=r,L=r.documentElement,M=!k(r),z.getElementsByTagName=o(function(e){return e.appendChild(r.createComment("")),!e.getElementsByTagName("*").length}),z.attributes=o(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),z.getElementsByClassName=o(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),z.getByName=o(function(e){e.id=F+0,e.appendChild(D.createElement("a")).setAttribute("name",F),e.appendChild(D.createElement("i")).setAttribute("name",F),L.appendChild(e);var t=r.getElementsByName&&r.getElementsByName(F).length===2+r.getElementsByName(F+0).length;return L.removeChild(e),t}),z.sortDetached=o(function(e){return e.compareDocumentPosition&&1&e.compareDocumentPosition(D.createElement("div"))}),N.attrHandle=o(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==Y&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},z.getByName?(N.find.ID=function(e,t){if(typeof t.getElementById!==Y&&M){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},N.filter.ID=function(e){var t=e.replace(wt,_t);return function(e){return e.getAttribute("id")===t}}):(N.find.ID=function(e,n){if(typeof n.getElementById!==Y&&M){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==Y&&r.getAttributeNode("id").value===e?[r]:t:[]}},N.filter.ID=function(e){var t=e.replace(wt,_t);return function(e){var n=typeof e.getAttributeNode!==Y&&e.getAttributeNode("id");return n&&n.value===t}}),N.find.TAG=z.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==Y?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},N.find.NAME=z.getByName&&function(e,t){return typeof t.getElementsByName!==Y?t.getElementsByName(name):void 0},N.find.CLASS=z.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==Y&&M?t.getElementsByClassName(e):void 0},P=[],H=[":focus"],(z.qsa=e(r.querySelectorAll))&&(o(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||H.push("\\["+rt+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||H.push(":checked")}),o(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&H.push("[*^$]="+rt+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||H.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),H.push(",.*:")})),(z.matchesSelector=e(O=L.matchesSelector||L.mozMatchesSelector||L.webkitMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&o(function(e){z.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),P.push("!=",lt)}),H=new RegExp(H.join("|")),P=P.length&&new RegExp(P.join("|")),I=e(L.contains)||L.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},G=L.compareDocumentPosition?function(e,t){if(e===t)return K=!0,0;var n=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return n?1&n||R&&t.compareDocumentPosition(e)===n?e===r||I(W,e)?-1:t===r||I(W,t)?1:A?nt.call(A,e)-nt.call(A,t):0:4&n?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var n,i=0,o=e.parentNode,a=t.parentNode,l=[e],c=[t];if(e===t)return K=!0,0;if(!o||!a)return e===r?-1:t===r?1:o?-1:a?1:0;if(o===a)return s(e,t);for(n=e;n=n.parentNode;)l.unshift(n);for(n=t;n=n.parentNode;)c.unshift(n);for(;l[i]===c[i];)i++;return i?s(l[i],c[i]):l[i]===W?-1:c[i]===W?1:0},D):D},a.matches=function(e,t){return a(e,null,null,t)},a.matchesSelector=function(e,t){if((e.ownerDocument||e)!==D&&B(e),t=t.replace(xt,"='$1']"),z.matchesSelector&&M&&(!P||!P.test(t))&&!H.test(t))try{var n=O.call(e,t);if(n||z.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return a(t,D,null,[e]).length>0},a.contains=function(e,t){return(e.ownerDocument||e)!==D&&B(e),I(e,t)},a.attr=function(e,t){var n;return(e.ownerDocument||e)!==D&&B(e),M&&(t=t.toLowerCase()),(n=N.attrHandle[t])?n(e):!M||z.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},a.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},a.uniqueSort=function(e){var t,n=[],r=0,i=0;if(K=!z.detectDuplicates,R=!z.sortDetached,A=!z.sortStable&&e.slice(0),e.sort(G),K){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return e},E=a.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=E(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=E(t);return n},N=a.selectors={cacheLength:50,createPseudo:i,match:ht,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(wt,_t),e[3]=(e[4]||e[5]||"").replace(wt,_t),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||a.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&a.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return ht.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&ft.test(n)&&(t=d(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(wt,_t).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=q[e+" "];return t||(t=new RegExp("(^|"+rt+")"+e+"("+rt+"|$)"))&&q(e,function(e){return t.test(e.className||typeof e.getAttribute!==Y&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=a.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var c,u,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(u=g[F]||(g[F]={}),c=u[e]||[],p=c[0]===V&&c[1],f=c[0]===V&&c[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){u[e]=[V,p,f];break}}else if(y&&(c=(t[F]||(t[F]={}))[e])&&c[0]===V)f=c[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[F]||(d[F]={}))[e]=[V,f]),d!==t)););return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=N.pseudos[e]||N.setFilters[e.toLowerCase()]||a.error("unsupported pseudo: "+e);return r[F]?r(t):r.length>1?(n=[e,e,"",t],N.setFilters.hasOwnProperty(e.toLowerCase())?i(function(e,n){for(var i,o=r(e,t),a=o.length;a--;)i=nt.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:i(function(e){var t=[],n=[],r=S(e.replace(ct,"$1"));return r[F]?i(function(e,t,n,i){for(var o,a=r(e,null,i,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:i(function(e){return function(t){return a(e,t).length>0}}),contains:i(function(e){return function(t){return(t.textContent||t.innerText||E(t)).indexOf(e)>-1}}),lang:i(function(e){return pt.test(e||"")||a.error("unsupported lang: "+e),e=e.replace(wt,_t).toLowerCase(),function(t){var n;do if(n=M?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(e){var t=window.location&&window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===L},focus:function(e){return e===D.activeElement&&(!D.hasFocus||D.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!N.pseudos.empty(e)},header:function(e){return bt.test(e.nodeName)},input:function(e){return yt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[0>n?n+t:n]}),even:u(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=0>n?n+t:n;++rn;n++)t[n]=e[n];return t}function f(e,t){var n;if(t.indexOf)return t.indexOf(e);for(n=t.length;n--;)if(t[n]===e)return n;return-1}function p(e,t){var n,r,i,o,a;if(e)if(n=e.length,n===o){for(r in e)if(e.hasOwnProperty(r)&&(a=e[r],t.call(a,a,r)===!1))break}else for(i=0;n>i&&(a=e[i],t.call(a,a,r)!==!1);i++);return e}function h(e,n,r){for(var i=[],o=e[n];o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!c(o).is(r));)1===o.nodeType&&i.push(o),o=o[n];return i}function m(e,t,n,r){for(var i=[];e;e=e[n])r&&e.nodeType!==r||e===t||i.push(e);return i}var g=document,v=Array.prototype.push,y=Array.prototype.slice,b=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,C=e.Event,x=l("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"),w=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},_=/^\s*|\s*$/g,N=function(e){return null===e||e===t?"":(""+e).replace(_,"")};return c.fn=c.prototype={constructor:c,selector:"",length:0,init:function(e,t){var n=this,r,a;if(!e)return n;if(e.nodeType)return n.context=n[0]=e,n.length=1,n;if(i(e)){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:b.exec(e),!r)return c(t||document).find(e);if(r[1])for(a=o(e).firstChild;a;)this.add(a),a=a.nextSibling;else{if(a=g.getElementById(r[2]),a.id!==r[2])return n.find(e);n.length=1,n[0]=a}}else this.add(e);return n},toArray:function(){return d(this)},add:function(e){var t=this;return w(e)?v.apply(t,e):e instanceof c?t.add(e.toArray()):v.call(t,e),t},attr:function(e,n){var i=this;if("object"==typeof e)p(e,function(e,t){i.attr(t,e)});else{if(!r(n))return i[0]&&1===i[0].nodeType?i[0].getAttribute(e):t;this.each(function(){1===this.nodeType&&this.setAttribute(e,n)})}return i},css:function(e,n){var i=this;if("object"==typeof e)p(e,function(e,t){i.css(t,e)});else{if(e=e.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),!r(n))return i[0]?i[0].style[e]:t;"number"!=typeof n||x[e]||(n+="px"),i.each(function(){var t=this.style;"opacity"===e&&this.runtimeStyle&&"undefined"==typeof this.runtimeStyle.opacity&&(t.filter=""===n?"":"alpha(opacity="+100*n+")");try{t[e]=n}catch(r){}})}return i},remove:function(){for(var e=this,t,n=this.length;n--;)t=e[n],C.clean(t),t.parentNode&&t.parentNode.removeChild(t);return this},empty:function(){for(var e=this,t,n=this.length;n--;)for(t=e[n];t.firstChild;)t.removeChild(t.firstChild);return this},html:function(e){var t=this,n;if(r(e)){for(n=t.length;n--;)t[n].innerHTML=e;return t}return t[0]?t[0].innerHTML:""},text:function(e){var t=this,n;if(r(e)){for(n=t.length;n--;)t[n].innerText=t[0].textContent=e;return t}return t[0]?t[0].innerText||t[0].textContent:""},append:function(){return a(this,arguments,function(e){1===this.nodeType&&this.appendChild(e)})},prepend:function(){return a(this,arguments,function(e){1===this.nodeType&&this.insertBefore(e,this.firstChild)})},before:function(){var e=this;return e[0]&&e[0].parentNode?a(e,arguments,function(e){this.parentNode.insertBefore(e,this.nextSibling)}):e},after:function(){var e=this;return e[0]&&e[0].parentNode?a(e,arguments,function(e){this.parentNode.insertBefore(e,this)}):e},appendTo:function(e){return c(e).append(this),this},addClass:function(e){return this.toggleClass(e,!0)},removeClass:function(e){return this.toggleClass(e,!1)},toggleClass:function(e,t){var n=this;return-1!==e.indexOf(" ")?p(e.split(" "),function(){n.toggleClass(this,t)}):n.each(function(){var n=this,r;s(n,e)!==t&&(r=n.className,t?n.className+=r?" "+e:e:n.className=N((" "+r+" ").replace(" "+e+" "," ")))}),n},hasClass:function(e){return s(this[0],e)},each:function(e){return p(this,e)},on:function(e,t){return this.each(function(){C.bind(this,e,t)})},off:function(e,t){return this.each(function(){C.unbind(this,e,t)})},show:function(){return this.css("display","")},hide:function(){return this.css("display","none")},slice:function(){return new c(y.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},replaceWith:function(e){var t=this;return t[0]&&t[0].parentNode.replaceChild(c(e)[0],t[0]),t},wrap:function(e){return e=c(e)[0],this.each(function(){var t=this,n=e.cloneNode(!1);t.parentNode.insertBefore(n,t),n.appendChild(t)})},unwrap:function(){return this.each(function(){for(var e=this,t=e.firstChild,n;t;)n=t,t=t.nextSibling,e.parentNode.insertBefore(n,e)})},clone:function(){var e=[];return this.each(function(){e.push(this.cloneNode(!0))}),c(e)},find:function(e){var t,n,r=[];for(t=0,n=this.length;n>t;t++)c.find(e,this[t],r);return c(r)},push:v,sort:[].sort,splice:[].splice},u(c,{extend:u,toArray:d,inArray:f,isArray:w,each:p,trim:N,makeMap:l,find:n,expr:n.selectors,unique:n.uniqueSort,text:n.getText,isXMLDoc:n.isXML,contains:n.contains,filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?c.find.matchesSelector(t[0],e)?[t[0]]:[]:c.find.matches(e,t)}}),p({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t){return h(e,"parentNode",t)},next:function(e){return m(e,"nextSibling",1)},prev:function(e){return m(e,"previousSibling",1)},nextNodes:function(e){return m(e,"nextSibling")},prevNodes:function(e){return m(e,"previousSibling")},children:function(e){return m(e.firstChild,"nextSibling",1)},contents:function(e){return d(("iframe"===e.nodeName?e.contentDocument||e.contentWindow.document:e).childNodes)}},function(e,t){c.fn[e]=function(n){var r=this,i;if(r.length>1)throw new Error("DomQuery only supports traverse functions on a single node.");return r[0]&&(i=t(r[0],n)),i=c(i),n&&"parentsUntil"!==e?i.filter(n):i}}),c.fn.filter=function(e){return c.filter(e)},c.fn.is=function(e){return!!e&&this.filter(e).length>0},c.fn.init.prototype=c.fn,c}),r(d,[],function(){return function(e,t){function n(e,t,n,r){function i(e){return e=parseInt(e,10).toString(16),e.length>1?e:"0"+e}return"#"+i(t)+i(n)+i(r)}var r=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,i=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,o=/\s*([^:]+):\s*([^;]+);?/g,a=/\s+$/,s,l,c={},u,d="\ufeff"; +for(e=e||{},u=("\\\" \\' \\; \\: ; : "+d).split(" "),l=0;l0&&("font-weight"===g&&"700"===v?v="bold":("color"===g||"background-color"===g)&&(v=v.toLowerCase()),v=v.replace(r,n),v=v.replace(i,p),h[g]=y?f(v,!0):v),o.lastIndex=m.index+m[0].length;s("border",""),s("border","-width"),s("border","-color"),s("border","-style"),s("padding",""),s("margin",""),u("border","border-width","border-style","border-color"),"medium none"===h.border&&delete h.border}return h},serialize:function(e,n){function r(n){var r,o,a,l;if(r=t.styles[n])for(o=0,a=r.length;a>o;o++)n=r[o],l=e[n],l!==s&&l.length>0&&(i+=(i.length>0?" ":"")+n+": "+l+";")}var i="",o,a;if(n&&t&&t.styles)r("*"),r(n);else for(o in e)a=e[o],a!==s&&a.length>0&&(i+=(i.length>0?" ":"")+o+": "+a+";");return i}}}}),r(f,[],function(){return function(e,t){function n(e,n,r,i){var o,a;if(e){if(!i&&e[n])return e[n];if(e!=t){if(o=e[r])return o;for(a=e.parentNode;a&&a!=t;a=a.parentNode)if(o=a[r])return o}}}var r=e;this.current=function(){return r},this.next=function(e){return r=n(r,"firstChild","nextSibling",e)},this.prev=function(e){return r=n(r,"lastChild","previousSibling",e)}}}),r(p,[],function(){function e(e,n){return n?"array"==n&&g(e)?!0:typeof e==n:e!==t}function n(e){var t=[],n,r;for(n=0,r=e.length;r>n;n++)t[n]=e[n];return t}function r(e,t,n){var r;for(e=e||[],t=t||",","string"==typeof e&&(e=e.split(t)),n=n||{},r=e.length;r--;)n[e[r]]={};return n}function i(e,n,r){var i,o;if(!e)return 0;if(r=r||e,e.length!==t){for(i=0,o=e.length;o>i;i++)if(n.call(r,e[i],i,e)===!1)return 0}else for(i in e)if(e.hasOwnProperty(i)&&n.call(r,e[i],i,e)===!1)return 0;return 1}function o(e,t){var n=[];return i(e,function(e){n.push(t(e))}),n}function a(e,t){var n=[];return i(e,function(e){(!t||t(e))&&n.push(e)}),n}function s(e,t,n){var r=this,i,o,a,s,l,c=0;if(e=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(e),a=e[3].match(/(^|\.)(\w+)$/i)[2],o=r.createNS(e[3].replace(/\.\w+$/,""),n),!o[a]){if("static"==e[2])return o[a]=t,this.onCreate&&this.onCreate(e[2],e[3],o[a]),void 0;t[a]||(t[a]=function(){},c=1),o[a]=t[a],r.extend(o[a].prototype,t),e[5]&&(i=r.resolve(e[5]).prototype,s=e[5].match(/\.(\w+)$/i)[1],l=o[a],o[a]=c?function(){return i[s].apply(this,arguments)}:function(){return this.parent=i[s],l.apply(this,arguments)},o[a].prototype[a]=o[a],r.each(i,function(e,t){o[a].prototype[t]=i[t]}),r.each(t,function(e,t){i[t]?o[a].prototype[t]=function(){return this.parent=i[t],e.apply(this,arguments)}:t!=a&&(o[a].prototype[t]=e)})),r.each(t["static"],function(e,t){o[a][t]=e})}}function l(e,t){var n,r;if(e)for(n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1}function c(e,n){var r,i,o,a=arguments,s;for(r=1,i=a.length;i>r;r++){n=a[r];for(o in n)n.hasOwnProperty(o)&&(s=n[o],s!==t&&(e[o]=s))}return e}function u(e,t,n,r){r=r||this,e&&(n&&(e=e[n]),i(e,function(e,i){return t.call(r,e,i,n)===!1?!1:(u(e,t,n,r),void 0)}))}function d(e,t){var n,r;for(t=t||window,e=e.split("."),n=0;nn&&(t=t[e[n]],t);n++);return t}function p(t,n){return!t||e(t,"array")?t:o(t.split(n||","),m)}var h=/^\s*|\s*$/g,m=function(e){return null===e||e===t?"":(""+e).replace(h,"")},g=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};return{trim:m,isArray:g,is:e,toArray:n,makeMap:r,each:i,map:o,grep:a,inArray:l,extend:c,create:s,walk:u,createNS:d,resolve:f,explode:p}}),r(h,[p],function(e){function t(n){function r(){return H.createDocumentFragment()}function i(e,t){_(F,e,t)}function o(e,t){_(W,e,t)}function a(e){i(e.parentNode,$(e))}function s(e){i(e.parentNode,$(e)+1)}function l(e){o(e.parentNode,$(e))}function c(e){o(e.parentNode,$(e)+1)}function u(e){e?(M[U]=M[V],M[q]=M[z]):(M[V]=M[U],M[z]=M[q]),M.collapsed=F}function d(e){a(e),c(e)}function f(e){i(e,0),o(e,1===e.nodeType?e.childNodes.length:e.nodeValue.length)}function p(e,t){var n=M[V],r=M[z],i=M[U],o=M[q],a=t.startContainer,s=t.startOffset,l=t.endContainer,c=t.endOffset;return 0===e?w(n,r,a,s):1===e?w(i,o,a,s):2===e?w(i,o,l,c):3===e?w(n,r,l,c):void 0}function h(){N(I)}function m(){return N(P)}function g(){return N(O)}function v(e){var t=this[V],r=this[z],i,o;3!==t.nodeType&&4!==t.nodeType||!t.nodeValue?(t.childNodes.length>0&&(o=t.childNodes[r]),o?t.insertBefore(e,o):t.appendChild(e)):r?r>=t.nodeValue.length?n.insertAfter(e,t):(i=t.splitText(r),t.parentNode.insertBefore(e,i)):t.parentNode.insertBefore(e,t)}function y(e){var t=M.extractContents();M.insertNode(e),e.appendChild(t),M.selectNode(e)}function b(){return j(new t(n),{startContainer:M[V],startOffset:M[z],endContainer:M[U],endOffset:M[q],collapsed:M.collapsed,commonAncestorContainer:M.commonAncestorContainer})}function C(e,t){var n;if(3==e.nodeType)return e;if(0>t)return e;for(n=e.firstChild;n&&t>0;)--t,n=n.nextSibling;return n?n:e}function x(){return M[V]==M[U]&&M[z]==M[q]}function w(e,t,r,i){var o,a,s,l,c,u;if(e==r)return t==i?0:i>t?-1:1;for(o=r;o&&o.parentNode!=e;)o=o.parentNode;if(o){for(a=0,s=e.firstChild;s!=o&&t>a;)a++,s=s.nextSibling;return a>=t?-1:1}for(o=e;o&&o.parentNode!=r;)o=o.parentNode;if(o){for(a=0,s=r.firstChild;s!=o&&i>a;)a++,s=s.nextSibling;return i>a?-1:1}for(l=n.findCommonAncestor(e,r),c=e;c&&c.parentNode!=l;)c=c.parentNode;for(c||(c=l),u=r;u&&u.parentNode!=l;)u=u.parentNode;if(u||(u=l),c==u)return 0;for(s=l.firstChild;s;){if(s==c)return-1;if(s==u)return 1;s=s.nextSibling}}function _(e,t,r){var i,o;for(e?(M[V]=t,M[z]=r):(M[U]=t,M[q]=r),i=M[U];i.parentNode;)i=i.parentNode;for(o=M[V];o.parentNode;)o=o.parentNode;o==i?w(M[V],M[z],M[U],M[q])>0&&M.collapse(e):M.collapse(e),M.collapsed=x(),M.commonAncestorContainer=n.findCommonAncestor(M[V],M[U])}function N(e){var t,n=0,r=0,i,o,a,s,l,c;if(M[V]==M[U])return E(e);for(t=M[U],i=t.parentNode;i;t=i,i=i.parentNode){if(i==M[V])return k(t,e);++n}for(t=M[V],i=t.parentNode;i;t=i,i=i.parentNode){if(i==M[U])return S(t,e);++r}for(o=r-n,a=M[V];o>0;)a=a.parentNode,o--;for(s=M[U];0>o;)s=s.parentNode,o++;for(l=a.parentNode,c=s.parentNode;l!=c;l=l.parentNode,c=c.parentNode)a=l,s=c;return T(a,s,e)}function E(e){var t,n,i,o,a,s,l,c,u;if(e!=I&&(t=r()),M[z]==M[q])return t;if(3==M[V].nodeType){if(n=M[V].nodeValue,i=n.substring(M[z],M[q]),e!=O&&(o=M[V],c=M[z],u=M[q]-M[z],0===c&&u>=o.nodeValue.length-1?o.parentNode.removeChild(o):o.deleteData(c,u),M.collapse(F)),e==I)return;return i.length>0&&t.appendChild(H.createTextNode(i)),t}for(o=C(M[V],M[z]),a=M[q]-M[z];o&&a>0;)s=o.nextSibling,l=D(o,e),t&&t.appendChild(l),--a,o=s;return e!=O&&M.collapse(F),t}function k(e,t){var n,i,o,a,s,l;if(t!=I&&(n=r()),i=R(e,t),n&&n.appendChild(i),o=$(e),a=o-M[z],0>=a)return t!=O&&(M.setEndBefore(e),M.collapse(W)),n;for(i=e.previousSibling;a>0;)s=i.previousSibling,l=D(i,t),n&&n.insertBefore(l,n.firstChild),--a,i=s;return t!=O&&(M.setEndBefore(e),M.collapse(W)),n}function S(e,t){var n,i,o,a,s,l;for(t!=I&&(n=r()),o=A(e,t),n&&n.appendChild(o),i=$(e),++i,a=M[q]-i,o=e.nextSibling;o&&a>0;)s=o.nextSibling,l=D(o,t),n&&n.appendChild(l),--a,o=s;return t!=O&&(M.setStartAfter(e),M.collapse(F)),n}function T(e,t,n){var i,o,a,s,l,c,u,d;for(n!=I&&(o=r()),i=A(e,n),o&&o.appendChild(i),a=e.parentNode,s=$(e),l=$(t),++s,c=l-s,u=e.nextSibling;c>0;)d=u.nextSibling,i=D(u,n),o&&o.appendChild(i),u=d,--c;return i=R(t,n),o&&o.appendChild(i),n!=O&&(M.setStartAfter(e),M.collapse(F)),o}function R(e,t){var n=C(M[U],M[q]-1),r,i,o,a,s,l=n!=M[U];if(n==e)return B(n,l,W,t);for(r=n.parentNode,i=B(r,W,W,t);r;){for(;n;)o=n.previousSibling,a=B(n,l,W,t),t!=I&&i.insertBefore(a,i.firstChild),l=F,n=o;if(r==e)return i;n=r.previousSibling,r=r.parentNode,s=B(r,W,W,t),t!=I&&s.appendChild(i),i=s}}function A(e,t){var n=C(M[V],M[z]),r=n!=M[V],i,o,a,s,l;if(n==e)return B(n,r,F,t);for(i=n.parentNode,o=B(i,W,F,t);i;){for(;n;)a=n.nextSibling,s=B(n,r,F,t),t!=I&&o.appendChild(s),r=F,n=a;if(i==e)return o;n=i.nextSibling,i=i.parentNode,l=B(i,W,F,t),t!=I&&l.appendChild(o),o=l}}function B(e,t,r,i){var o,a,s,l,c;if(t)return D(e,i);if(3==e.nodeType){if(o=e.nodeValue,r?(l=M[z],a=o.substring(l),s=o.substring(0,l)):(l=M[q],a=o.substring(0,l),s=o.substring(l)),i!=O&&(e.nodeValue=s),i==I)return;return c=n.clone(e,W),c.nodeValue=a,c}if(i!=I)return n.clone(e,W)}function D(e,t){return t!=I?t==O?n.clone(e,F):e:(e.parentNode.removeChild(e),void 0)}function L(){return n.create("body",null,g()).outerText}var M=this,H=n.doc,P=0,O=1,I=2,F=!0,W=!1,z="startOffset",V="startContainer",U="endContainer",q="endOffset",j=e.extend,$=n.nodeIndex;return j(M,{startContainer:H,startOffset:0,endContainer:H,endOffset:0,collapsed:F,commonAncestorContainer:H,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:i,setEnd:o,setStartBefore:a,setStartAfter:s,setEndBefore:l,setEndAfter:c,collapse:u,selectNode:d,selectNodeContents:f,compareBoundaryPoints:p,deleteContents:h,extractContents:m,cloneContents:g,insertNode:v,surroundContents:y,cloneRange:b,toStringIE:L}),M}return t.prototype.toString=function(){return this.toStringIE()},t}),r(m,[p],function(e){function t(e){var t;return t=document.createElement("div"),t.innerHTML=e,t.textContent||t.innerText||e}function n(e,t){var n,r,i,a={};if(e){for(e=e.split(","),t=t||10,n=0;n\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,l=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,c=/[<>&\"\']/g,u=/&(#x|#)?([\w]+);/g,d={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};o={'"':""","'":"'","<":"<",">":">","&":"&"},a={"<":"<",">":">","&":"&",""":'"',"'":"'"},i=n("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var f={encodeRaw:function(e,t){return e.replace(t?s:l,function(e){return o[e]||e})},encodeAllRaw:function(e){return(""+e).replace(c,function(e){return o[e]||e})},encodeNumeric:function(e,t){return e.replace(t?s:l,function(e){return e.length>1?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":o[e]||"&#"+e.charCodeAt(0)+";"})},encodeNamed:function(e,t,n){return n=n||i,e.replace(t?s:l,function(e){return o[e]||n[e]||e})},getEncodeFunc:function(e,t){function a(e,n){return e.replace(n?s:l,function(e){return o[e]||t[e]||"&#"+e.charCodeAt(0)+";"||e})}function c(e,n){return f.encodeNamed(e,n,t)}return t=n(t)||i,e=r(e.replace(/\+/g,",")),e.named&&e.numeric?a:e.named?t?c:f.encodeNamed:e.numeric?f.encodeNumeric:f.encodeRaw},decode:function(e){return e.replace(u,function(e,n,r){return n?(r=parseInt(r,2===n.length?16:10),r>65535?(r-=65536,String.fromCharCode(55296+(r>>10),56320+(1023&r))):d[r]||String.fromCharCode(r)):a[e]||i[e]||t(e)})}};return f}),r(g,[],function(){var e=navigator,t=e.userAgent,n,r,i,o,a,s,l;n=window.opera&&window.opera.buildNumber,r=/WebKit/.test(t),i=!r&&!n&&/MSIE/gi.test(t)&&/Explorer/gi.test(e.appName),i=i&&/MSIE (\w+)\./.exec(t)[1],o=-1!=t.indexOf("Trident")?11:!1,i=i||o,a=!r&&/Gecko/.test(t),s=-1!=t.indexOf("Mac"),l=/(iPad|iPhone)/.test(t);var c=!l||t.match(/AppleWebKit\/(\d*)/)[1]>=534;return{opera:n,webkit:r,ie:i,gecko:a,mac:s,iOS:l,contentEditable:c,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!=i,range:window.getSelection&&"Range"in window,documentMode:i?document.documentMode||7:10}}),r(v,[c,d,l,f,h,m,g,p],function(e,n,r,i,o,a,s,l){function c(e,t){var i=this,o;i.doc=e,i.win=window,i.files={},i.counter=0,i.stdMode=!g||e.documentMode>=8,i.boxModel=!g||"CSS1Compat"==e.compatMode||i.stdMode,i.hasOuterHTML="outerHTML"in e.createElement("a"),i.settings=t=h({keep_values:!1,hex_colors:1},t),i.schema=t.schema,i.styles=new n({url_converter:t.url_converter,url_converter_scope:t.url_converter_scope},t.schema),i.fixDoc(e),i.events=t.ownEvents?new r(t.proxy):r.Event,o=t.schema?t.schema.getBlockElements():{},i.isBlock=function(e){if(!e)return!1;var t=e.nodeType;return t?!(1!==t||!o[e.nodeName]):!!o[e]}}var u=l.each,d=l.is,f=l.grep,p=l.trim,h=l.extend,m=s.webkit,g=s.ie,v=/^([a-z0-9],?)+$/i,y=/^[ \t\r\n]*$/,b=l.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," ");return c.prototype={root:null,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},fixDoc:function(e){var t=this.settings,n;if(g&&t.schema){"abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video".replace(/\w+/g,function(t){e.createElement(t)});for(n in t.schema.getCustomElements())e.createElement(n)}},clone:function(e,t){var n=this,r,i;return!g||1!==e.nodeType||t?e.cloneNode(t):(i=n.doc,t?r.firstChild:(r=i.createElement(e.nodeName),u(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),r))},getRoot:function(){var e=this;return e.get(e.settings.root_element)||e.doc.body},getViewPort:function(e){var t,n;return e=e?e:this.win,t=e.document,n=this.boxModel?t.documentElement:t.body,{x:e.pageXOffset||n.scrollLeft,y:e.pageYOffset||n.scrollTop,w:e.innerWidth||n.clientWidth,h:e.innerHeight||n.clientHeight}},getRect:function(e){var t=this,n,r;return e=t.get(e),n=t.getPos(e),r=t.getSize(e),{x:n.x,y:n.y,w:r.w,h:r.h}},getSize:function(e){var t=this,n,r;return e=t.get(e),n=t.getStyle(e,"width"),r=t.getStyle(e,"height"),-1===n.indexOf("px")&&(n=0),-1===r.indexOf("px")&&(r=0),{w:parseInt(n,10)||e.offsetWidth||e.clientWidth,h:parseInt(r,10)||e.offsetHeight||e.clientHeight}},getParent:function(e,t,n){return this.getParents(e,t,n,!1)},getParents:function(e,n,r,i){var o=this,a,s=[];for(e=o.get(e),i=i===t,r=r||("BODY"!=o.getRoot().nodeName?o.getRoot().parentNode:null),d(n,"string")&&(a=n,n="*"===n?function(e){return 1==e.nodeType}:function(e){return o.is(e,a)});e&&e!=r&&e.nodeType&&9!==e.nodeType;){if(!n||n(e)){if(!i)return e;s.push(e)}e=e.parentNode}return i?s:null},get:function(e){var t;return e&&this.doc&&"string"==typeof e&&(t=e,e=this.doc.getElementById(e),e&&e.id!==t)?this.doc.getElementsByName(t)[1]:e},getNext:function(e,t){return this._findSib(e,t,"nextSibling")},getPrev:function(e,t){return this._findSib(e,t,"previousSibling")},select:function(t,n){var r=this;return e(t,r.get(n)||r.get(r.settings.root_element)||r.doc,[])},is:function(n,r){var i;if(n.length===t){if("*"===r)return 1==n.nodeType;if(v.test(r)){for(r=r.toLowerCase().split(/,/),n=n.nodeName.toLowerCase(),i=r.length-1;i>=0;i--)if(r[i]==n)return!0;return!1}}return n.nodeType&&1!=n.nodeType?!1:e.matches(r,n.nodeType?[n]:n).length>0},add:function(e,t,n,r,i){var o=this;return this.run(e,function(e){var a;return a=d(t,"string")?o.doc.createElement(t):t,o.setAttribs(a,n),r&&(r.nodeType?a.appendChild(r):o.setHTML(a,r)),i?a:e.appendChild(a)})},create:function(e,t,n){return this.add(this.doc.createElement(e),e,t,n,1)},createHTML:function(e,t,n){var r="",i;r+="<"+e;for(i in t)t.hasOwnProperty(i)&&null!==t[i]&&(r+=" "+i+'="'+this.encode(t[i])+'"');return"undefined"!=typeof n?r+">"+n+"":r+" />"},createFragment:function(e){var t,n,r=this.doc,i;for(i=r.createElement("div"),t=r.createDocumentFragment(),e&&(i.innerHTML=e);n=i.firstChild;)t.appendChild(n);return t},remove:function(e,t){return this.run(e,function(e){var n,r=e.parentNode;if(!r)return null;if(t)for(;n=e.firstChild;)!g||3!==n.nodeType||n.nodeValue?r.insertBefore(n,e):e.removeChild(n);return r.removeChild(e)})},setStyle:function(e,t,n){return this.run(e,function(e){var r=this,i,o;if(t)if("string"==typeof t){i=e.style,t=t.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),"number"!=typeof n||b[t]||(n+="px"),"opacity"===t&&e.runtimeStyle&&"undefined"==typeof e.runtimeStyle.opacity&&(i.filter=""===n?"":"alpha(opacity="+100*n+")"),"float"==t&&(t="cssFloat"in e.style?"cssFloat":"styleFloat");try{i[t]=n}catch(a){}r.settings.update_styles&&e.removeAttribute("data-mce-style")}else for(o in t)r.setStyle(e,o,t[o])})},getStyle:function(e,n,r){if(e=this.get(e)){if(this.doc.defaultView&&r){n=n.replace(/[A-Z]/g,function(e){return"-"+e});try{return this.doc.defaultView.getComputedStyle(e,null).getPropertyValue(n)}catch(i){return null}}return n=n.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),"float"==n&&(n=g?"styleFloat":"cssFloat"),e.currentStyle&&r?e.currentStyle[n]:e.style?e.style[n]:t}},setStyles:function(e,t){this.setStyle(e,t)},css:function(e,t,n){this.setStyle(e,t,n)},removeAllAttribs:function(e){return this.run(e,function(e){var t,n=e.attributes;for(t=n.length-1;t>=0;t--)e.removeAttributeNode(n.item(t))})},setAttrib:function(e,t,n){var r=this;if(e&&t)return this.run(e,function(e){var i=r.settings,o=e.getAttribute(t);if(null!==n)switch(t){case"style":if(!d(n,"string"))return u(n,function(t,n){r.setStyle(e,n,t)}),void 0;i.keep_values&&(n?e.setAttribute("data-mce-style",n,2):e.removeAttribute("data-mce-style",2)),e.style.cssText=n;break;case"class":e.className=n||"";break;case"src":case"href":i.keep_values&&(i.url_converter&&(n=i.url_converter.call(i.url_converter_scope||r,n,t,e)),r.setAttrib(e,"data-mce-"+t,n,2));break;case"shape":e.setAttribute("data-mce-style",n)}d(n)&&null!==n&&0!==n.length?e.setAttribute(t,""+n,2):e.removeAttribute(t,2),o!=n&&i.onSetAttrib&&i.onSetAttrib({attrElm:e,attrName:t,attrValue:n})})},setAttribs:function(e,t){var n=this;return this.run(e,function(e){u(t,function(t,r){n.setAttrib(e,r,t)})})},getAttrib:function(e,t,n){var r,i=this,o;if(e=i.get(e),!e||1!==e.nodeType)return n===o?!1:n;if(d(n)||(n=""),/^(src|href|style|coords|shape)$/.test(t)&&(r=e.getAttribute("data-mce-"+t)))return r;if(g&&i.props[t]&&(r=e[i.props[t]],r=r&&r.nodeValue?r.nodeValue:r),r||(r=e.getAttribute(t,2)),/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(t))return e[i.props[t]]===!0&&""===r?t:r?t:"";if("FORM"===e.nodeName&&e.getAttributeNode(t))return e.getAttributeNode(t).nodeValue;if("style"===t&&(r=r||e.style.cssText,r&&(r=i.serializeStyle(i.parseStyle(r),e.nodeName),i.settings.keep_values&&e.setAttribute("data-mce-style",r))),m&&"class"===t&&r&&(r=r.replace(/(apple|webkit)\-[a-z\-]+/gi,"")),g)switch(t){case"rowspan":case"colspan":1===r&&(r="");break;case"size":("+0"===r||20===r||0===r)&&(r="");break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":0===r&&(r="");break;case"hspace":-1===r&&(r="");break;case"maxlength":case"tabindex":(32768===r||2147483647===r||"32768"===r)&&(r="");break;case"multiple":case"compact":case"noshade":case"nowrap":return 65535===r?t:n;case"shape":r=r.toLowerCase();break;default:0===t.indexOf("on")&&r&&(r=(""+r).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1"))}return r!==o&&null!==r&&""!==r?""+r:n},getPos:function(e,t){var n=this,r=0,i=0,o,a=n.doc,s;if(e=n.get(e),t=t||a.body,e){if(t===a.body&&e.getBoundingClientRect)return s=e.getBoundingClientRect(),t=n.boxModel?a.documentElement:a.body,r=s.left+(a.documentElement.scrollLeft||a.body.scrollLeft)-t.clientTop,i=s.top+(a.documentElement.scrollTop||a.body.scrollTop)-t.clientLeft,{x:r,y:i};for(o=e;o&&o!=t&&o.nodeType;)r+=o.offsetLeft||0,i+=o.offsetTop||0,o=o.offsetParent;for(o=e.parentNode;o&&o!=t&&o.nodeType;)r-=o.scrollLeft||0,i-=o.scrollTop||0,o=o.parentNode}return{x:r,y:i}},parseStyle:function(e){return this.styles.parse(e)},serializeStyle:function(e,t){return this.styles.serialize(e,t)},addStyle:function(e){var t=this,n=t.doc,r,i;if(t!==c.DOM&&n===document){var o=c.DOM.addedStyles;if(o=o||[],o[e])return;o[e]=!0,c.DOM.addedStyles=o}i=n.getElementById("mceDefaultStyles"),i||(i=n.createElement("style"),i.id="mceDefaultStyles",i.type="text/css",r=n.getElementsByTagName("head")[0],r.firstChild?r.insertBefore(i,r.firstChild):r.appendChild(i)),i.styleSheet?i.styleSheet.cssText+=e:i.appendChild(n.createTextNode(e))},loadCSS:function(e){var t=this,n=t.doc,r;return t!==c.DOM&&n===document?(c.DOM.loadCSS(e),void 0):(e||(e=""),r=n.getElementsByTagName("head")[0],u(e.split(","),function(e){var i;t.files[e]||(t.files[e]=!0,i=t.create("link",{rel:"stylesheet",href:e}),g&&n.documentMode&&n.recalc&&(i.onload=function(){n.recalc&&n.recalc(),i.onload=null}),r.appendChild(i))}),void 0)},addClass:function(e,t){return this.run(e,function(e){var n;return t?this.hasClass(e,t)?e.className:(n=this.removeClass(e,t),e.className=n=(""!==n?n+" ":"")+t,n):0})},removeClass:function(e,t){var n=this,r;return n.run(e,function(e){var i;return n.hasClass(e,t)?(r||(r=new RegExp("(^|\\s+)"+t+"(\\s+|$)","g")),i=e.className.replace(r," "),i=p(" "!=i?i:""),e.className=i,i||(e.removeAttribute("class"),e.removeAttribute("className")),i):e.className})},hasClass:function(e,t){return e=this.get(e),e&&t?-1!==(" "+e.className+" ").indexOf(" "+t+" "):!1},toggleClass:function(e,n,r){r=r===t?!this.hasClass(e,n):r,this.hasClass(e,n)!==r&&(r?this.addClass(e,n):this.removeClass(e,n))},show:function(e){return this.setStyle(e,"display","block")},hide:function(e){return this.setStyle(e,"display","none")},isHidden:function(e){return e=this.get(e),!e||"none"==e.style.display||"none"==this.getStyle(e,"display")},uniqueId:function(e){return(e?e:"mce_")+this.counter++},setHTML:function(e,t){var n=this;return n.run(e,function(e){if(g){for(;e.firstChild;)e.removeChild(e.firstChild);try{e.innerHTML="
    "+t,e.removeChild(e.firstChild)}catch(r){var i=n.create("div");i.innerHTML="
    "+t,u(f(i.childNodes),function(t,n){n&&e.canHaveHTML&&e.appendChild(t)})}}else e.innerHTML=t;return t})},getOuterHTML:function(e){var t,n=this;return(e=n.get(e))?1===e.nodeType&&n.hasOuterHTML?e.outerHTML:(t=(e.ownerDocument||n.doc).createElement("body"),t.appendChild(e.cloneNode(!0)),t.innerHTML):null},setOuterHTML:function(e,t,n){var r=this;return r.run(e,function(e){function i(){var i,o;for(o=n.createElement("body"),o.innerHTML=t,i=o.lastChild;i;)r.insertAfter(i.cloneNode(!0),e),i=i.previousSibling;r.remove(e)}if(1==e.nodeType)if(n=n||e.ownerDocument||r.doc,g)try{1==e.nodeType&&r.hasOuterHTML?e.outerHTML=t:i()}catch(o){i()}else i()})},decode:a.decode,encode:a.encodeAllRaw,insertAfter:function(e,t){return t=this.get(t),this.run(e,function(e){var n,r;return n=t.parentNode,r=t.nextSibling,r?n.insertBefore(e,r):n.appendChild(e),e})},replace:function(e,t,n){var r=this;return r.run(t,function(t){return d(t,"array")&&(e=e.cloneNode(!0)),n&&u(f(t.childNodes),function(t){e.appendChild(t)}),t.parentNode.replaceChild(e,t)})},rename:function(e,t){var n=this,r;return e.nodeName!=t.toUpperCase()&&(r=n.create(t),u(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),n.replace(r,e,1)),r||e},findCommonAncestor:function(e,t){for(var n=e,r;n;){for(r=t;r&&n!=r;)r=r.parentNode;if(n==r)break;n=n.parentNode}return!n&&e.ownerDocument?e.ownerDocument.documentElement:n},toHex:function(e){return this.styles.toHex(l.trim(e))},run:function(e,t,n){var r=this,i;return"string"==typeof e&&(e=r.get(e)),e?(n=n||this,e.nodeType||!e.length&&0!==e.length?t.call(n,e):(i=[],u(e,function(e,o){e&&("string"==typeof e&&(e=r.get(e)),i.push(t.call(n,e,o)))}),i)):!1},getAttribs:function(e){var t;if(e=this.get(e),!e)return[];if(g){if(t=[],"OBJECT"==e.nodeName)return e.attributes;"OPTION"===e.nodeName&&this.getAttrib(e,"selected")&&t.push({specified:1,nodeName:"selected"});var n=/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;return e.cloneNode(!1).outerHTML.replace(n,"").replace(/[\w:\-]+/gi,function(e){t.push({specified:1,nodeName:e})}),t}return e.attributes},isEmpty:function(e,t){var n=this,r,o,a,s,l,c=0;if(e=e.firstChild){s=new i(e,e.parentNode),t=t||n.schema?n.schema.getNonEmptyElements():null;do{if(a=e.nodeType,1===a){if(e.getAttribute("data-mce-bogus"))continue;if(l=e.nodeName.toLowerCase(),t&&t[l]){if("br"===l){c++;continue}return!1}for(o=n.getAttribs(e),r=e.attributes.length;r--;)if(l=e.attributes[r].nodeName,"name"===l||"data-mce-bookmark"===l)return!1}if(8==a)return!1;if(3===a&&!y.test(e.nodeValue))return!1}while(e=s.next())}return 1>=c},createRng:function(){var e=this.doc;return e.createRange?e.createRange():new o(this)},nodeIndex:function(e,t){var n=0,r,i,o;if(e)for(r=e.nodeType,e=e.previousSibling,i=e;e;e=e.previousSibling)o=e.nodeType,(!t||3!=o||o!=r&&e.nodeValue.length)&&(n++,r=o);return n},split:function(e,t,n){function r(e){function t(e){var t=e.previousSibling&&"SPAN"==e.previousSibling.nodeName,n=e.nextSibling&&"SPAN"==e.nextSibling.nodeName;return t&&n}var n,o=e.childNodes,a=e.nodeType;if(1!=a||"bookmark"!=e.getAttribute("data-mce-type")){for(n=o.length-1;n>=0;n--)r(o[n]);if(9!=a){if(3==a&&e.nodeValue.length>0){var s=p(e.nodeValue).length;if(!i.isBlock(e.parentNode)||s>0||0===s&&t(e))return}else if(1==a&&(o=e.childNodes,1==o.length&&o[0]&&1==o[0].nodeType&&"bookmark"==o[0].getAttribute("data-mce-type")&&e.parentNode.insertBefore(o[0],e),o.length||/^(br|hr|input|img)$/i.test(e.nodeName)))return;i.remove(e)}return e}}var i=this,o=i.createRng(),a,s,l;return e&&t?(o.setStart(e.parentNode,i.nodeIndex(e)),o.setEnd(t.parentNode,i.nodeIndex(t)),a=o.extractContents(),o=i.createRng(),o.setStart(t.parentNode,i.nodeIndex(t)+1),o.setEnd(e.parentNode,i.nodeIndex(e)+1),s=o.extractContents(),l=e.parentNode,l.insertBefore(r(a),e),n?l.replaceChild(n,t):l.insertBefore(t,e),l.insertBefore(r(s),e),i.remove(e),n||t):void 0},bind:function(e,t,n,r){return this.events.bind(e,t,n,r||this)},unbind:function(e,t,n){return this.events.unbind(e,t,n)},fire:function(e,t,n){return this.events.fire(e,t,n)},getContentEditable:function(e){var t;return 1!=e.nodeType?null:(t=e.getAttribute("data-mce-contenteditable"),t&&"inherit"!==t?t:"inherit"!==e.contentEditable?e.contentEditable:null)},destroy:function(){var e=this;e.win=e.doc=e.root=e.events=e.frag=null},dumpRng:function(e){return"startContainer: "+e.startContainer.nodeName+", startOffset: "+e.startOffset+", endContainer: "+e.endContainer.nodeName+", endOffset: "+e.endOffset},_findSib:function(e,t,n){var r=this,i=t;if(e)for("string"==typeof i&&(i=function(e){return r.is(e,t)}),e=e[n];e;e=e[n])if(i(e))return e;return null}},c.DOM=new c(document),c}),r(y,[v,p],function(e,t){function n(){function e(e,t){function n(){o.remove(s),a&&(a.onreadystatechange=a.onload=a=null),t()}function i(){"undefined"!=typeof console&&console.log&&console.log("Failed to load: "+e)}var o=r,a,s;s=o.uniqueId(),a=document.createElement("script"),a.id=s,a.type="text/javascript",a.src=e,"onreadystatechange"in a?a.onreadystatechange=function(){/loaded|complete/.test(a.readyState)&&n()}:a.onload=n,a.onerror=i,(document.getElementsByTagName("head")[0]||document.body).appendChild(a)}var t=0,n=1,a=2,s={},l=[],c={},u=[],d=0,f;this.isDone=function(e){return s[e]==a},this.markDone=function(e){s[e]=a},this.add=this.load=function(e,n,r){var i=s[e];i==f&&(l.push(e),s[e]=t),n&&(c[e]||(c[e]=[]),c[e].push({func:n,scope:r||this}))},this.loadQueue=function(e,t){this.loadScripts(l,e,t)},this.loadScripts=function(t,r,l){function p(e){i(c[e],function(e){e.func.call(e.scope)}),c[e]=f}var h;u.push({func:r,scope:l||this}),h=function(){var r=o(t);t.length=0,i(r,function(t){return s[t]==a?(p(t),void 0):(s[t]!=n&&(s[t]=n,d++,e(t,function(){s[t]=a,d--,p(t),h()})),void 0)}),d||(i(u,function(e){e.func.call(e.scope)}),u.length=0)},h()}}var r=e.DOM,i=t.each,o=t.grep;return n.ScriptLoader=new n,n}),r(b,[y,p],function(e,n){function r(){var e=this;e.items=[],e.urls={},e.lookup={}}var i=n.each;return r.prototype={get:function(e){return this.lookup[e]?this.lookup[e].instance:t},dependencies:function(e){var t;return this.lookup[e]&&(t=this.lookup[e].dependencies),t||[]},requireLangPack:function(t){var n=r.settings;n&&n.language&&n.language_load!==!1&&e.ScriptLoader.add(this.urls[t]+"/langs/"+n.language+".js")},add:function(e,t,n){return this.items.push(t),this.lookup[e]={instance:t,dependencies:n},t},createUrl:function(e,t){return"object"==typeof t?t:{prefix:e.prefix,resource:t,suffix:e.suffix}},addComponents:function(t,n){var r=this.urls[t];i(n,function(t){e.ScriptLoader.add(r+"/"+t)})},load:function(n,o,a,s){function l(){var r=c.dependencies(n);i(r,function(e){var n=c.createUrl(o,e);c.load(n.resource,n,t,t)}),a&&(s?a.call(s):a.call(e))}var c=this,u=o;c.urls[n]||("object"==typeof o&&(u=o.prefix+o.resource+o.suffix),0!==u.indexOf("/")&&-1==u.indexOf("://")&&(u=r.baseURL+"/"+u),c.urls[n]=u.substring(0,u.lastIndexOf("/")),c.lookup[n]?l():e.ScriptLoader.add(u,l,s))}},r.PluginManager=new r,r.ThemeManager=new r,r}),r(C,[],function(){function e(e,t,n){var r,i,o=n?"lastChild":"firstChild",a=n?"prev":"next"; +if(e[o])return e[o];if(e!==t){if(r=e[a])return r;for(i=e.parent;i&&i!==t;i=i.parent)if(r=i[a])return r}}function t(e,t){this.name=e,this.type=t,1===t&&(this.attributes=[],this.attributes.map={})}var n=/^[ \t\r\n]*$/,r={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};return t.prototype={replace:function(e){var t=this;return e.parent&&e.remove(),t.insert(e,t),t.remove(),t},attr:function(e,t){var n=this,r,i,o;if("string"!=typeof e){for(i in e)n.attr(i,e[i]);return n}if(r=n.attributes){if(t!==o){if(null===t){if(e in r.map)for(delete r.map[e],i=r.length;i--;)if(r[i].name===e)return r=r.splice(i,1),n;return n}if(e in r.map){for(i=r.length;i--;)if(r[i].name===e){r[i].value=t;break}}else r.push({name:e,value:t});return r.map[e]=t,n}return r.map[e]}},clone:function(){var e=this,n=new t(e.name,e.type),r,i,o,a,s;if(o=e.attributes){for(s=[],s.map={},r=0,i=o.length;i>r;r++)a=o[r],"id"!==a.name&&(s[s.length]={name:a.name,value:a.value},s.map[a.name]=a.value);n.attributes=s}return n.value=e.value,n.shortEnded=e.shortEnded,n},wrap:function(e){var t=this;return t.parent.insert(e,t),e.append(t),t},unwrap:function(){var e=this,t,n;for(t=e.firstChild;t;)n=t.next,e.insert(t,e,!0),t=n;e.remove()},remove:function(){var e=this,t=e.parent,n=e.next,r=e.prev;return t&&(t.firstChild===e?(t.firstChild=n,n&&(n.prev=null)):r.next=n,t.lastChild===e?(t.lastChild=r,r&&(r.next=null)):n.prev=r,e.parent=e.next=e.prev=null),e},append:function(e){var t=this,n;return e.parent&&e.remove(),n=t.lastChild,n?(n.next=e,e.prev=n,t.lastChild=e):t.lastChild=t.firstChild=e,e.parent=t,e},insert:function(e,t,n){var r;return e.parent&&e.remove(),r=t.parent||this,n?(t===r.firstChild?r.firstChild=e:t.prev.next=e,e.prev=t.prev,e.next=t,t.prev=e):(t===r.lastChild?r.lastChild=e:t.next.prev=e,e.next=t.next,e.prev=t,t.next=e),e.parent=r,e},getAll:function(t){var n=this,r,i=[];for(r=n.firstChild;r;r=e(r,n))r.name===t&&i.push(r);return i},empty:function(){var t=this,n,r,i;if(t.firstChild){for(n=[],i=t.firstChild;i;i=e(i,t))n.push(i);for(r=n.length;r--;)i=n[r],i.parent=i.firstChild=i.lastChild=i.next=i.prev=null}return t.firstChild=t.lastChild=null,t},isEmpty:function(t){var r=this,i=r.firstChild,o,a;if(i)do{if(1===i.type){if(i.attributes.map["data-mce-bogus"])continue;if(t[i.name])return!1;for(o=i.attributes.length;o--;)if(a=i.attributes[o].name,"name"===a||0===a.indexOf("data-mce-"))return!1}if(8===i.type)return!1;if(3===i.type&&!n.test(i.value))return!1}while(i=e(i,r));return!0},walk:function(t){return e(this,null,t)}},t.create=function(e,n){var i,o;if(i=new t(e,r[e]||1),n)for(o in n)i.attr(o,n[o]);return i},t}),r(x,[p],function(e){function t(e,t){return e?e.split(t||" "):[]}function n(e){function n(e,n,r){function i(e){var t={},n,r;for(n=0,r=e.length;r>n;n++)t[e[n]]={};return t}var o,l,c,u=arguments;for(r=r||[],n=n||"","string"==typeof r&&(r=t(r)),l=3;lo;o++)i.attributes[n[o]]={},i.attributesOrder.push(n[o])}var a={},s,l,c,u,d,f,p;return r[e]?r[e]:(s=t("id accesskey class dir lang style tabindex title"),l=t("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange onwaiting"),c=t("address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"),u=t("a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment"),"html4"!=e&&(s.push.apply(s,t("contenteditable contextmenu draggable dropzone hidden spellcheck translate")),c.push.apply(c,t("article aside details dialog figure header footer hgroup section nav")),u.push.apply(u,t("audio canvas command datalist mark meter output progress time wbr video ruby bdi keygen"))),"html5-strict"!=e&&(s.push("xml:lang"),p=t("acronym applet basefont big font strike tt"),u.push.apply(u,p),o(p,function(e){n(e,"",u)}),f=t("center dir isindex noframes"),c.push.apply(c,f),d=[].concat(c,u),o(f,function(e){n(e,"",d)})),d=d||[].concat(c,u),n("html","manifest","head body"),n("head","","base command link meta noscript script style title"),n("title hr noscript br"),n("base","href target"),n("link","href rel media hreflang type sizes hreflang"),n("meta","name http-equiv content charset"),n("style","media type scoped"),n("script","src async defer type charset"),n("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",d),n("address dt dd div caption","",d),n("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",u),n("blockquote","cite",d),n("ol","reversed start type","li"),n("ul","","li"),n("li","value",d),n("dl","","dt dd"),n("a","href target rel media hreflang type",u),n("q","cite",u),n("ins del","cite datetime",d),n("img","src alt usemap ismap width height"),n("iframe","src name width height",d),n("embed","src type width height"),n("object","data type typemustmatch name usemap form width height",d,"param"),n("param","name value"),n("map","name",d,"area"),n("area","alt coords shape href target rel media hreflang type"),n("table","border","caption colgroup thead tfoot tbody tr"+("html4"==e?" col":"")),n("colgroup","span","col"),n("col","span"),n("tbody thead tfoot","","tr"),n("tr","","td th"),n("td","colspan rowspan headers",d),n("th","colspan rowspan headers scope abbr",d),n("form","accept-charset action autocomplete enctype method name novalidate target",d),n("fieldset","disabled form name",d,"legend"),n("label","form for",u),n("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),n("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"==e?d:u),n("select","disabled form multiple name required size","option optgroup"),n("optgroup","disabled label","option"),n("option","disabled label selected value"),n("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),n("menu","type label",d,"li"),n("noscript","",d),"html4"!=e&&(n("wbr"),n("ruby","",u,"rt rp"),n("figcaption","",d),n("mark rt rp summary bdi","",u),n("canvas","width height",d),n("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height",d,"track source"),n("audio","src crossorigin preload autoplay mediagroup loop muted controls",d,"track source"),n("source","src type media"),n("track","kind src srclang label default"),n("datalist","",u,"option"),n("article section nav aside header footer","",d),n("hgroup","","h1 h2 h3 h4 h5 h6"),n("figure","",d,"figcaption"),n("time","datetime",u),n("dialog","open",d),n("command","type label icon disabled checked radiogroup command"),n("output","for form name",u),n("progress","value max",u),n("meter","value min max low high optimum",u),n("details","open",d,"summary"),n("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!=e&&(i("script","language xml:space"),i("style","xml:space"),i("object","declare classid codebase codetype archive standby align border hspace vspace"),i("param","valuetype type"),i("a","charset name rev shape coords"),i("br","clear"),i("applet","codebase archive code object alt name width height align hspace vspace"),i("img","name longdesc align border hspace vspace"),i("iframe","longdesc frameborder marginwidth marginheight scrolling align"),i("font basefont","size color face"),i("input","usemap align"),i("select","onchange"),i("textarea"),i("h1 h2 h3 h4 h5 h6 div p legend caption","align"),i("ul","type compact"),i("li","type"),i("ol dl menu dir","compact"),i("pre","width xml:space"),i("hr","align noshade size width"),i("isindex","prompt"),i("table","summary width frame rules cellspacing cellpadding align bgcolor"),i("col","width align char charoff valign"),i("colgroup","width align char charoff valign"),i("thead","align char charoff valign"),i("tr","align char charoff valign bgcolor"),i("th","axis align char charoff valign nowrap bgcolor width height"),i("form","accept"),i("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),i("tfoot","align char charoff valign"),i("tbody","align char charoff valign"),i("area","nohref"),i("body","background bgcolor text link vlink alink")),"html4"!=e&&(i("input button select textarea","autofocus"),i("input textarea","placeholder"),i("a","download"),i("link script img","crossorigin"),i("iframe","srcdoc sandbox seamless allowfullscreen")),o(t("a form meter progress dfn"),function(e){a[e]&&delete a[e].children[e]}),delete a.caption.children.table,r[e]=a,a)}var r={},i=e.makeMap,o=e.each,a=e.extend,s=e.explode,l=e.inArray;return function(e){function c(t,n,o){var s=e[t];return s?s=i(s,",",i(s.toUpperCase()," ")):(s=r[t],s||(s=i(n," ",i(n.toUpperCase()," ")),s=a(s,o),r[t]=s)),s}function u(e){return new RegExp("^"+e.replace(/([?+*])/g,".$1")+"$")}function d(e){var n,r,o,a,s,c,d,f,p,h,m,g,y,C,x,w,_,N,E,k=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,S=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,T=/[*?+]/;if(e)for(e=t(e,","),v["@"]&&(w=v["@"].attributes,_=v["@"].attributesOrder),n=0,r=e.length;r>n;n++)if(s=k.exec(e[n])){if(C=s[1],p=s[2],x=s[3],f=s[5],g={},y=[],c={attributes:g,attributesOrder:y},"#"===C&&(c.paddEmpty=!0),"-"===C&&(c.removeEmpty=!0),"!"===s[4]&&(c.removeEmptyAttrs=!0),w){for(N in w)g[N]=w[N];y.push.apply(y,_)}if(f)for(f=t(f,"|"),o=0,a=f.length;a>o;o++)if(s=S.exec(f[o])){if(d={},m=s[1],h=s[2].replace(/::/g,":"),C=s[3],E=s[4],"!"===m&&(c.attributesRequired=c.attributesRequired||[],c.attributesRequired.push(h),d.required=!0),"-"===m){delete g[h],y.splice(l(y,h),1);continue}C&&("="===C&&(c.attributesDefault=c.attributesDefault||[],c.attributesDefault.push({name:h,value:E}),d.defaultValue=E),":"===C&&(c.attributesForced=c.attributesForced||[],c.attributesForced.push({name:h,value:E}),d.forcedValue=E),"<"===C&&(d.validValues=i(E,"?"))),T.test(h)?(c.attributePatterns=c.attributePatterns||[],d.pattern=u(h),c.attributePatterns.push(d)):(g[h]||y.push(h),g[h]=d)}w||"@"!=p||(w=g,_=y),x&&(c.outputName=p,v[x]=c),T.test(p)?(c.pattern=u(p),b.push(c)):v[p]=c}}function f(e){v={},b=[],d(e),o(x,function(e,t){y[t]=e.children})}function p(e){var n=/^(~)?(.+)$/;e&&o(t(e,","),function(e){var t=n.exec(e),r="~"===t[1],i=r?"span":"div",s=t[2];if(y[s]=y[i],R[s]=i,r||(k[s.toUpperCase()]={},k[s]={}),!v[s]){var l=v[i];l=a({},l),delete l.removeEmptyAttrs,delete l.removeEmpty,v[s]=l}o(y,function(e){e[i]&&(e[s]=e[i])})})}function h(e){var n=/^([+\-]?)(\w+)\[([^\]]+)\]$/;e&&o(t(e,","),function(e){var r=n.exec(e),i,a;r&&(a=r[1],i=a?y[r[2]]:y[r[2]]={"#comment":{}},i=y[r[2]],o(t(r[3],"|"),function(e){"-"===a?delete i[e]:i[e]={}}))})}function m(e){var t=v[e],n;if(t)return t;for(n=b.length;n--;)if(t=b[n],t.pattern.test(e))return t}var g=this,v={},y={},b=[],C,x,w,_,N,E,k,S,T,R={},A={};e=e||{},x=n(e.schema),e.verify_html===!1&&(e.valid_elements="*[*]"),e.valid_styles&&(C={},o(e.valid_styles,function(e,t){C[t]=s(e)})),w=c("whitespace_elements","pre script noscript style textarea video audio iframe object"),_=c("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),N=c("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),E=c("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),S=c("non_empty_elements","td th iframe video audio object",N),T=c("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure"),k=c("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex samp option datalist select optgroup",T),o((e.special||"script noscript style textarea").split(" "),function(e){A[e]=new RegExp("]*>","gi")}),e.valid_elements?f(e.valid_elements):(o(x,function(e,t){v[t]={attributes:e.attributes,attributesOrder:e.attributesOrder},y[t]=e.children}),"html5"!=e.schema&&o(t("strong/b em/i"),function(e){e=t(e,"/"),v[e[1]].outputName=e[0]}),v.img.attributesDefault=[{name:"alt",value:""}],o(t("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(e){v[e]&&(v[e].removeEmpty=!0)}),o(t("p h1 h2 h3 h4 h5 h6 th td pre div address caption"),function(e){v[e].paddEmpty=!0}),o(t("span"),function(e){v[e].removeEmptyAttrs=!0})),p(e.custom_elements),h(e.valid_children),d(e.extended_valid_elements),h("+ol[ul|ol],+ul[ul|ol]"),e.invalid_elements&&o(s(e.invalid_elements),function(e){v[e]&&delete v[e]}),m("span")||d("span[!data-mce-type|*]"),g.children=y,g.styles=C,g.getBoolAttrs=function(){return E},g.getBlockElements=function(){return k},g.getTextBlockElements=function(){return T},g.getShortEndedElements=function(){return N},g.getSelfClosingElements=function(){return _},g.getNonEmptyElements=function(){return S},g.getWhiteSpaceElements=function(){return w},g.getSpecialElements=function(){return A},g.isValidChild=function(e,t){var n=y[e];return!(!n||!n[t])},g.isValid=function(e,t){var n,r,i=m(e);if(i){if(!t)return!0;if(i.attributes[t])return!0;if(n=i.attributePatterns)for(r=n.length;r--;)if(n[r].pattern.test(e))return!0}return!1},g.getElementRule=m,g.getCustomElements=function(){return R},g.addValidElements=d,g.setValidElements=f,g.addCustomElements=p,g.addValidChildren=h,g.elements=v}}),r(w,[x,m,p],function(e,t,n){var r=n.each;return function(n,i){var o=this,a=function(){};n=n||{},o.schema=i=i||new e,n.fix_self_closing!==!1&&(n.fix_self_closing=!0),r("comment cdata text start end pi doctype".split(" "),function(e){e&&(o[e]=n[e]||a)}),o.parse=function(e){function r(e){var t,n;for(t=d.length;t--&&d[t].name!==e;);if(t>=0){for(n=d.length-1;n>=t;n--)e=d[n],e.valid&&a.end(e.name);d.length=t}}function o(e,t,n,r,i){var o,a;if(t=t.toLowerCase(),n=t in b?t:I(n||r||i||""),x&&!g&&0!==t.indexOf("data-")){if(o=k[t],!o&&S){for(a=S.length;a--&&(o=S[a],!o.pattern.test(t)););-1===a&&(o=null)}if(!o)return;if(o.validValues&&!(n in o.validValues))return}f.map[t]=n,f.push({name:t,value:n})}var a=this,s,l=0,c,u,d=[],f,p,h,m,g,v,y,b,C,x,w,_,N,E,k,S,T,R,A,B,D,L,M,H,P,O=0,I=t.decode,F;for(L=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),M=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,y=i.getShortEndedElements(),D=n.self_closing_elements||i.getSelfClosingElements(),b=i.getBoolAttrs(),x=n.validate,v=n.remove_internals,F=n.fix_self_closing,H=i.getSpecialElements();s=L.exec(e);){if(l0&&d[d.length-1].name===c&&r(c),!x||(w=i.getElementRule(c))){if(_=!0,x&&(k=w.attributes,S=w.attributePatterns),(E=s[8])?(g=-1!==E.indexOf("data-mce-type"),g&&v&&(_=!1),f=[],f.map={},E.replace(M,o)):(f=[],f.map={}),x&&!g){if(T=w.attributesRequired,R=w.attributesDefault,A=w.attributesForced,B=w.removeEmptyAttrs,B&&!f.length&&(_=!1),A)for(p=A.length;p--;)N=A[p],m=N.name,P=N.value,"{$uid}"===P&&(P="mce_"+O++),f.map[m]=P,f.push({name:m,value:P});if(R)for(p=R.length;p--;)N=R[p],m=N.name,m in f.map||(P=N.value,"{$uid}"===P&&(P="mce_"+O++),f.map[m]=P,f.push({name:m,value:P}));if(T){for(p=T.length;p--&&!(T[p]in f.map););-1===p&&(_=!1)}f.map["data-mce-bogus"]&&(_=!1)}_&&a.start(c,f,C)}else _=!1;if(u=H[c]){u.lastIndex=l=s.index+s[0].length,(s=u.exec(e))?(_&&(h=e.substr(l,s.index-l)),l=s.index+s[0].length):(h=e.substr(l),l=e.length),_&&(h.length>0&&a.text(h,!0),a.end(c)),L.lastIndex=l;continue}C||(E&&E.indexOf("/")==E.length-1?_&&a.end(c):d.push({name:c,valid:_}))}else(c=s[1])?a.comment(c):(c=s[2])?a.cdata(c):(c=s[3])?a.doctype(c):(c=s[4])&&a.pi(c,s[5]);l=s.index+s[0].length}for(l=0;p--)c=d[p],c.valid&&a.end(c.name)}}}),r(_,[C,x,w,p],function(e,t,n,r){var i=r.makeMap,o=r.each,a=r.explode,s=r.extend;return function(r,l){function c(t){var n,r,o,a,s,c,d,f,p,h,m,g,v,y;for(m=i("tr,td,th,tbody,thead,tfoot,table"),h=l.getNonEmptyElements(),g=l.getTextBlockElements(),n=0;n1){for(a.reverse(),s=c=u.filterNode(a[0].clone()),p=0;p0?(t.value=n,t=t.prev):(r=t.prev,t.remove(),t=r)}function g(e){var t,n={};for(t in e)"li"!==t&&"p"!=t&&(n[t]=e[t]);return n}var v,y,b,C,x,w,_,N,E,k,S,T,R,A=[],B,D,L,M,H,P,O,I;if(o=o||{},p={},h={},T=s(i("script,style,head,html,body,title,meta,param"),l.getBlockElements()),O=l.getNonEmptyElements(),P=l.children,S=r.validate,I="forced_root_block"in o?o.forced_root_block:r.forced_root_block,H=l.getWhiteSpaceElements(),R=/^[ \t\r\n]+/,D=/[ \t\r\n]+$/,L=/[ \t\r\n]+/g,M=/^[ \t\r\n]+$/,v=new n({validate:S,self_closing_elements:g(l.getSelfClosingElements()),cdata:function(e){b.append(u("#cdata",4)).value=e},text:function(e,t){var n;B||(e=e.replace(L," "),b.lastChild&&T[b.lastChild.name]&&(e=e.replace(R,""))),0!==e.length&&(n=u("#text",3),n.raw=!!t,b.append(n).value=e)},comment:function(e){b.append(u("#comment",8)).value=e},pi:function(e,t){b.append(u(e,7)).value=t,m(b)},doctype:function(e){var t;t=b.append(u("#doctype",10)),t.value=e,m(b)},start:function(e,t,n){var r,i,o,a,s;if(o=S?l.getElementRule(e):{}){for(r=u(o.outputName||e,1),r.attributes=t,r.shortEnded=n,b.append(r),s=P[b.name],s&&P[r.name]&&!s[r.name]&&A.push(r),i=f.length;i--;)a=f[i].name,a in t.map&&(E=h[a],E?E.push(r):h[a]=[r]);T[e]&&m(r),n||(b=r),!B&&H[e]&&(B=!0)}},end:function(t){var n,r,i,o,a;if(r=S?l.getElementRule(t):{}){if(T[t]&&!B){if(n=b.firstChild,n&&3===n.type)if(i=n.value.replace(R,""),i.length>0)n.value=i,n=n.next;else for(o=n.next,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.next,(0===i.length||M.test(i))&&(n.remove(),n=o),n=o;if(n=b.lastChild,n&&3===n.type)if(i=n.value.replace(D,""),i.length>0)n.value=i,n=n.prev;else for(o=n.prev,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.prev,(0===i.length||M.test(i))&&(n.remove(),n=o),n=o}if(B&&H[t]&&(B=!1),(r.removeEmpty||r.paddEmpty)&&b.isEmpty(O))if(r.paddEmpty)b.empty().append(new e("#text","3")).value="\xa0";else if(!b.attributes.map.name&&!b.attributes.map.id)return a=b.parent,b.empty().remove(),b=a,void 0;b=b.parent}}},l),y=b=new e(o.context||r.root_name,11),v.parse(t),S&&A.length&&(o.context?o.invalid=!0:c(A)),I&&("body"==y.name||o.isRootContent)&&a(),!o.invalid){for(k in p){for(E=d[k],C=p[k],_=C.length;_--;)C[_].parent||C.splice(_,1);for(x=0,w=E.length;w>x;x++)E[x](C,k,o)}for(x=0,w=f.length;w>x;x++)if(E=f[x],E.name in h){for(C=h[E.name],_=C.length;_--;)C[_].parent||C.splice(_,1);for(_=0,N=E.callbacks.length;N>_;_++)E.callbacks[_](C,E.name,o)}}return y},r.remove_trailing_brs&&u.addNodeFilter("br",function(t){var n,r=t.length,i,o=s({},l.getBlockElements()),a=l.getNonEmptyElements(),c,u,d,f,p,h;for(o.body=1,n=0;r>n;n++)if(i=t[n],c=i.parent,o[i.parent.name]&&i===c.lastChild){for(d=i.prev;d;){if(f=d.name,"span"!==f||"bookmark"!==d.attr("data-mce-type")){if("br"!==f)break;if("br"===f){i=null;break}}d=d.prev}i&&(i.remove(),c.isEmpty(a)&&(p=l.getElementRule(c.name),p&&(p.removeEmpty?c.remove():p.paddEmpty&&(c.empty().append(new e("#text",3)).value="\xa0"))))}else{for(u=i;c&&c.firstChild===u&&c.lastChild===u&&(u=c,!o[c.name]);)c=c.parent;u===c&&(h=new e("#text",3),h.value="\xa0",i.replace(h))}}),r.allow_html_in_named_anchor||u.addAttributeFilter("id,name",function(e){for(var t=e.length,n,r,i,o;t--;)if(o=e[t],"a"===o.name&&o.firstChild&&!o.attr("href")){i=o.parent,n=o.lastChild;do r=n.prev,i.insert(n,o),n=r;while(n)}})}}),r(N,[m,p],function(e,t){var n=t.makeMap;return function(t){var r=[],i,o,a,s,l;return t=t||{},i=t.indent,o=n(t.indent_before||""),a=n(t.indent_after||""),s=e.getEncodeFunc(t.entity_encoding||"raw",t.entities),l="html"==t.element_format,{start:function(e,t,n){var c,u,d,f;if(i&&o[e]&&r.length>0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n")),r.push("<",e),t)for(c=0,u=t.length;u>c;c++)d=t[c],r.push(" ",d.name,'="',s(d.value,!0),'"');r[r.length]=!n||l?">":" />",n&&i&&a[e]&&r.length>0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n"))},end:function(e){var t;r.push(""),i&&a[e]&&r.length>0&&(t=r[r.length-1],t.length>0&&"\n"!==t&&r.push("\n"))},text:function(e,t){e.length>0&&(r[r.length]=t?e:s(e))},cdata:function(e){r.push("")},comment:function(e){r.push("")},pi:function(e,t){t?r.push(""):r.push(""),i&&r.push("\n")},doctype:function(e){r.push("",i?"\n":"")},reset:function(){r.length=0},getContent:function(){return r.join("").replace(/\n$/,"")}}}}),r(E,[N,x],function(e,t){return function(n,r){var i=this,o=new e(n);n=n||{},n.validate="validate"in n?n.validate:!0,i.schema=r=r||new t,i.writer=o,i.serialize=function(e){function t(e){var n=i[e.type],s,l,c,u,d,f,p,h,m;if(n)n(e);else{if(s=e.name,l=e.shortEnded,c=e.attributes,a&&c&&c.length>1){for(f=[],f.map={},m=r.getElementRule(e.name),p=0,h=m.attributesOrder.length;h>p;p++)u=m.attributesOrder[p],u in c.map&&(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));for(p=0,h=c.length;h>p;p++)u=c[p].name,u in f.map||(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));c=f}if(o.start(e.name,c,l),!l){if(e=e.firstChild)do t(e);while(e=e.next);o.end(s)}}}var i,a;return a=n.validate,i={3:function(e){o.text(e.value,e.raw)},8:function(e){o.comment(e.value)},7:function(e){o.pi(e.name,e.value)},10:function(e){o.doctype(e.value)},4:function(e){o.cdata(e.value)},11:function(e){if(e=e.firstChild)do t(e);while(e=e.next)}},o.reset(),1!=e.type||n.inner?i[11](e):t(e),o.getContent()}}}),r(k,[v,_,m,E,C,x,g,p],function(e,t,n,r,i,o,a,s){var l=s.each,c=s.trim,u=e.DOM;return function(e,i){var s,d,f;return i&&(s=i.dom,d=i.schema),s=s||u,d=d||new o(e),e.entity_encoding=e.entity_encoding||"named",e.remove_trailing_brs="remove_trailing_brs"in e?e.remove_trailing_brs:!0,f=new t(e,d),f.addAttributeFilter("src,href,style",function(t,n){for(var r=t.length,i,o,a="data-mce-"+n,l=e.url_converter,c=e.url_converter_scope,u;r--;)i=t[r],o=i.attributes.map[a],o!==u?(i.attr(n,o.length>0?o:null),i.attr(a,null)):(o=i.attributes.map[n],"style"===n?o=s.serializeStyle(s.parseStyle(o),i.name):l&&(o=l.call(c,o,n,i.name)),i.attr(n,o.length>0?o:null))}),f.addAttributeFilter("class",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),n.attr("class",r.length>0?r:null)}),f.addAttributeFilter("data-mce-type",function(e,t,n){for(var r=e.length,i;r--;)i=e[r],"bookmark"!==i.attributes.map["data-mce-type"]||n.cleanup||i.remove()}),f.addAttributeFilter("data-mce-expando",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),f.addNodeFilter("noscript",function(e){for(var t=e.length,r;t--;)r=e[t].firstChild,r&&(r.value=n.decode(r.value))}),f.addNodeFilter("script,style",function(e,t){function n(e){return e.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(()?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}for(var r=e.length,i,o;r--;)if(i=e[r],o=i.firstChild?i.firstChild.value:"","script"===t){var a=(i.attr("type")||"text/javascript").replace(/^mce\-/,"");i.attr("type","text/javascript"===a?null:a),o.length>0&&(i.firstChild.value="// ")}else o.length>0&&(i.firstChild.value="")}),f.addNodeFilter("#comment",function(e){for(var t=e.length,n;t--;)n=e[t],0===n.value.indexOf("[CDATA[")?(n.name="#cdata",n.type=4,n.value=n.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===n.value.indexOf("mce:protected ")&&(n.name="#text",n.type=3,n.raw=!0,n.value=unescape(n.value).substr(14))}),f.addNodeFilter("xml:namespace,input",function(e,t){for(var n=e.length,r;n--;)r=e[n],7===r.type?r.remove():1===r.type&&("input"!==t||"type"in r.attributes.map||r.attr("type","text"))}),e.fix_list_elements&&f.addNodeFilter("ul,ol",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.parent,("ul"===r.name||"ol"===r.name)&&n.prev&&"li"===n.prev.name&&n.prev.append(n)}),f.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),{schema:d,addNodeFilter:f.addNodeFilter,addAttributeFilter:f.addAttributeFilter,serialize:function(t,n){var i=this,o,u,p,h,m;return a.ie&&s.select("script,style,select,map").length>0?(m=t.innerHTML,t=t.cloneNode(!1),s.setHTML(t,m)):t=t.cloneNode(!0),o=t.ownerDocument.implementation,o.createHTMLDocument&&(u=o.createHTMLDocument(""),l("BODY"==t.nodeName?t.childNodes:[t],function(e){u.body.appendChild(u.importNode(e,!0))}),t="BODY"!=t.nodeName?u.body.firstChild:u.body,p=s.doc,s.doc=u),n=n||{},n.format=n.format||"html",n.selection&&(n.forced_root_block=""),n.no_events||(n.node=t,i.onPreProcess(n)),h=new r(e,d),n.content=h.serialize(f.parse(c(n.getInner?t.innerHTML:s.getOuterHTML(t)),n)),n.cleanup||(n.content=n.content.replace(/\uFEFF/g,"")),n.no_events||i.onPostProcess(n),p&&(s.doc=p),n.node=null,n.content},addRules:function(e){d.addValidElements(e)},setRules:function(e){d.setValidElements(e)},onPreProcess:function(e){i&&i.fire("PreProcess",e)},onPostProcess:function(e){i&&i.fire("PostProcess",e)}}}}),r(S,[],function(){function e(e){function t(t,n){var r,i=0,o,a,s,l,c,u,d=-1,f;if(r=t.duplicate(),r.collapse(n),f=r.parentElement(),f.ownerDocument===e.dom.doc){for(;"false"===f.contentEditable;)f=f.parentNode;if(!f.hasChildNodes())return{node:f,inside:1};for(s=f.children,o=s.length-1;o>=i;)if(u=Math.floor((i+o)/2),l=s[u],r.moveToElementText(l),d=r.compareEndPoints(n?"StartToStart":"EndToEnd",t),d>0)o=u-1;else{if(!(0>d))return{node:l};i=u+1}if(0>d)for(l?r.collapse(!1):(r.moveToElementText(f),r.collapse(!0),l=f,a=!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",1)&&f==r.parentElement();)c++;else for(r.collapse(!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",-1)&&f==r.parentElement();)c++;return{node:l,position:d,offset:c,inside:a}}}function n(){function n(e){var n=t(o,e),r,i,s=0,l,c,u;if(r=n.node,i=n.offset,n.inside&&!r.hasChildNodes())return a[e?"setStart":"setEnd"](r,0),void 0;if(i===c)return a[e?"setStartBefore":"setEndAfter"](r),void 0;if(n.position<0){if(l=n.inside?r.firstChild:r.nextSibling,!l)return a[e?"setStartAfter":"setEndAfter"](r),void 0;if(!i)return 3==l.nodeType?a[e?"setStart":"setEnd"](l,0):a[e?"setStartBefore":"setEndBefore"](l),void 0;for(;l;){if(u=l.nodeValue,s+=u.length,s>=i){r=l,s-=i,s=u.length-s;break}l=l.nextSibling}}else{if(l=r.previousSibling,!l)return a[e?"setStartBefore":"setEndBefore"](r);if(!i)return 3==r.nodeType?a[e?"setStart":"setEnd"](l,r.nodeValue.length):a[e?"setStartAfter":"setEndAfter"](l),void 0;for(;l;){if(s+=l.nodeValue.length,s>=i){r=l,s-=i;break}l=l.previousSibling}}a[e?"setStart":"setEnd"](r,s)}var o=e.getRng(),a=i.createRng(),s,l,c,u,d;if(s=o.item?o.item(0):o.parentElement(),s.ownerDocument!=i.doc)return a;if(l=e.isCollapsed(),o.item)return a.setStart(s.parentNode,i.nodeIndex(s)),a.setEnd(a.startContainer,a.startOffset+1),a;try{n(!0),l||n()}catch(f){if(-2147024809!=f.number)throw f;d=r.getBookmark(2),c=o.duplicate(),c.collapse(!0),s=c.parentElement(),l||(c=o.duplicate(),c.collapse(!1),u=c.parentElement(),u.innerHTML=u.innerHTML),s.innerHTML=s.innerHTML,r.moveToBookmark(d),o=e.getRng(),n(!0),l||n()}return a}var r=this,i=e.dom,o=!1;this.getBookmark=function(n){function r(e){var t,n,r,o,a=[];for(t=e.parentNode,n=i.getRoot().parentNode;t!=n&&9!==t.nodeType;){for(r=t.children,o=r.length;o--;)if(e===r[o]){a.push(o);break}e=t,t=t.parentNode}return a}function o(e){var n;return n=t(a,e),n?{position:n.position,offset:n.offset,indexes:r(n.node),inside:n.inside}:void 0}var a=e.getRng(),s={};return 2===n&&(a.item?s.start={ctrl:!0,indexes:r(a.item(0))}:(s.start=o(!0),e.isCollapsed()||(s.end=o()))),s},this.moveToBookmark=function(e){function t(e){var t,n,r,o;for(t=i.getRoot(),n=e.length-1;n>=0;n--)o=t.children,r=e[n],r<=o.length-1&&(t=o[r]);return t}function n(n){var i=e[n?"start":"end"],a,s,l,c;i&&(a=i.position>0,s=o.createTextRange(),s.moveToElementText(t(i.indexes)),c=i.offset,c!==l?(s.collapse(i.inside||a),s.moveStart("character",a?-c:c)):s.collapse(n),r.setEndPoint(n?"StartToStart":"EndToStart",s),n&&r.collapse(!0))}var r,o=i.doc.body;e.start&&(e.start.ctrl?(r=o.createControlRange(),r.addElement(t(e.start.indexes)),r.select()):(r=o.createTextRange(),n(!0),n(),r.select()))},this.addRange=function(t){function n(e){var t,n,a,d,h;a=i.create("a"),t=e?s:c,n=e?l:u,d=r.duplicate(),(t==f||t==f.documentElement)&&(t=p,n=0),3==t.nodeType?(t.parentNode.insertBefore(a,t),d.moveToElementText(a),d.moveStart("character",n),i.remove(a),r.setEndPoint(e?"StartToStart":"EndToEnd",d)):(h=t.childNodes,h.length?(n>=h.length?i.insertAfter(a,h[h.length-1]):t.insertBefore(a,h[n]),d.moveToElementText(a)):t.canHaveHTML&&(t.innerHTML="",a=t.firstChild,d.moveToElementText(a),d.collapse(o)),r.setEndPoint(e?"StartToStart":"EndToEnd",d),i.remove(a)) +}var r,a,s,l,c,u,d,f=e.dom.doc,p=f.body,h,m;if(s=t.startContainer,l=t.startOffset,c=t.endContainer,u=t.endOffset,r=p.createTextRange(),s==c&&1==s.nodeType){if(l==u&&!s.hasChildNodes()){if(s.canHaveHTML)return d=s.previousSibling,d&&!d.hasChildNodes()&&i.isBlock(d)?d.innerHTML="":d=null,s.innerHTML="",r.moveToElementText(s.lastChild),r.select(),i.doc.selection.clear(),s.innerHTML="",d&&(d.innerHTML=""),void 0;l=i.nodeIndex(s),s=s.parentNode}if(l==u-1)try{if(m=s.childNodes[l],a=p.createControlRange(),a.addElement(m),a.select(),h=e.getRng(),h.item&&m===h.item(0))return}catch(g){}}n(!0),n(),r.select()},this.getRangeAt=n}return e}),r(T,[g],function(e){return{BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(e){return e.shiftKey||e.ctrlKey||e.altKey},metaKeyPressed:function(t){return(e.mac?t.ctrlKey||t.metaKey:t.ctrlKey)&&!t.altKey}}}),r(R,[T,p,g],function(e,t,n){return function(r,i){function o(e){return i.settings.object_resizing===!1?!1:/TABLE|IMG|DIV/.test(e.nodeName)?"false"===e.getAttribute("data-mce-resize")?!1:!0:!1}function a(t){var n,r;n=t.screenX-k,r=t.screenY-S,M=n*N[2]+A,H=r*N[3]+B,M=5>M?5:M,H=5>H?5:H,(e.modifierPressed(t)||"IMG"==x.nodeName&&0!==N[2]*N[3])&&(M=Math.round(H/D),H=Math.round(M*D)),b.setStyles(w,{width:M,height:H}),N[2]<0&&w.clientWidth<=M&&b.setStyle(w,"left",T+(A-M)),N[3]<0&&w.clientHeight<=H&&b.setStyle(w,"top",R+(B-H)),L||(i.fire("ObjectResizeStart",{target:x,width:A,height:B}),L=!0)}function s(){function e(e,t){t&&(x.style[e]||!i.schema.isValid(x.nodeName.toLowerCase(),e)?b.setStyle(x,e,t):b.setAttrib(x,e,t))}L=!1,e("width",M),e("height",H),b.unbind(P,"mousemove",a),b.unbind(P,"mouseup",s),O!=P&&(b.unbind(O,"mousemove",a),b.unbind(O,"mouseup",s)),b.remove(w),I&&"TABLE"!=x.nodeName||l(x),i.fire("ObjectResized",{target:x,width:M,height:H}),i.nodeChanged()}function l(e,t,n){var r,l,u,d,f,p=i.getBody().offsetParent||i.getBody();r=b.getPos(e,p),T=r.x,R=r.y,f=e.getBoundingClientRect(),l=f.width||f.right-f.left,u=f.height||f.bottom-f.top,x!=e&&(m(),x=e,M=H=0),d=i.fire("ObjectSelected",{target:e}),o(e)&&!d.isDefaultPrevented()?C(_,function(e,r){function o(t){L=!0,k=t.screenX,S=t.screenY,A=x.clientWidth,B=x.clientHeight,D=B/A,N=e,w=x.cloneNode(!0),b.addClass(w,"mce-clonedresizable"),w.contentEditable=!1,w.unSelectabe=!0,b.setStyles(w,{left:T,top:R,margin:0}),w.removeAttribute("data-mce-selected"),i.getBody().appendChild(w),b.bind(P,"mousemove",a),b.bind(P,"mouseup",s),O!=P&&(b.bind(O,"mousemove",a),b.bind(O,"mouseup",s))}var c,d;return t?(r==t&&o(n),void 0):(c=b.get("mceResizeHandle"+r),c?b.show(c):(d=i.getBody(),c=b.add(d,"div",{id:"mceResizeHandle"+r,"data-mce-bogus":!0,"class":"mce-resizehandle",contentEditable:!1,unSelectabe:!0,style:"cursor:"+r+"-resize; margin:0; padding:0"}),b.bind(c,"mousedown",function(e){e.preventDefault(),o(e)})),b.setStyles(c,{left:l*e[0]+T-c.offsetWidth/2,top:u*e[1]+R-c.offsetHeight/2}),void 0)}):c(),x.setAttribute("data-mce-selected","1")}function c(){var e,t;x&&x.removeAttribute("data-mce-selected");for(e in _)t=b.get("mceResizeHandle"+e),t&&(b.unbind(t),b.remove(t))}function u(e){function t(e,t){do if(e===t)return!0;while(e=e.parentNode)}var n;return C(b.select("img[data-mce-selected],hr[data-mce-selected]"),function(e){e.removeAttribute("data-mce-selected")}),n="mousedown"==e.type?e.target:r.getNode(),n=b.getParent(n,I?"table":"table,img,hr"),n&&(g(),t(r.getStart(),n)&&t(r.getEnd(),n)&&(!I||n!=r.getStart()&&"IMG"!==r.getStart().nodeName))?(l(n),void 0):(c(),void 0)}function d(e,t,n){e&&e.attachEvent&&e.attachEvent("on"+t,n)}function f(e,t,n){e&&e.detachEvent&&e.detachEvent("on"+t,n)}function p(e){var t=e.srcElement,n,r,o,a,s,c,u;n=t.getBoundingClientRect(),c=E.clientX-n.left,u=E.clientY-n.top;for(r in _)if(o=_[r],a=t.offsetWidth*o[0],s=t.offsetHeight*o[1],Math.abs(a-c)<8&&Math.abs(s-u)<8){N=o;break}L=!0,i.getDoc().selection.empty(),l(t,r,E)}function h(e){var t=e.srcElement;if(t!=x){if(m(),0===t.id.indexOf("mceResizeHandle"))return e.returnValue=!1,void 0;("IMG"==t.nodeName||"TABLE"==t.nodeName)&&(c(),x=t,d(t,"resizestart",p))}}function m(){f(x,"resizestart",p)}function g(){try{i.getDoc().execCommand("enableObjectResizing",!1,!1)}catch(e){}}function v(e){var t;if(I){t=P.body.createControlRange();try{return t.addElement(e),t.select(),!0}catch(n){}}}function y(){x=w=null,I&&(m(),f(i.getBody(),"controlselect",h))}var b=i.dom,C=t.each,x,w,_,N,E,k,S,T,R,A,B,D,L,M,H,P=i.getDoc(),O=document,I=n.ie&&n.ie<11;_={n:[.5,0,0,-1],e:[1,.5,1,0],s:[.5,1,0,1],w:[0,.5,-1,0],nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var F=".mce-content-body";return i.contentStyles.push(F+" div.mce-resizehandle {"+"position: absolute;"+"border: 1px solid black;"+"background: #FFF;"+"width: 5px;"+"height: 5px;"+"z-index: 10000"+"}"+F+" .mce-resizehandle:hover {"+"background: #000"+"}"+F+" img[data-mce-selected], hr[data-mce-selected] {"+"outline: 1px solid black;"+"resize: none"+"}"+F+" .mce-clonedresizable {"+"position: absolute;"+(n.gecko?"":"outline: 1px dashed black;")+"opacity: .5;"+"filter: alpha(opacity=50);"+"z-index: 10000"+"}"),i.on("init",function(){I?(i.on("ObjectResized",function(e){"TABLE"!=e.target.nodeName&&(c(),v(e.target))}),d(i.getBody(),"controlselect",h),i.on("mousedown",function(e){E=e})):(g(),n.ie>=11&&i.on("mouseup mousedown",function(e){("IMG"==e.target.nodeName||"IMG"==i.selection.getNode().nodeName)&&(e.preventDefault(),i.selection.select(e.target))})),i.on("nodechange mousedown ResizeEditor",u),i.on("keydown keyup",function(e){x&&"TABLE"==x.nodeName&&u(e)})}),{controlSelect:v,destroy:y}}}),r(A,[f,S,R,g,p],function(e,n,r,i,o){function a(e,t,i,o){var a=this;a.dom=e,a.win=t,a.serializer=i,a.editor=o,a.controlSelection=new r(a,o),a.win.getSelection||(a.tridentSel=new n(a))}var s=o.each,l=o.grep,c=o.trim,u=i.ie,d=i.opera;return a.prototype={setCursorLocation:function(e,t){var n=this,r=n.dom.createRng();r.setStart(e,t),r.setEnd(e,t),n.setRng(r),n.collapse(!1)},getContent:function(e){var n=this,r=n.getRng(),i=n.dom.create("body"),o=n.getSel(),a,s,l;return e=e||{},a=s="",e.get=!0,e.format=e.format||"html",e.selection=!0,n.editor.fire("BeforeGetContent",e),"text"==e.format?n.isCollapsed()?"":r.text||(o.toString?o.toString():""):(r.cloneContents?(l=r.cloneContents(),l&&i.appendChild(l)):r.item!==t||r.htmlText!==t?(i.innerHTML="
    "+(r.item?r.item(0).outerHTML:r.htmlText),i.removeChild(i.firstChild)):i.innerHTML=r.toString(),/^\s/.test(i.innerHTML)&&(a=" "),/\s+$/.test(i.innerHTML)&&(s=" "),e.getInner=!0,e.content=n.isCollapsed()?"":a+n.serializer.serialize(i,e)+s,n.editor.fire("GetContent",e),e.content)},setContent:function(e,t){var n=this,r=n.getRng(),i,o=n.win.document,a,s;if(t=t||{format:"html"},t.set=!0,t.selection=!0,e=t.content=e,t.no_events||n.editor.fire("BeforeSetContent",t),e=t.content,r.insertNode){e+='_',r.startContainer==o&&r.endContainer==o?o.body.innerHTML=e:(r.deleteContents(),0===o.body.childNodes.length?o.body.innerHTML=e:r.createContextualFragment?r.insertNode(r.createContextualFragment(e)):(a=o.createDocumentFragment(),s=o.createElement("div"),a.appendChild(s),s.outerHTML=e,r.insertNode(a))),i=n.dom.get("__caret"),r=o.createRange(),r.setStartBefore(i),r.setEndBefore(i),n.setRng(r),n.dom.remove("__caret");try{n.setRng(r)}catch(l){}}else r.item&&(o.execCommand("Delete",!1,null),r=n.getRng()),/^\s+/.test(e)?(r.pasteHTML('_'+e),n.dom.remove("__mce_tmp")):r.pasteHTML(e);t.no_events||n.editor.fire("SetContent",t)},getStart:function(){var e=this,t=e.getRng(),n,r,i,o;if(t.duplicate||t.item){if(t.item)return t.item(0);for(i=t.duplicate(),i.collapse(1),n=i.parentElement(),n.ownerDocument!==e.dom.doc&&(n=e.dom.getRoot()),r=o=t.parentElement();o=o.parentNode;)if(o==n){n=r;break}return n}return n=t.startContainer,1==n.nodeType&&n.hasChildNodes()&&(n=n.childNodes[Math.min(n.childNodes.length-1,t.startOffset)]),n&&3==n.nodeType?n.parentNode:n},getEnd:function(){var e=this,t=e.getRng(),n,r;return t.duplicate||t.item?t.item?t.item(0):(t=t.duplicate(),t.collapse(0),n=t.parentElement(),n.ownerDocument!==e.dom.doc&&(n=e.dom.getRoot()),n&&"BODY"==n.nodeName?n.lastChild||n:n):(n=t.endContainer,r=t.endOffset,1==n.nodeType&&n.hasChildNodes()&&(n=n.childNodes[r>0?r-1:r]),n&&3==n.nodeType?n.parentNode:n)},getBookmark:function(e,t){function n(e,t){var n=0;return s(a.select(e),function(e,r){e==t&&(n=r)}),n}function r(e){function t(t){var n,r,i,o=t?"start":"end";n=e[o+"Container"],r=e[o+"Offset"],1==n.nodeType&&"TR"==n.nodeName&&(i=n.childNodes,n=i[Math.min(t?r:r-1,i.length-1)],n&&(r=t?0:n.childNodes.length,e["set"+(t?"Start":"End")](n,r)))}return t(!0),t(),e}function i(){function e(e,n){var i=e[n?"startContainer":"endContainer"],a=e[n?"startOffset":"endOffset"],s=[],l,c,u=0;if(3==i.nodeType){if(t)for(l=i.previousSibling;l&&3==l.nodeType;l=l.previousSibling)a+=l.nodeValue.length;s.push(a)}else c=i.childNodes,a>=c.length&&c.length&&(u=1,a=Math.max(0,c.length-1)),s.push(o.dom.nodeIndex(c[a],t)+u);for(;i&&i!=r;i=i.parentNode)s.push(o.dom.nodeIndex(i,t));return s}var n=o.getRng(!0),r=a.getRoot(),i={};return i.start=e(n,!0),o.isCollapsed()||(i.end=e(n)),i}var o=this,a=o.dom,l,c,u,d,f,p,h="",m;if(2==e)return p=o.getNode(),f=p.nodeName,"IMG"==f?{name:f,index:n(f,p)}:o.tridentSel?o.tridentSel.getBookmark(e):i();if(e)return{rng:o.getRng()};if(l=o.getRng(),u=a.uniqueId(),d=o.isCollapsed(),m="overflow:hidden;line-height:0px",l.duplicate||l.item){if(l.item)return p=l.item(0),f=p.nodeName,{name:f,index:n(f,p)};c=l.duplicate();try{l.collapse(),l.pasteHTML(''+h+""),d||(c.collapse(!1),l.moveToElementText(c.parentElement()),0===l.compareEndPoints("StartToEnd",c)&&c.move("character",-1),c.pasteHTML(''+h+""))}catch(g){return null}}else{if(p=o.getNode(),f=p.nodeName,"IMG"==f)return{name:f,index:n(f,p)};c=r(l.cloneRange()),d||(c.collapse(!1),c.insertNode(a.create("span",{"data-mce-type":"bookmark",id:u+"_end",style:m},h))),l=r(l),l.collapse(!0),l.insertNode(a.create("span",{"data-mce-type":"bookmark",id:u+"_start",style:m},h))}return o.moveToBookmark({id:u,keep:1}),{id:u}},moveToBookmark:function(e){function t(t){var n=e[t?"start":"end"],r,i,o,s;if(n){for(o=n[0],i=c,r=n.length-1;r>=1;r--){if(s=i.childNodes,n[r]>s.length-1)return;i=s[n[r]]}3===i.nodeType&&(o=Math.min(n[0],i.nodeValue.length)),1===i.nodeType&&(o=Math.min(n[0],i.childNodes.length)),t?a.setStart(i,o):a.setEnd(i,o)}return!0}function n(t){var n=o.get(e.id+"_"+t),r,i,a,c,u=e.keep;if(n&&(r=n.parentNode,"start"==t?(u?(r=n.firstChild,i=1):i=o.nodeIndex(n),f=p=r,h=m=i):(u?(r=n.firstChild,i=1):i=o.nodeIndex(n),p=r,m=i),!u)){for(c=n.previousSibling,a=n.nextSibling,s(l(n.childNodes),function(e){3==e.nodeType&&(e.nodeValue=e.nodeValue.replace(/\uFEFF/g,""))});n=o.get(e.id+"_"+t);)o.remove(n,1);c&&a&&c.nodeType==a.nodeType&&3==c.nodeType&&!d&&(i=c.nodeValue.length,c.appendData(a.nodeValue),o.remove(a),"start"==t?(f=p=c,h=m=i):(p=c,m=i))}}function r(e){return!o.isBlock(e)||e.innerHTML||u||(e.innerHTML='
    '),e}var i=this,o=i.dom,a,c,f,p,h,m;if(e)if(e.start){if(a=o.createRng(),c=o.getRoot(),i.tridentSel)return i.tridentSel.moveToBookmark(e);t(!0)&&t()&&i.setRng(a)}else e.id?(n("start"),n("end"),f&&(a=o.createRng(),a.setStart(r(f),h),a.setEnd(r(p),m),i.setRng(a))):e.name?i.select(o.select(e.name)[e.index]):e.rng&&i.setRng(e.rng)},select:function(t,n){function r(t,n){var r=new e(t,t);do{if(3==t.nodeType&&0!==c(t.nodeValue).length)return n?a.setStart(t,0):a.setEnd(t,t.nodeValue.length),void 0;if("BR"==t.nodeName)return n?a.setStartBefore(t):a.setEndBefore(t),void 0}while(t=n?r.next():r.prev())}var i=this,o=i.dom,a=o.createRng(),s;if(i.lastFocusBookmark=null,t){if(!n&&i.controlSelection.controlSelect(t))return;s=o.nodeIndex(t),a.setStart(t.parentNode,s),a.setEnd(t.parentNode,s+1),n&&(r(t,1),r(t)),i.setRng(a)}return t},isCollapsed:function(){var e=this,t=e.getRng(),n=e.getSel();return!t||t.item?!1:t.compareEndPoints?0===t.compareEndPoints("StartToEnd",t):!n||t.collapsed},collapse:function(e){var t=this,n=t.getRng(),r;n.item&&(r=n.item(0),n=t.win.document.body.createTextRange(),n.moveToElementText(r)),n.collapse(!!e),t.setRng(n)},getSel:function(){var e=this.win;return e.getSelection?e.getSelection():e.document.selection},getRng:function(e){var t=this,n,r,i,o=t.win.document,a;if(!e&&t.lastFocusBookmark){var s=t.lastFocusBookmark;return s.startContainer?(r=o.createRange(),r.setStart(s.startContainer,s.startOffset),r.setEnd(s.endContainer,s.endOffset)):r=s,r}if(e&&t.tridentSel)return t.tridentSel.getRangeAt(0);try{(n=t.getSel())&&(r=n.rangeCount>0?n.getRangeAt(0):n.createRange?n.createRange():o.createRange())}catch(l){}if(u&&r&&r.setStart){try{a=o.selection.createRange()}catch(l){}a&&a.item&&(i=a.item(0),r=o.createRange(),r.setStartBefore(i),r.setEndAfter(i))}return r||(r=o.createRange?o.createRange():o.body.createTextRange()),r.setStart&&9===r.startContainer.nodeType&&r.collapsed&&(i=t.dom.getRoot(),r.setStart(i,0),r.setEnd(i,0)),t.selectedRange&&t.explicitRange&&(0===r.compareBoundaryPoints(r.START_TO_START,t.selectedRange)&&0===r.compareBoundaryPoints(r.END_TO_END,t.selectedRange)?r=t.explicitRange:(t.selectedRange=null,t.explicitRange=null)),r},setRng:function(e,t){var n=this,r;if(e.select)try{e.select()}catch(i){}else if(n.tridentSel){if(e.cloneRange)try{return n.tridentSel.addRange(e),void 0}catch(i){}}else if(r=n.getSel()){n.explicitRange=e;try{r.removeAllRanges(),r.addRange(e)}catch(i){}t===!1&&r.extend&&(r.collapse(e.endContainer,e.endOffset),r.extend(e.startContainer,e.startOffset)),n.selectedRange=r.rangeCount>0?r.getRangeAt(0):null}},setNode:function(e){var t=this;return t.setContent(t.dom.getOuterHTML(e)),e},getNode:function(){function e(e,t){for(var n=e;e&&3===e.nodeType&&0===e.length;)e=t?e.nextSibling:e.previousSibling;return e||n}var t=this,n=t.getRng(),r,i=n.startContainer,o=n.endContainer,a=n.startOffset,s=n.endOffset;return n?n.setStart?(r=n.commonAncestorContainer,!n.collapsed&&(i==o&&2>s-a&&i.hasChildNodes()&&(r=i.childNodes[a]),3===i.nodeType&&3===o.nodeType&&(i=i.length===a?e(i.nextSibling,!0):i.parentNode,o=0===s?e(o.previousSibling,!1):o.parentNode,i&&i===o))?i:r&&3==r.nodeType?r.parentNode:r):n.item?n.item(0):n.parentElement():t.dom.getRoot()},getSelectedBlocks:function(t,n){var r=this,i=r.dom,o,a,s=[];if(a=i.getRoot(),t=i.getParent(t||r.getStart(),i.isBlock),n=i.getParent(n||r.getEnd(),i.isBlock),t&&t!=a&&s.push(t),t&&n&&t!=n){o=t;for(var l=new e(t,a);(o=l.next())&&o!=n;)i.isBlock(o)&&s.push(o)}return n&&t!=n&&n!=a&&s.push(n),s},isForward:function(){var e=this.dom,t=this.getSel(),n,r;return t&&t.anchorNode&&t.focusNode?(n=e.createRng(),n.setStart(t.anchorNode,t.anchorOffset),n.collapse(!0),r=e.createRng(),r.setStart(t.focusNode,t.focusOffset),r.collapse(!0),n.compareBoundaryPoints(n.START_TO_START,r)<=0):!0},normalize:function(){function t(t){function a(t,n){for(var r=new e(t,f.getParent(t.parentNode,f.isBlock)||p);t=r[n?"prev":"next"]();)if("BR"===t.nodeName)return!0}function s(e,t){return e.previousSibling&&e.previousSibling.nodeName==t}function l(t,n){var r,a;for(n=n||c,r=new e(n,f.getParent(n.parentNode,f.isBlock)||p);h=r[t?"prev":"next"]();){if(3===h.nodeType&&h.nodeValue.length>0)return c=h,u=t?h.nodeValue.length:0,i=!0,void 0;if(f.isBlock(h)||m[h.nodeName.toLowerCase()])return;a=h}o&&a&&(c=a,i=!0,u=0)}var c,u,d,f=n.dom,p=f.getRoot(),h,m,g;if(c=r[(t?"start":"end")+"Container"],u=r[(t?"start":"end")+"Offset"],m=f.schema.getNonEmptyElements(),9===c.nodeType&&(c=f.getRoot(),u=0),c===p){if(t&&(h=c.childNodes[u>0?u-1:0],h&&(g=h.nodeName.toLowerCase(),m[h.nodeName]||"TABLE"==h.nodeName)))return;if(c.hasChildNodes()&&(u=Math.min(!t&&u>0?u-1:u,c.childNodes.length-1),c=c.childNodes[u],u=0,c.hasChildNodes()&&!/TABLE/.test(c.nodeName))){h=c,d=new e(c,p);do{if(3===h.nodeType&&h.nodeValue.length>0){u=t?0:h.nodeValue.length,c=h,i=!0;break}if(m[h.nodeName.toLowerCase()]){u=f.nodeIndex(h),c=h.parentNode,"IMG"!=h.nodeName||t||u++,i=!0;break}}while(h=t?d.next():d.prev())}}o&&(3===c.nodeType&&0===u&&l(!0),1===c.nodeType&&(h=c.childNodes[u],!h||"BR"!==h.nodeName||s(h,"A")||a(h)||a(h,!0)||l(!0,c.childNodes[u]))),t&&!o&&3===c.nodeType&&u===c.nodeValue.length&&l(!1),i&&r["set"+(t?"Start":"End")](c,u)}var n=this,r,i,o;u||(r=n.getRng(),o=r.collapsed,t(!0),o||t(),i&&(o&&r.collapse(!0),n.setRng(r,n.isForward())))},selectorChanged:function(e,t){var n=this,r;return n.selectorChangedData||(n.selectorChangedData={},r={},n.editor.on("NodeChange",function(e){var t=e.element,i=n.dom,o=i.getParents(t,null,i.getRoot()),a={};s(n.selectorChangedData,function(e,t){s(o,function(n){return i.is(n,t)?(r[t]||(s(e,function(e){e(!0,{node:n,selector:t,parents:o})}),r[t]=e),a[t]=e,!1):void 0})}),s(r,function(e,n){a[n]||(delete r[n],s(e,function(e){e(!1,{node:t,selector:n,parents:o})}))})})),n.selectorChangedData[e]||(n.selectorChangedData[e]=[]),n.selectorChangedData[e].push(t),n},scrollIntoView:function(e){var t,n,r=this,i=r.dom;n=i.getViewPort(r.editor.getWin()),t=i.getPos(e).y,(tn.y+n.h)&&r.editor.getWin().scrollTo(0,t=t.nodeValue.length&&e.splice(0,1),t=e[e.length-1],0===d&&e.length>0&&t===u&&3===t.nodeType&&e.splice(e.length-1,1),e}function o(e,t,n){for(var r=[];e&&e!=n;e=e[t])r.push(e);return r}function a(e,t){do{if(e.parentNode==t)return e;e=e.parentNode}while(e)}function s(e,t,n){var a=n?"nextSibling":"previousSibling";for(m=e,g=m.parentNode;m&&m!=t;m=g)g=m.parentNode,v=o(m==e?m:m[a],a),v.length&&(n||v.reverse(),r(i(v)))}var l=t.startContainer,c=t.startOffset,u=t.endContainer,d=t.endOffset,f,p,h,m,g,v,y;if(y=e.select("td.mce-item-selected,th.mce-item-selected"),y.length>0)return n(y,function(e){r([e])}),void 0;if(1==l.nodeType&&l.hasChildNodes()&&(l=l.childNodes[c]),1==u.nodeType&&u.hasChildNodes()&&(u=u.childNodes[Math.min(d-1,u.childNodes.length-1)]),l==u)return r(i([l]));for(f=e.findCommonAncestor(l,u),m=l;m;m=m.parentNode){if(m===u)return s(l,f,!0);if(m===f)break}for(m=u;m;m=m.parentNode){if(m===l)return s(u,f);if(m===f)break}p=a(l,f)||l,h=a(u,f)||u,s(l,p,!0),v=o(p==l?p:p.nextSibling,"nextSibling",h==u?h.nextSibling:h),v.length&&r(i(v)),s(u,h)},this.split=function(e){function t(e,t){return e.splitText(t)}var n=e.startContainer,r=e.startOffset,i=e.endContainer,o=e.endOffset;return n==i&&3==n.nodeType?r>0&&rr?(o-=r,n=i=t(i,o).previousSibling,o=i.nodeValue.length,r=0):o=0):(3==n.nodeType&&r>0&&r0&&o=e;e++)r.addShortcut("ctrl+"+e,"",["FormatBlock",!1,"h"+e]);r.addShortcut("ctrl+7","",["FormatBlock",!1,"p"]),r.addShortcut("ctrl+8","",["FormatBlock",!1,"div"]),r.addShortcut("ctrl+9","",["FormatBlock",!1,"address"])}function c(e){return e?O[e]:O}function u(e,t){e&&("string"!=typeof e?et(e,function(e,t){u(t,e)}):(t=t.length?t:[t],et(t,function(e){e.deep===X&&(e.deep=!e.selector),e.split===X&&(e.split=!e.selector||e.inline),e.remove===X&&e.selector&&!e.inline&&(e.remove="none"),e.selector&&e.inline&&(e.mixed=!0,e.block_expand=!0),"string"==typeof e.classes&&(e.classes=e.classes.split(/\s+/))}),O[e]=t))}function d(e){var t;return r.dom.getParent(e,function(e){return t=r.dom.getStyle(e,"text-decoration"),t&&"none"!==t}),t}function f(e){var t;1===e.nodeType&&e.parentNode&&1===e.parentNode.nodeType&&(t=d(e.parentNode),r.dom.getStyle(e,"color")&&t?r.dom.setStyle(e,"text-decoration",t):r.dom.getStyle(e,"textdecoration")===t&&r.dom.setStyle(e,"text-decoration",null))}function p(t,n,o){function s(e,t){t=t||m,e&&(t.onformat&&t.onformat(e,t,n,o),et(t.styles,function(t,r){I.setStyle(e,r,E(t,n))}),et(t.attributes,function(t,r){I.setAttrib(e,r,E(t,n))}),et(t.classes,function(t){t=E(t,n),I.hasClass(e,t)||I.addClass(e,t)}))}function l(){function t(t,n){var r=new e(n);for(o=r.current();o;o=r.prev())if(o.childNodes.length>1||o==t||"BR"==o.tagName)return o}var n=r.selection.getRng(),i=n.startContainer,a=n.endContainer;if(i!=a&&0===n.endOffset){var s=t(i,a),l=3==s.nodeType?s.length:s.childNodes.length;n.setEnd(s,l)}return n}function u(e,t,n,r,i){var o=[],a=-1,s,l=-1,c=-1,u;return et(e.childNodes,function(e,t){return"UL"===e.nodeName||"OL"===e.nodeName?(a=t,s=e,!1):void 0}),et(e.childNodes,function(e,n){"SPAN"===e.nodeName&&"bookmark"==I.getAttrib(e,"data-mce-type")&&(e.id==t.id+"_start"?l=n:e.id==t.id+"_end"&&(c=n))}),0>=a||a>l&&c>a?(et(tt(e.childNodes),i),0):(u=I.clone(n,K),et(tt(e.childNodes),function(e,t){(a>l&&a>t||l>a&&t>a)&&(o.push(e),e.parentNode.removeChild(e))}),a>l?e.insertBefore(u,s):l>a&&e.insertBefore(u,s.nextSibling),r.push(u),et(o,function(e){u.appendChild(e)}),u)}function d(e,r,o){var l=[],c,d,f=!0;c=m.inline||m.block,d=I.create(c),s(d),W.walk(e,function(e){function p(e){var y,C,x,_,N;return N=f,y=e.nodeName.toLowerCase(),C=e.parentNode.nodeName.toLowerCase(),1===e.nodeType&&J(e)&&(N=f,f="true"===J(e),_=!0),w(y,"br")?(v=0,m.block&&I.remove(e),void 0):m.wrapper&&g(e,t,n)?(v=0,void 0):f&&!_&&m.block&&!m.wrapper&&i(y)&&z(C,c)?(e=I.rename(e,c),s(e),l.push(e),v=0,void 0):m.selector&&(et(h,function(t){"collapsed"in t&&t.collapsed!==b||I.is(e,t.selector)&&!a(e)&&(s(e,t),x=!0)}),!m.inline||x)?(v=0,void 0):(!f||_||!z(c,y)||!z(C,c)||!o&&3===e.nodeType&&1===e.nodeValue.length&&65279===e.nodeValue.charCodeAt(0)||a(e)||m.inline&&V(e)?"li"==y&&r?v=u(e,r,d,l,p):(v=0,et(tt(e.childNodes),p),_&&(f=N),v=0):(v||(v=I.clone(d,K),e.parentNode.insertBefore(v,e),l.push(v)),v.appendChild(e)),void 0)}var v;et(e,p)}),m.wrap_links===!1&&et(l,function(e){function t(e){var n,r,i;if("A"===e.nodeName){for(r=I.clone(d,K),l.push(r),i=tt(e.childNodes),n=0;n1||!V(e))&&0===o)return I.remove(e,1),void 0;if(m.inline||m.wrapper){if(m.exact||1!==o||(e=i(e)),et(h,function(t){et(I.select(t.inline,e),function(e){var r;if(t.wrap_links===!1){r=e.parentNode;do if("A"===r.nodeName)return;while(r=r.parentNode)}R(t,n,e,t.exact?e:null)})}),g(e.parentNode,t,n))return I.remove(e,1),e=0,G;m.merge_with_parents&&I.getParent(e.parentNode,function(r){return g(r,t,n)?(I.remove(e,1),e=0,G):void 0}),e&&m.merge_siblings!==!1&&(e=L(B(e),e),e=L(e,B(e,G)))}})}var h=c(t),m=h[0],v,y,b=!o&&F.isCollapsed();if(m)if(o)o.nodeType?(y=I.createRng(),y.setStartBefore(o),y.setEndAfter(o),d(T(y,h),null,!0)):d(o,null,!0);else if(b&&m.inline&&!I.select("td.mce-item-selected,th.mce-item-selected").length)H("apply",t,n);else{var C=r.selection.getNode();U||!h[0].defaultBlock||I.getParent(C,I.isBlock)||p(h[0].defaultBlock),r.selection.setRng(l()),v=F.getBookmark(),d(T(F.getRng(G),h),v),m.styles&&(m.styles.color||m.styles.textDecoration)&&(nt(C,f,"childNodes"),f(C)),F.moveToBookmark(v),P(F.getRng(G)),r.nodeChanged()}}function h(e,t,n){function i(e){var n,r,o,a,s;if(1===e.nodeType&&J(e)&&(a=b,b="true"===J(e),s=!0),n=tt(e.childNodes),b&&!s)for(r=0,o=p.length;o>r&&!R(p[r],t,e,e);r++);if(h.deep&&n.length){for(r=0,o=n.length;o>r;r++)i(n[r]);s&&(b=a)}}function a(n){var r;return et(o(n.parentNode).reverse(),function(n){var i;r||"_start"==n.id||"_end"==n.id||(i=g(n,e,t),i&&i.split!==!1&&(r=n))}),r}function s(e,n,r,i){var o,a,s,l,c,u;if(e){for(u=e.parentNode,o=n.parentNode;o&&o!=u;o=o.parentNode){for(a=I.clone(o,K),c=0;c=0;a--){if(s=t[a].selector,!s||t[a].defaultBlock)return G;for(i=r.length-1;i>=0;i--)if(I.is(r[i],s))return G}return K}function C(e,t,n){var i;return Y||(Y={},i={},r.on("NodeChange",function(e){var t=o(e.element),n={};et(Y,function(e,r){et(t,function(o){return g(o,r,{},e.similar)?(i[r]||(et(e,function(e){e(!0,{node:o,format:r,parents:t})}),i[r]=e),n[r]=e,!1):void 0})}),et(i,function(r,o){n[o]||(delete i[o],et(r,function(n){n(!1,{node:e.element,format:o,parents:t})}))})})),et(e.split(","),function(e){Y[e]||(Y[e]=[],Y[e].similar=n),Y[e].push(t)}),this}function x(e,t){return w(e,t.inline)?G:w(e,t.block)?G:t.selector?1==e.nodeType&&I.is(e,t.selector):void 0}function w(e,t){return e=e||"",t=t||"",e=""+(e.nodeName||e),t=""+(t.nodeName||t),e.toLowerCase()==t.toLowerCase()}function _(e,t){return N(I.getStyle(e,t),t)}function N(e,t){return("color"==t||"backgroundColor"==t)&&(e=I.toHex(e)),"fontWeight"==t&&700==e&&(e="bold"),"fontFamily"==t&&(e=e.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+e}function E(e,t){return"string"!=typeof e?e=e(t):t&&(e=e.replace(/%(\w+)/g,function(e,n){return t[n]||e})),e}function k(e){return e&&3===e.nodeType&&/^([\t \r\n]+|)$/.test(e.nodeValue)}function S(e,t,n){var r=I.create(t,n);return e.parentNode.insertBefore(r,e),r.appendChild(e),r}function T(t,n,a){function s(e){function t(e){return"BR"==e.nodeName&&e.getAttribute("data-mce-bogus")&&!e.nextSibling}var r,i,o,a,s;if(r=i=e?g:y,a=e?"previousSibling":"nextSibling",s=I.getRoot(),3==r.nodeType&&!k(r)&&(e?v>0:br?n:r,-1===n||a||n++):(n=o.indexOf(" ",t),r=o.indexOf("\xa0",t),n=-1!==n&&(-1===r||r>n)?n:r),n}var s,l,c,u;if(3===t.nodeType){if(c=o(t,n),-1!==c)return{container:t,offset:c};u=t}for(s=new e(t,I.getParent(t,V)||r.getBody());l=s[i?"prev":"next"]();)if(3===l.nodeType){if(u=l,c=o(l),-1!==c)return{container:l,offset:c}}else if(V(l))break;return u?(n=i?0:u.length,{container:u,offset:n}):void 0}function d(e,r){var i,a,s,l;for(3==e.nodeType&&0===e.nodeValue.length&&e[r]&&(e=e[r]),i=o(e),a=0;ap?p:v],3==g.nodeType&&(v=0)),1==y.nodeType&&y.hasChildNodes()&&(p=y.childNodes.length-1,y=y.childNodes[b>p?p:b-1],3==y.nodeType&&(b=y.nodeValue.length)),g=c(g),y=c(y),(D(g.parentNode)||D(g))&&(g=D(g)?g:g.parentNode,g=g.nextSibling||g,3==g.nodeType&&(v=0)),(D(y.parentNode)||D(y))&&(y=D(y)?y:y.parentNode,y=y.previousSibling||y,3==y.nodeType&&(b=y.length)),n[0].inline&&(t.collapsed&&(m=u(g,v,!0),m&&(g=m.container,v=m.offset),m=u(y,b),m&&(y=m.container,b=m.offset)),h=l(y,b),h.node)){for(;h.node&&0===h.offset&&h.node.previousSibling;)h=l(h.node.previousSibling);h.node&&h.offset>0&&3===h.node.nodeType&&" "===h.node.nodeValue.charAt(h.offset-1)&&h.offset>1&&(y=h.node,y.splitText(h.offset-1))}return(n[0].inline||n[0].block_expand)&&(n[0].inline&&3==g.nodeType&&0!==v||(g=s(!0)),n[0].inline&&3==y.nodeType&&b!==y.nodeValue.length||(y=s())),n[0].selector&&n[0].expand!==K&&!n[0].inline&&(g=d(g,"previousSibling"),y=d(y,"nextSibling")),(n[0].block||n[0].selector)&&(g=f(g,"previousSibling"),y=f(y,"nextSibling"),n[0].block&&(V(g)||(g=s(!0)),V(y)||(y=s()))),1==g.nodeType&&(v=q(g),g=g.parentNode),1==y.nodeType&&(b=q(y)+1,y=y.parentNode),{startContainer:g,startOffset:v,endContainer:y,endOffset:b}}function R(e,t,n,r){var i,o,a;if(!x(n,e))return K;if("all"!=e.remove)for(et(e.styles,function(e,i){e=N(E(e,t),i),"number"==typeof i&&(i=e,r=0),(!r||w(_(r,i),e))&&I.setStyle(n,i,""),a=1}),a&&""===I.getAttrib(n,"style")&&(n.removeAttribute("style"),n.removeAttribute("data-mce-style")),et(e.attributes,function(e,i){var o;if(e=E(e,t),"number"==typeof i&&(i=e,r=0),!r||w(I.getAttrib(r,i),e)){if("class"==i&&(e=I.getAttrib(n,i),e&&(o="",et(e.split(/\s+/),function(e){/mce\w+/.test(e)&&(o+=(o?" ":"")+e)}),o)))return I.setAttrib(n,i,o),void 0;"class"==i&&n.removeAttribute("className"),$.test(i)&&n.removeAttribute("data-mce-"+i),n.removeAttribute(i)}}),et(e.classes,function(e){e=E(e,t),(!r||I.hasClass(r,e))&&I.removeClass(n,e)}),o=I.getAttribs(n),i=0;ia?a:o]),3===i.nodeType&&n&&o>=i.nodeValue.length&&(i=new e(i,r.getBody()).next()||i),3!==i.nodeType||n||0!==o||(i=new e(i,r.getBody()).prev()||i),i}function H(t,n,o){function a(e){var t=I.create("span",{id:y,"data-mce-bogus":!0,style:b?"color:red":""});return e&&t.appendChild(r.getDoc().createTextNode(j)),t}function s(e,t){for(;e;){if(3===e.nodeType&&e.nodeValue!==j||e.childNodes.length>1)return!1;t&&1===e.nodeType&&t.push(e),e=e.firstChild}return!0}function l(e){for(;e;){if(e.id===y)return e;e=e.parentNode}}function u(t){var n;if(t)for(n=new e(t,t),t=n.current();t;t=n.next())if(3===t.nodeType)return t}function d(e,t){var n,r;if(e)r=F.getRng(!0),s(e)?(t!==!1&&(r.setStartBefore(e),r.setEndBefore(e)),I.remove(e)):(n=u(e),n.nodeValue.charAt(0)===j&&(n=n.deleteData(0,1)),I.remove(e,1)),F.setRng(r);else if(e=l(F.getStart()),!e)for(;e=I.get(y);)d(e,!1)}function f(){var e,t,r,i,s,d,f;e=F.getRng(!0),i=e.startOffset,d=e.startContainer,f=d.nodeValue,t=l(F.getStart()),t&&(r=u(t)),f&&i>0&&i=0;p--)u.appendChild(I.clone(f[p],!1)),u=u.firstChild;u.appendChild(I.doc.createTextNode(j)),u=u.firstChild;var v=I.getParent(d,i);v&&I.isEmpty(v)?d.parentNode.replaceChild(m,d):I.insertAfter(m,d),F.setCursorLocation(u,1),I.isEmpty(d)&&I.remove(d)}}function v(){var e;e=l(F.getStart()),e&&!I.isEmpty(e)&&nt(e,function(e){1!=e.nodeType||e.id===y||I.isEmpty(e)||I.setAttrib(e,"data-mce-bogus",null)},"childNodes")}var y="_mce_caret",b=r.settings.caret_debug;r._hasCaretEvents||(Z=function(){var e=[],t;if(s(l(F.getStart()),e))for(t=e.length;t--;)I.setAttrib(e[t],"data-mce-bogus","1")},Q=function(e){var t=e.keyCode;d(),(8==t||37==t||39==t)&&d(l(F.getStart())),v()},r.on("SetContent",function(e){e.selection&&v()}),r._hasCaretEvents=!0),"apply"==t?f():m()}function P(t){var n=t.startContainer,r=t.startOffset,i,o,a,s,l;if(3==n.nodeType&&r>=n.nodeValue.length&&(r=q(n),n=n.parentNode,i=!0),1==n.nodeType)for(s=n.childNodes,n=s[Math.min(r,s.length-1)],o=new e(n,I.getParent(n,I.isBlock)),(r>s.length-1||i)&&o.next(),a=o.current();a;a=o.next())if(3==a.nodeType&&!k(a))return l=I.create("a",null,j),a.parentNode.insertBefore(l,a),t.setStart(a,0),F.setRng(t),I.remove(l),void 0}var O={},I=r.dom,F=r.selection,W=new t(I),z=r.schema.isValidChild,V=I.isBlock,U=r.settings.forced_root_block,q=I.nodeIndex,j="\ufeff",$=/^(src|href|style)$/,K=!1,G=!0,Y,X,J=I.getContentEditable,Q,Z,et=n.each,tt=n.grep,nt=n.walk,rt=n.extend;rt(this,{get:c,register:u,apply:p,remove:h,toggle:m,match:v,matchAll:y,matchNode:g,canApply:b,formatChanged:C}),s(),l(),r.on("BeforeGetContent",function(){Z&&Z()}),r.on("mouseup keydown",function(e){Q&&Q(e)})}}),r(L,[g,p],function(e,t){var n=t.trim,r;return r=new RegExp(["]+data-mce-bogus[^>]+>[\u200b\ufeff]+<\\/span>","]+data-mce-bogus[^>]+><\\/div>",'\\s?data-mce-selected="[^"]+"'].join("|"),"gi"),function(t){function i(){return n(t.getContent({format:"raw",no_events:1}).replace(r,""))}function o(){a.typing=!1,a.add()}var a,s=0,l=[],c,u,d;return t.on("init",function(){a.add()}),t.on("BeforeExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&a.beforeChange()}),t.on("ExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&a.add()}),t.on("ObjectResizeStart",function(){a.beforeChange()}),t.on("SaveContent ObjectResized",o),t.dom.bind(t.dom.getRoot(),"dragend",o),t.dom.bind(t.getBody(),"focusout",function(){!t.removed&&a.typing&&o()}),t.on("KeyUp",function(n){var r=n.keyCode;(r>=33&&36>=r||r>=37&&40>=r||45==r||13==r||n.ctrlKey)&&(o(),t.nodeChanged()),(46==r||8==r||e.mac&&(91==r||93==r))&&t.nodeChanged(),u&&a.typing&&(t.isDirty()||(t.isNotDirty=!l[0]||i()==l[0].content,t.isNotDirty||t.fire("change",{level:l[0],lastLevel:null})),t.fire("TypingUndo"),u=!1,t.nodeChanged())}),t.on("KeyDown",function(e){var t=e.keyCode;return t>=33&&36>=t||t>=37&&40>=t||45==t?(a.typing&&o(),void 0):((16>t||t>20)&&224!=t&&91!=t&&!a.typing&&(a.beforeChange(),a.typing=!0,a.add(),u=!0),void 0)}),t.on("MouseDown",function(){a.typing&&o()}),t.addShortcut("ctrl+z","","Undo"),t.addShortcut("ctrl+y,ctrl+shift+z","","Redo"),t.on("AddUndo Undo Redo ClearUndos MouseUp",function(e){e.isDefaultPrevented()||t.nodeChanged()}),a={data:l,typing:!1,beforeChange:function(){d||(c=t.selection.getBookmark(2,!0))},add:function(e){var n,r=t.settings,o;if(e=e||{},e.content=i(),d||t.fire("BeforeAddUndo",{level:e}).isDefaultPrevented())return null;if(o=l[s],o&&o.content==e.content)return null;if(l[s]&&(l[s].beforeBookmark=c),r.custom_undo_redo_levels&&l.length>r.custom_undo_redo_levels){for(n=0;n0&&(t.fire("change",a),t.isNotDirty=!1),e},undo:function(){var e;return a.typing&&(a.add(),a.typing=!1),s>0&&(e=l[--s],t.setContent(e.content,{format:"raw"}),t.selection.moveToBookmark(e.beforeBookmark),t.fire("undo",{level:e})),e},redo:function(){var e;return s0||a.typing&&l[0]&&i()!=l[0].content},hasRedo:function(){return sR)&&(l=i.create("br"),t.parentNode.insertBefore(l,t)),a.setStartBefore(t),a.setEndBefore(t)):(a.setStartAfter(t),a.setEndAfter(t)):(a.setStart(t,0),a.setEnd(t,0));o.setRng(a),i.remove(l),o.scrollIntoView(t)}function h(e){var t=k,r,o,s;if(r=e||"TABLE"==M?i.create(e||P):T.cloneNode(!1),s=r,a.keep_styles!==!1)do if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(t.nodeName)){if("_mce_caret"==t.id)continue;o=t.cloneNode(!1),i.setAttrib(o,"id",""),r.hasChildNodes()?(o.appendChild(r.firstChild),r.appendChild(o)):(s=o,r.appendChild(o))}while(t=t.parentNode);return n||(s.innerHTML='
    '),r}function m(t){var n,r,i;if(3==k.nodeType&&(t?S>0:S0)return!0}function b(){var e,t,r;k&&3==k.nodeType&&S>=k.nodeValue.length&&(n||y()||(e=i.create("br"),_.insertNode(e),_.setStartAfter(e),_.setEndAfter(e),t=!0)),e=i.create("br"),_.insertNode(e),n&&"PRE"==M&&(!R||8>R)&&e.parentNode.insertBefore(i.doc.createTextNode("\r"),e),r=i.create("span",{}," "),e.parentNode.insertBefore(r,e),o.scrollIntoView(r),i.remove(r),t?(_.setStartBefore(e),_.setEndBefore(e)):(_.setStartAfter(e),_.setEndAfter(e)),o.setRng(_),s.add()}function C(e){do 3===e.nodeType&&(e.nodeValue=e.nodeValue.replace(/^[\r\n]+/,"")),e=e.firstChild;while(e)}function x(e){var t=i.getRoot(),n,r;for(n=e;n!==t&&"false"!==i.getContentEditable(n);)"true"===i.getContentEditable(n)&&(r=n),n=n.parentNode;return n!==t?r:t}function w(e){var t;n||(e.normalize(),t=e.lastChild,(!t||/^(left|right)$/gi.test(i.getStyle(t,"float",!0)))&&i.add(e,"br"))}var _=o.getRng(!0),N,E,k,S,T,R,A,B,D,L,M,H,P,O;if(!_.collapsed)return t.execCommand("Delete"),void 0;if(!r.isDefaultPrevented()&&(k=_.startContainer,S=_.startOffset,P=(a.force_p_newlines?"p":"")||a.forced_root_block,P=P?P.toUpperCase():"",R=i.doc.documentMode,A=r.shiftKey,1==k.nodeType&&k.hasChildNodes()&&(O=S>k.childNodes.length-1,k=k.childNodes[Math.min(S,k.childNodes.length-1)]||k,S=O&&3==k.nodeType?k.nodeValue.length:0),E=x(k))){if(s.beforeChange(),!i.isBlock(E)&&E!=i.getRoot())return(!P||A)&&b(),void 0;if((P&&!A||!P&&A)&&(k=g(k,S)),T=i.getParent(k,i.isBlock),L=T?i.getParent(T.parentNode,i.isBlock):null,M=T?T.nodeName.toUpperCase():"",H=L?L.nodeName.toUpperCase():"","LI"!=H||r.ctrlKey||(T=L,M=H),"LI"==M){if(!P&&A)return b(),void 0;if(i.isEmpty(T))return v(),void 0}if("PRE"==M&&a.br_in_pre!==!1){if(!A)return b(),void 0}else if(!P&&!A&&"LI"!=M||P&&A)return b(),void 0;P&&T===t.getBody()||(P=P||"P",m()?(B=/^(H[1-6]|PRE|FIGURE)$/.test(M)&&"HGROUP"!=H?h(P):h(),a.end_container_on_empty_block&&u(L)&&i.isEmpty(T)?B=i.split(L,T):i.insertAfter(B,T),p(B)):m(!0)?(B=T.parentNode.insertBefore(h(),T),d(B)):(N=_.cloneRange(),N.setEndAfter(T),D=N.extractContents(),C(D),B=D.firstChild,i.insertAfter(D,T),f(B),w(T),p(B)),i.setAttrib(B,"id",""),s.add())}}var i=t.dom,o=t.selection,a=t.settings,s=t.undoManager,l=t.schema,c=l.getNonEmptyElements();t.on("keydown",function(e){13==e.keyCode&&r(e)!==!1&&e.preventDefault()})}}),r(H,[],function(){return function(e){function t(){var t=i.getStart(),s=e.getBody(),l,c,u,d,f,p,h,m=-16777215,g,v,y,b,C;if(C=n.forced_root_block,t&&1===t.nodeType&&C){for(;t&&t!=s;){if(a[t.nodeName])return;t=t.parentNode}if(l=i.getRng(),l.setStart){c=l.startContainer,u=l.startOffset,d=l.endContainer,f=l.endOffset;try{v=e.getDoc().activeElement===s}catch(x){}}else l.item&&(t=l.item(0),l=e.getDoc().body.createTextRange(),l.moveToElementText(t)),v=l.parentElement().ownerDocument===e.getDoc(),y=l.duplicate(),y.collapse(!0),u=-1*y.move("character",m),y.collapsed||(y=l.duplicate(),y.collapse(!1),f=-1*y.move("character",m)-u);for(t=s.firstChild,b=s.nodeName.toLowerCase();t;)if((3===t.nodeType||1==t.nodeType&&!a[t.nodeName])&&o.isValidChild(b,C.toLowerCase())){if(3===t.nodeType&&0===t.nodeValue.length){h=t,t=t.nextSibling,r.remove(h);continue}p||(p=r.create(C),t.parentNode.insertBefore(p,t),g=!0),h=t,t=t.nextSibling,p.appendChild(h)}else p=null,t=t.nextSibling;if(g&&v){if(l.setStart)l.setStart(c,u),l.setEnd(d,f),i.setRng(l);else try{l=e.getDoc().body.createTextRange(),l.moveToElementText(s),l.collapse(!0),l.moveStart("character",u),f>0&&l.moveEnd("character",f),l.select()}catch(x){}e.nodeChanged()}}}var n=e.settings,r=e.dom,i=e.selection,o=e.schema,a=o.getBlockElements();n.forced_root_block&&e.on("NodeChange",t)}}),r(P,[E,g,p],function(e,n,r){var i=r.each,o=r.extend,a=r.map,s=r.inArray,l=r.explode,c=n.gecko,u=n.ie,d=!0,f=!1;return function(n){function r(e,t,n){var r;return e=e.toLowerCase(),(r=_.exec[e])?(r(e,t,n),d):f}function p(e){var t;return e=e.toLowerCase(),(t=_.state[e])?t(e):-1}function h(e){var t;return e=e.toLowerCase(),(t=_.value[e])?t(e):f}function m(e,t){t=t||"exec",i(e,function(e,n){i(n.toLowerCase().split(","),function(n){_[t][n]=e})})}function g(e,r,i){return r===t&&(r=f),i===t&&(i=null),n.getDoc().execCommand(e,r,i)}function v(e){return E.match(e)}function y(e,r){E.toggle(e,r?{value:r}:t),n.nodeChanged()}function b(e){k=w.getBookmark(e)}function C(){w.moveToBookmark(k)}var x=n.dom,w=n.selection,_={state:{},exec:{},value:{}},N=n.settings,E=n.formatter,k;o(this,{execCommand:r,queryCommandState:p,queryCommandValue:h,addCommands:m}),m({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(e){var t=n.getDoc(),r;try{g(e)}catch(i){r=d}(r||!t.queryCommandSupported(e))&&n.windowManager.alert("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.")},unlink:function(e){w.isCollapsed()&&w.select(w.getNode()),g(e),w.collapse(f)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t=e.substring(7);"full"==t&&(t="justify"),i("left,center,right,justify".split(","),function(e){t!=e&&E.remove("align"+e)}),y("align"+t),r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(e){var t,n;g(e),t=x.getParent(w.getNode(),"ol,ul"),t&&(n=t.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)&&(b(),x.split(n,t),C()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){y(e)},"ForeColor,HiliteColor,FontName":function(e,t,n){y(e,n)},FontSize:function(e,t,n){var r,i;n>=1&&7>=n&&(i=l(N.font_size_style_values),r=l(N.font_size_classes),n=r?r[n-1]||n:i[n-1]||n),y(e,n)},RemoveFormat:function(e){E.remove(e)},mceBlockQuote:function(){y("blockquote")},FormatBlock:function(e,t,n){return y(n||"p")},mceCleanup:function(){var e=w.getBookmark();n.setContent(n.getContent({cleanup:d}),{cleanup:d}),w.moveToBookmark(e)},mceRemoveNode:function(e,t,r){var i=r||w.getNode();i!=n.getBody()&&(b(),n.dom.remove(i,d),C())},mceSelectNodeDepth:function(e,t,r){var i=0;x.getParent(w.getNode(),function(e){return 1==e.nodeType&&i++==r?(w.select(e),f):void 0},n.getBody())},mceSelectNode:function(e,t,n){w.select(n)},mceInsertContent:function(t,r,i){function o(e){function t(e){return r[e]&&3==r[e].nodeType}var n,r,i;return n=w.getRng(!0),r=n.startContainer,i=n.startOffset,3==r.nodeType&&(i>0?e=e.replace(/^ /," "):t("previousSibling")||(e=e.replace(/^ /," ")),i|)$/," "):t("nextSibling")||(e=e.replace(/( | )(
    |)$/," "))),e}var a,s,l,c,d,f,p,h,m,g,v,y,b,C;if(/^ | $/.test(i)&&(i=o(i)),a=n.parser,s=new e({},n.schema),b='',f={content:i,format:"html",selection:!0},n.fire("BeforeSetContent",f),i=f.content,-1==i.indexOf("{$caret}")&&(i+="{$caret}"),i=i.replace(/\{\$caret\}/,b),w.isCollapsed()||n.getDoc().execCommand("Delete",!1,null),l=w.getNode(),f={context:l.nodeName.toLowerCase()},d=a.parse(i,f),v=d.lastChild,"mce_marker"==v.attr("id"))for(p=v,v=v.prev;v;v=v.walk(!0))if(3==v.type||!x.isBlock(v.name)){v.parent.insert(p,v,"br"===v.name);break}if(f.invalid){for(w.setContent(b),l=w.getNode(),c=n.getBody(),9==l.nodeType?l=v=c:v=l;v!==c;)l=v,v=v.parentNode;i=l==c?c.innerHTML:x.getOuterHTML(l),i=s.serialize(a.parse(i.replace(//i,function(){return s.serialize(d)}))),l==c?x.setHTML(c,i):x.setOuterHTML(l,i)}else i=s.serialize(d),v=l.firstChild,y=l.lastChild,!v||v===y&&"BR"===v.nodeName?x.setHTML(l,i):w.setContent(i);p=x.get("mce_marker"),h=x.getRect(p),m=x.getViewPort(n.getWin()),(h.y+h.h>m.y+m.h||h.ym.x+m.w||h.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual,n.addVisual()},mceReplaceContent:function(e,t,r){n.execCommand("mceInsertContent",!1,r.replace(/\{\$selection\}/g,w.getContent({format:"text"})))},mceInsertLink:function(e,t,n){var r;"string"==typeof n&&(n={href:n}),r=x.getParent(w.getNode(),"a"),n.href=n.href.replace(" ","%20"),r&&n.href||E.remove("link"),n.href&&E.apply("link",n,r)},selectAll:function(){var e=x.getRoot(),t=x.createRng();w.getRng().setStart?(t.setStart(e,0),t.setEnd(e,e.childNodes.length),w.setRng(t)):g("SelectAll")},mceNewDocument:function(){n.setContent("")}}),m({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t="align"+e.substring(7),n=w.isCollapsed()?[x.getParent(w.getNode(),x.isBlock)]:w.getSelectedBlocks(),r=a(n,function(e){return!!E.matchNode(e,t)});return-1!==s(r,d)},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){return v(e)},mceBlockQuote:function(){return v("blockquote")},Outdent:function(){var e;if(N.inline_styles){if((e=x.getParent(w.getStart(),x.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return d;if((e=x.getParent(w.getEnd(),x.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return d}return p("InsertUnorderedList")||p("InsertOrderedList")||!N.inline_styles&&!!x.getParent(w.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(e){var t=x.getParent(w.getNode(),"ul,ol");return t&&("insertunorderedlist"===e&&"UL"===t.tagName||"insertorderedlist"===e&&"OL"===t.tagName)}},"state"),m({"FontSize,FontName":function(e){var t=0,n;return(n=x.getParent(w.getNode(),"span"))&&(t="fontsize"==e?n.style.fontSize:n.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()),t}},"value"),m({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}),r(O,[p],function(e){function t(e,i){var o=this,a,s;return e=r(e),i=o.settings=i||{},/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e)?(o.source=e,void 0):(0===e.indexOf("/")&&0!==e.indexOf("//")&&(e=(i.base_uri?i.base_uri.protocol||"http":"http")+"://mce_host"+e),/^[\w\-]*:?\/\//.test(e)||(s=i.base_uri?i.base_uri.path:new t(location.href).directory,e=(i.base_uri&&i.base_uri.protocol||"http")+"://mce_host"+o.toAbsPath(s,e)),e=e.replace(/@@/g,"(mce_at)"),e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e),n(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(t,n){var r=e[n];r&&(r=r.replace(/\(mce_at\)/g,"@@")),o[t]=r}),a=i.base_uri,a&&(o.protocol||(o.protocol=a.protocol),o.userInfo||(o.userInfo=a.userInfo),o.port||"mce_host"!==o.host||(o.port=a.port),o.host&&"mce_host"!==o.host||(o.host=a.host),o.source=""),void 0)}var n=e.each,r=e.trim;return t.prototype={setPath:function(e){var t=this;e=/^(.*?)\/?(\w+)?$/.exec(e),t.path=e[0],t.directory=e[1],t.file=e[2],t.source="",t.getURI()},toRelative:function(e){var n=this,r;if("./"===e)return e;if(e=new t(e,{base_uri:n}),"mce_host"!=e.host&&n.host!=e.host&&e.host||n.port!=e.port||n.protocol!=e.protocol)return e.getURI();var i=n.getURI(),o=e.getURI();return i==o||"/"==i.charAt(i.length-1)&&i.substr(0,i.length-1)==o?i:(r=n.toRelPath(n.path,e.path),e.query&&(r+="?"+e.query),e.anchor&&(r+="#"+e.anchor),r)},toAbsolute:function(e,n){return e=new t(e,{base_uri:this}),e.getURI(this.host==e.host&&this.protocol==e.protocol?n:0)},toRelPath:function(e,t){var n,r=0,i="",o,a;if(e=e.substring(0,e.lastIndexOf("/")),e=e.split("/"),n=t.split("/"),e.length>=n.length)for(o=0,a=e.length;a>o;o++)if(o>=n.length||e[o]!=n[o]){r=o+1;break}if(e.lengtho;o++)if(o>=e.length||e[o]!=n[o]){r=o+1;break}if(1===r)return t;for(o=0,a=e.length-(r-1);a>o;o++)i+="../";for(o=r-1,a=n.length;a>o;o++)i+=o!=r-1?"/"+n[o]:n[o];return i},toAbsPath:function(e,t){var r,i=0,o=[],a,s;for(a=/\/$/.test(t)?"/":"",e=e.split("/"),t=t.split("/"),n(e,function(e){e&&o.push(e)}),e=o,r=t.length-1,o=[];r>=0;r--)0!==t[r].length&&"."!==t[r]&&(".."!==t[r]?i>0?i--:o.push(t[r]):i++);return r=e.length-i,s=0>=r?o.reverse().join("/"):e.slice(0,r).join("/")+"/"+o.reverse().join("/"),0!==s.indexOf("/")&&(s="/"+s),a&&s.lastIndexOf("/")!==s.length-1&&(s+=a),s},getURI:function(e){var t,n=this;return(!n.source||e)&&(t="",e||(n.protocol&&(t+=n.protocol+"://"),n.userInfo&&(t+=n.userInfo+"@"),n.host&&(t+=n.host),n.port&&(t+=":"+n.port)),n.path&&(t+=n.path),n.query&&(t+="?"+n.query),n.anchor&&(t+="#"+n.anchor),n.source=t),n.source}},t}),r(I,[p],function(e){function t(){}var n=e.each,r=e.extend,i,o;return t.extend=i=function(e){function t(){var e,t,n,r;if(!o&&(r=this,r.init&&r.init.apply(r,arguments),t=r.Mixins))for(e=t.length;e--;)n=t[e],n.init&&n.init.apply(r,arguments)}function a(){return this}function s(e,t){return function(){var n=this,r=n._super,i;return n._super=c[e],i=t.apply(n,arguments),n._super=r,i}}var l=this,c=l.prototype,u,d,f;o=!0,u=new l,o=!1,e.Mixins&&(n(e.Mixins,function(t){t=t;for(var n in t)"init"!==n&&(e[n]=t[n])}),c.Mixins&&(e.Mixins=c.Mixins.concat(e.Mixins))),e.Methods&&n(e.Methods.split(","),function(t){e[t]=a}),e.Properties&&n(e.Properties.split(","),function(t){var n="_"+t;e[t]=function(e){var t=this,r;return e!==r?(t[n]=e,t):t[n]}}),e.Statics&&n(e.Statics,function(e,n){t[n]=e}),e.Defaults&&c.Defaults&&(e.Defaults=r({},c.Defaults,e.Defaults));for(d in e)f=e[d],u[d]="function"==typeof f&&c[d]?s(d,f):f;return t.prototype=u,t.constructor=t,t.extend=i,t},t}),r(F,[I,p],function(e,t){function n(e){for(var t=[],n=e.length,r;n--;)r=e[n],r.__checked||(t.push(r),r.__checked=1);for(n=t.length;n--;)delete t[n].__checked;return t}var r=/^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i,i=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,o=/^\s*|\s*$/g,a,s=e.extend({init:function(e){function t(e){return e?(e=e.toLowerCase(),function(t){return"*"===e||t.type===e}):void 0}function n(e){return e?function(t){return t._name===e}:void 0}function a(e){return e?(e=e.split("."),function(t){for(var n=e.length;n--;)if(!t.hasClass(e[n]))return!1;return!0}):void 0}function s(e,t,n){return e?function(r){var i=r[e]?r[e]():"";return t?"="===t?i===n:"*="===t?i.indexOf(n)>=0:"~="===t?(" "+i+" ").indexOf(" "+n+" ")>=0:"!="===t?i!=n:"^="===t?0===i.indexOf(n):"$="===t?i.substr(i.length-n.length)===n:!1:!!n}:void 0}function l(e){var t;return e?(e=/(?:not\((.+)\))|(.+)/i.exec(e),e[1]?(t=u(e[1],[]),function(e){return!d(e,t)}):(e=e[2],function(t,n,r){return"first"===e?0===n:"last"===e?n===r-1:"even"===e?0===n%2:"odd"===e?1===n%2:t[e]?t[e]():!1})):void 0}function c(e,i,c){function u(e){e&&i.push(e)}var d;return d=r.exec(e.replace(o,"")),u(t(d[1])),u(n(d[2])),u(a(d[3])),u(s(d[4],d[5],d[6])),u(l(d[7])),i.psuedo=!!d[7],i.direct=c,i}function u(e,t){var n=[],r,o,a;do if(i.exec(""),o=i.exec(e),o&&(e=o[3],n.push(o[1]),o[2])){r=o[3];break}while(o);for(r&&u(r,t),e=[],a=0;a"!=n[a]&&e.push(c(n[a],[],">"===n[a-1]));return t.push(e),t}var d=this.match;this._selectors=u(e,[])},match:function(e,n){var r,i,o,a,s,l,c,u,d,f,p,h,m;for(n=n||this._selectors,r=0,i=n.length;i>r;r++){for(s=n[r],a=s.length,m=e,h=0,o=a-1;o>=0;o--)for(u=s[o];m;){for(u.psuedo&&(p=m.parent().items(),d=t.inArray(m,p),f=p.length),l=0,c=u.length;c>l;l++)if(!u[l](m,d,f)){l=c+1;break}if(l===c){h++;break}if(o===a-1)break;m=m.parent()}if(h===a)return!0}return!1},find:function(e){function t(e,n,i){var o,a,s,l,c,u=n[i];for(o=0,a=e.length;a>o;o++){for(c=e[o],s=0,l=u.length;l>s;s++)if(!u[s](c,o,a)){s=l+1;break}if(s===l)i==n.length-1?r.push(c):c.items&&t(c.items(),n,i+1);else if(u.direct)return;c.items&&t(c.items(),n,i)}}var r=[],i,o,l=this._selectors;if(e.items){for(i=0,o=l.length;o>i;i++)t(e.items(),l[i],0);o>1&&(r=n(r))}return a||(a=s.Collection),new a(r)}});return s}),r(W,[p,F,I],function(e,t,n){var r,i,o=Array.prototype.push,a=Array.prototype.slice;return i={length:0,init:function(e){e&&this.add(e)},add:function(t){var n=this;return e.isArray(t)?o.apply(n,t):t instanceof r?n.add(t.toArray()):o.call(n,t),n},set:function(e){var t=this,n=t.length,r;for(t.length=0,t.add(e),r=t.length;n>r;r++)delete t[r];return t},filter:function(e){var n=this,i,o,a=[],s,l;for("string"==typeof e?(e=new t(e),l=function(t){return e.match(t)}):l=e,i=0,o=n.length;o>i;i++)s=n[i],l(s)&&a.push(s);return new r(a)},slice:function(){return new r(a.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},each:function(t){return e.each(this,t),this},toArray:function(){return e.toArray(this)},indexOf:function(e){for(var t=this,n=t.length;n--&&t[n]!==e;);return n},reverse:function(){return new r(e.toArray(this).reverse())},hasClass:function(e){return this[0]?this[0].hasClass(e):!1},prop:function(e,t){var n=this,r,i;return t!==r?(n.each(function(n){n[e]&&n[e](t)}),n):(i=n[0],i&&i[e]?i[e]():void 0)},exec:function(t){var n=this,r=e.toArray(arguments).slice(1);return n.each(function(e){e[t]&&e[t].apply(e,r)}),n},remove:function(){for(var e=this.length;e--;)this[e].remove();return this}},e.each("fire on off show hide addClass removeClass append prepend before after reflow".split(" "),function(t){i[t]=function(){var n=e.toArray(arguments);return this.each(function(e){t in e&&e[t].apply(e,n)}),this}}),e.each("text name disabled active selected checked visible parent value data".split(" "),function(e){i[e]=function(t){return this.prop(e,t) +}}),r=n.extend(i),t.Collection=r,r}),r(z,[p,v],function(e,t){return{id:function(){return t.DOM.uniqueId()},createFragment:function(e){return t.DOM.createFragment(e)},getWindowSize:function(){return t.DOM.getViewPort()},getSize:function(e){return t.DOM.getSize(e)},getPos:function(e,n){return t.DOM.getPos(e,n)},getViewPort:function(e){return t.DOM.getViewPort(e)},get:function(e){return document.getElementById(e)},addClass:function(e,n){return t.DOM.addClass(e,n)},removeClass:function(e,n){return t.DOM.removeClass(e,n)},hasClass:function(e,n){return t.DOM.hasClass(e,n)},toggleClass:function(e,n,r){return t.DOM.toggleClass(e,n,r)},css:function(e,n,r){return t.DOM.setStyle(e,n,r)},on:function(e,n,r,i){return t.DOM.bind(e,n,r,i)},off:function(e,n,r){return t.DOM.unbind(e,n,r)},fire:function(e,n,r){return t.DOM.fire(e,n,r)},innerHtml:function(e,n){t.DOM.setHTML(e,n)}}}),r(V,[I,p,W,z],function(e,t,n,r){var i=t.makeMap("focusin focusout scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave wheel keydown keypress keyup contextmenu"," "),o={},a="onmousewheel"in document,s=!1,l=e.extend({Statics:{controlIdLookup:{}},classPrefix:"mce-",init:function(e){var n=this,i,o;if(n.settings=e=t.extend({},n.Defaults,e),n._id=r.id(),n._text=n._name="",n._width=n._height=0,n._aria={role:e.role},i=e.classes)for(i=i.split(" "),i.map={},o=i.length;o--;)i.map[i[o]]=!0;n._classes=i||[],n.visible(!0),t.each("title text width height name classes visible disabled active value".split(" "),function(t){var r=e[t],i;r!==i?n[t](r):n["_"+t]===i&&(n["_"+t]=!1)}),n.on("click",function(){return n.disabled()?!1:void 0}),e.classes&&t.each(e.classes.split(" "),function(e){n.addClass(e)}),n.settings=e,n._borderBox=n.parseBox(e.border),n._paddingBox=n.parseBox(e.padding),n._marginBox=n.parseBox(e.margin),e.hidden&&n.hide()},Properties:"parent,title,text,width,height,disabled,active,name,value",Methods:"renderHtml",getContainerElm:function(){return document.body},getParentCtrl:function(e){for(var t;e&&!(t=l.controlIdLookup[e.id]);)e=e.parentNode;return t},parseBox:function(e){var t,n=10;if(e)return"number"==typeof e?(e=e||0,{top:e,left:e,bottom:e,right:e}):(e=e.split(" "),t=e.length,1===t?e[1]=e[2]=e[3]=e[0]:2===t?(e[2]=e[0],e[3]=e[1]):3===t&&(e[3]=e[1]),{top:parseInt(e[0],n)||0,right:parseInt(e[1],n)||0,bottom:parseInt(e[2],n)||0,left:parseInt(e[3],n)||0})},borderBox:function(){return this._borderBox},paddingBox:function(){return this._paddingBox},marginBox:function(){return this._marginBox},measureBox:function(e,t){function n(t){var n=document.defaultView;return n?(t=t.replace(/[A-Z]/g,function(e){return"-"+e}),n.getComputedStyle(e,null).getPropertyValue(t)):e.currentStyle[t]}function r(e){var t=parseInt(n(e),10);return isNaN(t)?0:t}return{top:r(t+"TopWidth"),right:r(t+"RightWidth"),bottom:r(t+"BottomWidth"),left:r(t+"LeftWidth")}},initLayoutRect:function(){var e=this,t=e.settings,n,r,i=e.getEl(),o,a,s,l,c,u,d;n=e._borderBox=e._borderBox||e.measureBox(i,"border"),e._paddingBox=e._paddingBox||e.measureBox(i,"padding"),e._marginBox=e._marginBox||e.measureBox(i,"margin"),u=t.minWidth,d=t.minHeight,s=u||i.offsetWidth,l=d||i.offsetHeight,o=t.width,a=t.height,c=t.autoResize,c="undefined"!=typeof c?c:!o&&!a,o=o||s,a=a||l;var f=n.left+n.right,p=n.top+n.bottom,h=t.maxWidth||65535,m=t.maxHeight||65535;return e._layoutRect=r={x:t.x||0,y:t.y||0,w:o,h:a,deltaW:f,deltaH:p,contentW:o-f,contentH:a-p,innerW:o-f,innerH:a-p,startMinWidth:u||0,startMinHeight:d||0,minW:Math.min(s,h),minH:Math.min(l,m),maxW:h,maxH:m,autoResize:c,scrollW:0},e._lastLayoutRect={},r},layoutRect:function(e){var t=this,n=t._layoutRect,r,i,o,a,s,c;return n||(n=t.initLayoutRect()),e?(o=n.deltaW,a=n.deltaH,e.x!==s&&(n.x=e.x),e.y!==s&&(n.y=e.y),e.minW!==s&&(n.minW=e.minW),e.minH!==s&&(n.minH=e.minH),i=e.w,i!==s&&(i=in.maxW?n.maxW:i,n.w=i,n.innerW=i-o),i=e.h,i!==s&&(i=in.maxH?n.maxH:i,n.h=i,n.innerH=i-a),i=e.innerW,i!==s&&(i=in.maxW-o?n.maxW-o:i,n.innerW=i,n.w=i+o),i=e.innerH,i!==s&&(i=in.maxH-a?n.maxH-a:i,n.innerH=i,n.h=i+a),e.contentW!==s&&(n.contentW=e.contentW),e.contentH!==s&&(n.contentH=e.contentH),r=t._lastLayoutRect,(r.x!==n.x||r.y!==n.y||r.w!==n.w||r.h!==n.h)&&(c=l.repaintControls,c&&c.map&&!c.map[t._id]&&(c.push(t),c.map[t._id]=!0),r.x=n.x,r.y=n.y,r.w=n.w,r.h=n.h),t):n},repaint:function(){var e=this,t,n,r,i,o=0,a=0,s;t=e.getEl().style,r=e._layoutRect,s=e._lastRepaintRect||{},i=e._borderBox,o=i.left+i.right,a=i.top+i.bottom,r.x!==s.x&&(t.left=r.x+"px",s.x=r.x),r.y!==s.y&&(t.top=r.y+"px",s.y=r.y),r.w!==s.w&&(t.width=r.w-o+"px",s.w=r.w),r.h!==s.h&&(t.height=r.h-a+"px",s.h=r.h),e._hasBody&&r.innerW!==s.innerW&&(n=e.getEl("body").style,n.width=r.innerW+"px",s.innerW=r.innerW),e._hasBody&&r.innerH!==s.innerH&&(n=n||e.getEl("body").style,n.height=r.innerH+"px",s.innerH=r.innerH),e._lastRepaintRect=s,e.fire("repaint",{},!1)},on:function(e,t){function n(e){var t,n;return function(i){return t||r.parents().each(function(r){var i=r.settings.callbacks;return i&&(t=i[e])?(n=r,!1):void 0}),t.call(n,i)}}var r=this,o,a,s,l;if(t)for("string"==typeof t&&(t=n(t)),s=e.toLowerCase().split(" "),l=s.length;l--;)e=s[l],o=r._bindings,o||(o=r._bindings={}),a=o[e],a||(a=o[e]=[]),a.push(t),i[e]&&(r._nativeEvents?r._nativeEvents[e]=!0:r._nativeEvents={name:!0},r._rendered&&r.bindPendingEvents());return r},off:function(e,t){var n=this,r,i=n._bindings,o,a,s,l;if(i)if(e)for(s=e.toLowerCase().split(" "),r=s.length;r--;){if(e=s[r],o=i[e],!e){for(a in i)i[a].length=0;return n}if(o)if(t)for(l=o.length;l--;)o[l]===t&&o.splice(l,1);else o.length=0}else n._bindings=[];return n},fire:function(e,t,n){function r(){return!1}function i(){return!0}var o=this,a,s,l,c;if(e=e.toLowerCase(),t=t||{},t.type||(t.type=e),t.control||(t.control=o),t.preventDefault||(t.preventDefault=function(){t.isDefaultPrevented=i},t.stopPropagation=function(){t.isPropagationStopped=i},t.stopImmediatePropagation=function(){t.isImmediatePropagationStopped=i},t.isDefaultPrevented=r,t.isPropagationStopped=r,t.isImmediatePropagationStopped=r),o._bindings&&(l=o._bindings[e]))for(a=0,s=l.length;s>a&&(t.isImmediatePropagationStopped()||l[a].call(o,t)!==!1);a++);if(n!==!1)for(c=o.parent();c&&!t.isPropagationStopped();)c.fire(e,t,!1),c=c.parent();return t},parents:function(e){var t=this,r=new n;for(t=t.parent();t;t=t.parent())r.add(t);return e&&(r=r.filter(e)),r},next:function(){var e=this.parent().items();return e[e.indexOf(this)+1]},prev:function(){var e=this.parent().items();return e[e.indexOf(this)-1]},findCommonAncestor:function(e,t){for(var n;e;){for(n=t;n&&e!=n;)n=n.parent();if(e==n)break;e=e.parent()}return e},hasClass:function(e,t){var n=this._classes[t||"control"];return e=this.classPrefix+e,n&&!!n.map[e]},addClass:function(e,t){var n=this,r,i;return e=this.classPrefix+e,r=n._classes[t||"control"],r||(r=[],r.map={},n._classes[t||"control"]=r),r.map[e]||(r.map[e]=e,r.push(e),n._rendered&&(i=n.getEl(t),i&&(i.className=r.join(" ")))),n},removeClass:function(e,t){var n=this,r,i,o;if(e=this.classPrefix+e,r=n._classes[t||"control"],r&&r.map[e])for(delete r.map[e],i=r.length;i--;)r[i]===e&&r.splice(i,1);return n._rendered&&(o=n.getEl(t),o&&(o.className=r.join(" "))),n},toggleClass:function(e,t,n){var r=this;return t?r.addClass(e,n):r.removeClass(e,n),r},classes:function(e){var t=this._classes[e||"control"];return t?t.join(" "):""},innerHtml:function(e){return r.innerHtml(this.getEl(),e),this},getEl:function(e,t){var n,i=e?this._id+"-"+e:this._id;return n=o[i]=(t===!0?null:o[i])||r.get(i)},visible:function(e){var t=this,n;return"undefined"!=typeof e?(t._visible!==e&&(t._rendered&&(t.getEl().style.display=e?"":"none"),t._visible=e,n=t.parent(),n&&(n._lastRect=null),t.fire(e?"show":"hide")),t):t._visible},show:function(){return this.visible(!0)},hide:function(){return this.visible(!1)},focus:function(){try{this.getEl().focus()}catch(e){}return this},blur:function(){return this.getEl().blur(),this},aria:function(e,t){var n=this,r=n.getEl();return"undefined"==typeof t?n._aria[e]:(n._aria[e]=t,n._rendered&&("label"==e&&r.setAttribute("aria-labeledby",n._id),r.setAttribute("role"==e?e:"aria-"+e,t)),n)},encode:function(e,t){return t!==!1&&l.translate&&(e=l.translate(e)),(e||"").replace(/[&<>"]/g,function(e){return"&#"+e.charCodeAt(0)+";"})},before:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t),!0),t},after:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t)),t},remove:function(){var e=this,t=e.getEl(),n=e.parent(),i;if(e.items)for(var o=e.items().toArray(),a=o.length;a--;)o[a].remove();return n&&n.items&&(i=[],n.items().each(function(t){t!==e&&i.push(t)}),n.items().set(i),n._lastRect=null),e._eventsRoot&&e._eventsRoot==e&&r.off(t),delete l.controlIdLookup[e._id],t.parentNode&&t.parentNode.removeChild(t),e},renderBefore:function(e){var t=this;return e.parentNode.insertBefore(r.createFragment(t.renderHtml()),e),t.postRender(),t},renderTo:function(e){var t=this;return e=e||t.getContainerElm(),e.appendChild(r.createFragment(t.renderHtml())),t.postRender(),t},postRender:function(){var e=this,t=e.settings,n,i,o,a,s;for(a in t)0===a.indexOf("on")&&e.on(a.substr(2),t[a]);if(e._eventsRoot){for(o=e.parent();!s&&o;o=o.parent())s=o._eventsRoot;if(s)for(a in s._nativeEvents)e._nativeEvents[a]=!0}e.bindPendingEvents(),t.style&&(n=e.getEl(),n&&(n.setAttribute("style",t.style),n.style.cssText=t.style)),e._visible||r.css(e.getEl(),"display","none"),e.settings.border&&(i=e.borderBox(),r.css(e.getEl(),{"border-top-width":i.top,"border-right-width":i.right,"border-bottom-width":i.bottom,"border-left-width":i.left})),l.controlIdLookup[e._id]=e;for(var c in e._aria)e.aria(c,e._aria[c]);e.fire("postrender",{},!1)},scrollIntoView:function(e){function t(e,t){var n,r,i=e;for(n=r=0;i&&i!=t&&i.nodeType;)n+=i.offsetLeft||0,r+=i.offsetTop||0,i=i.offsetParent;return{x:n,y:r}}var n=this.getEl(),r=n.parentNode,i,o,a,s,l,c,u=t(n,r);return i=u.x,o=u.y,a=n.offsetWidth,s=n.offsetHeight,l=r.clientWidth,c=r.clientHeight,"end"==e?(i-=l-a,o-=c-s):"center"==e&&(i-=l/2-a/2,o-=c/2-s/2),r.scrollLeft=i,r.scrollTop=o,this},bindPendingEvents:function(){function e(e){var t=o.getParentCtrl(e.target);t&&t.fire(e.type,e)}function t(){var e=d._lastHoverCtrl;e&&(e.fire("mouseleave",{target:e.getEl()}),e.parents().each(function(e){e.fire("mouseleave",{target:e.getEl()})}),d._lastHoverCtrl=null)}function n(e){var t=o.getParentCtrl(e.target),n=d._lastHoverCtrl,r=0,i,a,s;if(t!==n){if(d._lastHoverCtrl=t,a=t.parents().toArray().reverse(),a.push(t),n){for(s=n.parents().toArray().reverse(),s.push(n),r=0;r=r;i--)n=s[i],n.fire("mouseleave",{target:n.getEl()})}for(i=r;il;l++)d=u[l]._eventsRoot;for(d||(d=u[u.length-1]||o),o._eventsRoot=d,c=l,l=0;c>l;l++)u[l]._eventsRoot=d;for(p in f){if(!f)return!1;"wheel"!==p||s?("mouseenter"===p||"mouseleave"===p?d._hasMouseEnter||(r.on(d.getEl(),"mouseleave",t),r.on(d.getEl(),"mouseover",n),d._hasMouseEnter=1):d[p]||(r.on(d.getEl(),p,e),d[p]=!0),f[p]=!1):a?r.on(o.getEl(),"mousewheel",i):r.on(o.getEl(),"DOMMouseScroll",i)}}},reflow:function(){return this.repaint(),this}});return l}),r(U,[],function(){var e={},t;return{add:function(t,n){e[t.toLowerCase()]=n},has:function(t){return!!e[t.toLowerCase()]},create:function(n,r){var i,o,a;if(!t){a=tinymce.ui;for(o in a)e[o.toLowerCase()]=a[o];t=!0}if("string"==typeof n?(r=r||{},r.type=n):(r=n,n=r.type),n=n.toLowerCase(),i=e[n],!i)throw new Error("Could not find control by type: "+n);return i=new i(r),i.type=n,i}}}),r(q,[V,W,F,U,p,z],function(e,t,n,r,i,o){var a={};return e.extend({layout:"",innerClass:"container-inner",init:function(e){var n=this;n._super(e),e=n.settings,n._fixed=e.fixed,n._items=new t,n.addClass("container"),n.addClass("container-body","body"),e.containerCls&&n.addClass(e.containerCls),n._layout=r.create((e.layout||n.layout)+"layout"),n.settings.items&&n.add(n.settings.items),n._hasBody=!0},items:function(){return this._items},find:function(e){return e=a[e]=a[e]||new n(e),e.find(this)},add:function(e){var t=this;return t.items().add(t.create(e)).parent(t),t},focus:function(){var e=this;return e.keyNav?e.keyNav.focusFirst():e._super(),e},replace:function(e,t){for(var n,r=this.items(),i=r.length;i--;)if(r[i]===e){r[i]=t;break}i>=0&&(n=t.getEl(),n&&n.parentNode.removeChild(n),n=e.getEl(),n&&n.parentNode.removeChild(n)),t.parent(this)},create:function(t){var n=this,o,a=[];return i.isArray(t)||(t=[t]),i.each(t,function(t){t&&(t instanceof e||("string"==typeof t&&(t={type:t}),o=i.extend({},n.settings.defaults,t),t.type=o.type=o.type||t.type||n.settings.defaultType||(o.defaults?o.defaults.type:null),t=r.create(o)),a.push(t))}),a},renderNew:function(){var e=this;return e.items().each(function(t,n){var r,i;t.parent(e),t._rendered||(r=e.getEl("body"),i=o.createFragment(t.renderHtml()),r.hasChildNodes()&&n<=r.childNodes.length-1?r.insertBefore(i,r.childNodes[n]):r.appendChild(i),t.postRender())}),e._layout.applyClasses(e),e._lastRect=null,e},append:function(e){return this.add(e).renderNew()},prepend:function(e){var t=this;return t.items().set(t.create(e).concat(t.items().toArray())),t.renderNew()},insert:function(e,t,n){var r=this,i,o,a;return e=r.create(e),i=r.items(),!n&&t=0&&t'+'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "+"
    "},postRender:function(){var e=this,t;return e.items().exec("postRender"),e._super(),e._layout.postRender(e),e._rendered=!0,e.settings.style&&o.css(e.getEl(),e.settings.style),e.settings.border&&(t=e.borderBox(),o.css(e.getEl(),{"border-top-width":t.top,"border-right-width":t.right,"border-bottom-width":t.bottom,"border-left-width":t.left})),e},initLayoutRect:function(){var e=this,t=e._super();return e._layout.recalc(e),t},recalc:function(){var e=this,t=e._layoutRect,n=e._lastRect;return n&&n.w==t.w&&n.h==t.h?void 0:(e._layout.recalc(e),t=e.layoutRect(),e._lastRect={x:t.x,y:t.y,w:t.w,h:t.h},!0)},reflow:function(){var t,n;if(this.visible()){for(e.repaintControls=[],e.repaintControls.map={},n=this.recalc(),t=e.repaintControls.length;t--;)e.repaintControls[t].repaint();"flow"!==this.settings.layout&&"stack"!==this.settings.layout&&this.repaint(),e.repaintControls=[]}return this}})}),r(j,[z],function(e){function t(){var e=document,t,n,r,i,o,a,s,l,c=Math.max;return t=e.documentElement,n=e.body,r=c(t.scrollWidth,n.scrollWidth),i=c(t.clientWidth,n.clientWidth),o=c(t.offsetWidth,n.offsetWidth),a=c(t.scrollHeight,n.scrollHeight),s=c(t.clientHeight,n.clientHeight),l=c(t.offsetHeight,n.offsetHeight),{width:o>r?i:r,height:l>a?s:a}}return function(n,r){function i(){return a.getElementById(r.handle||n)}var o,a=document,s,l,c,u,d,f;r=r||{},l=function(n){var l=t(),p,h;n.preventDefault(),s=n.button,p=i(),d=n.screenX,f=n.screenY,h=window.getComputedStyle?window.getComputedStyle(p,null).getPropertyValue("cursor"):p.runtimeStyle.cursor,o=a.createElement("div"),e.css(o,{position:"absolute",top:0,left:0,width:l.width,height:l.height,zIndex:2147483647,opacity:1e-4,background:"red",cursor:h}),a.body.appendChild(o),e.on(a,"mousemove",u),e.on(a,"mouseup",c),r.start(n)},u=function(e){return e.button!==s?c(e):(e.deltaX=e.screenX-d,e.deltaY=e.screenY-f,e.preventDefault(),r.drag(e),void 0)},c=function(t){e.off(a,"mousemove",u),e.off(a,"mouseup",c),o.parentNode.removeChild(o),r.stop&&r.stop(t)},this.destroy=function(){e.off(i())},e.on(i(),"mousedown",l)}}),r($,[z,j],function(e,t){return{init:function(){var e=this;e.on("repaint",e.renderScroll)},renderScroll:function(){function n(){function t(t,a,s,l,c,u){var d,f,p,h,m,g,v,y,b;if(f=i.getEl("scroll"+t)){if(y=a.toLowerCase(),b=s.toLowerCase(),i.getEl("absend")&&e.css(i.getEl("absend"),y,i.layoutRect()[l]-1),!c)return e.css(f,"display","none"),void 0;e.css(f,"display","block"),d=i.getEl("body"),p=i.getEl("scroll"+t+"t"),h=d["client"+s]-2*o,h-=n&&r?f["client"+u]:0,m=d["scroll"+s],g=h/m,v={},v[y]=d["offset"+a]+o,v[b]=h,e.css(f,v),v={},v[y]=d["scroll"+a]*g,v[b]=h*g,e.css(p,v)}}var n,r,a;a=i.getEl("body"),n=a.scrollWidth>a.clientWidth,r=a.scrollHeight>a.clientHeight,t("h","Left","Width","contentW",n,"Height"),t("v","Top","Height","contentH",r,"Width")}function r(){function n(n,r,a,s,l){var c,u=i._id+"-scroll"+n,d=i.classPrefix;i.getEl().appendChild(e.createFragment('
    '+'
    '+"
    ")),i.draghelper=new t(u+"t",{start:function(){c=i.getEl("body")["scroll"+r],e.addClass(e.get(u),d+"active")},drag:function(e){var t,u,d,f,p=i.layoutRect();u=p.contentW>p.innerW,d=p.contentH>p.innerH,f=i.getEl("body")["client"+a]-2*o,f-=u&&d?i.getEl("scroll"+n)["client"+l]:0,t=f/i.getEl("body")["scroll"+a],i.getEl("body")["scroll"+r]=c+e["delta"+s]/t},stop:function(){e.removeClass(e.get(u),d+"active")}})}i.addClass("scroll"),n("v","Top","Height","Y","Width"),n("h","Left","Width","X","Height")}var i=this,o=2;i.settings.autoScroll&&(i._hasScroll||(i._hasScroll=!0,r(),i.on("wheel",function(e){var t=i.getEl("body");t.scrollLeft+=10*(e.deltaX||0),t.scrollTop+=10*e.deltaY,n()}),e.on(i.getEl("body"),"scroll",n)),n())}}}),r(K,[q,$],function(e,t){return e.extend({Defaults:{layout:"fit",containerCls:"panel"},Mixins:[t],renderHtml:function(){var e=this,t=e._layout,n=e.settings.html;return e.preRender(),t.preRender(e),"undefined"==typeof n?n='
    '+t.renderHtml(e)+"
    ":("function"==typeof n&&(n=n.call(e)),e._hasBody=!1),'
    '+(e._preBodyHtml||"")+n+"
    "}})}),r(G,[z],function(e){function t(t,n,r){var i,o,a,s,l,c,u,d,f;return f=e.getViewPort(),o=e.getPos(n),a=o.x,s=o.y,t._fixed&&(a-=f.x,s-=f.y),i=t.getEl(),l=i.offsetWidth,c=i.offsetHeight,u=n.offsetWidth,d=n.offsetHeight,r=(r||"").split(""),"b"===r[0]&&(s+=d),"r"===r[1]&&(a+=u),"c"===r[0]&&(s+=Math.round(d/2)),"c"===r[1]&&(a+=Math.round(u/2)),"b"===r[3]&&(s-=c),"r"===r[4]&&(a-=l),"c"===r[3]&&(s-=Math.round(c/2)),"c"===r[4]&&(a-=Math.round(l/2)),{x:a,y:s,w:l,h:c}}return{testMoveRel:function(n,r){for(var i=e.getViewPort(),o=0;o0&&a.x+a.w0&&a.y+a.hi.x&&a.x+a.wi.y&&a.y+a.he?0:e+n>t?(e=t-n,0>e?0:e):e}var i=this;if(i.settings.constrainToViewport){var o=e.getViewPort(window),a=i.layoutRect();t=r(t,o.w+o.x,a.w),n=r(n,o.h+o.y,a.h)}return i._rendered?i.layoutRect({x:t,y:n}).repaint():(i.settings.x=t,i.settings.y=n),i.fire("move",{x:t,y:n}),i}}}),r(Y,[z],function(e){return{resizeToContent:function(){this._layoutRect.autoResize=!0,this._lastRect=null,this.reflow()},resizeTo:function(t,n){if(1>=t||1>=n){var r=e.getWindowSize();t=1>=t?t*r.w:t,n=1>=n?n*r.h:n}return this._layoutRect.autoResize=!1,this.layoutRect({minW:t,minH:n,w:t,h:n}).reflow()},resizeBy:function(e,t){var n=this,r=n.layoutRect();return n.resizeTo(r.w+e,r.h+t)}}}),r(X,[K,G,Y,z],function(e,t,n,r){function i(e){var t;for(t=s.length;t--;)s[t]===e&&s.splice(t,1)}var o,a,s=[],l=[],c,u=e.extend({Mixins:[t,n],init:function(e){function t(){var e,t=u.zIndex||65535,n;if(l.length)for(e=0;en&&(e.fixed(!1).layoutRect({y:e._autoFixY}).repaint(),t(!1,e._autoFixY-n)):(e._autoFixY=e.layoutRect().y,e._autoFixY'),n=n.firstChild,d.getContainerElm().appendChild(n),setTimeout(function(){r.addClass(n,i+"in"),r.addClass(d.getEl(),i+"in")},0),c=!0),l.push(d),t()}}),d.on("close hide",function(e){if(e.control==d){for(var n=l.length;n--;)l[n]===d&&l.splice(n,1);t()}}),d.on("show",function(){d.parents().each(function(e){return e._fixed?(d.fixed(!0),!1):void 0})}),e.popover&&(d._preBodyHtml='
    ',d.addClass("popover").addClass("bottom").addClass("start"))},fixed:function(e){var t=this;if(t._fixed!=e){if(t._rendered){var n=r.getViewPort();e?t.layoutRect().y-=n.y:t.layoutRect().y+=n.y}t.toggleClass("fixed",e),t._fixed=e}return t},show:function(){var e=this,t,n=e._super();for(t=s.length;t--&&s[t]!==e;);return-1===t&&s.push(e),n},hide:function(){return i(this),this._super()},hideAll:function(){u.hideAll()},close:function(){var e=this;return e.fire("close"),e.remove()},remove:function(){i(this),this._super()}});return u.hideAll=function(){for(var e=s.length;e--;){var t=s[e];t.settings.autohide&&(t.fire("cancel",{},!1),t.hide(),s.splice(e,1))}},u}),r(J,[z],function(e){return function(t){function n(){if(!h)if(h=[],d.find)d.find("*").each(function(e){e.canFocus&&h.push(e.getEl())});else for(var e=d.getEl().getElementsByTagName("*"),t=0;ti?i=l.length-1:i>=l.length&&(i=0),o=l[i],o.focus(),m=o.id,t.actOnFocus&&s()}function u(){var e,r;for(r=i(t.root.getEl()),n(),e=h.length;e--;)if("toolbar"==r&&h[e].id===m)return h[e].focus(),void 0;h[0].focus()}var d=t.root,f=t.enableUpDown!==!1,p=t.enableLeftRight!==!1,h=t.items,m;return d.on("keydown",function(e){var n=37,r=39,u=38,d=40,h=27,m=14,g=13,v=32,y=9,b;switch(e.keyCode){case n:p&&(t.leftAction?t.leftAction():c(-1),b=!0);break;case r:p&&("menuitem"==i()&&"menu"==o()?a("haspopup")&&s():c(1),b=!0);break;case u:f&&(c(-1),b=!0);break;case d:f&&("menuitem"==i()&&"menubar"==o()?s():"button"==i()&&a("haspopup")?s():c(1),b=!0);break;case y:b=!0,e.shiftKey?c(-1):c(1);break;case h:b=!0,l();break;case m:case g:case v:b=s()}b&&(e.stopPropagation(),e.preventDefault())}),d.on("focusin",function(e){n(),m=e.target.id}),{moveFocus:c,focusFirst:u,cancel:l}}}),r(Q,[X,K,z,J,j],function(e,t,n,r,i){var o=e.extend({modal:!0,Defaults:{border:1,layout:"flex",containerCls:"panel",role:"dialog",callbacks:{submit:function(){this.fire("submit",{data:this.toJSON()})},close:function(){this.close()}}},init:function(e){var n=this;n._super(e),n.addClass("window"),n._fixed=!0,e.buttons&&(n.statusbar=new t({layout:"flex",border:"1 0 0 0",spacing:3,padding:10,align:"center",pack:"end",defaults:{type:"button"},items:e.buttons}),n.statusbar.addClass("foot"),n.statusbar.parent(n)),n.on("click",function(e){-1!=e.target.className.indexOf(n.classPrefix+"close")&&n.close()}),n.aria("label",e.title),n._fullscreen=!1},recalc:function(){var e=this,t=e.statusbar,r,i,o;e._fullscreen&&(e.layoutRect(n.getWindowSize()),e.layoutRect().contentH=e.layoutRect().innerH),e._super(),r=e.layoutRect(),e.settings.title&&!e._fullscreen&&(i=r.headerW,i>r.w&&(e.layoutRect({w:i}),o=!0)),t&&(t.layoutRect({w:e.layoutRect().innerW}).recalc(),i=t.layoutRect().minW+r.deltaW,i>r.w&&(e.layoutRect({w:i}),o=!0)),o&&e.recalc()},initLayoutRect:function(){var e=this,t=e._super(),r=0,i;e.settings.title&&!e._fullscreen&&(i=e.getEl("head"),t.headerW=i.offsetWidth,t.headerH=i.offsetHeight,r+=t.headerH),e.statusbar&&(r+=e.statusbar.layoutRect().h),t.deltaH+=r,t.minH+=r,t.h+=r;var o=n.getWindowSize();return t.x=Math.max(0,o.w/2-t.w/2),t.y=Math.max(0,o.h/2-t.h/2),t},renderHtml:function(){var e=this,t=e._layout,n=e._id,r=e.classPrefix,i=e.settings,o="",a="",s=i.html;return e.preRender(),t.preRender(e),i.title&&(o='
    '+'
    '+e.encode(i.title)+"
    "+''+'
    '+"
    "),i.url&&(s=''),"undefined"==typeof s&&(s=t.renderHtml(e)),e.statusbar&&(a=e.statusbar.renderHtml()),'
    '+o+'
    '+s+"
    "+a+"
    "},fullscreen:function(e){var t=this,r=document.documentElement,i,o=t.classPrefix,a;if(e!=t._fullscreen)if(n.on(window,"resize",function(){var e;if(t._fullscreen)if(i)t._timer||(t._timer=setTimeout(function(){var e=n.getWindowSize();t.moveTo(0,0).resizeTo(e.w,e.h),t._timer=0},50));else{e=(new Date).getTime();var r=n.getWindowSize();t.moveTo(0,0).resizeTo(r.w,r.h),(new Date).getTime()-e>50&&(i=!0)}}),a=t.layoutRect(),t._fullscreen=e,e){t._initial={x:a.x,y:a.y,w:a.w,h:a.h},t._borderBox=t.parseBox("0"),t.getEl("head").style.display="none",a.deltaH-=a.headerH+2,n.addClass(r,o+"fullscreen"),n.addClass(document.body,o+"fullscreen"),t.addClass("fullscreen");var s=n.getWindowSize();t.moveTo(0,0).resizeTo(s.w,s.h)}else t._borderBox=t.parseBox(t.settings.border),t.getEl("head").style.display="",a.deltaH+=a.headerH,n.removeClass(r,o+"fullscreen"),n.removeClass(document.body,o+"fullscreen"),t.removeClass("fullscreen"),t.moveTo(t._initial.x,t._initial.y).resizeTo(t._initial.w,t._initial.h);return t.reflow()},postRender:function(){var e=this,t=[],n,o,a;setTimeout(function(){e.addClass("in")},0),e.keyboardNavigation=new r({root:e,enableLeftRight:!1,enableUpDown:!1,items:t,onCancel:function(){e.close()}}),e.find("*").each(function(e){e.canFocus&&(o=o||e.settings.autofocus,n=n||e,"filepicker"==e.type?(t.push(e.getEl("inp")),e.getEl("open")&&t.push(e.getEl("open").firstChild)):t.push(e.getEl()))}),e.statusbar&&e.statusbar.find("*").each(function(e){e.canFocus&&(o=o||e.settings.autofocus,n=n||e,t.push(e.getEl()))}),e._super(),e.statusbar&&e.statusbar.postRender(),!o&&n&&n.focus(),this.dragHelper=new i(e._id+"-dragh",{start:function(){a={x:e.layoutRect().x,y:e.layoutRect().y}},drag:function(t){e.moveTo(a.x+t.deltaX,a.y+t.deltaY)}}),e.on("submit",function(t){t.isDefaultPrevented()||e.close()})},submit:function(){var e=this.getParentCtrl(document.activeElement);return e&&e.blur(),this.fire("submit",{data:this.toJSON()})},remove:function(){var e=this;e._super(),e.dragHelper.destroy(),e.statusbar&&this.statusbar.remove()}});return o}),r(Z,[Q],function(e){var t=e.extend({init:function(e){e={border:1,padding:20,layout:"flex",pack:"center",align:"center",containerCls:"panel",autoScroll:!0,buttons:{type:"button",text:"Ok",action:"ok"},items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200}},this._super(e)},Statics:{OK:1,OK_CANCEL:2,YES_NO:3,YES_NO_CANCEL:4,msgBox:function(n){var r,i=n.callback||function(){};switch(n.buttons){case t.OK_CANCEL:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close(),i(!0)}},{type:"button",text:"Cancel",onClick:function(e){e.control.parents()[1].close(),i(!1)}}];break;case t.YES_NO:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close(),i(!0)}}];break;case t.YES_NO_CANCEL:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close()}}];break;default:r=[{type:"button",text:"Ok",subtype:"primary",onClick:function(e){e.control.parents()[1].close()}}]}return new e({padding:20,x:n.x,y:n.y,minWidth:300,minHeight:100,layout:"flex",pack:"center",align:"center",buttons:r,title:n.title,items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200,text:n.text},onClose:n.onClose}).renderTo(document.body).reflow()},alert:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,t.msgBox(e)},confirm:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,e.buttons=t.OK_CANCEL,t.msgBox(e)}}});return t}),r(et,[Q,Z],function(e,t){return function(n){var r=this,i=[];r.windows=i,r.open=function(t,r){var o;return t.url=t.url||t.file,t.url&&(t.width=parseInt(t.width||320,10),t.height=parseInt(t.height||240,10)),t.body&&(t.items={defaults:t.defaults,type:t.bodyType||"form",items:t.body}),t.url||t.buttons||(t.buttons=[{text:"Ok",subtype:"primary",onclick:function(){o.find("form")[0].submit(),o.close()}},{text:"Cancel",onclick:function(){o.close()}}]),o=new e(t),i.push(o),o.on("close",function(){for(var e=i.length;e--;)i[e]===o&&i.splice(e,1);n.focus()}),t.data&&o.on("postRender",function(){this.find("*").each(function(e){var n=e.name();n in t.data&&e.value(t.data[n])})}),o.params=r||{},n.nodeChanged(),o.renderTo(document.body).reflow()},r.alert=function(e,n,r){t.alert(e,function(){n.call(r||this)})},r.confirm=function(e,n,r){t.confirm(e,function(e){n.call(r||this,e)})},r.close=function(){i.length&&i[i.length-1].close()},r.getParams=function(){return i.length?i[i.length-1].params:null},r.setParams=function(e){i.length&&(i[i.length-1].params=e)}}}),r(tt,[T,B,C,m,g,p],function(e,t,n,r,i,o){return function(a){function s(e,t){try{a.getDoc().execCommand(e,!1,t)}catch(n){}}function l(){var e=a.getDoc().documentMode;return e?e:6}function c(e){return e.isDefaultPrevented()}function u(){function t(e){function t(){if(3==l.nodeType){if(e&&c==l.length)return!0;if(!e&&0===c)return!0}}var n,r,i,s,l,c,u;n=W.getRng();var d=[n.startContainer,n.startOffset,n.endContainer,n.endOffset];if(n.collapsed||(e=!0),l=n[(e?"start":"end")+"Container"],c=n[(e?"start":"end")+"Offset"],3==l.nodeType&&(r=F.getParent(n.startContainer,F.isBlock),e&&(r=F.getNext(r,F.isBlock)),!r||!t()&&n.collapsed||(i=F.create("em",{id:"__mceDel"}),P(o.grep(r.childNodes),function(e){i.appendChild(e)}),r.appendChild(i))),n=F.createRng(),n.setStart(d[0],d[1]),n.setEnd(d[2],d[3]),W.setRng(n),a.getDoc().execCommand(e?"ForwardDelete":"Delete",!1,null),i){for(s=W.getBookmark();u=F.get("__mceDel");)F.remove(u,!0); +W.moveToBookmark(s)}}a.on("keydown",function(n){var r;r=n.keyCode==I,c(n)||!r&&n.keyCode!=O||e.modifierPressed(n)||(n.preventDefault(),t(r))}),a.addCommand("Delete",function(){t()})}function d(){function e(e){var t=F.create("body"),n=e.cloneContents();return t.appendChild(n),W.serializer.serialize(t,{format:"html"})}function t(t){var n=e(t),r=F.createRng();r.selectNode(a.getBody());var i=e(r);return n===i}a.on("keydown",function(e){var n=e.keyCode,r;if(!c(e)&&(n==I||n==O)){if(r=a.selection.isCollapsed(),r&&!F.isEmpty(a.getBody()))return;if(j&&!r)return;if(!r&&!t(a.selection.getRng()))return;e.preventDefault(),a.setContent(""),a.selection.setCursorLocation(a.getBody(),0),a.nodeChanged()}})}function f(){a.on("keydown",function(t){!c(t)&&65==t.keyCode&&e.metaKeyPressed(t)&&(t.preventDefault(),a.execCommand("SelectAll"))})}function p(){a.settings.content_editable||(F.bind(a.getDoc(),"focusin",function(){W.setRng(W.getRng())}),F.bind(a.getDoc(),"mousedown",function(e){e.target==a.getDoc().documentElement&&(a.getWin().focus(),W.setRng(W.getRng()))}))}function h(){a.on("keydown",function(e){if(!c(e)&&e.keyCode===O&&W.isCollapsed()&&0===W.getRng(!0).startOffset){var t=W.getNode(),n=t.previousSibling;n&&n.nodeName&&"hr"===n.nodeName.toLowerCase()&&(F.remove(n),e.preventDefault())}})}function m(){window.Range.prototype.getClientRects||a.on("mousedown",function(e){if(!c(e)&&"HTML"===e.target.nodeName){var t=a.getBody();t.blur(),setTimeout(function(){t.focus()},0)}})}function g(){a.on("click",function(e){e=e.target,/^(IMG|HR)$/.test(e.nodeName)&&W.getSel().setBaseAndExtent(e,0,e,1),"A"==e.nodeName&&F.hasClass(e,"mce-item-anchor")&&W.select(e),a.nodeChanged()})}function v(){function e(){var e=F.getAttribs(W.getStart().cloneNode(!1));return function(){var t=W.getStart();t!==a.getBody()&&(F.setAttrib(t,"style",null),P(e,function(e){t.setAttributeNode(e.cloneNode(!0))}))}}function t(){return!W.isCollapsed()&&F.getParent(W.getStart(),F.isBlock)!=F.getParent(W.getEnd(),F.isBlock)}a.on("keypress",function(n){var r;return c(n)||8!=n.keyCode&&46!=n.keyCode||!t()?void 0:(r=e(),a.getDoc().execCommand("delete",!1,null),r(),n.preventDefault(),!1)}),F.bind(a.getDoc(),"cut",function(n){var r;!c(n)&&t()&&(r=e(),setTimeout(function(){r()},0))})}function y(){var e,n;a.on("selectionchange",function(){n&&(clearTimeout(n),n=0),n=window.setTimeout(function(){var n=W.getRng();e&&t.compareRanges(n,e)||(a.nodeChanged(),e=n)},50)})}function b(){document.body.setAttribute("role","application")}function C(){a.on("keydown",function(e){if(!c(e)&&e.keyCode===O&&W.isCollapsed()&&0===W.getRng(!0).startOffset){var t=W.getNode().previousSibling;if(t&&t.nodeName&&"table"===t.nodeName.toLowerCase())return e.preventDefault(),!1}})}function x(){l()>7||(s("RespectVisibilityInDesign",!0),a.contentStyles.push(".mceHideBrInPre pre br {display: none}"),F.addClass(a.getBody(),"mceHideBrInPre"),V.addNodeFilter("pre",function(e){for(var t=e.length,r,i,o,a;t--;)for(r=e[t].getAll("br"),i=r.length;i--;)o=r[i],a=o.prev,a&&3===a.type&&"\n"!=a.value.charAt(a.value-1)?a.value+="\n":o.parent.insert(new n("#text",3),o,!0).value="\n"}),U.addNodeFilter("pre",function(e){for(var t=e.length,n,r,i,o;t--;)for(n=e[t].getAll("br"),r=n.length;r--;)i=n[r],o=i.prev,o&&3==o.type&&(o.value=o.value.replace(/\r?\n$/,""))}))}function w(){F.bind(a.getBody(),"mouseup",function(){var e,t=W.getNode();"IMG"==t.nodeName&&((e=F.getStyle(t,"width"))&&(F.setAttrib(t,"width",e.replace(/[^0-9%]+/g,"")),F.setStyle(t,"width","")),(e=F.getStyle(t,"height"))&&(F.setAttrib(t,"height",e.replace(/[^0-9%]+/g,"")),F.setStyle(t,"height","")))})}function _(){a.on("keydown",function(t){var n,r,i,o,s,l,u,d;if(n=t.keyCode==I,!c(t)&&(n||t.keyCode==O)&&!e.modifierPressed(t)&&(r=W.getRng(),i=r.startContainer,o=r.startOffset,u=r.collapsed,3==i.nodeType&&i.nodeValue.length>0&&(0===o&&!u||u&&o===(n?0:1)))){if(l=i.previousSibling,l&&"IMG"==l.nodeName)return;d=a.schema.getNonEmptyElements(),t.preventDefault(),s=F.create("br",{id:"__tmp"}),i.parentNode.insertBefore(s,i),a.getDoc().execCommand(n?"ForwardDelete":"Delete",!1,null),i=W.getRng().startContainer,l=i.previousSibling,l&&1==l.nodeType&&!F.isBlock(l)&&F.isEmpty(l)&&!d[l.nodeName.toLowerCase()]&&F.remove(l),F.remove("__tmp")}})}function N(){a.on("keydown",function(t){var n,r,i,o,s;if(!c(t)&&t.keyCode==e.BACKSPACE&&(n=W.getRng(),r=n.startContainer,i=n.startOffset,o=F.getRoot(),s=r,n.collapsed&&0===i)){for(;s&&s.parentNode&&s.parentNode.firstChild==s&&s.parentNode!=o;)s=s.parentNode;"BLOCKQUOTE"===s.tagName&&(a.formatter.toggle("blockquote",null,s),n=F.createRng(),n.setStart(r,0),n.setEnd(r,0),W.setRng(n))}})}function E(){function e(){a._refreshContentEditable(),s("StyleWithCSS",!1),s("enableInlineTableEditing",!1),z.object_resizing||s("enableObjectResizing",!1)}z.readonly||a.on("BeforeExecCommand MouseDown",e)}function k(){function e(){P(F.select("a"),function(e){var t=e.parentNode,n=F.getRoot();if(t.lastChild===e){for(;t&&!F.isBlock(t);){if(t.parentNode.lastChild!==t||t===n)return;t=t.parentNode}F.add(t,"br",{"data-mce-bogus":1})}})}a.on("SetContent ExecCommand",function(t){("setcontent"==t.type||"mceInsertLink"===t.command)&&e()})}function S(){z.forced_root_block&&a.on("init",function(){s("DefaultParagraphSeparator",z.forced_root_block)})}function T(){a.on("Undo Redo SetContent",function(e){e.initial||a.execCommand("mceRepaint")})}function R(){a.on("keydown",function(e){var t;c(e)||e.keyCode!=O||(t=a.getDoc().selection.createRange(),t&&t.item&&(e.preventDefault(),a.undoManager.beforeChange(),F.remove(t.item(0)),a.undoManager.add()))})}function A(){var e;l()>=10&&(e="",P("p div h1 h2 h3 h4 h5 h6".split(" "),function(t,n){e+=(n>0?",":"")+t+":empty"}),a.contentStyles.push(e+"{padding-right: 1px !important}"))}function B(){l()<9&&(V.addNodeFilter("noscript",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.firstChild,r&&n.attr("data-mce-innertext",r.value)}),U.addNodeFilter("noscript",function(e){for(var t=e.length,i,o,a;t--;)i=e[t],o=e[t].firstChild,o?o.value=r.decode(o.value):(a=i.attributes.map["data-mce-innertext"],a&&(i.attr("data-mce-innertext",null),o=new n("#text",3),o.value=a,o.raw=!0,i.append(o)))}))}function D(){function e(e,t){var n=i.createTextRange();try{n.moveToPoint(e,t)}catch(r){n=null}return n}function t(t){var r;t.button?(r=e(t.x,t.y),r&&(r.compareEndPoints("StartToStart",a)>0?r.setEndPoint("StartToStart",a):r.setEndPoint("EndToEnd",a),r.select())):n()}function n(){var e=r.selection.createRange();a&&!e.item&&0===e.compareEndPoints("StartToEnd",e)&&a.select(),F.unbind(r,"mouseup",n),F.unbind(r,"mousemove",t),a=o=0}var r=F.doc,i=r.body,o,a,s;r.documentElement.unselectable=!0,F.bind(r,"mousedown contextmenu",function(i){if("HTML"===i.target.nodeName){if(o&&n(),s=r.documentElement,s.scrollHeight>s.clientHeight)return;o=1,a=e(i.x,i.y),a&&(F.bind(r,"mouseup",n),F.bind(r,"mousemove",t),F.win.focus(),a.select())}})}function L(){a.on("keyup focusin",function(t){65==t.keyCode&&e.metaKeyPressed(t)||W.normalize()})}function M(){a.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}")}function H(){a.inline||a.on("keydown",function(){document.activeElement==document.body&&a.getWin().focus()})}var P=o.each,O=e.BACKSPACE,I=e.DELETE,F=a.dom,W=a.selection,z=a.settings,V=a.parser,U=a.serializer,q=i.gecko,j=i.ie,$=i.webkit;C(),N(),d(),L(),$&&(_(),u(),p(),g(),S(),i.iOS?(y(),H()):f()),j&&i.ie<11&&(h(),b(),x(),w(),R(),A(),B(),D()),q&&(h(),m(),v(),E(),k(),T(),M())}}),r(nt,[p],function(e){function t(){return!1}function n(){return!0}var r="__bindings",i=e.makeMap("focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave keydown keypress keyup contextmenu dragend dragover draggesture dragdrop drop drag"," ");return{fire:function(e,i,o){var a=this,s,l,c,u,d;if(e=e.toLowerCase(),i=i||{},i.type=e,i.target||(i.target=a),i.preventDefault||(i.preventDefault=function(){i.isDefaultPrevented=n},i.stopPropagation=function(){i.isPropagationStopped=n},i.stopImmediatePropagation=function(){i.isImmediatePropagationStopped=n},i.isDefaultPrevented=t,i.isPropagationStopped=t,i.isImmediatePropagationStopped=t),a[r]&&(s=a[r][e]))for(l=0,c=s.length;c>l&&(s[l]=u=s[l],!i.isImmediatePropagationStopped());l++)if(u.call(a,i)===!1)return i.preventDefault(),i;if(o!==!1&&a.parent)for(d=a.parent();d&&!i.isPropagationStopped();)d.fire(e,i,!1),d=d.parent();return i},on:function(e,t){var n=this,o,a,s,l;if(t===!1&&(t=function(){return!1}),t)for(s=e.toLowerCase().split(" "),l=s.length;l--;)e=s[l],o=n[r],o||(o=n[r]={}),a=o[e],a||(a=o[e]=[],n.bindNative&&i[e]&&n.bindNative(e)),a.push(t);return n},off:function(e,t){var n=this,o,a=n[r],s,l,c,u;if(a)if(e)for(c=e.toLowerCase().split(" "),o=c.length;o--;){if(e=c[o],s=a[e],!e){for(l in a)a[e].length=0;return n}if(s){if(t)for(u=s.length;u--;)s[u]===t&&s.splice(u,1);else s.length=0;!s.length&&n.unbindNative&&i[e]&&(n.unbindNative(e),delete a[e])}}else{if(n.unbindNative)for(e in a)n.unbindNative(e);n[r]=[]}return n}}}),r(rt,[p,g],function(e,t){var n=e.each,r=e.explode,i={f9:120,f10:121,f11:122};return function(o){var a=this,s={};o.on("keyup keypress keydown",function(e){(e.altKey||e.ctrlKey||e.metaKey)&&n(s,function(n){var r=t.mac?e.ctrlKey||e.metaKey:e.ctrlKey;if(n.ctrl==r&&n.alt==e.altKey&&n.shift==e.shiftKey)return e.keyCode==n.keyCode||e.charCode&&e.charCode==n.charCode?(e.preventDefault(),"keydown"==e.type&&n.func.call(n.scope),!0):void 0})}),a.add=function(t,a,l,c){var u;return u=l,"string"==typeof l?l=function(){o.execCommand(u,!1,null)}:e.isArray(u)&&(l=function(){o.execCommand(u[0],u[1],u[2])}),n(r(t.toLowerCase()),function(e){var t={func:l,scope:c||o,desc:o.translate(a),alt:!1,ctrl:!1,shift:!1};n(r(e,"+"),function(e){switch(e){case"alt":case"ctrl":case"shift":t[e]=!0;break;default:t.charCode=e.charCodeAt(0),t.keyCode=i[e]||e.toUpperCase().charCodeAt(0)}}),s[(t.ctrl?"ctrl":"")+","+(t.alt?"alt":"")+","+(t.shift?"shift":"")+","+t.keyCode]=t}),!0}}}),r(it,[v,b,C,k,E,A,D,L,M,H,P,O,y,l,et,x,_,tt,g,p,nt,rt],function(e,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v,y,b,C,x,w){function _(e,t){return"selectionchange"==t||"drop"==t?e.getDoc():!e.inline&&/^mouse|click|contextmenu/.test(t)?e.getDoc():e.getBody()}function N(e,t,r){var i=this,o,a;o=i.documentBaseUrl=r.documentBaseURL,a=r.baseURI,i.settings=t=T({id:e,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:o,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,padd_empty_editor:!0,render_ui:!0,indentation:"30px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",validate:!0,entity_encoding:"named",url_converter:i.convertURL,url_converter_scope:i,ie7_compat:!0},t),n.settings=t,n.baseURL=r.baseURL,i.id=t.id=e,i.isNotDirty=!0,i.plugins={},i.documentBaseURI=new f(t.document_base_url||o,{base_uri:a}),i.baseURI=a,i.contentCSS=[],i.contentStyles=[],i.shortcuts=new w(i),i.execCommands={},i.queryStateCommands={},i.queryValueCommands={},i.loadedCSS={},i.suffix=r.suffix,i.editorManager=r,i.inline=t.inline,i.execCallback("setup",i)}var E=e.DOM,k=n.ThemeManager,S=n.PluginManager,T=C.extend,R=C.each,A=C.explode,B=C.inArray,D=C.trim,L=C.resolve,M=h.Event,H=b.gecko,P=b.ie,O=b.opera;return N.prototype={render:function(){function e(){var e=p.ScriptLoader;n.language&&"en"!=n.language&&(n.language_url=t.editorManager.baseURL+"/langs/"+n.language+".js"),n.language_url&&e.add(n.language_url),n.theme&&"function"!=typeof n.theme&&"-"!=n.theme.charAt(0)&&!k.urls[n.theme]&&k.load(n.theme,"themes/"+n.theme+"/theme"+i+".js"),C.isArray(n.plugins)&&(n.plugins=n.plugins.join(" ")),R(n.external_plugins,function(e,t){S.load(t,e),n.plugins+=" "+t}),R(n.plugins.split(/[ ,]/),function(e){if(e=D(e),e&&!S.urls[e])if("-"==e.charAt(0)){e=e.substr(1,e.length);var t=S.dependencies(e);R(t,function(e){var t={prefix:"plugins/",resource:e,suffix:"/plugin"+i+".js"};e=S.createUrl(t,e),S.load(e.resource,e)})}else S.load(e,{prefix:"plugins/",resource:e,suffix:"/plugin"+i+".js"})}),e.loadQueue(function(){t.removed||t.init()})}var t=this,n=t.settings,r=t.id,i=t.suffix;if(!M.domLoaded)return E.bind(window,"ready",function(){t.render()}),void 0;if(t.editorManager.settings=n,t.getElement()&&b.contentEditable){n.inline?t.inline=!0:(t.orgVisibility=t.getElement().style.visibility,t.getElement().style.visibility="hidden");var o=t.getElement().form||E.getParent(r,"form");o&&(t.formElement=o,n.hidden_input&&!/TEXTAREA|INPUT/i.test(t.getElement().nodeName)&&E.insertAfter(E.create("input",{type:"hidden",name:r}),r),t.formEventDelegate=function(e){t.fire(e.type,e)},E.bind(o,"submit reset",t.formEventDelegate),t.on("reset",function(){t.setContent(t.startContent,{format:"raw"})}),!n.submit_patch||o.submit.nodeType||o.submit.length||o._mceOldSubmit||(o._mceOldSubmit=o.submit,o.submit=function(){return t.editorManager.triggerSave(),t.isNotDirty=!0,o._mceOldSubmit(o)})),t.windowManager=new m(t),"xml"==n.encoding&&t.on("GetContent",function(e){e.save&&(e.content=E.encode(e.content))}),n.add_form_submit_trigger&&t.on("submit",function(){t.initialized&&t.save()}),n.add_unload_trigger&&(t._beforeUnload=function(){!t.initialized||t.destroyed||t.isHidden()||t.save({format:"raw",no_events:!0,set_dirty:!1})},t.editorManager.on("BeforeUnload",t._beforeUnload)),e()}},init:function(){function e(n){var r=S.get(n),i,o;i=S.urls[n]||t.documentBaseUrl.replace(/\/$/,""),n=D(n),r&&-1===B(h,n)&&(R(S.dependencies(n),function(t){e(t)}),o=new r(t,i),t.plugins[n]=o,o.init&&(o.init(t,i),h.push(n)))}var t=this,n=t.settings,r=t.getElement(),i,o,a,s,l,c,u,d,f,p,h=[];if(t.editorManager.add(t),n.aria_label=n.aria_label||E.getAttrib(r,"aria-label",t.getLang("aria.rich_text_area")),n.theme&&("function"!=typeof n.theme?(n.theme=n.theme.replace(/-/,""),l=k.get(n.theme),t.theme=new l(t,k.urls[n.theme]),t.theme.init&&t.theme.init(t,k.urls[n.theme]||t.documentBaseUrl.replace(/\/$/,""))):t.theme=n.theme),R(n.plugins.replace(/\-/g,"").split(/[ ,]/),e),t.fire("BeforeRenderUI"),n.render_ui&&t.theme&&(t.orgDisplay=r.style.display,"function"!=typeof n.theme?(i=n.width||r.style.width||r.offsetWidth,o=n.height||r.style.height||r.offsetHeight,a=n.min_height||100,f=/^[0-9\.]+(|px)$/i,f.test(""+i)&&(i=Math.max(parseInt(i,10)+(l.deltaWidth||0),100)),f.test(""+o)&&(o=Math.max(parseInt(o,10)+(l.deltaHeight||0),a)),l=t.theme.renderUI({targetNode:r,width:i,height:o,deltaWidth:n.delta_width,deltaHeight:n.delta_height}),n.content_editable||(E.setStyles(l.sizeContainer||l.editorContainer,{wi2dth:i,h2eight:o}),o=(l.iframeHeight||o)+("number"==typeof o?l.deltaHeight||0:""),a>o&&(o=a))):(l=n.theme(t,r),l.editorContainer.nodeType&&(l.editorContainer=l.editorContainer.id=l.editorContainer.id||t.id+"_parent"),l.iframeContainer.nodeType&&(l.iframeContainer=l.iframeContainer.id=l.iframeContainer.id||t.id+"_iframecontainer"),o=l.iframeHeight||r.offsetHeight),t.editorContainer=l.editorContainer),n.content_css&&R(A(n.content_css),function(e){t.contentCSS.push(t.documentBaseURI.toAbsolute(e))}),n.content_style&&t.contentStyles.push(n.content_style),n.content_editable)return r=s=l=null,t.initContentBody();for(document.domain&&location.hostname!=document.domain&&(t.editorManager.relaxedDomain=document.domain),t.iframeHTML=n.doctype+"",n.document_base_url!=t.documentBaseUrl&&(t.iframeHTML+=''),!b.caretAfter&&n.ie7_compat&&(t.iframeHTML+=''),t.iframeHTML+='',p=0;p',t.loadedCSS[m]=!0}u=n.body_id||"tinymce",-1!=u.indexOf("=")&&(u=t.getParam("body_id","","hash"),u=u[t.id]||u),d=n.body_class||"",-1!=d.indexOf("=")&&(d=t.getParam("body_class","","hash"),d=d[t.id]||""),t.iframeHTML+='
    ",t.editorManager.relaxedDomain&&(P||O&&parseFloat(window.opera.version())<11)&&(c='javascript:(function(){document.open();document.domain="'+document.domain+'";'+'var ed = window.parent.tinymce.get("'+t.id+'");document.write(ed.iframeHTML);'+"document.close();ed.initContentBody();})()"),s=E.add(l.iframeContainer,"iframe",{id:t.id+"_ifr",src:c||'javascript:""',frameBorder:"0",allowTransparency:"true",title:t.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),style:{width:"100%",height:o,display:"block"}}),t.contentAreaContainer=l.iframeContainer,l.editorContainer&&(E.get(l.editorContainer).style.display=t.orgDisplay),E.get(t.id).style.display="none",E.setAttrib(t.id,"aria-hidden",!0),t.editorManager.relaxedDomain&&c||t.initContentBody(),r=s=l=null},initContentBody:function(){var t=this,n=t.settings,o=E.get(t.id),f=t.getDoc(),p,h;n.inline||(t.getElement().style.visibility=t.orgVisibility),P&&t.editorManager.relaxedDomain||n.content_editable||(f.open(),f.write(t.iframeHTML),f.close(),t.editorManager.relaxedDomain&&(f.domain=t.editorManager.relaxedDomain)),n.content_editable&&(t.on("remove",function(){var e=this.getBody();E.removeClass(e,"mce-content-body"),E.removeClass(e,"mce-edit-focus"),E.setAttrib(e,"tabIndex",null),E.setAttrib(e,"contentEditable",null)}),E.addClass(o,"mce-content-body"),o.tabIndex=-1,t.contentDocument=f=n.content_document||document,t.contentWindow=n.content_window||window,t.bodyElement=o,n.content_document=n.content_window=null,n.root_name=o.nodeName.toLowerCase()),p=t.getBody(),p.disabled=!0,n.readonly||(p.contentEditable=t.getParam("content_editable_state",!0)),p.disabled=!1,t.schema=new g(n),t.dom=new e(f,{keep_values:!0,url_converter:t.convertURL,url_converter_scope:t,hex_colors:n.force_hex_style_colors,class_filter:n.class_filter,update_styles:!0,root_element:n.content_editable?t.id:null,schema:t.schema,onSetAttrib:function(e){t.fire("SetAttrib",e)}}),t.parser=new v(n,t.schema),t.parser.addAttributeFilter("src,href,style",function(e,n){for(var r=e.length,i,o=t.dom,a,s;r--;)i=e[r],a=i.attr(n),s="data-mce-"+n,i.attributes.map[s]||("style"===n?i.attr(s,o.serializeStyle(o.parseStyle(a),i.name)):i.attr(s,t.convertURL(a,n,i.name)))}),t.parser.addNodeFilter("script",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("type","mce-"+(n.attr("type")||"text/javascript"))}),t.parser.addNodeFilter("#cdata",function(e){for(var t=e.length,n;t--;)n=e[t],n.type=8,n.name="#comment",n.value="[CDATA["+n.value+"]]"}),t.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(e){for(var n=e.length,i,o=t.schema.getNonEmptyElements();n--;)i=e[n],i.isEmpty(o)&&(i.empty().append(new r("br",1)).shortEnded=!0)}),t.serializer=new i(n,t),t.selection=new a(t.dom,t.getWin(),t.serializer,t),t.formatter=new s(t),t.undoManager=new l(t),t.forceBlocks=new u(t),t.enterKey=new c(t),t.editorCommands=new d(t),t.fire("PreInit"),n.browser_spellcheck||n.gecko_spellcheck||(f.body.spellcheck=!1,E.setAttrib(p,"spellcheck","false")),t.fire("PostRender"),t.quirks=y(t),n.directionality&&(p.dir=n.directionality),n.nowrap&&(p.style.whiteSpace="nowrap"),n.protect&&t.on("BeforeSetContent",function(e){R(n.protect,function(t){e.content=e.content.replace(t,function(e){return""})})}),t.on("SetContent",function(){t.addVisual(t.getBody())}),n.padd_empty_editor&&t.on("PostProcess",function(e){e.content=e.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")}),t.load({initial:!0,format:"html"}),t.startContent=t.getContent({format:"raw"}),t.initialized=!0,R(t._pendingNativeEvents,function(e){t.dom.bind(_(t,e),e,function(e){t.fire(e.type,e)})}),t.fire("init"),t.focus(!0),t.nodeChanged({initial:!0}),t.execCallback("init_instance_callback",t),t.contentStyles.length>0&&(h="",R(t.contentStyles,function(e){h+=e+"\r\n"}),t.dom.addStyle(h)),R(t.contentCSS,function(e){t.loadedCSS[e]||(t.dom.loadCSS(e),t.loadedCSS[e]=!0)}),n.auto_focus&&setTimeout(function(){var e=t.editorManager.get(n.auto_focus);e.selection.select(e.getBody(),1),e.selection.collapse(1),e.getBody().focus(),e.getWin().focus()},100),o=f=p=null},focus:function(e){var t,n=this,r=n.selection,i=n.settings.content_editable,o,a,s=n.getDoc(),l;e||(o=r.getRng(),o.item&&(a=o.item(0)),n._refreshContentEditable(),i||(b.opera||n.getBody().focus(),n.getWin().focus()),(H||i)&&(l=n.getBody(),l.setActive?l.setActive():l.focus(),i&&r.normalize()),a&&a.ownerDocument==s&&(o=s.body.createControlRange(),o.addElement(a),o.select())),n.editorManager.activeEditor!=n&&((t=n.editorManager.activeEditor)&&t.fire("deactivate",{relatedTarget:n}),n.fire("activate",{relatedTarget:t})),n.editorManager.activeEditor=n},execCallback:function(e){var t=this,n=t.settings[e],r;if(n)return t.callbackLookup&&(r=t.callbackLookup[e])&&(n=r.func,r=r.scope),"string"==typeof n&&(r=n.replace(/\.\w+$/,""),r=r?L(r):0,n=L(n),t.callbackLookup=t.callbackLookup||{},t.callbackLookup[e]={func:n,scope:r}),n.apply(r||t,Array.prototype.slice.call(arguments,1))},translate:function(e){var t=this.settings.language||"en",n=this.editorManager.i18n;return e?n[t+"."+e]||e.replace(/\{\#([^\}]+)\}/g,function(e,r){return n[t+"."+r]||"{#"+r+"}"}):""},getLang:function(e,n){return this.editorManager.i18n[(this.settings.language||"en")+"."+e]||(n!==t?n:"{#"+e+"}")},getParam:function(e,t,n){var r=e in this.settings?this.settings[e]:t,i;return"hash"===n?(i={},"string"==typeof r?R(r.indexOf("=")>0?r.split(/[;,](?![^=;,]*(?:[;,]|$))/):r.split(","),function(e){e=e.split("="),i[D(e[0])]=e.length>1?D(e[1]):D(e)}):i=r,i):r},nodeChanged:function(){var e=this,t=e.selection,n,r,i;e.initialized&&!e.settings.disable_nodechange&&(i=e.getBody(),n=t.getStart()||i,n=P&&n.ownerDocument!=e.getDoc()?e.getBody():n,"IMG"==n.nodeName&&t.isCollapsed()&&(n=n.parentNode),r=[],e.dom.getParent(n,function(e){return e===i?!0:(r.push(e),void 0)}),e.fire("NodeChange",{element:n,parents:r}))},addButton:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),t.text||t.icon||(t.icon=e),n.buttons=n.buttons||{},t.tooltip=t.tooltip||t.title,n.buttons[e]=t},addMenuItem:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),n.menuItems=n.menuItems||{},n.menuItems[e]=t},addCommand:function(e,t,n){this.execCommands[e]={func:t,scope:n||this}},addQueryStateHandler:function(e,t,n){this.queryStateCommands[e]={func:t,scope:n||this}},addQueryValueHandler:function(e,t,n){this.queryValueCommands[e]={func:t,scope:n||this}},addShortcut:function(e,t,n,r){this.shortcuts.add(e,t,n,r)},execCommand:function(e,t,n,r){var i=this,o=0,a;return/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(e)||r&&r.skip_focus||i.focus(),r=T({},r),r=i.fire("BeforeExecCommand",{command:e,ui:t,value:n}),r.isDefaultPrevented()?!1:(a=i.execCommands[e])&&a.func.call(a.scope,t,n)!==!0?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):(R(i.plugins,function(r){return r.execCommand&&r.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),o=!0,!1):void 0}),o?o:i.theme&&i.theme.execCommand&&i.theme.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):i.editorCommands.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):(i.getDoc().execCommand(e,t,n),i.fire("ExecCommand",{command:e,ui:t,value:n}),void 0))},queryCommandState:function(e){var t=this,n,r;if(!t._isHidden()){if((n=t.queryStateCommands[e])&&(r=n.func.call(n.scope),r!==!0))return r;if(r=t.editorCommands.queryCommandState(e),-1!==r)return r;try{return t.getDoc().queryCommandState(e)}catch(i){}}},queryCommandValue:function(e){var n=this,r,i;if(!n._isHidden()){if((r=n.queryValueCommands[e])&&(i=r.func.call(r.scope),i!==!0))return i;if(i=n.editorCommands.queryCommandValue(e),i!==t)return i;try{return n.getDoc().queryCommandValue(e)}catch(o){}}},show:function(){var e=this;E.show(e.getContainer()),E.hide(e.id),e.load(),e.fire("show")},hide:function(){var e=this,t=e.getDoc();P&&t&&t.execCommand("SelectAll"),e.save(),E.hide(e.getContainer()),E.setStyle(e.id,"display",e.orgDisplay),e.fire("hide")},isHidden:function(){return!E.isHidden(this.id)},setProgressState:function(e,t){this.fire("ProgressState",{state:e,time:t})},load:function(e){var n=this,r=n.getElement(),i;return r?(e=e||{},e.load=!0,i=n.setContent(r.value!==t?r.value:r.innerHTML,e),e.element=r,e.no_events||n.fire("LoadContent",e),e.element=r=null,i):void 0},save:function(e){var t=this,n=t.getElement(),r,i;if(n&&t.initialized)return e=e||{},e.save=!0,e.element=n,r=e.content=t.getContent(e),e.no_events||t.fire("SaveContent",e),r=e.content,/TEXTAREA|INPUT/i.test(n.nodeName)?n.value=r:(n.innerHTML=r,(i=E.getParent(t.id,"form"))&&R(i.elements,function(e){return e.name==t.id?(e.value=r,!1):void 0})),e.element=n=null,e.set_dirty!==!1&&(t.isNotDirty=!0),r},setContent:function(e,t){var n=this,r=n.getBody(),i;return t=t||{},t.format=t.format||"html",t.set=!0,t.content=e,t.no_events||n.fire("BeforeSetContent",t),e=t.content,0===e.length||/^\s+$/.test(e)?(i=n.settings.forced_root_block,i&&n.schema.isValidChild(r.nodeName.toLowerCase(),i.toLowerCase())?e=P&&11>P?"<"+i+">":"<"+i+'>
    ":P||(e='
    '),r.innerHTML=e,n.fire("SetContent",t)):("raw"!==t.format&&(e=new o({},n.schema).serialize(n.parser.parse(e,{isRootContent:!0}))),t.content=D(e),n.dom.setHTML(r,t.content),t.no_events||n.fire("SetContent",t)),t.initial||(n.selection.select(r,!0),n.selection.collapse(!0)),t.content},getContent:function(e){var t=this,n,r=t.getBody();return e=e||{},e.format=e.format||"html",e.get=!0,e.getInner=!0,e.no_events||t.fire("BeforeGetContent",e),n="raw"==e.format?r.innerHTML:"text"==e.format?r.innerText||r.textContent:t.serializer.serialize(r,e),e.content="text"!=e.format?D(n):n,e.no_events||t.fire("GetContent",e),e.content},insertContent:function(e){this.execCommand("mceInsertContent",!1,e)},isDirty:function(){return!this.isNotDirty},getContainer:function(){var e=this;return e.container||(e.container=E.get(e.editorContainer||e.id+"_parent")),e.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return E.get(this.settings.content_element||this.id)},getWin:function(){var e=this,t;return e.contentWindow||(t=E.get(e.id+"_ifr"),t&&(e.contentWindow=t.contentWindow)),e.contentWindow},getDoc:function(){var e=this,t;return e.contentDocument||(t=e.getWin(),t&&(e.contentDocument=t.document)),e.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(e,t,n){var r=this,i=r.settings;return i.urlconverter_callback?r.execCallback("urlconverter_callback",e,n,!0,t):!i.convert_urls||n&&"LINK"==n.nodeName||0===e.indexOf("file:")||0===e.length?e:i.relative_urls?r.documentBaseURI.toRelative(e):e=r.documentBaseURI.toAbsolute(e,i.remove_script_host)},addVisual:function(e){var n=this,r=n.settings,i=n.dom,o;e=e||n.getBody(),n.hasVisual===t&&(n.hasVisual=r.visual),R(i.select("table,a",e),function(e){var t;switch(e.nodeName){case"TABLE":return o=r.visual_table_class||"mce-item-table",t=i.getAttrib(e,"border"),t&&"0"!=t||(n.hasVisual?i.addClass(e,o):i.removeClass(e,o)),void 0;case"A":return i.getAttrib(e,"href",!1)||(t=i.getAttrib(e,"name")||e.id,o="mce-item-anchor",t&&(n.hasVisual?i.addClass(e,o):i.removeClass(e,o))),void 0}}),n.fire("VisualAid",{element:e,hasVisual:n.hasVisual})},remove:function(){var e=this,t=e.getContainer(),n=e.getDoc();e.removed||(e.removed=1,P&&n&&n.execCommand("SelectAll"),e.save(),E.setStyle(e.id,"display",e.orgDisplay),e.settings.content_editable||(M.unbind(e.getWin()),M.unbind(e.getDoc())),M.unbind(e.getBody()),M.unbind(t),e.fire("remove"),e.editorManager.remove(e),E.remove(t))},bindNative:function(e){var t=this;t.initialized?t.dom.bind(_(t,e),e,function(n){t.fire(e,n)}):t._pendingNativeEvents?t._pendingNativeEvents.push(e):t._pendingNativeEvents=[e]},unbindNative:function(e){var t=this;t.initialized&&t.dom.unbind(e)},destroy:function(e){var t=this,n;t.destroyed||(H&&(M.unbind(t.getDoc()),M.unbind(t.getWin()),M.unbind(t.getBody())),e||(t.editorManager.off("beforeunload",t._beforeUnload),t.theme&&t.theme.destroy&&t.theme.destroy(),t.selection.destroy(),t.dom.destroy()),n=t.formElement,n&&(n.submit=n._mceOldSubmit,n._mceOldSubmit=null,E.unbind(n,"submit reset",t.formEventDelegate)),t.contentAreaContainer=t.formElement=t.container=null,t.settings.content_element=t.bodyElement=t.contentDocument=t.contentWindow=null,t.selection&&(t.selection=t.selection.win=t.selection.dom=t.selection.dom.doc=null),t.destroyed=1)},_refreshContentEditable:function(){var e=this,t,n;e._isHidden()&&(t=e.getBody(),n=t.parentNode,n.removeChild(t),n.appendChild(t),t.focus())},_isHidden:function(){var e;return H?(e=this.selection.getSel(),!e||!e.rangeCount||0===e.rangeCount):0}},T(N.prototype,x),N}),r(ot,[],function(){var e={};return{add:function(t,n){for(var r in n)e[r]=n[r]},translate:function(t){if("undefined"==typeof t)return t;if("string"!=typeof t&&t.raw)return t.raw;if(t.push){var n=t.slice(1);t=(e[t[0]]||t[0]).replace(/\{([^\}]+)\}/g,function(e,t){return n[t]})}return e[t]||t},data:e}}),r(at,[v,g],function(e,t){function n(r){function i(){try{return document.activeElement}catch(e){return document.body}}function o(e){return e&&e.startContainer?{startContainer:e.startContainer,startOffset:e.startOffset,endContainer:e.endContainer,endOffset:e.endOffset}:e}function a(e,t){var n;return t.startContainer?(n=e.getDoc().createRange(),n.setStart(t.startContainer,t.startOffset),n.setEnd(t.endContainer,t.endOffset)):n=t,n}function s(s){function l(t){return!!e.DOM.getParent(t,n.isEditorUIElement)}var c=s.editor,u,d;c.on("init",function(){"onbeforedeactivate"in document&&t.ie<11?c.dom.bind(c.getBody(),"beforedeactivate",function(){var e=c.getDoc().selection;try{u=e&&e.createRange?e.createRange():c.selection.getRng()}catch(t){}}):(c.inline||t.ie>10)&&(c.on("nodechange keyup",function(){var e,t=document.activeElement;for(t&&t.id==c.id+"_ifr"&&(t=c.getBody());t;){if(t==c.getBody()){e=!0;break}t=t.parentNode}e&&(u=c.selection.getRng())}),t.webkit&&(d=function(){var e=c.selection.getRng();e.collapsed||(u=e)},e.DOM.bind(document,"selectionchange",d),c.on("remove",function(){e.DOM.unbind(document,"selectionchange",d)})))}),c.on("focusin",function(){var e=r.focusedEditor;c.selection.lastFocusBookmark&&(c.selection.setRng(a(c,c.selection.lastFocusBookmark)),c.selection.lastFocusBookmark=null),e!=c&&(e&&e.fire("blur",{focusedEditor:c}),r.activeEditor=c,c.fire("focus",{blurredEditor:e}),c.focus(!1),r.focusedEditor=c)}),c.on("focusout",function(){c.selection.lastFocusBookmark=o(u),window.setTimeout(function(){var e=r.focusedEditor;e!=c&&(c.selection.lastFocusBookmark=null),l(i())||e!=c||(c.fire("blur",{focusedEditor:null}),r.focusedEditor=null,c.selection.lastFocusBookmark=null)},0)})}r.on("AddEditor",s)}return n.isEditorUIElement=function(e){return-1!==e.className.indexOf("mce-")},n}),r(st,[it,v,O,g,p,nt,ot,at],function(e,n,r,i,o,a,s,l){var c=n.DOM,u=o.explode,d=o.each,f=o.extend,p=0,h,m={majorVersion:"4",minorVersion:"0.5",releaseDate:"2013-08-27",editors:[],i18n:s,activeEditor:null,setup:function(){var e=this,t,n,i="",o;if(n=document.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,""),/[\/\\]$/.test(n)||(n+="/"),o=window.tinymce||window.tinyMCEPreInit)t=o.base||o.baseURL,i=o.suffix; +else for(var a=document.getElementsByTagName("script"),s=0;s0&&d(u(l),function(n){c.get(n)?(s=new e(n,t,o),a.push(s),s.render(!0)):d(document.forms,function(r){d(r.elements,function(r){r.name===n&&(n="mce_editor_"+p++,c.setAttrib(r,"id",n),s=new e(n,t,o),a.push(s),s.render(1))})})});break;case"textareas":case"specific_textareas":d(c.select("textarea"),function(r){t.editor_deselector&&i(r,t.editor_deselector)||(!t.editor_selector||i(r,t.editor_selector))&&(s=new e(n(r),t,o),a.push(s),s.render(!0))})}t.oninit&&(l=h=0,d(a,function(e){h++,e.initialized?l++:e.on("init",function(){l++,l==h&&r(t,"oninit")}),l==h&&r(t,"oninit")}))})},get:function(e){return e===t?this.editors:this.editors[e]},add:function(e){var t=this,n=t.editors;return n[e.id]=e,n.push(e),t.activeEditor=e,t.fire("AddEditor",{editor:e}),h||(h=function(){t.fire("BeforeUnload")},c.bind(window,"beforeunload",h)),e},createEditor:function(t,n){return this.add(new e(t,n,this))},remove:function(e){var t=this,n,r=t.editors,i;if(e){if("string"==typeof e)return e=e.selector||e,d(c.select(e),function(e){t.remove(r[e.id])}),void 0;if(i=e,!r[i.id])return null;for(delete r[i.id],n=0;n=0;n--)t.remove(r[n])},execCommand:function(t,n,r){var i=this,o=i.get(r);switch(t){case"mceAddEditor":return i.get(r)||new e(r,i.settings,i).render(),!0;case"mceRemoveEditor":return o&&o.remove(),!0;case"mceToggleEditor":return o?(o.isHidden()?o.show():o.hide(),!0):(i.execCommand("mceAddEditor",0,r),!0)}return i.activeEditor?i.activeEditor.execCommand(t,n,r):!1},triggerSave:function(){d(this.editors,function(e){e.save()})},addI18n:function(e,t){s.add(e,t)},translate:function(e){return s.translate(e)}};return f(m,a),m.setup(),window.tinymce=window.tinyMCE=m,m}),r(lt,[st,p],function(e,t){var n=t.each,r=t.explode;e.on("AddEditor",function(e){var t=e.editor;t.on("preInit",function(){function e(e,t){n(t,function(t,n){t&&s.setStyle(e,n,t)}),s.rename(e,"span")}function i(e){s=t.dom,l.convert_fonts_to_spans&&n(s.select("font,u,strike",e.node),function(e){o[e.nodeName.toLowerCase()](s,e)})}var o,a,s,l=t.settings;l.inline_styles&&(a=r(l.font_size_legacy_values),o={font:function(t,n){e(n,{backgroundColor:n.style.backgroundColor,color:n.color,fontFamily:n.face,fontSize:a[parseInt(n.size,10)-1]})},u:function(t,n){e(n,{textDecoration:"underline"})},strike:function(t,n){e(n,{textDecoration:"line-through"})}},t.on("PreProcess SetContent",i))})})}),r(ct,[],function(){return{send:function(e){function t(){!e.async||4==n.readyState||r++>1e4?(e.success&&1e4>r&&200==n.status?e.success.call(e.success_scope,""+n.responseText,n,e):e.error&&e.error.call(e.error_scope,r>1e4?"TIMED_OUT":"GENERAL",n,e),n=null):setTimeout(t,10)}var n,r=0;if(e.scope=e.scope||this,e.success_scope=e.success_scope||e.scope,e.error_scope=e.error_scope||e.scope,e.async=e.async===!1?!1:!0,e.data=e.data||"",n=new XMLHttpRequest){if(n.overrideMimeType&&n.overrideMimeType(e.content_type),n.open(e.type||(e.data?"POST":"GET"),e.url,e.async),e.content_type&&n.setRequestHeader("Content-Type",e.content_type),n.setRequestHeader("X-Requested-With","XMLHttpRequest"),n.send(e.data),!e.async)return t();setTimeout(t,10)}}}}),r(ut,[],function(){function e(t,n){var r,i,o,a;if(n=n||'"',null===t)return"null";if(o=typeof t,"string"==o)return i="\bb t\nn\ff\rr\"\"''\\\\",n+t.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(e,t){return'"'===n&&"'"===e?e:(r=i.indexOf(t),r+1?"\\"+i.charAt(r+1):(e=t.charCodeAt().toString(16),"\\u"+"0000".substring(e.length)+e))})+n;if("object"==o){if(t.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(t)){for(r=0,i="[";r0?",":"")+e(t[r],n);return i+"]"}i="{";for(a in t)t.hasOwnProperty(a)&&(i+="function"!=typeof t[a]?(i.length>1?","+n:n)+a+n+":"+e(t[a],n):"");return i+"}"}return""+t}return{serialize:e,parse:function(e){try{return window[String.fromCharCode(101)+"val"]("("+e+")")}catch(t){}}}}),r(dt,[ut,ct,p],function(e,t,n){function r(e){this.settings=i({},e),this.count=0}var i=n.extend;return r.sendRPC=function(e){return(new r).send(e)},r.prototype={send:function(n){var r=n.error,o=n.success;n=i(this.settings,n),n.success=function(t,i){t=e.parse(t),"undefined"==typeof t&&(t={error:"JSON Parse error."}),t.error?r.call(n.error_scope||n.scope,t.error,i):o.call(n.success_scope||n.scope,t.result)},n.error=function(e,t){r&&r.call(n.error_scope||n.scope,e,t)},n.data=e.serialize({id:n.id||"c"+this.count++,method:n.method,params:n.params}),n.content_type="application/json",t.send(n)}},r}),r(ft,[v],function(e){return{callbacks:{},count:0,send:function(n){var r=this,i=e.DOM,o=n.count!==t?n.count:r.count,a="tinymce_jsonp_"+o;r.callbacks[o]=function(e){i.remove(a),delete r.callbacks[o],n.callback(e)},i.add(i.doc.body,"script",{id:a,src:n.url,type:"text/javascript"}),r.count++}}}),r(pt,[],function(){function e(){s=[];for(var e in a)s.push(e);i.length=s.length}function n(){function n(e){var n,r;return r=e!==t?u+e:i.indexOf(",",u),-1===r||r>i.length?null:(n=i.substring(u,r),u=r+1,n)}var r,i,s,u=0;if(a={},c){o.load(l),i=o.getAttribute(l)||"";do r=n(parseInt(n(),32)||0),null!==r&&(s=n(parseInt(n(),32)||0),a[r]=s);while(null!==r);e()}}function r(){var t,n="";if(c){for(var r in a)t=a[r],n+=(n?",":"")+r.length.toString(32)+","+r+","+t.length.toString(32)+","+t;o.setAttribute(l,n),o.save(l),e()}}var i,o,a,s,l,c;return window.localStorage?localStorage:(l="tinymce",o=document.documentElement,c=!!o.addBehavior,c&&o.addBehavior("#default#userData"),i={key:function(e){return s[e]},getItem:function(e){return e in a?a[e]:null},setItem:function(e,t){a[e]=""+t,r()},removeItem:function(e){delete a[e],r()},clear:function(){a={},r()}},n(),i)}),r(ht,[v,l,y,b,p,g],function(e,t,n,r,i,o){var a=window.tinymce;return a.DOM=e.DOM,a.ScriptLoader=n.ScriptLoader,a.PluginManager=r.PluginManager,a.ThemeManager=r.ThemeManager,a.dom=a.dom||{},a.dom.Event=t.Event,i.each(i,function(e,t){a[t]=e}),i.each("isOpera isWebKit isIE isGecko isMac".split(" "),function(e){a[e]=o[e.substr(2).toLowerCase()]}),{}}),r(mt,[I,p],function(e,t){return e.extend({Defaults:{firstControlClass:"first",lastControlClass:"last"},init:function(e){this.settings=t.extend({},this.Defaults,e)},preRender:function(e){e.addClass(this.settings.containerClass,"body")},applyClasses:function(e){var t=this,n=t.settings,r,i,o;r=e.items().filter(":visible"),i=n.firstControlClass,o=n.lastControlClass,r.each(function(e){e.removeClass(i).removeClass(o),n.controlClass&&e.addClass(n.controlClass)}),r.eq(0).addClass(i),r.eq(-1).addClass(o)},renderHtml:function(e){var t=this,n=t.settings,r,i="";return r=e.items(),r.eq(0).addClass(n.firstControlClass),r.eq(-1).addClass(n.lastControlClass),r.each(function(e){n.controlClass&&e.addClass(n.controlClass),i+=e.renderHtml()}),i},recalc:function(){},postRender:function(){}})}),r(gt,[mt],function(e){return e.extend({Defaults:{containerClass:"abs-layout",controlClass:"abs-layout-item"},recalc:function(e){e.items().filter(":visible").each(function(e){var t=e.settings;e.layoutRect({x:t.x,y:t.y,w:t.w,h:t.h}),e.recalc&&e.recalc()})},renderHtml:function(e){return'
    '+this._super(e)}})}),r(vt,[V,G],function(e,t){return e.extend({Mixins:[t],Defaults:{classes:"widget tooltip tooltip-n"},text:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t._rendered&&(t.getEl().lastChild.innerHTML=t.encode(e)),t):t._value},renderHtml:function(){var e=this,t=e.classPrefix;return'"},repaint:function(){var e=this,t,n;t=e.getEl().style,n=e._layoutRect,t.left=n.x+"px",t.top=n.y+"px",t.zIndex=131070}})}),r(yt,[V,vt],function(e,t){var n,r=e.extend({init:function(e){var t=this;t._super(e),t.canFocus=!0,e.tooltip&&r.tooltips!==!1&&t.on("mouseenter mouseleave",function(n){var r=t.tooltip().moveTo(-65535);if(n.control==t&&"mouseenter"==n.type){var i=r.text(e.tooltip).show().testMoveRel(t.getEl(),["bc-tc","bc-tl","bc-tr"]);r.toggleClass("tooltip-n","bc-tc"==i),r.toggleClass("tooltip-nw","bc-tl"==i),r.toggleClass("tooltip-ne","bc-tr"==i),r.moveRel(t.getEl(),i)}else r.hide()}),t.aria("label",e.tooltip)},tooltip:function(){var e=this;return n||(n=new t({type:"tooltip"}),n.renderTo(e.getContainerElm())),n},active:function(e){var t=this,n;return e!==n&&(t.aria("pressed",e),t.toggleClass("active",e)),t._super(e)},disabled:function(e){var t=this,n;return e!==n&&(t.aria("disabled",e),t.toggleClass("disabled",e)),t._super(e)},postRender:function(){var e=this,t=e.settings;e._rendered=!0,e._super(),e.parent()||!t.width&&!t.height||(e.initLayoutRect(),e.repaint()),t.autofocus&&setTimeout(function(){e.focus()},0)},remove:function(){this._super(),n&&(n.remove(),n=null)}});return r}),r(bt,[yt],function(e){return e.extend({Defaults:{classes:"widget btn",role:"button"},init:function(e){var t=this,n;t.on("click mousedown",function(e){e.preventDefault()}),t._super(e),n=e.size,e.subtype&&t.addClass(e.subtype),n&&t.addClass("btn-"+n)},repaint:function(){var e=this.getEl().firstChild.style;e.width=e.height="100%",this._super()},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon,i="";return e.settings.image&&(r="none",i=" style=\"background-image: url('"+e.settings.image+"')\""),r=e.settings.icon?n+"ico "+n+"i-"+r:"",'
    '+'"+"
    "}})}),r(Ct,[q],function(e){return e.extend({Defaults:{defaultType:"button",role:"toolbar"},renderHtml:function(){var e=this,t=e._layout;return e.addClass("btn-group"),e.preRender(),t.preRender(e),'
    '+'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "+"
    "}})}),r(xt,[yt],function(e){return e.extend({Defaults:{classes:"checkbox",role:"checkbox",checked:!1},init:function(e){var t=this;t._super(e),t.on("click mousedown",function(e){e.preventDefault()}),t.on("click",function(e){e.preventDefault(),t.disabled()||t.checked(!t.checked())}),t.checked(t.settings.checked)},checked:function(e){var t=this;return"undefined"!=typeof e?(e?t.addClass("checked"):t.removeClass("checked"),t._checked=e,t.aria("checked",e),t):t._checked},value:function(e){return this.checked(e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix;return'
    '+''+''+e.encode(e._text)+""+"
    "}})}),r(wt,[bt,X],function(e,t){return e.extend({showPanel:function(){var e=this,n=e.settings;n.panel.popover=!0,n.panel.autohide=!0,e.active(!0),e.panel?e.panel.show():(e.panel=new t(n.panel).on("hide",function(){e.active(!1)}).parent(e).renderTo(e.getContainerElm()),e.panel.fire("show"),e.panel.reflow()),e.panel.moveRel(e.getEl(),n.popoverAlign||"bc-tc")},hidePanel:function(){var e=this;e.panel&&e.panel.hide()},postRender:function(){var e=this;return e.on("click",function(t){t.control===e&&(e.panel&&e.panel.visible()?e.hidePanel():e.showPanel())}),e._super()}})}),r(_t,[wt,v],function(e,t){var n=t.DOM;return e.extend({init:function(e){this._super(e),this.addClass("colorbutton")},color:function(e){return e?(this._color=e,this.getEl("preview").style.backgroundColor=e,this):this._color},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon?n+"ico "+n+"i-"+e.settings.icon:"",i=e.settings.image?" style=\"background-image: url('"+e.settings.image+"')\"":"";return'
    '+'"+'"+"
    "},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(r){r.control!=e||n.getParent(r.target,"."+e.classPrefix+"open")||(r.stopImmediatePropagation(),t.call(e,r))}),delete e.settings.onclick,e._super()}})}),r(Nt,[yt,z],function(e,t){return e.extend({init:function(e){var n=this;n._super(e),n.addClass("combobox"),n.on("click",function(e){for(var t=e.target;t;)t.id&&-1!=t.id.indexOf("-open")&&n.fire("action"),t=t.parentNode}),n.on("keydown",function(e){"INPUT"==e.target.nodeName&&13==e.keyCode&&n.parents().reverse().each(function(t){return e.preventDefault(),n.fire("change"),t.submit?(t.submit(),!1):void 0})}),e.placeholder&&(n.addClass("placeholder"),n.on("focusin",function(){n._hasOnChange||(t.on(n.getEl("inp"),"change",function(){n.fire("change")}),n._hasOnChange=!0),n.hasClass("placeholder")&&(n.getEl("inp").value="",n.removeClass("placeholder"))}),n.on("focusout",function(){0===n.value().length&&(n.getEl("inp").value=e.placeholder,n.addClass("placeholder"))}))},value:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t.removeClass("placeholder"),t._rendered&&(t.getEl("inp").value=e),t):t._rendered?(e=t.getEl("inp").value,e!=t.settings.placeholder?e:""):t._value},disabled:function(e){var t=this;t._super(e),t._rendered&&(t.getEl("inp").disabled=e)},focus:function(){this.getEl("inp").focus()},repaint:function(){var e=this,n=e.getEl(),r=e.getEl("open"),i=e.layoutRect(),o,a;o=r?i.w-r.offsetWidth-10:i.w-10;var s=document;return s.all&&(!s.documentMode||s.documentMode<=8)&&(a=e.layoutRect().h-2+"px"),t.css(n.firstChild,{width:o,lineHeight:a}),e._super(),e},postRender:function(){var e=this;return t.on(this.getEl("inp"),"change",function(){e.fire("change")}),e._super()},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.classPrefix,i=n.value||n.placeholder||"",o,a,s="";return o=n.icon?r+"ico "+r+"i-"+n.icon:"",a=e._text,(o||a)&&(s='
    '+'"+"
    ",e.addClass("has-open")),'
    '+''+s+"
    "}})}),r(Et,[V,J],function(e,t){return e.extend({Defaults:{delimiter:"\xbb"},init:function(e){var t=this;t._super(e),t.addClass("path"),t.canFocus=!0,t.on("click",function(e){var n,r=e.target;(n=r.getAttribute("data-index"))&&t.fire("select",{value:t.data()[n],index:n})})},focus:function(){var e=this;return e.keyNav=new t({root:e,enableLeftRight:!0}),e.keyNav.focusFirst(),e},data:function(e){var t=this;return"undefined"!=typeof e?(t._data=e,t.update(),t):t._data},update:function(){this.innerHtml(this._getPathHtml())},postRender:function(){var e=this;e._super(),e.data(e.settings.data)},renderHtml:function(){var e=this;return'
    '+e._getPathHtml()+"
    "},_getPathHtml:function(){var e=this,t=e._data||[],n,r,i="",o=e.classPrefix;for(n=0,r=t.length;r>n;n++)i+=(n>0?'":"")+'
    '+t[n].name+"
    ";return i||(i='
     
    '),i}})}),r(kt,[Et,st],function(e,t){return e.extend({postRender:function(){function e(e){return 1===e.nodeType&&("BR"==e.nodeName||!!e.getAttribute("data-mce-bogus"))}var n=this,r=t.activeEditor;return n.on("select",function(t){var n=[],i,o=r.getBody();for(r.focus(),i=r.selection.getStart();i&&i!=o;)e(i)||n.push(i),i=i.parentNode;r.selection.select(n[n.length-1-t.index]),r.nodeChanged()}),r.on("nodeChange",function(t){for(var i=[],o=t.parents,a=o.length;a--;)if(1==o[a].nodeType&&!e(o[a])){var s=r.fire("ResolveName",{name:o[a].nodeName.toLowerCase(),target:o[a]});i.push({name:s.name})}n.data(i)}),n._super()}})}),r(St,[q],function(e){return e.extend({Defaults:{layout:"flex",align:"center",defaults:{flex:1}},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.addClass("formitem"),t.preRender(e),'
    '+(e.settings.title?'
    '+e.settings.title+"
    ":"")+'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "+"
    "}})}),r(Tt,[q,St],function(e,t){return e.extend({Defaults:{containerCls:"form",layout:"flex",direction:"column",align:"stretch",flex:1,padding:20,labelGap:30,spacing:10},preRender:function(){var e=this,n=e.items();n.each(function(n){var r,i=n.settings.label;i&&(r=new t({layout:"flex",autoResize:"overflow",defaults:{flex:1},items:[{type:"label",text:i,flex:0,forId:n._id}]}),r.type="formitem","undefined"==typeof n.settings.flex&&(n.settings.flex=1),e.replace(n,r),r.add(n))})},recalcLabels:function(){var e=this,t=0,n=[],r,i;if(e.settings.labelGapCalc!==!1)for(e.items().filter("formitem").each(function(e){var r=e.items()[0],i=r.getEl().clientWidth;t=i>t?i:t,n.push(r)}),i=e.settings.labelGap||0,r=n.length;r--;)n[r].settings.minWidth=t+i},visible:function(e){var t=this._super(e);return e===!0&&this._rendered&&this.recalcLabels(),t},submit:function(){var e=this.getParentCtrl(document.activeElement);return e&&e.blur(),this.fire("submit",{data:this.toJSON()})},postRender:function(){var e=this;e._super(),e.recalcLabels(),e.fromJSON(e.settings.data)}})}),r(Rt,[Tt],function(e){return e.extend({Defaults:{containerCls:"fieldset",layout:"flex",direction:"column",align:"stretch",flex:1,padding:"25 15 5 15",labelGap:30,spacing:10,border:1},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.preRender(),t.preRender(e),'
    '+(e.settings.title?''+e.settings.title+"":"")+'
    '+(e.settings.html||"")+t.renderHtml(e)+"
    "+"
    "}})}),r(At,[Nt],function(e){return e.extend({init:function(e){var t=this,n=tinymce.activeEditor,r;e.spellcheck=!1,r=n.settings.file_browser_callback,r&&(e.icon="browse",e.onaction=function(){r(t.getEl("inp").id,t.getEl("inp").value,e.filetype,window)}),t._super(e)}})}),r(Bt,[gt],function(e){return e.extend({recalc:function(e){var t=e.layoutRect(),n=e.paddingBox();e.items().filter(":visible").each(function(e){e.layoutRect({x:n.left,y:n.top,w:t.innerW-n.right-n.left,h:t.innerH-n.top-n.bottom}),e.recalc&&e.recalc()})}})}),r(Dt,[gt],function(e){return e.extend({recalc:function(e){var t,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v=[],y,b,C,x,w,_,N,E,k,S,T,R,A,B,D,L,M,H,P,O,I,F,W,z,V=Math.max,U=Math.min;for(r=e.items().filter(":visible"),i=e.layoutRect(),o=e._paddingBox,a=e.settings,f=a.direction,s=a.align,l=a.pack,c=a.spacing||0,("row-reversed"==f||"column-reverse"==f)&&(r=r.set(r.toArray().reverse()),f=f.split("-")[0]),"column"==f?(k="y",N="h",E="minH",S="maxH",R="innerH",T="top",A="bottom",B="deltaH",D="contentH",I="left",H="w",L="x",M="innerW",P="minW",O="maxW",F="right",W="deltaW",z="contentW"):(k="x",N="w",E="minW",S="maxW",R="innerW",T="left",A="right",B="deltaW",D="contentW",I="top",H="h",L="y",M="innerH",P="minH",O="maxH",F="bottom",W="deltaH",z="contentH"),d=i[R]-o[T]-o[T],_=u=0,t=0,n=r.length;n>t;t++)p=r[t],h=p.layoutRect(),m=p.settings,g=m.flex,d-=n-1>t?c:0,g>0&&(u+=g,h[S]&&v.push(p),h.flex=g),d-=h[E],y=o[I]+h[P]+o[F],y>_&&(_=y);if(x={},x[E]=0>d?i[E]-d+i[B]:i[R]-d+i[B],x[P]=_+i[W],x[D]=i[R]-d,x[z]=_,x.minW=U(x.minW,i.maxW),x.minH=U(x.minH,i.maxH),x.minW=V(x.minW,i.startMinWidth),x.minH=V(x.minH,i.startMinHeight),!i.autoResize||x.minW==i.minW&&x.minH==i.minH){for(C=d/u,t=0,n=v.length;n>t;t++)p=v[t],h=p.layoutRect(),b=h[S],y=h[E]+Math.ceil(h.flex*C),y>b?(d-=h[S]-h[E],u-=h.flex,h.flex=0,h.maxFlexSize=b):h.maxFlexSize=0;for(C=d/u,w=o[T],x={},0===u&&("end"==l?w=d+o[T]:"center"==l?(w=Math.round(i[R]/2-(i[R]-d)/2)+o[T],0>w&&(w=o[T])):"justify"==l&&(w=o[T],c=Math.floor(d/(r.length-1)))),x[L]=o[I],t=0,n=r.length;n>t;t++)p=r[t],h=p.layoutRect(),y=h.maxFlexSize||h[E],"center"===s?x[L]=Math.round(i[M]/2-h[H]/2):"stretch"===s?(x[H]=V(h[P]||0,i[M]-o[I]-o[F]),x[L]=o[I]):"end"===s&&(x[L]=i[M]-h[H]-o.top),h.flex>0&&(y+=Math.ceil(h.flex*C)),x[N]=y,x[k]=w,p.layoutRect(x),p.recalc&&p.recalc(),w+=y+c}else if(x.w=x.minW,x.h=x.minH,e.layoutRect(x),this.recalc(e),null===e._lastRect){var q=e.parent();q&&(q._lastRect=null,q.recalc())}}})}),r(Lt,[mt],function(e){return e.extend({Defaults:{containerClass:"flow-layout",controlClass:"flow-layout-item",endClass:"break"},recalc:function(e){e.items().filter(":visible").each(function(e){e.recalc&&e.recalc()})}})}),r(Mt,[V,yt,X,p,st,g],function(e,t,n,r,i,o){function a(e){function t(t){function n(e){return e.replace(/%(\w+)/g,"")}var r,i,o=e.dom,a="",l,c;return c=e.settings.preview_styles,c===!1?"":(c||(c="font-family font-size font-weight text-decoration text-transform color background-color border border-radius"),(t=e.formatter.get(t))?(t=t[0],r=t.block||t.inline||"span",i=o.create(r),s(t.styles,function(e,t){e=n(e),e&&o.setStyle(i,t,e)}),s(t.attributes,function(e,t){e=n(e),e&&o.setAttrib(i,t,e)}),s(t.classes,function(e){e=n(e),o.hasClass(i,e)||o.addClass(i,e)}),e.fire("PreviewFormats"),o.setStyles(i,{position:"absolute",left:-65535}),e.getBody().appendChild(i),l=o.getStyle(e.getBody(),"fontSize",!0),l=/px$/.test(l)?parseInt(l,10):0,s(c.split(" "),function(t){var n=o.getStyle(i,t,!0);if(!("background-color"==t&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(n)&&(n=o.getStyle(e.getBody(),t,!0),"#ffffff"==o.toHex(n).toLowerCase())||"color"==t&&"#000000"==o.toHex(n).toLowerCase())){if("font-size"==t&&/em|%$/.test(n)){if(0===l)return;n=parseFloat(n,10)/(/%$/.test(n)?100:1),n=n*l+"px"}"border"==t&&n&&(a+="padding:0 2px;"),a+=t+":"+n+";"}}),e.fire("AfterPreviewFormats"),o.remove(i),a):void 0)}function r(t,n){return function(){var r=this;e.on("nodeChange",function(i){var o=e.formatter,a=null;s(i.parents,function(e){return s(t,function(t){return n?o.matchNode(e,n,{value:t.value})&&(a=t.value):o.matchNode(e,t.value)&&(a=t.value),a?!1:void 0}),a?!1:void 0}),r.value(a)})}}function i(e){e=e.split(";");for(var t=e.length;t--;)e[t]=e[t].split("=");return e}function o(){function n(e){var t=[];if(e)return s(e,function(e){var o={text:e.title,icon:e.icon};if(e.items)o.menu=n(e.items);else{var a=e.format||"custom"+r++;e.format||(e.name=a,i.push(e)),o.format=a}t.push(o)}),t}var r=0,i=[],o=[{title:"Headers",items:[{title:"Header 1",format:"h1"},{title:"Header 2",format:"h2"},{title:"Header 3",format:"h3"},{title:"Header 4",format:"h4"},{title:"Header 5",format:"h5"},{title:"Header 6",format:"h6"}]},{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}];e.on("init",function(){s(i,function(t){e.formatter.register(t.name,t)})});var a=n(e.settings.style_formats||o);return a={type:"menu",items:a,onPostRender:function(t){e.fire("renderFormatsMenu",{control:t.control})},itemDefaults:{preview:!0,textStyle:function(){return this.settings.format?t(this.settings.format):void 0},onPostRender:function(){var t=this,n=this.settings.format;n&&t.parent().on("show",function(){t.disabled(!e.formatter.canApply(n)),t.active(e.formatter.match(n))})},onclick:function(){this.settings.format&&f(this.settings.format)}}}}function a(){return e.undoManager?e.undoManager.hasUndo():!1}function l(){return e.undoManager?e.undoManager.hasRedo():!1}function c(){var t=this;t.disabled(!a()),e.on("Undo Redo AddUndo TypingUndo",function(){t.disabled(!a())})}function u(){var t=this;t.disabled(!l()),e.on("Undo Redo AddUndo TypingUndo",function(){t.disabled(!l())})}function d(){var t=this;e.on("VisualAid",function(e){t.active(e.hasVisual)}),t.active(e.hasVisual)}function f(t){t.control&&(t=t.control.value()),t&&e.execCommand("mceToggleFormat",!1,t)}var p;p=o(),s({bold:"Bold",italic:"Italic",underline:"Underline",strikethrough:"Strikethrough",subscript:"Subscript",superscript:"Superscript"},function(t,n){e.addButton(n,{tooltip:t,onPostRender:function(){var t=this;e.formatter?e.formatter.formatChanged(n,function(e){t.active(e)}):e.on("init",function(){e.formatter.formatChanged(n,function(e){t.active(e)})})},onclick:function(){f(n)}})}),s({outdent:["Decrease indent","Outdent"],indent:["Increase indent","Indent"],cut:["Cut","Cut"],copy:["Copy","Copy"],paste:["Paste","Paste"],help:["Help","mceHelp"],selectall:["Select all","SelectAll"],hr:["Insert horizontal rule","InsertHorizontalRule"],removeformat:["Clear formatting","RemoveFormat"],visualaid:["Visual aids","mceToggleVisualAid"],newdocument:["New document","mceNewDocument"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1]})}),s({blockquote:["Toggle blockquote","mceBlockQuote"],numlist:["Numbered list","InsertOrderedList"],bullist:["Bullet list","InsertUnorderedList"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],alignleft:["Align left","JustifyLeft"],aligncenter:["Align center","JustifyCenter"],alignright:["Align right","JustifyRight"],alignjustify:["Justify","JustifyFull"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1],onPostRender:function(){var t=this;e.formatter?e.formatter.formatChanged(n,function(e){t.active(e)}):e.on("init",function(){e.formatter.formatChanged(n,function(e){t.active(e)})})}})}),e.addButton("undo",{tooltip:"Undo",onPostRender:c,cmd:"undo"}),e.addButton("redo",{tooltip:"Redo",onPostRender:u,cmd:"redo"}),e.addMenuItem("newdocument",{text:"New document",shortcut:"Ctrl+N",icon:"newdocument",cmd:"mceNewDocument"}),e.addMenuItem("undo",{text:"Undo",icon:"undo",shortcut:"Ctrl+Z",onPostRender:c,cmd:"undo"}),e.addMenuItem("redo",{text:"Redo",icon:"redo",shortcut:"Ctrl+Y",onPostRender:u,cmd:"redo"}),e.addMenuItem("visualaid",{text:"Visual aids",selectable:!0,onPostRender:d,cmd:"mceToggleVisualAid"}),s({cut:["Cut","Cut","Ctrl+X"],copy:["Copy","Copy","Ctrl+C"],paste:["Paste","Paste","Ctrl+V"],selectall:["Select all","SelectAll","Ctrl+A"],bold:["Bold","Bold","Ctrl+B"],italic:["Italic","Italic","Ctrl+I"],underline:["Underline","Underline"],strikethrough:["Strikethrough","Strikethrough"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],removeformat:["Clear formatting","RemoveFormat"]},function(t,n){e.addMenuItem(n,{text:t[0],icon:n,shortcut:t[2],cmd:t[1]})}),e.on("mousedown",function(){n.hideAll()}),e.addButton("styleselect",{type:"menubutton",text:"Formats",menu:p}),e.addButton("formatselect",function(){var n=[],o=i(e.settings.block_formats||"Paragraph=p;Address=address;Pre=pre;Header 1=h1;Header 2=h2;Header 3=h3;Header 4=h4;Header 5=h5;Header 6=h6");return s(o,function(e){n.push({text:e[0],value:e[1],textStyle:function(){return t(e[1])}})}),{type:"listbox",text:{raw:o[0][0]},values:n,fixedWidth:!0,onselect:f,onPostRender:r(n)}}),e.addButton("fontselect",function(){var t="Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",n=[],o=i(e.settings.font_formats||t);return s(o,function(e){n.push({text:{raw:e[0]},value:e[1],textStyle:-1==e[1].indexOf("dings")?"font-family:"+e[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:n,fixedWidth:!0,onPostRender:r(n,"fontname"),onselect:function(t){t.control.settings.value&&e.execCommand("FontName",!1,t.control.settings.value)}}}),e.addButton("fontsizeselect",function(){var t=[],n="8pt 10pt 12pt 14pt 18pt 24pt 36pt",i=e.settings.fontsize_formats||n;return s(i.split(" "),function(e){t.push({text:e,value:e})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:t,fixedWidth:!0,onPostRender:r(t,"fontsize"),onclick:function(t){t.control.settings.value&&e.execCommand("FontSize",!1,t.control.settings.value)}}}),e.addMenuItem("formats",{text:"Formats",menu:p})}var s=r.each;i.on("AddEditor",function(e){a(e.editor)}),e.translate=function(e){return i.translate(e)},t.tooltips=!o.iOS}),r(Ht,[gt],function(e){return e.extend({recalc:function(e){var t=e.settings,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v,y,b,C,x,w,_,N=[],E=[],k,S,T,R,A,B;for(t=e.settings,i=e.items().filter(":visible"),o=e.layoutRect(),r=t.columns||Math.ceil(Math.sqrt(i.length)),n=Math.ceil(i.length/r),y=t.spacingH||t.spacing||0,b=t.spacingV||t.spacing||0,C=t.alignH||t.align,x=t.alignV||t.align,g=e._paddingBox,C&&"string"==typeof C&&(C=[C]),x&&"string"==typeof x&&(x=[x]),d=0;r>d;d++)N.push(0);for(f=0;n>f;f++)E.push(0);for(f=0;n>f;f++)for(d=0;r>d&&(u=i[f*r+d],u);d++)c=u.layoutRect(),k=c.minW,S=c.minH,N[d]=k>N[d]?k:N[d],E[f]=S>E[f]?S:E[f];for(A=o.innerW-g.left-g.right,w=0,d=0;r>d;d++)w+=N[d]+(d>0?y:0),A-=(d>0?y:0)+N[d];for(B=o.innerH-g.top-g.bottom,_=0,f=0;n>f;f++)_+=E[f]+(f>0?b:0),B-=(f>0?b:0)+E[f];if(w+=g.left+g.right,_+=g.top+g.bottom,l={},l.minW=w+(o.w-o.innerW),l.minH=_+(o.h-o.innerH),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH,l.minW=Math.min(l.minW,o.maxW),l.minH=Math.min(l.minH,o.maxH),l.minW=Math.max(l.minW,o.startMinWidth),l.minH=Math.max(l.minH,o.startMinHeight),!o.autoResize||l.minW==o.minW&&l.minH==o.minH){o.autoResize&&(l=e.layoutRect(l),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH);var D;D="start"==t.packV?0:B>0?Math.floor(B/n):0;var L=0,M=t.flexWidths;if(M)for(d=0;dd;d++)N[d]+=M?Math.ceil(M[d]*H):H;for(h=g.top,f=0;n>f;f++){for(p=g.left,s=E[f]+D,d=0;r>d&&(u=i[f*r+d],u);d++)m=u.settings,c=u.layoutRect(),a=N[d],T=R=0,c.x=p,c.y=h,v=m.alignH||(C?C[d]||C[0]:null),"center"==v?c.x=p+a/2-c.w/2:"right"==v?c.x=p+a-c.w:"stretch"==v&&(c.w=a),v=m.alignV||(x?x[d]||x[0]:null),"center"==v?c.y=h+s/2-c.h/2:"bottom"==v?c.y=h+s-c.h:"stretch"==v&&(c.h=s),u.layoutRect(c),p+=a+y,u.recalc&&u.recalc(); +h+=s+b}}else if(l.w=l.minW,l.h=l.minH,e.layoutRect(l),this.recalc(e),null===e._lastRect){var P=e.parent();P&&(P._lastRect=null,P.recalc())}}})}),r(Pt,[yt],function(e){return e.extend({renderHtml:function(){var e=this;return e.addClass("iframe"),e.canFocus=!1,''},src:function(e){this.getEl().src=e},html:function(e,t){var n=this,r=this.getEl().contentWindow.document.body;return r?(r.innerHTML=e,t&&t()):setTimeout(function(){n.html(e)},0),this}})}),r(Ot,[yt],function(e){return e.extend({init:function(e){var t=this;t._super(e),t.addClass("widget"),t.addClass("label"),t.canFocus=!1,e.multiline&&t.addClass("autoscroll"),e.strong&&t.addClass("strong")},initLayoutRect:function(){var e=this,t=e._super();return e.settings.multiline&&(e.getEl().offsetWidth>t.maxW&&(t.minW=t.maxW,e.addClass("multiline")),e.getEl().style.width=t.minW+"px",t.startMinH=t.h=t.minH=Math.min(t.maxH,e.getEl().offsetHeight)),t},disabled:function(e){var t=this,n;return e!==n&&(t.toggleClass("label-disabled",e),t._rendered&&(t.getEl()[0].className=t.classes())),t._super(e)},repaint:function(){var e=this;return e.settings.multiline||(e.getEl().style.lineHeight=e.layoutRect().h+"px"),e._super()},text:function(e){var t=this;return t._rendered&&e&&this.innerHtml(t.encode(e)),t._super(e)},renderHtml:function(){var e=this,t=e.settings.forId;return'"}})}),r(It,[q,J],function(e,t){return e.extend({Defaults:{role:"toolbar",layout:"flow"},init:function(e){var t=this;t._super(e),t.addClass("toolbar")},postRender:function(){var e=this;return e.items().addClass("toolbar-item"),e.keyNav=new t({root:e,enableLeftRight:!0}),e._super()}})}),r(Ft,[It],function(e){return e.extend({Defaults:{role:"menubar",containerCls:"menubar",defaults:{type:"menubutton"}}})}),r(Wt,[bt,U,Ft],function(e,t,n){function r(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1}var i=e.extend({init:function(e){var t=this;t._renderOpen=!0,t._super(e),t.addClass("menubtn"),e.fixedWidth&&t.addClass("fixed-width"),t.aria("haspopup",!0),t.hasPopup=!0},showMenu:function(){var e=this,n=e.settings,r;return e.menu&&e.menu.visible()?e.hideMenu():(e.menu||(r=n.menu||[],r.length?r={type:"menu",items:r}:r.type=r.type||"menu",e.menu=t.create(r).parent(e).renderTo(e.getContainerElm()),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control===e.menu&&e.focus()}),e.menu.on("show hide",function(t){t.control==e.menu&&e.activeMenu("show"==t.type)}).fire("show"),e.aria("expanded",!0)),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),e.menu.moveRel(e.getEl(),["bl-tl","tl-bl"]),void 0)},hideMenu:function(){var e=this;e.menu&&(e.menu.items().each(function(e){e.hideMenu&&e.hideMenu()}),e.menu.hide(),e.aria("expanded",!1))},activeMenu:function(e){this.toggleClass("active",e)},renderHtml:function(){var e=this,t=e._id,r=e.classPrefix,i=e.settings.icon?r+"ico "+r+"i-"+e.settings.icon:"";return e.aria("role",e.parent()instanceof n?"menuitem":"button"),'
    '+'"+"
    "},postRender:function(){var e=this;return e.on("click",function(t){t.control===e&&r(t.target,e.getEl())&&(e.showMenu(),t.keyboard&&e.menu.items()[0].focus())}),e.on("mouseenter",function(t){var n=t.control,r=e.parent(),o;n&&r&&n instanceof i&&n.parent()==r&&(r.items().filter("MenuButton").each(function(e){e.hideMenu&&e!=n&&(e.menu&&e.menu.visible()&&(o=!0),e.hideMenu())}),o&&(n.focus(),n.showMenu()))}),e._super()},text:function(e){var t=this,n,r;if(t._rendered)for(r=t.getEl("open").getElementsByTagName("span"),n=0;n'+("-"!==i?' ':"")+("-"!==i?''+i+"":"")+(n.shortcut?'
    '+n.shortcut+"
    ":"")+(n.menu?'
    ':"")+""},postRender:function(){var e=this,t=e.settings,n=t.textStyle;if("function"==typeof n&&(n=n.call(this)),n){var r=e.getEl("text");r&&r.setAttribute("style",n)}return e._super()},remove:function(){this._super(),this.menu&&this.menu.remove()}})}),r(Ut,[X,J,Vt,p],function(e,t,n,r){var i=e.extend({Defaults:{defaultType:"menuitem",border:1,layout:"stack",role:"menu"},init:function(e){var i=this;if(e.autohide=!0,e.constrainToViewport=!0,e.itemDefaults)for(var o=e.items,a=o.length;a--;)o[a]=r.extend({},e.itemDefaults,o[a]);i._super(e),i.addClass("menu"),i.keyNav=new t({root:i,enableUpDown:!0,enableLeftRight:!0,leftAction:function(){i.parent()instanceof n&&i.keyNav.cancel()},onCancel:function(){i.fire("cancel",{},!1),i.hide()}})},repaint:function(){return this.toggleClass("menu-align",!0),this._super(),this.getEl().style.height="",this.getEl("body").style.height="",this},cancel:function(){var e=this;e.hideAll(),e.fire("cancel"),e.fire("select")},hideAll:function(){var e=this;return this.find("menuitem").exec("hideMenu"),e._super()},preRender:function(){var e=this;return e.items().each(function(t){var n=t.settings;return n.icon||n.selectable?(e._hasIcons=!0,!1):void 0}),e._super()}});return i}),r(qt,[xt],function(e){return e.extend({Defaults:{classes:"radio",role:"radio"}})}),r(jt,[yt,j],function(e,t){return e.extend({renderHtml:function(){var e=this,t=e.classPrefix;return e.addClass("resizehandle"),"both"==e.settings.direction&&e.addClass("resizehandle-both"),e.canFocus=!1,'
    '+''+"
    "},postRender:function(){var e=this;e._super(),e.resizeDragHelper=new t(this._id,{start:function(){e.fire("ResizeStart")},drag:function(t){"both"!=e.settings.direction&&(t.deltaX=0),e.fire("Resize",t)},end:function(){e.fire("ResizeEnd")}})}})}),r($t,[yt],function(e){return e.extend({renderHtml:function(){var e=this;return e.addClass("spacer"),e.canFocus=!1,'
    '}})}),r(Kt,[Wt,v],function(e,t){var n=t.DOM;return e.extend({Defaults:{classes:"widget btn splitbtn",role:"splitbutton"},repaint:function(){var e=this,t=e.getEl(),r=e.layoutRect(),i,o,a;return e._super(),i=t.firstChild,o=t.lastChild,n.css(i,{width:r.w-o.offsetWidth,height:r.h-2}),n.css(o,{height:r.h-2}),a=i.firstChild.style,a.width=a.height="100%",a=o.firstChild.style,a.width=a.height="100%",e},activeMenu:function(e){var t=this;n.toggleClass(t.getEl().lastChild,t.classPrefix+"active",e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon?n+"ico "+n+"i-"+e.settings.icon:"";return'
    '+'"+'"+"
    "},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(e){e.control!=this||n.getParent(e.target,"."+this.classPrefix+"open")||(e.stopImmediatePropagation(),t.call(this,e))}),delete e.settings.onclick,e._super()}})}),r(Gt,[Lt],function(e){return e.extend({Defaults:{containerClass:"stack-layout",controlClass:"stack-layout-item",endClass:"break"}})}),r(Yt,[K,z],function(e,t){"use stict";return e.extend({lastIdx:0,Defaults:{layout:"absolute",defaults:{type:"panel"}},activateTab:function(e){this.activeTabId&&t.removeClass(this.getEl(this.activeTabId),this.classPrefix+"active"),this.activeTabId="t"+e,t.addClass(this.getEl("t"+e),this.classPrefix+"active"),e!=this.lastIdx&&(this.items()[this.lastIdx].hide(),this.lastIdx=e),this.items()[e].show().fire("showtab"),this.reflow()},renderHtml:function(){var e=this,t=e._layout,n="",r=e.classPrefix;return e.preRender(),t.preRender(e),e.items().each(function(t,i){n+='
    '+e.encode(t.settings.title)+"
    "}),'
    '+'
    '+n+"
    "+'
    '+t.renderHtml(e)+"
    "+"
    "},postRender:function(){var e=this;e._super(),e.settings.activeTab=e.settings.activeTab||0,e.activateTab(e.settings.activeTab),this.on("click",function(t){var n=t.target.parentNode;if(t.target.parentNode.id==e._id+"-head")for(var r=n.childNodes.length;r--;)n.childNodes[r]==t.target&&e.activateTab(r)})},initLayoutRect:function(){var e=this,t,n,r;n=r=0,e.items().each(function(t,i){n=Math.max(n,t.layoutRect().minW),r=Math.max(r,t.layoutRect().minH),e.settings.activeTab!=i&&t.hide()}),e.items().each(function(e){e.settings.x=0,e.settings.y=0,e.settings.w=n,e.settings.h=r,e.layoutRect({x:0,y:0,w:n,h:r})});var i=e.getEl("head").offsetHeight;return e.settings.minWidth=n,e.settings.minHeight=r+i,t=e._super(),t.deltaH+=e.getEl("head").offsetHeight,t.innerH=t.h-t.deltaH,t}})}),r(Xt,[yt,z],function(e,t){return e.extend({init:function(e){var t=this;t._super(e),t._value=e.value||"",t.addClass("textbox"),e.multiline?t.addClass("multiline"):t.on("keydown",function(e){13==e.keyCode&&t.parents().reverse().each(function(t){return e.preventDefault(),t.submit?(t.submit(),!1):void 0})})},value:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t._rendered&&(t.getEl().value=e),t):t._rendered?t.getEl().value:t._value},repaint:function(){var e=this,t,n,r,i=0,o=0,a;t=e.getEl().style,n=e._layoutRect,a=e._lastRepaintRect||{};var s=document;return!e.settings.multiline&&s.all&&(!s.documentMode||s.documentMode<=8)&&(t.lineHeight=n.h-o+"px"),r=e._borderBox,i=r.left+r.right+8,o=r.top+r.bottom+(e.settings.multiline?8:0),n.x!==a.x&&(t.left=n.x+"px",a.x=n.x),n.y!==a.y&&(t.top=n.y+"px",a.y=n.y),n.w!==a.w&&(t.width=n.w-i+"px",a.w=n.w),n.h!==a.h&&(t.height=n.h-o+"px",a.h=n.h),e._lastRepaintRect=a,e.fire("repaint",{},!1),e},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.encode(e._value,!1),i="";return"spellcheck"in n&&(i+=' spellcheck="'+n.spellcheck+'"'),n.maxLength&&(i+=' maxlength="'+n.maxLength+'"'),n.size&&(i+=' size="'+n.size+'"'),n.subtype&&(i+=' type="'+n.subtype+'"'),n.multiline?'":'"},postRender:function(){var e=this;return t.on(e.getEl(),"change",function(t){e.fire("change",t)}),e._super()}})}),r(Jt,[z],function(e){return function(t){var n=this,r;n.show=function(i){return n.hide(),r=!0,window.setTimeout(function(){r&&t.appendChild(e.createFragment('
    '))},i||0),n},n.hide=function(){var e=t.lastChild;return e&&-1!=e.className.indexOf("throbber")&&e.parentNode.removeChild(e),r=!1,n}}}),a([l,c,u,d,f,p,h,m,g,v,y,b,C,x,w,_,N,E,k,S,T,R,A,B,D,L,M,H,P,O,I,F,W,z,V,U,q,j,$,K,G,Y,X,J,Q,Z,et,tt,nt,rt,it,ot,at,st,lt,ct,ut,dt,ft,pt,ht,mt,gt,vt,yt,bt,Ct,xt,wt,_t,Nt,Et,kt,St,Tt,Rt,At,Bt,Dt,Lt,Mt,Ht,Pt,Ot,It,Ft,Wt,zt,Vt,Ut,qt,jt,$t,Kt,Gt,Yt,Xt,Jt])}(this); \ No newline at end of file diff --git a/gui/baculum/framework/Web/Services/TFeedService.php b/gui/baculum/framework/Web/Services/TFeedService.php new file mode 100644 index 0000000000..a1518d4edc --- /dev/null +++ b/gui/baculum/framework/Web/Services/TFeedService.php @@ -0,0 +1,187 @@ + + * @author Knut Urdalen + * @link http://www.pradosoft.com + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.Services + */ + +/** + * TFeedService class + * + * TFeedService provides to end-users feed content. + * + * TFeedService manages a set of feeds. The service parameter, referring + * to the ID of the feed, specifies which feed content to be provided to end-users. + * + * To use TFeedService, configure it in application configuration as follows, + * + * + * + * + * + * + * + * where each <feed> element specifies a feed identified by its "id" value (case-sensitive). + * + * PHP configuration style: + * + * array( + * 'feed' => array( + * 'ch1' => array( + * 'class' => 'Path.To.FeedClass1', + * 'properties' => array( + * ... + * ), + * ), + * ) + * + * + * The class attribute indicates which PHP class will provide the actual feed + * content. Note, the class must implement {@link IFeedContentProvider} interface. + * Other initial properties for the feed class may also be specified in the + * corresponding <feed> element. + * + * To retrieve the feed content identified by "ch2", use the URL + * /path/to/index.php?feed=ch2 + * + * @author Qiang Xue + * @author Knut Urdalen + * @author Carl G. Mathisen + * @package System.Web.Services + * @since 3.1 + */ +class TFeedService extends TService +{ + private $_feeds=array(); + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + */ + public function init($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(is_array($config)) + { + foreach($config as $id => $feed) + $this->_feeds[$id] = $feed; + } + } + else + { + foreach($config->getElementsByTagName('feed') as $feed) + { + if(($id=$feed->getAttributes()->remove('id'))!==null) + $this->_feeds[$id]=$feed; + else + throw new TConfigurationException('feedservice_id_required'); + } + } + } + + /** + * @return string the requested feed path + */ + protected function determineRequestedFeedPath() + { + return $this->getRequest()->getServiceParameter(); + } + + /** + * Runs the service. + * This method is invoked by application automatically. + */ + public function run() + { + $id=$this->getRequest()->getServiceParameter(); + if(isset($this->_feeds[$id])) + { + $feedConfig=$this->_feeds[$id]; + $properties = array(); + $feed = null; + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(isset($feedConfig['class'])) + { + $feed=Prado::createComponent($feedConfig['class']); + if($service instanceof IFeedContentProvider) + $properties=isset($feedConfig['properties'])?$feedConfig['properties']:array(); + else + throw new TConfigurationException('jsonservice_response_type_invalid',$id); + } + else + throw new TConfigurationException('jsonservice_class_required',$id); + } + else + { + $properties=$feedConfig->getAttributes(); + if(($class=$properties->remove('class'))!==null) + { + $feed=Prado::createComponent($class); + if(!($feed instanceof IFeedContentProvider)) + throw new TConfigurationException('feedservice_feedtype_invalid',$id); + } + else + throw new TConfigurationException('feedservice_class_required',$id); + } + + // init feed properties + foreach($properties as $name=>$value) + $feed->setSubproperty($name,$value); + $feed->init($feedConfig); + + $content=$feed->getFeedContent(); + //$this->getResponse()->setContentType('application/rss+xml'); + $this->getResponse()->setContentType($feed->getContentType()); + $this->getResponse()->write($content); + } + else + throw new THttpException(404,'feedservice_feed_unknown',$id); + } +} + +/** + * IFeedContentProvider interface. + * + * IFeedContentProvider interface must be implemented by a feed class who + * provides feed content. + * + * @author Qiang Xue + * @author Knut Urdalen + * @package System.Web.Services + * @since 3.1 + */ +interface IFeedContentProvider +{ + /** + * Initializes the feed content provider. + * This method is invoked (before {@link getFeedContent}) + * when the feed provider is requested by a user. + * @param TXmlElement configurations specified within the <feed> element + * corresponding to this feed provider when configuring {@link TFeedService}. + */ + public function init($config); + /** + * @return string feed content in proper XML format + */ + public function getFeedContent(); + /** + * Sets the content type of the feed content to be sent. + * Some examples are: + * RSS 1.0 feed: application/rdf+xml + * RSS 2.0 feed: application/rss+xml or application/xml or text/xml + * ATOM feed: application/atom+xml + * @return string the content type for the feed content. + * @since 3.1.1 + */ + public function getContentType(); +} + diff --git a/gui/baculum/framework/Web/Services/TJsonService.php b/gui/baculum/framework/Web/Services/TJsonService.php new file mode 100644 index 0000000000..15701a9a8d --- /dev/null +++ b/gui/baculum/framework/Web/Services/TJsonService.php @@ -0,0 +1,212 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TJsonService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + */ + +/** + * TJsonService class provides to end-users javascript content response in + * JSON format. + * + * TJsonService manages a set of {@link TJsonResponse}, each + * representing specific response with javascript content. + * The service parameter, referring to the ID of the service, specifies + * which javascript content to be provided to end-users. + * + * To use TJsonService, configure it in application configuration as follows, + * + * + * + * + * + * + * where each JSON response is specified via a <json> element. + * Initial property values can be configured in a <json> element. + * + * + * PHP configuration style: + * + * 'services' => array( + * 'get_article' => array( + * 'class' => 'Path.To.JsonResponseClass1', + * 'properties' => array( + * ... + * ) + * ) + * ) + * + * + * To retrieve the JSON content provided by "get_article", use the URL + * index.php?json=get_article + * + * @author Wei Zhuo + * @author Carl G. Mathisen + * @version $Id: TJsonService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + * @since 3.1 + */ +class TJsonService extends TService +{ + /** + * @var array registered services + */ + private $_services=array(); + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + */ + public function init($xml) + { + $this->loadJsonServices($xml); + } + + /** + * Load the service definitions. + * @param mixed configuration for this module, can be null + */ + protected function loadJsonServices($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(is_array($config)) + { + foreach($config['json'] as $id => $json) + $this->_services[$id] = $json; + } + } + else + { + foreach($config->getElementsByTagName('json') as $json) + { + if(($id=$json->getAttribute('id'))!==null) + $this->_services[$id]=$json; + else + throw new TConfigurationException('jsonservice_id_required'); + } + } + } + + /** + * Runs the service. + * This method is invoked by application automatically. + */ + public function run() + { + $id=$this->getRequest()->getServiceParameter(); + if(isset($this->_services[$id])) + { + $serviceConfig=$this->_services[$id]; + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(isset($serviceConfig['class'])) + { + $service=Prado::createComponent($serviceConfig['class']); + if($service instanceof TJsonResponse) + { + $properties = isset($serviceConfig['properties'])?$serviceConfig['properties']:array(); + $this->createJsonResponse($service,$properties,$serviceConfig); + } + else + throw new TConfigurationException('jsonservice_response_type_invalid',$id); + } + else + throw new TConfigurationException('jsonservice_class_required',$id); + } + else + { + $properties=$serviceConfig->getAttributes(); + if(($class=$properties->remove('class'))!==null) + { + $service=Prado::createComponent($class); + if($service instanceof TJsonResponse) + $this->createJsonResponse($service,$properties,$serviceConfig); + else + throw new TConfigurationException('jsonservice_response_type_invalid',$id); + } + else + throw new TConfigurationException('jsonservice_class_required',$id); + } + } + else + throw new THttpException(404,'jsonservice_provider_unknown',$id); + } + + /** + * Renders content provided by TJsonResponse::getJsonContent() as + * javascript in JSON format. + */ + protected function createJsonResponse($service,$properties,$config) + { + // init service properties + foreach($properties as $name=>$value) + $service->setSubproperty($name,$value); + $service->init($config); + + //send content if not null + if(($content=$service->getJsonContent())!==null) + { + $response = $this->getResponse(); + $response->setContentType('application/json'); + $response->setCharset('UTF-8'); + //send content + $response->write(TJavaScript::jsonEncode($content)); + } + } +} + +/** + * TJsonResponse Class + * + * TJsonResponse is the base class for all JSON response provider classes. + * + * Derived classes must implement {@link getJsonContent()} to return + * an object or literals to be converted to JSON format. The response + * will be empty if the returned content is null. + * + * @author Wei Zhuo + * @version $Id: TJsonService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + * @since 3.1 + */ +abstract class TJsonResponse extends TApplicationComponent +{ + private $_id=''; + + /** + * Initializes the feed. + * @param TXmlElement configurations specified in {@link TJsonService}. + */ + public function init($config) + { + } + + /** + * @return string ID of this response + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string ID of this response + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return object json response content, null to suppress output. + */ + abstract public function getJsonContent(); +} + diff --git a/gui/baculum/framework/Web/Services/TPageService.php b/gui/baculum/framework/Web/Services/TPageService.php new file mode 100644 index 0000000000..8e0526938c --- /dev/null +++ b/gui/baculum/framework/Web/Services/TPageService.php @@ -0,0 +1,893 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPageService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + */ + +/** + * Include classes to be used by page service + */ +Prado::using('System.Web.UI.TPage'); +Prado::using('System.Web.UI.TTemplateManager'); +Prado::using('System.Web.UI.TThemeManager'); + +/** + * TPageService class. + * + * TPageService implements the service for serving user page requests. + * + * Pages that are available to client users are stored under a directory specified by + * {@link setBasePath BasePath}. The directory may contain subdirectories. + * Pages serving for a similar goal are usually placed under the same directory. + * A directory may contain a configuration file config.xml whose content + * is similar to that of application configuration file. + * + * A page is requested via page path, which is a dot-connected directory names + * appended by the page name. Assume '/Users/Admin' is the directory + * containing the page 'Update'. Then the page can be requested via 'Users.Admin.Update'. + * By default, the {@link setBasePath BasePath} of the page service is the "pages" + * directory under the application base path. You may change this default + * by setting {@link setBasePath BasePath} with a different path you prefer. + * + * Page name refers to the file name (without extension) of the page template. + * In order to differentiate from the common control template files, the extension + * name of the page template files must be '.page'. If there is a PHP file with + * the same page name under the same directory as the template file, that file + * will be considered as the page class file and the file name is the page class name. + * If such a file is not found, the page class is assumed as {@link TPage}. + * + * Modules can be configured and loaded in page directory configurations. + * Configuration of a module in a subdirectory will overwrite its parent + * directory's configuration, if both configurations refer to the same module. + * + * By default, TPageService will automatically load two modules: + * - {@link TTemplateManager} : manages page and control templates + * - {@link TThemeManager} : manages themes used in a Prado application + * + * In page directory configurations, static authorization rules can also be specified, + * which governs who and which roles can access particular pages. + * Refer to {@link TAuthorizationRule} for more details about authorization rules. + * Page authorization rules can be configured within an tag in + * each page directory configuration as follows, + * + * + * + * + * + * where the 'pages' attribute may be filled with a sequence of comma-separated + * page IDs. If 'pages' attribute does not appear in a rule, the rule will be + * applied to all pages in this directory and all subdirectories (recursively). + * Application of authorization rules are in a bottom-up fashion, starting from + * the directory containing the requested page up to all parent directories. + * The first matching rule will be used. The last rule always allows all users + * accessing to any resources. + * + * @author Qiang Xue + * @author Carl G. Mathisen + * @version $Id: TPageService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + * @since 3.0 + */ +class TPageService extends TService +{ + /** + * Configuration file name + */ + const CONFIG_FILE_XML='config.xml'; + /** + * Configuration file name + */ + const CONFIG_FILE_PHP='config.php'; + /** + * Default base path + */ + const DEFAULT_BASEPATH='Pages'; + /** + * Fallback base path - used to be the default up to Prado < 3.2 + */ + const FALLBACK_BASEPATH='pages'; + /** + * Prefix of ID used for storing parsed configuration in cache + */ + const CONFIG_CACHE_PREFIX='prado:pageservice:'; + /** + * Page template file extension + */ + const PAGE_FILE_EXT='.page'; + /** + * @var string root path of pages + */ + private $_basePath=null; + /** + * @var string base path class in namespace format + */ + private $_basePageClass='TPage'; + /** + * @var string clientscript manager class in namespace format + * @since 3.1.7 + */ + private $_clientScriptManagerClass='System.Web.UI.TClientScriptManager'; + /** + * @var string default page + */ + private $_defaultPage='Home'; + /** + * @var string requested page (path) + */ + private $_pagePath=null; + /** + * @var TPage the requested page + */ + private $_page=null; + /** + * @var array list of initial page property values + */ + private $_properties=array(); + /** + * @var boolean whether service is initialized + */ + private $_initialized=false; + /** + * @var TThemeManager theme manager + */ + private $_themeManager=null; + /** + * @var TTemplateManager template manager + */ + private $_templateManager=null; + + /** + * Initializes the service. + * This method is required by IService interface and is invoked by application. + * @param TXmlElement service configuration + */ + public function init($config) + { + Prado::trace("Initializing TPageService",'System.Web.Services.TPageService'); + + $pageConfig=$this->loadPageConfig($config); + + $this->initPageContext($pageConfig); + + $this->_initialized=true; + } + + /** + * Initializes page context. + * Page context includes path alias settings, namespace usages, + * parameter initialization, module loadings, page initial properties + * and authorization rules. + * @param TPageConfiguration + */ + protected function initPageContext($pageConfig) + { + $application=$this->getApplication(); + foreach($pageConfig->getApplicationConfigurations() as $appConfig) + $application->applyConfiguration($appConfig); + + $this->applyConfiguration($pageConfig); + } + + /** + * Applies a page configuration. + * @param TPageConfiguration the configuration + */ + protected function applyConfiguration($config) + { + // initial page properties (to be set when page runs) + $this->_properties=array_merge($this->_properties, $config->getProperties()); + $this->getApplication()->getAuthorizationRules()->mergeWith($config->getRules()); + $pagePath=$this->getRequestedPagePath(); + // external configurations + foreach($config->getExternalConfigurations() as $filePath=>$params) + { + list($configPagePath,$condition)=$params; + if($condition!==true) + $condition=$this->evaluateExpression($condition); + if($condition) + { + if(($path=Prado::getPathOfNamespace($filePath,Prado::getApplication()->getConfigurationFileExt()))===null || !is_file($path)) + throw new TConfigurationException('pageservice_includefile_invalid',$filePath); + $c=new TPageConfiguration($pagePath); + $c->loadFromFile($path,$configPagePath); + $this->applyConfiguration($c); + } + } + + } + + /** + * Determines the requested page path. + * @return string page path requested + */ + protected function determineRequestedPagePath() + { + $pagePath=$this->getRequest()->getServiceParameter(); + if(empty($pagePath)) + $pagePath=$this->getDefaultPage(); + return $pagePath; + } + + /** + * Collects configuration for a page. + * @param TXmlElement additional configuration specified in the application configuration + * @return TPageConfiguration + */ + protected function loadPageConfig($config) + { + $application=$this->getApplication(); + $pagePath=$this->getRequestedPagePath(); + if(($cache=$application->getCache())===null) + { + $pageConfig=new TPageConfiguration($pagePath); + if($config!==null) + { + if($application->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $pageConfig->loadPageConfigurationFromPhp($config,$application->getBasePath(),''); + else + $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); + } + $pageConfig->loadFromFiles($this->getBasePath()); + } + else + { + $configCached=true; + $currentTimestamp=array(); + $arr=$cache->get(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath); + if(is_array($arr)) + { + list($pageConfig,$timestamps)=$arr; + if($application->getMode()!==TApplicationMode::Performance) + { + foreach($timestamps as $fileName=>$timestamp) + { + if($fileName===0) // application config file + { + $appConfigFile=$application->getConfigurationFile(); + $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); + if($currentTimestamp[0]>$timestamp || ($timestamp>0 && !$currentTimestamp[0])) + $configCached=false; + } + else + { + $currentTimestamp[$fileName]=@filemtime($fileName); + if($currentTimestamp[$fileName]>$timestamp || ($timestamp>0 && !$currentTimestamp[$fileName])) + $configCached=false; + } + } + } + } + else + { + $configCached=false; + $paths=explode('.',$pagePath); + $configPath=$this->getBasePath(); + $fileName = $this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP + ? self::CONFIG_FILE_PHP + : self::CONFIG_FILE_XML; + foreach($paths as $path) + { + $configFile=$configPath.DIRECTORY_SEPARATOR.$fileName; + $currentTimestamp[$configFile]=@filemtime($configFile); + $configPath.=DIRECTORY_SEPARATOR.$path; + } + $appConfigFile=$application->getConfigurationFile(); + $currentTimestamp[0]=$appConfigFile===null?0:@filemtime($appConfigFile); + } + if(!$configCached) + { + $pageConfig=new TPageConfiguration($pagePath); + if($config!==null) + { + if($application->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + $pageConfig->loadPageConfigurationFromPhp($config,$application->getBasePath(),''); + else + $pageConfig->loadPageConfigurationFromXml($config,$application->getBasePath(),''); + } + $pageConfig->loadFromFiles($this->getBasePath()); + $cache->set(self::CONFIG_CACHE_PREFIX.$this->getID().$pagePath,array($pageConfig,$currentTimestamp)); + } + } + return $pageConfig; + } + + /** + * @return TTemplateManager template manager + */ + public function getTemplateManager() + { + if(!$this->_templateManager) + { + $this->_templateManager=new TTemplateManager; + $this->_templateManager->init(null); + } + return $this->_templateManager; + } + + /** + * @param TTemplateManager template manager + */ + public function setTemplateManager(TTemplateManager $value) + { + $this->_templateManager=$value; + } + + /** + * @return TThemeManager theme manager + */ + public function getThemeManager() + { + if(!$this->_themeManager) + { + $this->_themeManager=new TThemeManager; + $this->_themeManager->init(null); + } + return $this->_themeManager; + } + + /** + * @param TThemeManager theme manager + */ + public function setThemeManager(TThemeManager $value) + { + $this->_themeManager=$value; + } + + /** + * @return string the requested page path + */ + public function getRequestedPagePath() + { + if($this->_pagePath===null) + { + $this->_pagePath=strtr($this->determineRequestedPagePath(),'/\\','..'); + if(empty($this->_pagePath)) + throw new THttpException(404,'pageservice_page_required'); + } + return $this->_pagePath; + } + + /** + * @return TPage the requested page + */ + public function getRequestedPage() + { + return $this->_page; + } + + /** + * @return string default page path to be served if no explicit page is request. Defaults to 'Home'. + */ + public function getDefaultPage() + { + return $this->_defaultPage; + } + + /** + * @param string default page path to be served if no explicit page is request + * @throws TInvalidOperationException if the page service is initialized + */ + public function setDefaultPage($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_defaultpage_unchangeable'); + else + $this->_defaultPage=$value; + } + + /** + * @return string the URL for the default page + */ + public function getDefaultPageUrl() + { + return $this->constructUrl($this->getDefaultPage()); + } + + /** + * @return string the root directory for storing pages. Defaults to the 'pages' directory under the application base path. + */ + public function getBasePath() + { + if($this->_basePath===null) + { + $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH; + if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) + { + $basePath=$this->getApplication()->getBasePath().DIRECTORY_SEPARATOR.self::FALLBACK_BASEPATH; + if(($this->_basePath=realpath($basePath))===false || !is_dir($this->_basePath)) + throw new TConfigurationException('pageservice_basepath_invalid',$basePath); + } + } + return $this->_basePath; + } + + /** + * @param string root directory (in namespace form) storing pages + * @throws TInvalidOperationException if the service is initialized already or basepath is invalid + */ + public function setBasePath($value) + { + if($this->_initialized) + throw new TInvalidOperationException('pageservice_basepath_unchangeable'); + else if(($path=Prado::getPathOfNamespace($value))===null || !is_dir($path)) + throw new TConfigurationException('pageservice_basepath_invalid',$value); + $this->_basePath=realpath($path); + } + + /** + * Sets the base page class name (in namespace format). + * If a page only has a template file without page class file, + * this base page class will be instantiated. + * @param string class name + */ + public function setBasePageClass($value) + { + $this->_basePageClass=$value; + } + + /** + * @return string base page class name in namespace format. Defaults to 'TPage'. + */ + public function getBasePageClass() + { + return $this->_basePageClass; + } + + /** + * Sets the clientscript manager class (in namespace format). + * @param string class name + * @since 3.1.7 + */ + public function setClientScriptManagerClass($value) + { + $this->_clientScriptManagerClass=$value; + } + + /** + * @return string clientscript manager class in namespace format. Defaults to 'System.Web.UI.TClientScriptManager'. + * @since 3.1.7 + */ + public function getClientScriptManagerClass() + { + return $this->_clientScriptManagerClass; + } + + /** + * Runs the service. + * This will create the requested page, initializes it with the property values + * specified in the configuration, and executes the page. + */ + public function run() + { + Prado::trace("Running page service",'System.Web.Services.TPageService'); + $this->_page=$this->createPage($this->getRequestedPagePath()); + $this->runPage($this->_page,$this->_properties); + } + + /** + * Creates a page instance based on requested page path. + * @param string requested page path + * @return TPage the requested page instance + * @throws THttpException if requested page path is invalid + * @throws TConfigurationException if the page class cannot be found + */ + protected function createPage($pagePath) + { + $path=$this->getBasePath().DIRECTORY_SEPARATOR.strtr($pagePath,'.',DIRECTORY_SEPARATOR); + $hasTemplateFile=is_file($path.self::PAGE_FILE_EXT); + $hasClassFile=is_file($path.Prado::CLASS_FILE_EXT); + + if(!$hasTemplateFile && !$hasClassFile) + throw new THttpException(404,'pageservice_page_unknown',$pagePath); + + if($hasClassFile) + { + $className=basename($path); + if(!class_exists($className,false)) + include_once($path.Prado::CLASS_FILE_EXT); + } + else + { + $className=$this->getBasePageClass(); + Prado::using($className); + if(($pos=strrpos($className,'.'))!==false) + $className=substr($className,$pos+1); + } + + if(!class_exists($className,false) || ($className!=='TPage' && !is_subclass_of($className,'TPage'))) + throw new THttpException(404,'pageservice_page_unknown',$pagePath); + + $page=new $className; + $page->setPagePath($pagePath); + + if($hasTemplateFile) + $page->setTemplate($this->getTemplateManager()->getTemplateByFileName($path.self::PAGE_FILE_EXT)); + + return $page; + } + + /** + * Executes a page. + * @param TPage the page instance to be run + * @param array list of initial page properties + */ + protected function runPage($page,$properties) + { + foreach($properties as $name=>$value) + $page->setSubProperty($name,$value); + $page->run($this->getResponse()->createHtmlWriter()); + } + + /** + * Constructs a URL with specified page path and GET parameters. + * @param string page path + * @param array list of GET parameters, null if no GET parameters required + * @param boolean whether to encode the ampersand in URL, defaults to true. + * @param boolean whether to encode the GET parameters (their names and values), defaults to true. + * @return string URL for the page and GET parameters + */ + public function constructUrl($pagePath,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true) + { + return $this->getRequest()->constructUrl($this->getID(),$pagePath,$getParams,$encodeAmpersand,$encodeGetItems); + } +} + + +/** + * TPageConfiguration class + * + * TPageConfiguration represents the configuration for a page. + * The page is specified by a dot-connected path. + * Configurations along this path are merged together to be provided for the page. + * + * @author Qiang Xue + * @version $Id: TPageService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + * @since 3.0 + */ +class TPageConfiguration extends TComponent +{ + /** + * @var array list of application configurations + */ + private $_appConfigs=array(); + /** + * @var array list of page initial property values + */ + private $_properties=array(); + /** + * @var TAuthorizationRuleCollection list of authorization rules + */ + private $_rules=array(); + /** + * @var array list of included configurations + */ + private $_includes=array(); + /** + * @var string the currently request page in the format of Path.To.PageName + */ + private $_pagePath=''; + + /** + * Constructor. + * @param string the currently request page in the format of Path.To.PageName + */ + public function __construct($pagePath) + { + $this->_pagePath=$pagePath; + } + + /** + * @return array list of external configuration files. Each element is like $filePath=>$condition + */ + public function getExternalConfigurations() + { + return $this->_includes; + } + + /** + * Returns list of page initial property values. + * Each array element represents a single property with the key + * being the property name and the value the initial property value. + * @return array list of page initial property values + */ + public function getProperties() + { + return $this->_properties; + } + + /** + * Returns list of authorization rules. + * The authorization rules are aggregated (bottom-up) from configuration files + * along the path to the specified page. + * @return TAuthorizationRuleCollection collection of authorization rules + */ + public function getRules() + { + return $this->_rules; + } + + /** + * @return array list of application configurations specified along page path + */ + public function getApplicationConfigurations() + { + return $this->_appConfigs; + } + + /** + * Loads configuration for a page specified in a path format. + * @param string root path for pages + */ + public function loadFromFiles($basePath) + { + $paths=explode('.',$this->_pagePath); + $page=array_pop($paths); + $path=$basePath; + $configPagePath=''; + $fileName = Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP + ? TPageService::CONFIG_FILE_PHP + : TPageService::CONFIG_FILE_XML; + foreach($paths as $p) + { + $this->loadFromFile($path.DIRECTORY_SEPARATOR.$fileName,$configPagePath); + $path.=DIRECTORY_SEPARATOR.$p; + if($configPagePath==='') + $configPagePath=$p; + else + $configPagePath.='.'.$p; + } + $this->loadFromFile($path.DIRECTORY_SEPARATOR.$fileName,$configPagePath); + $this->_rules=new TAuthorizationRuleCollection($this->_rules); + } + + /** + * Loads a specific config file. + * @param string config file name + * @param string the page path that the config file is associated with. The page path doesn't include the page name. + */ + public function loadFromFile($fname,$configPagePath) + { + Prado::trace("Loading page configuration file $fname",'System.Web.Services.TPageService'); + if(empty($fname) || !is_file($fname)) + return; + + if(Prado::getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $fcontent = include $fname; + $this->loadFromPhp($fcontent,dirname($fname),$configPagePath); + } + else + { + $dom=new TXmlDocument; + if($dom->loadFromFile($fname)) + $this->loadFromXml($dom,dirname($fname),$configPagePath); + else + throw new TConfigurationException('pageserviceconf_file_invalid',$fname); + } + } + + public function loadFromPhp($config,$configPath,$configPagePath) + { + $this->loadApplicationConfigurationFromPhp($config,$configPath); + $this->loadPageConfigurationFromPhp($config,$configPath,$configPagePath); + } + + /** + * Loads a page configuration. + * The configuration includes information for both application + * and page service. + * @param TXmlElement config xml element + * @param string the directory containing this configuration + * @param string the page path that the config XML is associated with. The page path doesn't include the page name. + */ + public function loadFromXml($dom,$configPath,$configPagePath) + { + $this->loadApplicationConfigurationFromXml($dom,$configPath); + $this->loadPageConfigurationFromXml($dom,$configPath,$configPagePath); + } + + public function loadApplicationConfigurationFromPhp($config,$configPath) + { + $appConfig=new TApplicationConfiguration; + $appConfig->loadFromPhp($config,$configPath); + $this->_appConfigs[]=$appConfig; + } + + /** + * Loads the configuration specific for application part + * @param TXmlElement config xml element + * @param string base path corresponding to this xml element + */ + public function loadApplicationConfigurationFromXml($dom,$configPath) + { + $appConfig=new TApplicationConfiguration; + $appConfig->loadFromXml($dom,$configPath); + $this->_appConfigs[]=$appConfig; + } + + public function loadPageConfigurationFromPhp($config, $configPath, $configPagePath) + { + // authorization + if(isset($config['authorization']) && is_array($config['authorization'])) + { + $rules = array(); + foreach($config['authorization'] as $authorization) + { + $patterns=isset($authorization['pages'])?$authorization['pages']:''; + $ruleApplies=false; + if(empty($patterns) || trim($patterns)==='*') // null or empty string + $ruleApplies=true; + else + { + foreach(explode(',',$patterns) as $pattern) + { + if(($pattern=trim($pattern))!=='') + { + // we know $configPagePath and $this->_pagePath + if($configPagePath!=='') // prepend the pattern with ConfigPagePath + $pattern=$configPagePath.'.'.$pattern; + if(strcasecmp($pattern,$this->_pagePath)===0) + { + $ruleApplies=true; + break; + } + if($pattern[strlen($pattern)-1]==='*') // try wildcard matching + { + if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0) + { + $ruleApplies=true; + break; + } + } + } + } + } + if($ruleApplies) + { + $action = isset($authorization['action'])?$authorization['action']:''; + $users = isset($authorization['users'])?$authorization['users']:''; + $roles = isset($authorization['roles'])?$authorization['roles']:''; + $verb = isset($authorization['verb'])?$authorization['verb']:''; + $ips = isset($authorization['ips'])?$authorization['ips']:''; + $rules[]=new TAuthorizationRule($action,$users,$roles,$verb,$ips); + } + } + $this->_rules=array_merge($rules,$this->_rules); + } + // pages + if(isset($config['pages']) && is_array($config['pages'])) + { + if(isset($config['pages']['properties'])) + { + $this->_properties = array_merge($this->_properties, $config['pages']['properties']); + unset($config['pages']['properties']); + } + foreach($config['pages'] as $id => $page) + { + $properties = array(); + if(isset($page['properties'])) + { + $properties=$page['properties']; + unset($page['properties']); + } + $matching=false; + $id=($configPagePath==='')?$id:$configPagePath.'.'.$id; + if(strcasecmp($id,$this->_pagePath)===0) + $matching=true; + else if($id[strlen($id)-1]==='*') // try wildcard matching + $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0; + if($matching) + $this->_properties=array_merge($this->_properties,$properties); + } + } + + // external configurations + if(isset($config['includes']) && is_array($config['includes'])) + { + foreach($config['includes'] as $include) + { + $when = isset($include['when'])?true:false; + if(!isset($include['file'])) + throw new TConfigurationException('pageserviceconf_includefile_required'); + $filePath = $include['file']; + if(isset($this->_includes[$filePath])) + $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); + else + $this->_includes[$filePath]=array($configPagePath,$when); + } + } + } + + /** + * Loads the configuration specific for page service. + * @param TXmlElement config xml element + * @param string base path corresponding to this xml element + * @param string the page path that the config XML is associated with. The page path doesn't include the page name. + */ + public function loadPageConfigurationFromXml($dom,$configPath,$configPagePath) + { + // authorization + if(($authorizationNode=$dom->getElementByTagName('authorization'))!==null) + { + $rules=array(); + foreach($authorizationNode->getElements() as $node) + { + $patterns=$node->getAttribute('pages'); + $ruleApplies=false; + if(empty($patterns) || trim($patterns)==='*') // null or empty string + $ruleApplies=true; + else + { + foreach(explode(',',$patterns) as $pattern) + { + if(($pattern=trim($pattern))!=='') + { + // we know $configPagePath and $this->_pagePath + if($configPagePath!=='') // prepend the pattern with ConfigPagePath + $pattern=$configPagePath.'.'.$pattern; + if(strcasecmp($pattern,$this->_pagePath)===0) + { + $ruleApplies=true; + break; + } + if($pattern[strlen($pattern)-1]==='*') // try wildcard matching + { + if(strncasecmp($this->_pagePath,$pattern,strlen($pattern)-1)===0) + { + $ruleApplies=true; + break; + } + } + } + } + } + if($ruleApplies) + $rules[]=new TAuthorizationRule($node->getTagName(),$node->getAttribute('users'),$node->getAttribute('roles'),$node->getAttribute('verb'),$node->getAttribute('ips')); + } + $this->_rules=array_merge($rules,$this->_rules); + } + + // pages + if(($pagesNode=$dom->getElementByTagName('pages'))!==null) + { + $this->_properties=array_merge($this->_properties,$pagesNode->getAttributes()->toArray()); + // at the page folder + foreach($pagesNode->getElementsByTagName('page') as $node) + { + $properties=$node->getAttributes(); + $id=$properties->remove('id'); + if(empty($id)) + throw new TConfigurationException('pageserviceconf_page_invalid',$configPath); + $matching=false; + $id=($configPagePath==='')?$id:$configPagePath.'.'.$id; + if(strcasecmp($id,$this->_pagePath)===0) + $matching=true; + else if($id[strlen($id)-1]==='*') // try wildcard matching + $matching=strncasecmp($this->_pagePath,$id,strlen($id)-1)===0; + if($matching) + $this->_properties=array_merge($this->_properties,$properties->toArray()); + } + } + + // external configurations + foreach($dom->getElementsByTagName('include') as $node) + { + if(($when=$node->getAttribute('when'))===null) + $when=true; + if(($filePath=$node->getAttribute('file'))===null) + throw new TConfigurationException('pageserviceconf_includefile_required'); + if(isset($this->_includes[$filePath])) + $this->_includes[$filePath]=array($configPagePath,'('.$this->_includes[$filePath][1].') || ('.$when.')'); + else + $this->_includes[$filePath]=array($configPagePath,$when); + } + } +} + diff --git a/gui/baculum/framework/Web/Services/TRpcService.php b/gui/baculum/framework/Web/Services/TRpcService.php new file mode 100644 index 0000000000..a2691af490 --- /dev/null +++ b/gui/baculum/framework/Web/Services/TRpcService.php @@ -0,0 +1,723 @@ + + * @link http://www.pradosoft.com/ + * @copyright 2010 Bigpoint GmbH + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @since 3.2 + * @package System.Web.Services + */ + +/** + * TRpcService class + * + * The TRpcService class is a generic class that can be extended and used to implement + * rpc services using different servers and protocols. + * + * A server is a {@link TModule} that must subclass {@link TRpcServer}: its role is + * to be an intermediate, moving data between the service and the provider. The base + * {@link TRpcServer} class should suit the most common needs, but can be sublassed for + * logging and debugging purposes, or to filter and modify the request/response on the fly. + * + * A protocol is a {@link TModule} that must subclass {@link TRpcProtocol}: its role is + * to implement the protocol that exposes the rpc api. Prado already implements two + * protocols: {@link TXmlRpcProtocol} for Xml-Rpc request and {@link TJsonRpcProtocol} for + * JSON-Rpc requests. + * + * A provider is a {@link TModule} that must subclass {@link TRpcApiProvider}: its role is + * to implement the methods that are available through the api. Each defined api must be + * a sublass of the abstract class {@link TRpcApiProvider} and implement its methods. + * + * The flow of requests and reponses is the following: + * Request <-> TRpcService <-> TRpcServer <-> TRpcProtocol <-> TRpcApiProvider <-> Response + * + * To define an rpc service, add the proper application configuration: + * + * + * + * + * + * + * + * + * + * + * An api can be registered adding a proper definition inside the service + * configuration. Each api definition must contain an id property and a class name + * expressed in namespace format. When the service receives a request for that api, + * the specified class will be instanciated in order to satisfy the request. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + **/ +class TRpcService extends TService +{ + /** + * const string base api provider class which every API must extend + */ + const BASE_API_PROVIDER = 'TRpcApiProvider'; + + /** + * const string base RPC server implementation + */ + const BASE_RPC_SERVER = 'TRpcServer'; + + /** + * @var array containing mimetype to protocol handler mappings + */ + protected $protocolHandlers = array( + 'application/json' => 'TJsonRpcProtocol', + 'text/xml' => 'TXmlRpcProtocol' + ); + + /** + * @var array containing API provider and their configured properties + */ + protected $apiProviders = array(); + + // methods + + /** + * Creates the API provider instance for the current request + * @param TRpcProtocol $protocolHandler instance + * @param string $providerId + */ + public function createApiProvider(TRpcProtocol $protocolHandler, $providerId) + { + $_properties = $this->apiProviders[$providerId]; + + if(($_providerClass = $_properties->remove('class')) === null) + throw new TConfigurationException('rpcservice_apiprovider_required'); + + Prado::using($_providerClass); + + $_providerClassName = ($_pos = strrpos($_providerClass, '.')) !== false ? substr($_providerClass, $_pos + 1) : $_providerClass; + if(!is_subclass_of($_providerClassName, self::BASE_API_PROVIDER)) + throw new TConfigurationException('rpcservice_apiprovider_invalid'); + + if(($_rpcServerClass = $_properties->remove('server')) === null) + $_rpcServerClass = self::BASE_RPC_SERVER; + + Prado::using($_rpcServerClass); + + $_rpcServerClassName = ($_pos = strrpos($_rpcServerClass, '.')) !== false ? substr($_rpcServerClass, $_pos + 1) : $_rpcServerClass; + if($_rpcServerClassName!==self::BASE_RPC_SERVER && !is_subclass_of($_rpcServerClassName, self::BASE_RPC_SERVER)) + throw new TConfigurationException('rpcservice_rpcserver_invalid'); + + $_apiProvider = new $_providerClassName(new $_rpcServerClassName($protocolHandler)); + $_apiProvider->setId($providerId); + + foreach($_properties as $_key => $_value) + $_apiProvider->setSubProperty($_key, $_value); + + return $_apiProvider; + } + + /** + * Initializes the service + * @param TXmlElement $config containing the module configuration + */ + public function init($config) + { + $this->loadConfig($config); + } + + /** + * Loads the service configuration + * @param TXmlElement $xml configuration + */ + public function loadConfig(TXmlElement $xml) + { + foreach($xml->getElementsByTagName('rpcapi') as $_apiProviderXml) + { + $_properties = $_apiProviderXml->getAttributes(); + + if(($_id = $_properties->remove('id')) === null || $_id == "") + throw new TConfigurationException('rpcservice_apiproviderid_required'); + + if(isset($this->apiProviders[$_id])) + throw new TConfigurationException('rpcservice_apiproviderid_duplicated'); + + $this->apiProviders[$_id] = $_properties; + } + } + + /** + * Runs the service + */ + public function run() + { + $_request = $this->getRequest(); + + if(($_providerId = $_request->getServiceParameter()) == "") + throw new THttpException(400, 'RPC API-Provider id required'); + + if(($_method = $_request->getRequestType()) != 'POST') + throw new THttpException(405, 'Invalid request method "'.$_method.'"!'); // TODO Exception muss "Allow POST" Header setzen + + if(($_mimeType = $_request->getContentType()) === null) + throw new THttpException(406, 'Content-Type is missing!'); // TODO Exception muss gültige Content-Type werte zurück geben + + if(!in_array($_mimeType, array_keys($this->protocolHandlers))) + throw new THttpException(406, 'Unsupported Content-Type!'); // TODO see previous + + $_protocolHandlerClass = $this->protocolHandlers[$_mimeType]; + $_protocolHandler = new $_protocolHandlerClass; + + if(($_result = $this->createApiProvider($_protocolHandler, $_providerId)->processRequest()) !== null) + { + $_response = $this->getResponse(); + $_protocolHandler->createResponseHeaders($_response); + $_response->write($_result); + } + } +} + +/** + * TRpcServer class + * + * TRpcServer is a class + * + * TRpcServer is the base class used to creare a server to be used in conjunction with + * {@link TRpcService}. + * The role of TRpcServer is to be an intermediate, moving data between the service and + * the provider. This base class should suit the most common needs, but can be sublassed for + * logging and debugging purposes, or to filter and modify the request/response on the fly. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + **/ +class TRpcServer extends TModule +{ + /** + * @var TRpcProtocol instance + */ + protected $handler; + + /** + * Constructor + * @param TRpcProtocol $protocolHandler instance + */ + public function __construct(TRpcProtocol $protocolHandler) + { + $this->handler = $protocolHandler; + } + + /** + * Registers the method in the protocol handler + * @param string $methodName + * @param array $methodDetails + */ + public function addRpcMethod($methodName, $methodDetails) + { + $this->handler->addMethod($methodName, $methodDetails); + } + + /** + * Retrieves the request payload + * @return string request payload + */ + public function getPayload() + { + return file_get_contents('php://input'); + } + + /** + * Passes the request payload to the protocol handler and returns the result + * @return string rpc response + */ + public function processRequest() + { + try + { + return $this->handler->callMethod($this->getPayload()); + } + catch(TRpcException $e) + { + return $this->handler->createErrorResponse($e); + } + } +} + +/** + * TRpcException class + * + * A TRpcException represents a RPC fault i.e. an error that is caused by the input data + * sent from the client. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + */ +class TRpcException extends TException +{ + public function __construct($message, $errorCode = -1) + { + $this->setErrorCode($errorCode); + + parent::__construct($message); + } +} + +/** + * TRpcApiProvider class + * + * TRpcApiProvider is an abstract class the can be subclasses in order to implement an + * api for a {@link TRpcService}. A subclass of TRpcApiProvider must implement the + * {@link registerMethods} method in order to declare the available methods, their + * names and the associated callback. + * + * + * public function registerMethods() + * { + * return array( + * 'apiMethodName1' => array('method' => array($this, 'objectMethodName1')), + * 'apiMethodName2' => array('method' => array('ClassName', 'staticMethodName')), + * ); + * } + * + * + * In this example, two api method have been defined. The first refers to an object + * method that must be implemented in the same class, the second to a static method + * implemented in a 'ClassName' class. + * In both cases, the method implementation will receive the request parameters as its + * method parameters. Since the number of received parameters depends on + * external-supplied data, it's adviced to use php's func_get_args() funtion to + * validate them. + * + * Providers must be registered in the service configuration in order to be available, + * as explained in {@link TRpcService}'s documentation. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + */ +abstract class TRpcApiProvider extends TModule +{ + /** + * @var TRpcServer instance + */ + protected $rpcServer; + + /** + * Must return an array of the available methods + * @abstract + */ + abstract public function registerMethods(); + + /** + * Constructor: informs the rpc server of the registered methods + */ + public function __construct(TRpcServer $rpcServer) + { + $this->rpcServer = $rpcServer; + + foreach($this->registerMethods() as $_methodName => $_methodDetails) + $this->rpcServer->addRpcMethod($_methodName, $_methodDetails); + } + + /** + * Processes the request using the server + * @return processed request + */ + public function processRequest() + { + return $this->rpcServer->processRequest(); + } + + /** + * @return rpc server instance + */ + public function getRpcServer() + { + return $this->rpcServer; + } +} + +/** + * TRpcProtocol class + * + * TRpcProtocol is the base class used to implement a protocol in a {@link TRpcService}. + * Prado already implements two protocols: {@link TXmlRpcProtocol} for Xml-Rpc request + * and {@link TJsonRpcProtocol} for JSON-Rpc requests. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + **/ +abstract class TRpcProtocol +{ + /** + * @var array containing the mapping from RPC method names to the actual handlers + */ + protected $rpcMethods = array(); + + // abstracts + + /** + * @param string request payload + * Processed the request ans returns the response, if any + * @return processed response + * @abstract + */ + abstract public function callMethod($requestPayload); + /** + * @param TRpcException the exception with error details + * Creates a proper response for an error condition + * @return a response representing the error + * @abstract + */ + abstract public function createErrorResponse(TRpcException $exception); + /** + * @param response + * Sets the needed headers for the response (eg: content-type, charset) + * @abstract + */ + abstract public function createResponseHeaders($response); + /** + * Encodes the response + * @param mixed reponse data + * @return string encoded response + * @abstract + */ + abstract public function encode($data); + /** + * Decodes the request payload + * @param string request payload + * @return mixed decoded request + * @abstract + */ + abstract public function decode($data); + + // methods + + /** + * Registers a new RPC method and handler details + * @param string $methodName + * @param array $handlerDetails containing the callback handler + */ + public function addMethod($methodName, $handlerDetails) + { + $this->rpcMethods[$methodName] = $handlerDetails; + } + + /** + * Calls the callback handler for the given method + * @param string $methodName of the RPC + * @param array $parameters for the callback handler as provided by the client + * @return mixed whatever the callback handler returns + */ + public function callApiMethod($methodName, $parameters) + { + if(!isset($this->rpcMethods[$methodName])) + throw new TRpcException('Method "'.$methodName.'" not found'); + + if($parameters === null) + $parameters = array(); + + if(!is_array($parameters)) + $parameters = array($parameters); + return call_user_func_array($this->rpcMethods[$methodName]['method'], $parameters); + } +} + +/** + * TJsonRpcProtocol class + * + * TJsonRpcProtocol is a class that implements JSON-Rpc protocol in {@link TRpcService}. + * Both version 1.0 and 2.0 of the specification are implemented, and the server will try + * to answer using the same version of the protocol used by the requesting client. + * + * @author Robin J. Rogge + * @author Fabio Bas + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + */ +class TJsonRpcProtocol extends TRpcProtocol +{ + protected $_id=null; + protected $_specificationVersion=1.0; + + /** + * Handles the RPC request + * @param string $requestPayload + * @return string JSON RPC response + */ + public function callMethod($requestPayload) + { + try + { + $_request = $this->decode($requestPayload); + + if(isset($_request['jsonrpc'])) + { + $this->_specificationVersion=$_request['jsonrpc']; + if($this->_specificationVersion > 2.0) + throw new TRpcException('Unsupported specification version', '-32600'); + } + + if(isset($_request['id'])) + $this->_id=$_request['id']; + + if(!isset($_request['method'])) + throw new TRpcException('Missing request method', '-32600'); + + if(!isset($_request['params'])) + $parameters = array(); + else + $parameters = $_request['params']; + + if(!is_array($parameters)) + $parameters = array($parameters); + + $ret = $this->callApiMethod($_request['method'], $parameters); + // a request without an id is a notification that doesn't need a response + if($this->_id !== null) + { + if($this->_specificationVersion==2.0) + { + return $this->encode(array( + 'jsonrpc' => '2.0', + 'id' => $this->_id, + 'result' => $ret + )); + } else { + return $this->encode(array( + 'id' => $this->_id, + 'result' => $this->callApiMethod($_request['method'], $_request['params']), + 'error' => null + )); + } + } + } + catch(TRpcException $e) + { + return $this->createErrorResponse($e); + } + catch(THttpException $e) + { + throw $e; + } + catch(Exception $e) + { + return $this->createErrorResponse(new TRpcException('An internal error occured', '-32603')); + } + } + + /** + * Turns the given exception into an JSON RPC fault + * @param TRpcException $exception + * @return string JSON RPC fault + */ + public function createErrorResponse(TRpcException $exception) + { + if($this->_specificationVersion==2.0) + { + return $this->encode(array( + 'id' => $this->_id, + 'result' => null, + 'error'=> array( + 'code' => $exception->getCode(), + 'message'=> $exception->getMessage(), + 'data' => null, + ) + )); + } else { + return $this->encode(array( + 'id' => $this->_id, + 'error'=> array( + 'code' => $exception->getCode(), + 'message'=> $exception->getMessage(), + 'data' => null, + ) + )); + } + } + + /** + * Sets the correct response headers + * @param THttpResponse $response + */ + public function createResponseHeaders($response) + { + $response->setContentType('application/json'); + $response->setCharset('UTF-8'); + } + + /** + * Decodes JSON encoded data into PHP data + * @param string $data in JSON format + * @return array PHP data + */ + public function decode($data) + { + $s = json_decode($data, true); + self::checkJsonError(); + return $s; + } + + /** + * Encodes PHP data into JSON data + * @param mixed PHP data + * @return string JSON encoded PHP data + */ + public function encode($data) + { + $s = json_encode($data); + self::checkJsonError(); + return $s; + } + + private static function checkJsonError() + { + $errnum = json_last_error(); + if($errnum != JSON_ERROR_NONE) + throw new Exception("JSON error: $msg", $err); + } + + /** + * Calls the callback handler for the given method + * Overrides parent implementation to correctly handle error codes + * @param string $methodName of the RPC + * @param array $parameters for the callback handler as provided by the client + * @return mixed whatever the callback handler returns + */ + public function callApiMethod($methodName, $parameters) + { + if(!isset($this->rpcMethods[$methodName])) + throw new TRpcException('Method "'.$methodName.'" not found', '-32601'); + + return call_user_func_array($this->rpcMethods[$methodName]['method'], $parameters); + } +} + +/** + * TXmlRpcProtocol class + * + * TXmlRpcProtocol is a class that implements XML-Rpc protocol in {@link TRpcService}. + * It's basically a wrapper to the xmlrpc_server_* family of php methods. + * + * @author Robin J. Rogge + * @version $Id$ + * @package System.Web.Services + * @since 3.2 + */ +class TXmlRpcProtocol extends TRpcProtocol +{ + /** + * @var XML RPC server resource + */ + private $_xmlrpcServer; + + // magics + + /** + * Constructor + */ + public function __construct() + { + $this->_xmlrpcServer = xmlrpc_server_create(); + } + + /** + * Destructor + */ + public function __destruct() + { + xmlrpc_server_destroy($this->_xmlrpcServer); + } + + // methods + + /** + * Registers a new RPC method and handler details + * @param string $methodName + * @param array $handlerDetails containing the callback handler + */ + public function addMethod($methodName, $methodDetails) + { + parent::addMethod($methodName, $methodDetails); + + xmlrpc_server_register_method($this->_xmlrpcServer, $methodName, array($this, 'callApiMethod')); + } + + // methods + + /** + * Handles the RPC request + * @param string $requestPayload + * @return string XML RPC response + */ + public function callMethod($requestPayload) + { + try + { + return xmlrpc_server_call_method($this->_xmlrpcServer, $requestPayload, null); + } + catch(TRpcException $e) + { + return $this->createErrorResponse($e); + } + catch(THttpException $e) + { + throw $e; + } + catch(Exception $e) + { + return $this->createErrorResponse(new TRpcException('An internal error occured')); + } + } + + /** + * Turns the given exception into an XML RPC fault + * @param TRpcException $exception + * @return string XML RPC fault + */ + public function createErrorResponse(TRpcException $exception) + { + return $this->encode(array( + 'faultCode' => $exception->getCode(), + 'faultString' => $exception->getMessage() + )); + } + + /** + * Sets the correct response headers + * @param THttpResponse $response + */ + public function createResponseHeaders($response) + { + $response->setContentType('text/xml'); + $response->setCharset('UTF-8'); + } + + /** + * Decodes XML encoded data into PHP data + * @param string $data in XML format + * @return array PHP data + */ + public function decode($data) + { + return xmlrpc_decode($data); + } + + /** + * Encodes PHP data into XML data + * @param mixed PHP data + * @return string XML encoded PHP data + */ + public function encode($data) + { + return xmlrpc_encode($data); + } +} diff --git a/gui/baculum/framework/Web/Services/TSoapService.php b/gui/baculum/framework/Web/Services/TSoapService.php new file mode 100644 index 0000000000..2d59f4620a --- /dev/null +++ b/gui/baculum/framework/Web/Services/TSoapService.php @@ -0,0 +1,624 @@ + + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSoapService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + */ + +/** + * TSoapService class + * + * TSoapService processes SOAP requests for a PRADO application. + * TSoapService requires PHP SOAP extension to be loaded. + * + * TSoapService manages a set of SOAP providers. Each SOAP provider + * is a class that implements a set of SOAP methods which are exposed + * to SOAP clients for remote invocation. TSoapService generates WSDL + * automatically for the SOAP providers by default. + * + * To use TSoapService, configure it in the application specification like following: + * + * + * + * + * + * + * + * PHP configuration style: + * + * 'services' => array( + * 'soap' => array( + * 'class' => 'System.Web.Services.TSoapService' + * 'properties' => array( + * 'provider' => 'MyStockQuote' + * ) + * ) + * ) + * + * + * The WSDL for the provider class "MyStockQuote" is generated based on special + * comment tags in the class. In particular, if a class method's comment + * contains the keyword "@soapmethod", it is considered to be a SOAP method + * and will be exposed to SOAP clients. For example, + * + * class MyStockQuote { + * / ** + * * @param string $symbol the stock symbol + * * @return float the stock price + * * @soapmethod + * * / + * public function getQuote($symbol) {...} + * } + * + * + * With the above SOAP provider, a typical SOAP client may call the method "getQuote" + * remotely like the following: + * + * $client=new SoapClient("http://hostname/path/to/index.php?soap=stockquote.wsdl"); + * echo $client->getQuote("ibm"); + * + * + * Each element in the application specification actually configures + * the properties of a SOAP server which defaults to {@link TSoapServer}. + * Therefore, any writable property of {@link TSoapServer} may appear as an attribute + * in the element. For example, the "provider" attribute refers to + * the {@link TSoapServer::setProvider Provider} property of {@link TSoapServer}. + * The following configuration specifies that the SOAP server is persistent within + * the user session (that means a MyStockQuote object will be stored in session) + * + * + * + * + * + * + * + * + * You may also use your own SOAP server class by specifying the "class" attribute of . + * + * @author Knut Urdalen + * @author Qiang Xue + * @author Carl G. Mathisen + * @package System.Web.Services + * @since 3.1 + */ +class TSoapService extends TService +{ + const DEFAULT_SOAP_SERVER='TSoapServer'; + private $_servers=array(); + private $_configFile=null; + private $_wsdlRequest=false; + private $_serverID=null; + + /** + * Constructor. + * Sets default service ID to 'soap'. + */ + public function __construct() + { + $this->setID('soap'); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + public function init($config) + { + if($this->_configFile!==null) + { + if(is_file($this->_configFile)) + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadConfig($dom); + } + else + throw new TConfigurationException('soapservice_configfile_invalid',$this->_configFile); + } + $this->loadConfig($config); + + $this->resolveRequest(); + } + + /** + * Resolves the request parameter. + * It identifies the server ID and whether the request is for WSDL. + * @throws THttpException if the server ID cannot be found + * @see getServerID + * @see getIsWsdlRequest + */ + protected function resolveRequest() + { + $serverID=$this->getRequest()->getServiceParameter(); + if(($pos=strrpos($serverID,'.wsdl'))===strlen($serverID)-5) + { + $serverID=substr($serverID,0,$pos); + $this->_wsdlRequest=true; + } + else + $this->_wsdlRequest=false; + $this->_serverID=$serverID; + if(!isset($this->_servers[$serverID])) + throw new THttpException(400,'soapservice_request_invalid',$serverID); + } + + /** + * Loads configuration from an XML element + * @param mixed configuration node + * @throws TConfigurationException if soap server id is not specified or duplicated + */ + private function loadConfig($config) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + if(is_array($config)) + { + foreach($config['soap'] as $id => $server) + { + $properties = isset($server['properties'])?$server['properties']:array(); + if(isset($this->_servers[$id])) + throw new TConfigurationException('soapservice_serverid_duplicated',$id); + $this->_servers[$id]=$properties; + } + } + } + else + { + foreach($config->getElementsByTagName('soap') as $serverXML) + { + $properties=$serverXML->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('soapservice_serverid_required'); + if(isset($this->_servers[$id])) + throw new TConfigurationException('soapservice_serverid_duplicated',$id); + $this->_servers[$id]=$properties; + } + } + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,Prado::getApplication()->getConfigurationFileExt()))===null) + throw new TConfigurationException('soapservice_configfile_invalid',$value); + } + + /** + * Constructs a URL with specified page path and GET parameters. + * @param string soap server ID + * @param array list of GET parameters, null if no GET parameters required + * @param boolean whether to encode the ampersand in URL, defaults to true. + * @param boolean whether to encode the GET parameters (their names and values), defaults to true. + * @return string URL for the page and GET parameters + */ + public function constructUrl($serverID,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true) + { + return $this->getRequest()->constructUrl($this->getID(),$serverID,$getParams,$encodeAmpersand,$encodeGetItems); + } + + /** + * @return boolean whether this is a request for WSDL + */ + public function getIsWsdlRequest() + { + return $this->_wsdlRequest; + } + + /** + * @return string the SOAP server ID + */ + public function getServerID() + { + return $this->_serverID; + } + + /** + * Creates the requested SOAP server. + * The SOAP server is initialized with the property values specified + * in the configuration. + * @return TSoapServer the SOAP server instance + */ + protected function createServer() + { + $properties=$this->_servers[$this->_serverID]; + $serverClass=null; + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP && isset($config['class'])) + $serverClass=$config['class']; + else if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_XML) + $serverClass=$properties->remove('class'); + if($serverClass===null) + $serverClass=self::DEFAULT_SOAP_SERVER; + Prado::using($serverClass); + $className=($pos=strrpos($serverClass,'.'))!==false?substr($serverClass,$pos+1):$serverClass; + if($className!==self::DEFAULT_SOAP_SERVER && !is_subclass_of($className,self::DEFAULT_SOAP_SERVER)) + throw new TConfigurationException('soapservice_server_invalid',$serverClass); + $server=new $className; + $server->setID($this->_serverID); + foreach($properties as $name=>$value) + $server->setSubproperty($name,$value); + return $server; + } + + /** + * Runs the service. + * If the service parameter ends with '.wsdl', it will serve a WSDL file for + * the specified soap server. + * Otherwise, it will handle the soap request using the specified server. + */ + public function run() + { + Prado::trace("Running SOAP service",'System.Web.Services.TSoapService'); + $server=$this->createServer(); + $this->getResponse()->setContentType('text/xml'); + $this->getResponse()->setCharset($server->getEncoding()); + if($this->getIsWsdlRequest()) + { + // server WSDL file + Prado::trace("Generating WSDL",'System.Web.Services.TSoapService'); + $this->getResponse()->clear(); + $this->getResponse()->write($server->getWsdl()); + } + else + { + // provide SOAP service + Prado::trace("Handling SOAP request",'System.Web.Services.TSoapService'); + $server->run(); + } + } +} + + +/** + * TSoapServer class. + * + * TSoapServer is a wrapper of the PHP SoapServer class. + * It associates a SOAP provider class to the SoapServer object. + * It also manages the URI for the SOAP service and WSDL. + * + * @author Qiang Xue + * @version $Id: TSoapService.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.Services + * @since 3.1 + */ +class TSoapServer extends TApplicationComponent +{ + const WSDL_CACHE_PREFIX='wsdl.'; + + private $_id; + private $_provider; + + private $_version=''; + private $_actor=''; + private $_encoding=''; + private $_uri=''; + private $_classMap; + private $_persistent=false; + private $_wsdlUri=''; + + private $_requestedMethod; + + private $_server; + + /** + * @return string the ID of the SOAP server + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string the ID of the SOAP server + * @throws TInvalidDataValueException if the ID ends with '.wsdl'. + */ + public function setID($id) + { + if(strrpos($this->_id,'.wsdl')===strlen($this->_id)-5) + throw new TInvalidDataValueException('soapserver_id_invalid',$id); + $this->_id=$id; + } + + /** + * Handles the SOAP request. + */ + public function run() + { + if(($provider=$this->getProvider())!==null) + { + Prado::using($provider); + $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider; + $this->guessMethodCallRequested($providerClass); + $server=$this->createServer(); + $server->setClass($providerClass, $this); + if($this->_persistent) + $server->setPersistence(SOAP_PERSISTENCE_SESSION); + } + else + $server=$this->createServer(); + try + { + $server->handle(); + } + catch (Exception $e) + { + if($this->getApplication()->getMode()===TApplicationMode::Debug) + $this->fault($e->getMessage(), $e->__toString()); + else + $this->fault($e->getMessage()); + } + } + + /** + * Generate a SOAP fault message. + * @param string message title + * @param mixed message details + * @param string message code, defalt is 'SERVER'. + * @param string actors + * @param string message name + */ + public function fault($title, $details='', $code='SERVER', $actor='', $name='') + { + Prado::trace('SOAP-Fault '.$code. ' '.$title.' : '.$details, 'System.Web.Services.TSoapService'); + $this->_server->fault($code, $title, $actor, $details, $name); + } + + /** + * Guess the SOAP method request from the actual SOAP message + * + * @param string $class current handler class. + */ + protected function guessMethodCallRequested($class) + { + $namespace = $class.'wsdl'; + $message = file_get_contents("php://input"); + $matches= array(); + if(preg_match('/xmlns:([^=]+)="urn:'.$namespace.'"/', $message, $matches)) + { + if(preg_match('/<'.$matches[1].':([a-zA-Z_]+[a-zA-Z0-9_]+)/', $message, $method)) + { + $this->_requestedMethod = $method[1]; + } + } + } + + /** + * Soap method guessed from the SOAP message received. + * @return string soap method request, null if not found. + */ + public function getRequestedMethod() + { + return $this->_requestedMethod; + } + + /** + * Creates the SoapServer instance. + * @return SoapServer + */ + protected function createServer() + { + if($this->_server===null) + { + if($this->getApplication()->getMode()===TApplicationMode::Debug) + ini_set("soap.wsdl_cache_enabled",0); + $this->_server = new SoapServer($this->getWsdlUri(),$this->getOptions()); + } + return $this->_server; + } + + /** + * @return array options for creating SoapServer instance + */ + protected function getOptions() + { + $options=array(); + if($this->_version==='1.1') + $options['soap_version']=SOAP_1_1; + else if($this->_version==='1.2') + $options['soap_version']=SOAP_1_2; + if(!empty($this->_actor)) + $options['actor']=$this->_actor; + if(!empty($this->_encoding)) + $options['encoding']=$this->_encoding; + if(!empty($this->_uri)) + $options['uri']=$this->_uri; + if(is_string($this->_classMap)) + { + foreach(preg_split('/\s*,\s*/', $this->_classMap) as $className) + $options['classmap'][$className]=$className; //complex type uses the class name in the wsdl + } + return $options; + } + + /** + * Returns the WSDL content of the SOAP server. + * If {@link getWsdlUri WsdlUri} is set, its content will be returned. + * If not, the {@link setProvider Provider} class will be investigated + * and the WSDL will be automatically genearted. + * @return string the WSDL content of the SOAP server + */ + public function getWsdl() + { + if($this->_wsdlUri==='') + { + $provider=$this->getProvider(); + $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider; + Prado::using($provider); + if($this->getApplication()->getMode()===TApplicationMode::Performance && ($cache=$this->getApplication()->getCache())!==null) + { + $wsdl=$cache->get(self::WSDL_CACHE_PREFIX.$providerClass); + if(is_string($wsdl)) + return $wsdl; + Prado::using('System.3rdParty.WsdlGen.WsdlGenerator'); + $wsdl=WsdlGenerator::generate($providerClass, $this->getUri(), $this->getEncoding()); + $cache->set(self::WSDL_CACHE_PREFIX.$providerClass,$wsdl); + return $wsdl; + } + else + { + Prado::using('System.3rdParty.WsdlGen.WsdlGenerator'); + return WsdlGenerator::generate($providerClass, $this->getUri(), $this->getEncoding()); + } + } + else + return file_get_contents($this->_wsdlUri); + } + + /** + * @return string the URI for WSDL + */ + public function getWsdlUri() + { + if($this->_wsdlUri==='') + return $this->getRequest()->getBaseUrl().$this->getService()->constructUrl($this->getID().'.wsdl',false); + else + return $this->_wsdlUri; + } + + /** + * @param string the URI for WSDL + */ + public function setWsdlUri($value) + { + $this->_wsdlUri=$value; + } + + /** + * @return string the URI for the SOAP service + */ + public function getUri() + { + if($this->_uri==='') + return $this->getRequest()->getBaseUrl().$this->getService()->constructUrl($this->getID(),false); + else + return $this->_uri; + } + + /** + * @param string the URI for the SOAP service + */ + public function setUri($uri) + { + $this->_uri=$uri; + } + + /** + * @return string the SOAP provider class (in namespace format) + */ + public function getProvider() + { + return $this->_provider; + } + + /** + * @param string the SOAP provider class (in namespace format) + */ + public function setProvider($provider) + { + $this->_provider=$provider; + } + + /** + * @return string SOAP version, defaults to empty (meaning not set). + */ + public function getVersion() + { + return $this->_version; + } + + /** + * @param string SOAP version, either '1.1' or '1.2' + * @throws TInvalidDataValueException if neither '1.1' nor '1.2' + */ + public function setVersion($value) + { + if($value==='1.1' || $value==='1.2' || $value==='') + $this->_version=$value; + else + throw new TInvalidDataValueException('soapserver_version_invalid',$value); + } + + /** + * @return string actor of the SOAP service + */ + public function getActor() + { + return $this->_actor; + } + + /** + * @param string actor of the SOAP service + */ + public function setActor($value) + { + $this->_actor=$value; + } + + /** + * @return string encoding of the SOAP service + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * @param string encoding of the SOAP service + */ + public function setEncoding($value) + { + $this->_encoding=$value; + } + + /** + * @return boolean whether the SOAP service is persistent within session. Defaults to false. + */ + public function getSessionPersistent() + { + return $this->_persistent; + } + + /** + * @param boolean whether the SOAP service is persistent within session. + */ + public function setSessionPersistent($value) + { + $this->_persistent=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string comma delimit list of complex type classes. + */ + public function getClassMaps() + { + return $this->_classMap; + } + + /** + * @return string comma delimit list of class names + */ + public function setClassMaps($classes) + { + $this->_classMap = $classes; + } +} + diff --git a/gui/baculum/framework/Web/TAssetManager.php b/gui/baculum/framework/Web/TAssetManager.php new file mode 100644 index 0000000000..f29b0707ab --- /dev/null +++ b/gui/baculum/framework/Web/TAssetManager.php @@ -0,0 +1,356 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TAssetManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + */ + +/** + * TAssetManager class + * + * TAssetManager provides a scheme to allow web clients visiting + * private files that are normally web-inaccessible. + * + * TAssetManager will copy the file to be published into a web-accessible + * directory. The default base directory for storing the file is "assets", which + * should be under the application directory. This can be changed by setting + * the {@link setBasePath BasePath} property together with the + * {@link setBaseUrl BaseUrl} property that refers to the URL for accessing the base path. + * + * By default, TAssetManager will not publish a file or directory if it already + * exists in the publishing directory and has an older modification time. + * If the application mode is set as 'Performance', the modification time check + * will be skipped. You can explicitly require a modification time check + * with the function {@link publishFilePath}. This is usually + * very useful during development. + * + * TAssetManager may be configured in application configuration file as follows, + * + * + * + * where {@link getBasePath BasePath} and {@link getBaseUrl BaseUrl} are + * configurable properties of TAssetManager. Make sure that BasePath is a namespace + * pointing to a valid directory writable by the Web server process. + * + * @author Qiang Xue + * @version $Id: TAssetManager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class TAssetManager extends TModule +{ + /** + * Default web accessible base path for storing private files + */ + const DEFAULT_BASEPATH='assets'; + /** + * @var string base web accessible path for storing private files + */ + private $_basePath=null; + /** + * @var string base URL for accessing the publishing directory. + */ + private $_baseUrl=null; + /** + * @var boolean whether to use timestamp checking to ensure files are published with up-to-date versions. + */ + private $_checkTimestamp=false; + /** + * @var TApplication application instance + */ + private $_application; + /** + * @var array published assets + */ + private $_published=array(); + /** + * @var boolean whether the module is initialized + */ + private $_initialized=false; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + $application=$this->getApplication(); + if($this->_basePath===null) + $this->_basePath=dirname($application->getRequest()->getApplicationFilePath()).DIRECTORY_SEPARATOR.self::DEFAULT_BASEPATH; + if(!is_writable($this->_basePath) || !is_dir($this->_basePath)) + throw new TConfigurationException('assetmanager_basepath_invalid',$this->_basePath); + if($this->_baseUrl===null) + $this->_baseUrl=rtrim(dirname($application->getRequest()->getApplicationUrl()),'/\\').'/'.self::DEFAULT_BASEPATH; + $application->setAssetManager($this); + $this->_initialized=true; + } + + /** + * @return string the root directory storing published asset files + */ + public function getBasePath() + { + return $this->_basePath; + } + + /** + * Sets the root directory storing published asset files. + * The directory must be in namespace format. + * @param string the root directory storing published asset files + * @throws TInvalidOperationException if the module is initialized already + */ + public function setBasePath($value) + { + if($this->_initialized) + throw new TInvalidOperationException('assetmanager_basepath_unchangeable'); + else + { + $this->_basePath=Prado::getPathOfNamespace($value); + if($this->_basePath===null || !is_dir($this->_basePath) || !is_writable($this->_basePath)) + throw new TInvalidDataValueException('assetmanager_basepath_invalid',$value); + } + } + + /** + * @return string the base url that the published asset files can be accessed + */ + public function getBaseUrl() + { + return $this->_baseUrl; + } + + /** + * @param string the base url that the published asset files can be accessed + * @throws TInvalidOperationException if the module is initialized already + */ + public function setBaseUrl($value) + { + if($this->_initialized) + throw new TInvalidOperationException('assetmanager_baseurl_unchangeable'); + else + $this->_baseUrl=rtrim($value,'/'); + } + + /** + * Publishes a file or a directory (recursively). + * This method will copy the content in a directory (recursively) to + * a web accessible directory and returns the URL for the directory. + * If the application is not in performance mode, the file modification + * time will be used to make sure the published file is latest or not. + * If not, a file copy will be performed. + * @param string the path to be published + * @param boolean If true, file modification time will be checked even if the application + * is in performance mode. + * @return string an absolute URL to the published directory + * @throws TInvalidDataValueException if the file path to be published is + * invalid + */ + public function publishFilePath($path,$checkTimestamp=false) + { + if(isset($this->_published[$path])) + return $this->_published[$path]; + else if(empty($path) || ($fullpath=realpath($path))===false) + throw new TInvalidDataValueException('assetmanager_filepath_invalid',$path); + else if(is_file($fullpath)) + { + $dir=$this->hash(dirname($fullpath)); + $fileName=basename($fullpath); + $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir; + if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) + $this->copyFile($fullpath,$dst); + return $this->_published[$path]=$this->_baseUrl.'/'.$dir.'/'.$fileName; + } + else + { + $dir=$this->hash($fullpath); + if(!is_dir($this->_basePath.DIRECTORY_SEPARATOR.$dir) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) + { + Prado::trace("Publishing directory $fullpath",'System.Web.UI.TAssetManager'); + $this->copyDirectory($fullpath,$this->_basePath.DIRECTORY_SEPARATOR.$dir); + } + return $this->_published[$path]=$this->_baseUrl.'/'.$dir; + } + } + + /** + * @return array List of published assets + * @since 3.1.6 + */ + public function getPublished() + { + return $this->_published; + } + + /** + * @param $values List of published assets + * @since 3.1.6 + */ + protected function setPublished($values=array()) + { + $this->_published = $values; + } + + /** + * Returns the published path of a file path. + * This method does not perform any publishing. It merely tells you + * if the file path is published, where it will go. + * @param string directory or file path being published + * @return string the published file path + */ + public function getPublishedPath($path) + { + $path=realpath($path); + if(is_file($path)) + return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash(dirname($path)).DIRECTORY_SEPARATOR.basename($path); + else + return $this->_basePath.DIRECTORY_SEPARATOR.$this->hash($path); + } + + /** + * Returns the URL of a published file path. + * This method does not perform any publishing. It merely tells you + * if the file path is published, what the URL will be to access it. + * @param string directory or file path being published + * @return string the published URL for the file path + */ + public function getPublishedUrl($path) + { + $path=realpath($path); + if(is_file($path)) + return $this->_baseUrl.'/'.$this->hash(dirname($path)).'/'.basename($path); + else + return $this->_baseUrl.'/'.$this->hash($path); + } + + /** + * Generate a CRC32 hash for the directory path. Collisions are higher + * than MD5 but generates a much smaller hash string. + * @param string string to be hashed. + * @return string hashed string. + */ + protected function hash($dir) + { + return sprintf('%x',crc32($dir.Prado::getVersion())); + } + + /** + * Copies a file to a directory. + * Copying is done only when the destination file does not exist + * or has an older file modification time. + * @param string source file path + * @param string destination directory (if not exists, it will be created) + */ + protected function copyFile($src,$dst) + { + if(!is_dir($dst)) + { + @mkdir($dst); + @chmod($dst, PRADO_CHMOD); + } + $dstFile=$dst.DIRECTORY_SEPARATOR.basename($src); + if(@filemtime($dstFile)<@filemtime($src)) + { + Prado::trace("Publishing file $src to $dstFile",'System.Web.TAssetManager'); + @copy($src,$dstFile); + } + } + + /** + * Copies a directory recursively as another. + * If the destination directory does not exist, it will be created. + * File modification time is used to ensure the copied files are latest. + * @param string the source directory + * @param string the destination directory + * @todo a generic solution to ignore certain directories and files + */ + public function copyDirectory($src,$dst) + { + if(!is_dir($dst)) + { + @mkdir($dst); + @chmod($dst, PRADO_CHMOD); + } + if($folder=@opendir($src)) + { + while($file=@readdir($folder)) + { + if($file==='.' || $file==='..' || $file==='.svn') + continue; + else if(is_file($src.DIRECTORY_SEPARATOR.$file)) + { + if(@filemtime($dst.DIRECTORY_SEPARATOR.$file)<@filemtime($src.DIRECTORY_SEPARATOR.$file)) + { + @copy($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file); + @chmod($dst.DIRECTORY_SEPARATOR.$file, PRADO_CHMOD); + } + } + else + $this->copyDirectory($src.DIRECTORY_SEPARATOR.$file,$dst.DIRECTORY_SEPARATOR.$file); + } + closedir($folder); + } else { + throw new TInvalidDataValueException('assetmanager_source_directory_invalid', $src); + } + } + + /** + * Publish a tar file by extracting its contents to the assets directory. + * Each tar file must be accomplished with its own MD5 check sum file. + * The MD5 file is published when the tar contents are successfully + * extracted to the assets directory. The presence of the MD5 file + * as published asset assumes that the tar file has already been extracted. + * @param string tar filename + * @param string MD5 checksum for the corresponding tar file. + * @param boolean Wether or not to check the time stamp of the file for publishing. Defaults to false. + * @return string URL path to the directory where the tar file was extracted. + */ + public function publishTarFile($tarfile, $md5sum, $checkTimestamp=false) + { + if(isset($this->_published[$md5sum])) + return $this->_published[$md5sum]; + else if(($fullpath=realpath($md5sum))===false || !is_file($fullpath)) + throw new TInvalidDataValueException('assetmanager_tarchecksum_invalid',$md5sum); + else + { + $dir=$this->hash(dirname($fullpath)); + $fileName=basename($fullpath); + $dst=$this->_basePath.DIRECTORY_SEPARATOR.$dir; + if(!is_file($dst.DIRECTORY_SEPARATOR.$fileName) || $checkTimestamp || $this->getApplication()->getMode()!==TApplicationMode::Performance) + { + if(@filemtime($dst.DIRECTORY_SEPARATOR.$fileName)<@filemtime($fullpath)) + { + $this->copyFile($fullpath,$dst); + $this->deployTarFile($tarfile,$dst); + } + } + return $this->_published[$md5sum]=$this->_baseUrl.'/'.$dir; + } + } + + /** + * Extracts the tar file to the destination directory. + * N.B Tar file must not be compressed. + * @param string tar file + * @param string path where the contents of tar file are to be extracted + * @return boolean true if extract successful, false otherwise. + */ + protected function deployTarFile($path,$destination) + { + if(($fullpath=realpath($path))===false || !is_file($fullpath)) + throw new TIOException('assetmanager_tarfile_invalid',$path); + else + { + Prado::using('System.IO.TTarFileExtractor'); + $tar = new TTarFileExtractor($fullpath); + return $tar->extract($destination); + } + } + +} + diff --git a/gui/baculum/framework/Web/TCacheHttpSession.php b/gui/baculum/framework/Web/TCacheHttpSession.php new file mode 100644 index 0000000000..d658c0f38c --- /dev/null +++ b/gui/baculum/framework/Web/TCacheHttpSession.php @@ -0,0 +1,150 @@ + + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCacheHttpSession.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.1.1 + */ + +/** + * TCacheHttpSession class + * + * TCacheHttpSession provides access for storing session data using a cache module (e.g. TMemCache, TDbCache). + * To specify the cache module for data storage, set the {@link setCacheModuleID CacheModuleID} property + * which should refer to a valid cache module configured in the application configuration. + * + * The following example shows how we configure TCacheHttpSession: + * + * + * + * + * + * + * + * Beware, by definition cache storage are volatile, which means the data stored on them + * may be swapped out and get lost. This may not be the case for certain cache storage, + * such as database. So make sure you manage your cache properly to avoid loss of session data. + * + * @author Carl G. Mathisen + * @author Qiang Xue + * @version $Id: TCacheHttpSession.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.1.1 + */ +class TCacheHttpSession extends THttpSession +{ + private $_prefix='session'; + private $_cacheModuleID=''; + private $_cache; + + /** + * Initializes the module. + * This method is required by IModule. + * It reads the CacheModule property. + * @param TXmlElement module configuration + */ + public function init($config) + { + if($this->_cacheModuleID==='') + throw new TConfigurationException('cachesession_cachemoduleid_required'); + else if(($cache=$this->getApplication()->getModule($this->_cacheModuleID))===null) + throw new TConfigurationException('cachesession_cachemodule_inexistent',$this->_cacheModuleID); + else if($cache instanceof ICache) + $this->_cache=$cache; + else + throw new TConfigurationException('cachesession_cachemodule_invalid',$this->_cacheModuleID); + $this->setUseCustomStorage(true); + parent::init($config); + } + + /** + * @return string the ID of the cache module. + */ + public function getCacheModuleID() + { + return $this->_cacheModuleID; + } + + /** + * @param string the ID of the cache module. + */ + public function setCacheModuleID($value) + { + $this->_cacheModuleID=$value; + } + + /** + * @return ICache the cache module being used for data storage + */ + public function getCache() + { + return $this->_cache; + } + + /** + * Session read handler. + * @param string session ID + * @return string the session data + */ + public function _read($id) + { + return $this->_cache->get($this->calculateKey($id)); + } + + /** + * Session write handler. + * @param string session ID + * @param string session data + * @return boolean whether session write is successful + */ + public function _write($id,$data) + { + return $this->_cache->set($this->calculateKey($id),$data,$this->getTimeout()); + } + + /** + * Session destroy handler. + * This method should be overriden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @return boolean whether session is destroyed successfully + */ + public function _destroy($id) + { + return $this->_cache->delete($this->calculateKey($id)); + } + + /** + * @return string prefix of session variable name to avoid conflict with other cache data. Defaults to 'session'. + */ + public function getKeyPrefix() + { + return $this->_prefix; + } + + /** + * @param string prefix of session variable name to avoid conflict with other cache data + */ + public function setKeyPrefix($value) + { + $this->_prefix=$value; + } + + /** + * @param string session variable name + * @return string a safe cache key associated with the session variable name + */ + protected function calculateKey($id) + { + return $this->_prefix.':'.$id; + } +} + diff --git a/gui/baculum/framework/Web/THttpRequest.php b/gui/baculum/framework/Web/THttpRequest.php new file mode 100644 index 0000000000..f621d1eb6d --- /dev/null +++ b/gui/baculum/framework/Web/THttpRequest.php @@ -0,0 +1,1412 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THttpRequest.php 3273 2013-02-13 21:51:21Z ctrlaltca $ + * @package System.Web + */ + +Prado::using('System.Web.TUrlManager'); + +/** + * THttpRequest class + * + * THttpRequest provides storage and access scheme for user request sent via HTTP. + * It also encapsulates a uniform way to parse and construct URLs. + * + * User post data can be retrieved from THttpRequest by using it like an associative array. + * For example, to test if a user supplies a variable named 'param1', you can use, + * + * if(isset($request['param1'])) ... + * // equivalent to: + * // if($request->contains('param1')) ... + * + * To get the value of 'param1', use, + * + * $value=$request['param1']; + * // equivalent to: + * // $value=$request->itemAt('param1'); + * + * To traverse the user post data, use + * + * foreach($request as $name=>$value) ... + * + * Note, POST and GET variables are merged together in THttpRequest. + * If a variable name appears in both POST and GET data, then POST data + * takes precedence. + * + * To construct a URL that can be recognized by Prado, use {@link constructUrl()}. + * The format of the recognizable URLs is determined according to + * {@link setUrlManager UrlManager}. By default, the following two formats + * are recognized: + * + * /index.php?ServiceID=ServiceParameter&Name1=Value1&Name2=Value2 + * /index.php/ServiceID,ServiceParameter/Name1,Value1/Name2,Value2 + * + * The first format is called 'Get' while the second 'Path', which is specified + * via {@link setUrlFormat UrlFormat}. For advanced users who want to use + * their own URL formats, they can write customized URL management modules + * and install the managers as application modules and set {@link setUrlManager UrlManager}. + * + * The ServiceID in the above URLs is as defined in the application configuration + * (e.g. the default page service's service ID is 'page'). + * As a consequence, your GET variable names should not conflict with the service + * IDs that your application supports. + * + * THttpRequest also provides the cookies sent by the user, user information such + * as his browser capabilities, accepted languages, etc. + * + * By default, THttpRequest is registered with {@link TApplication} as the + * request module. It can be accessed via {@link TApplication::getRequest()}. + * + * @author Qiang Xue + * @version $Id: THttpRequest.php 3273 2013-02-13 21:51:21Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule +{ + const CGIFIX__PATH_INFO = 1; + const CGIFIX__SCRIPT_NAME = 2; + /** + * @var TUrlManager the URL manager module + */ + private $_urlManager=null; + /** + * @var string the ID of the URL manager module + */ + private $_urlManagerID=''; + /** + * @var string Separator used to separate GET variable name and value when URL format is Path. + */ + private $_separator=','; + /** + * @var string requested service ID + */ + private $_serviceID=null; + /** + * @var string requested service parameter + */ + private $_serviceParam=null; + /** + * @var THttpCookieCollection cookies sent from user + */ + private $_cookies=null; + /** + * @var string requested URI (URL w/o host info) + */ + private $_requestUri; + /** + * @var string path info of URL + */ + private $_pathInfo; + /** + * @var boolean whether the session ID should be kept in cookie only + */ + private $_cookieOnly=null; + private $_urlFormat=THttpRequestUrlFormat::Get; + private $_services; + private $_requestResolved=false; + private $_enableCookieValidation=false; + private $_cgiFix=0; + /** + * @var boolean whether to cache the TUrlManager class (useful with a lot of TUrlMappings) + */ + private $_enableCache=false; + /** + * @var string request URL + */ + private $_url=null; + + /** + * @var string module id + */ + private $_id; + + /** + * @var array contains all request variables + */ + private $_items=array(); + + /** + * @return string id of this module + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this module + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * @param TXmlElement module configuration + */ + public function init($config) + { + // Fill in default request info when the script is run in command line + if(php_sapi_name()==='cli') + { + $_SERVER['REMOTE_ADDR']='127.0.0.1'; + $_SERVER['REQUEST_METHOD']='GET'; + $_SERVER['SERVER_NAME']='localhost'; + $_SERVER['SERVER_PORT']=80; + $_SERVER['HTTP_USER_AGENT']=''; + } + + // Info about server variables: + // PHP_SELF contains real URI (w/ path info, w/o query string) + // SCRIPT_NAME is the real URI for the requested script (w/o path info and query string) + // QUERY_STRING is the string following the '?' in the ur (eg the a=x part in http://foo/bar?a=x) + // REQUEST_URI contains the URI part entered in the browser address bar + // SCRIPT_FILENAME is the file path to the executing script + if(isset($_SERVER['REQUEST_URI'])) + $this->_requestUri=$_SERVER['REQUEST_URI']; + else // TBD: in this case, SCRIPT_NAME need to be escaped + $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']); + + if($this->_cgiFix&self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO'])) + $this->_pathInfo=substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME'])); + elseif(isset($_SERVER['PATH_INFO'])) + $this->_pathInfo=$_SERVER['PATH_INFO']; + else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME']) + $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME'])); + else + $this->_pathInfo=''; + + if(get_magic_quotes_gpc()) + { + if(isset($_GET)) + $_GET=$this->stripSlashes($_GET); + if(isset($_POST)) + $_POST=$this->stripSlashes($_POST); + if(isset($_REQUEST)) + $_REQUEST=$this->stripSlashes($_REQUEST); + if(isset($_COOKIE)) + $_COOKIE=$this->stripSlashes($_COOKIE); + } + + $this->getApplication()->setRequest($this); + } + + /** + * Strips slashes from input data. + * This method is applied when magic quotes is enabled. + * @param mixed input data to be processed + * @return mixed processed data + */ + public function stripSlashes(&$data) + { + return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data); + } + + /** + * @return TUri the request URL + */ + public function getUrl() + { + if($this->_url===null) + { + $secure=$this->getIsSecureConnection(); + $url=$secure?'https://':'http://'; + if(empty($_SERVER['HTTP_HOST'])) + { + $url.=$_SERVER['SERVER_NAME']; + $port=$_SERVER['SERVER_PORT']; + if(($port!=80 && !$secure) || ($port!=443 && $secure)) + $url.=':'.$port; + } + else + $url.=$_SERVER['HTTP_HOST']; + $url.=$this->getRequestUri(); + $this->_url=new TUri($url); + } + return $this->_url; + } + + /** + * Set true to cache the UrlManager instance. Consider to enable this cache + * when the application defines a lot of TUrlMappingPatterns + * @param boolean true to cache urlmanager instance. + */ + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean true if urlmanager instance should be cached, false otherwise. + */ + public function getEnableCache() + { + return $this->_enableCache; + } + + protected function getCacheKey() + { + return $this->getID(); + } + + /** + * Saves the current UrlManager instance to cache. + * @return boolean true if UrlManager instance was cached, false otherwise. + */ + protected function cacheUrlManager($manager) + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) + { + $dependencies = null; + if($this->getApplication()->getMode() !== TApplicationMode::Performance) + if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile()) + { + $fn = Prado::getPathOfNamespace($fn,$this->getApplication()->getConfigurationFileExt()); + $dependencies = new TFileCacheDependency($fn); + } + return $cache->set($this->getCacheKey(), $manager, 0, $dependencies); + } + } + return false; + } + + /** + * Loads UrlManager instance from cache. + * @return TUrlManager intance if load was successful, null otherwise. + */ + protected function loadCachedUrlManager() + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) + { + $manager = $cache->get($this->getCacheKey()); + if($manager instanceof TUrlManager) + return $manager; + } + } + return null; + } + + /** + * @return string the ID of the URL manager module + */ + public function getUrlManager() + { + return $this->_urlManagerID; + } + + /** + * Sets the URL manager module. + * By default, {@link TUrlManager} is used for managing URLs. + * You may specify a different module for URL managing tasks + * by loading it as an application module and setting this property + * with the module ID. + * @param string the ID of the URL manager module + */ + public function setUrlManager($value) + { + $this->_urlManagerID=$value; + } + + /** + * @return TUrlManager the URL manager module + */ + public function getUrlManagerModule() + { + if($this->_urlManager===null) + { + if(($this->_urlManager = $this->loadCachedUrlManager())===null) + { + if(empty($this->_urlManagerID)) + { + $this->_urlManager=new TUrlManager; + $this->_urlManager->init(null); + } + else + { + $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID); + if($this->_urlManager===null) + throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID); + if(!($this->_urlManager instanceof TUrlManager)) + throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID); + } + $this->cacheUrlManager($this->_urlManager); + } + } + return $this->_urlManager; + } + + /** + * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. + */ + public function getUrlFormat() + { + return $this->_urlFormat; + } + + /** + * Sets the format of URLs constructed and interpretted by the request module. + * A Get URL format is like index.php?name1=value1&name2=value2 + * while a Path URL format is like index.php/name1,value1/name2,value. + * Changing the UrlFormat will affect {@link constructUrl} and how GET variables + * are parsed. + * @param THttpRequestUrlFormat the format of URLs. + */ + public function setUrlFormat($value) + { + $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); + } + + /** + * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to comma ','. + */ + public function getUrlParamSeparator() + { + return $this->_separator; + } + + /** + * @param string separator used to separate GET variable name and value when URL format is Path. + * @throws TInvalidDataValueException if the separator is not a single character + */ + public function setUrlParamSeparator($value) + { + if(strlen($value)===1) + $this->_separator=$value; + else + throw new TInvalidDataValueException('httprequest_separator_invalid'); + } + + /** + * @return string request type, can be GET, POST, HEAD, or PUT + */ + public function getRequestType() + { + return isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:null; + } + + /** + * @param boolean $mimetypeOnly whether to return only the mimetype (default: true) + * @return string content type (e.g. 'application/json' or 'text/html; encoding=gzip') or null if not specified + */ + public function getContentType($mimetypeOnly = true) + { + if(!isset($_SERVER['CONTENT_TYPE'])) + return null; + + if($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false) + return substr($_SERVER['CONTENT_TYPE'], 0, $_pos); + + return $_SERVER['CONTENT_TYPE']; + } + + /** + * @return boolean if the request is sent via secure channel (https) + */ + public function getIsSecureConnection() + { + return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off'); + } + + /** + * @return string part of the request URL after script name and before question mark. + */ + public function getPathInfo() + { + return $this->_pathInfo; + } + + /** + * @return string part of that request URL after the question mark + */ + public function getQueryString() + { + return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:null; + } + + /** + * @return string the requested http procolol. Blank string if not defined. + */ + public function getHttpProtocolVersion() + { + return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:null; + } + + /** + * @param integer|null Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default) + * @return array + */ + public function getHeaders($case=null) + { + static $result; + + if($result === null && function_exists('apache_request_headers')) { + $result = apache_request_headers(); + } + elseif($result === null) { + $result = array(); + foreach($_SERVER as $key=>$value) { + if(strncasecmp($key, 'HTTP_', 5) !== 0) continue; + $key = str_replace(' ','-', ucwords(strtolower(str_replace('_',' ', substr($key, 5))))); + $result[$key] = $value; + } + } + + if($case !== null) + return array_change_key_case($result, $case); + + return $result; + } + + /** + * @return string part of that request URL after the host info (including pathinfo and query string) + */ + public function getRequestUri() + { + return $this->_requestUri; + } + + /** + * @param boolean|null whether to use HTTPS instead of HTTP even if the current request is sent via HTTP or vice versa + * null - keep current schema + * true - force https + * false - force http + * @return string schema and hostname of the requested URL + */ + public function getBaseUrl($forceSecureConnection=null) + { + $url=$this->getUrl(); + $scheme=($forceSecureConnection)?"https": (($forceSecureConnection === null)?$url->getScheme():'http'); + $host=$url->getHost(); + if (($port=$url->getPort())) $host.=':'.$port; + return $scheme.'://'.$host; + } + + /** + * @return string entry script URL (w/o host part) + */ + public function getApplicationUrl() + { + if($this->_cgiFix&self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME'])) + return $_SERVER['ORIG_SCRIPT_NAME']; + + return isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:null; + } + + /** + * @param boolean|null whether to use HTTPS instead of HTTP even if the current request is sent via HTTP or vice versa + * null - keep current schema + * true - force https + * false - force http + * @return string entry script URL (w/ host part) + */ + public function getAbsoluteApplicationUrl($forceSecureConnection=null) + { + return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl(); + } + + /** + * @return string application entry script file path (processed w/ realpath()) + */ + public function getApplicationFilePath() + { + return realpath(isset($_SERVER['SCRIPT_FILENAME'])?$_SERVER['SCRIPT_FILENAME']:null); + } + + /** + * @return string server name + */ + public function getServerName() + { + return isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:null; + } + + /** + * @return integer server port number + */ + public function getServerPort() + { + return isset($_SERVER['SERVER_PORT'])?$_SERVER['SERVER_PORT']:null; + } + + /** + * @return string URL referrer, null if not present + */ + public function getUrlReferrer() + { + return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null; + } + + /** + * @return array user browser capabilities + * @see get_browser + */ + public function getBrowser() + { + try + { + return get_browser(); + } + catch(TPhpErrorException $e) + { + throw new TConfigurationException('httprequest_browscap_required'); + } + } + + /** + * @return string user agent + */ + public function getUserAgent() + { + return isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null; + } + + /** + * @return string user IP address + */ + public function getUserHostAddress() + { + return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null; + } + + /** + * @return string user host name, null if cannot be determined + */ + public function getUserHost() + { + return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null; + } + + /** + * @return string user browser accept types + */ + public function getAcceptTypes() + { + // TBD: break it into array?? + return isset($_SERVER['HTTP_ACCEPT'])?$_SERVER['HTTP_ACCEPT']:null; + } + + /** + * Returns a list of user preferred languages. + * The languages are returned as an array. Each array element + * represents a single language preference. The languages are ordered + * according to user preferences. The first language is the most preferred. + * @return array list of user preferred languages. + */ + public function getUserLanguages() + { + return Prado::getUserLanguages(); + } + + /** + * @return boolean whether cookies should be validated. Defaults to false. + */ + public function getEnableCookieValidation() + { + return $this->_enableCookieValidation; + } + + /** + * @param boolean whether cookies should be validated. + */ + public function setEnableCookieValidation($value) + { + $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer whether to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME. Defaults to 0. + * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME + */ + public function getCgiFix() + { + return $this->_cgiFix; + } + + /** + * Enable this, if you're using PHP via CGI with php.ini setting "cgi.fix_pathinfo=1" + * and have trouble with friendly URL feature. Enable this only if you really know what you are doing! + * @param integer enable bitwise to use ORIG_PATH_INFO and/or ORIG_SCRIPT_NAME. + * @see THttpRequest::CGIFIX__PATH_INFO, THttpRequest::CGIFIX__SCRIPT_NAME + */ + public function setCgiFix($value) + { + $this->_cgiFix=TPropertyValue::ensureInteger($value); + } + + /** + * @return THttpCookieCollection list of cookies to be sent + */ + public function getCookies() + { + if($this->_cookies===null) + { + $this->_cookies=new THttpCookieCollection; + if($this->getEnableCookieValidation()) + { + $sm=$this->getApplication()->getSecurityManager(); + foreach($_COOKIE as $key=>$value) + { + if(($value=$sm->validateData($value))!==false) + $this->_cookies->add(new THttpCookie($key,$value)); + } + } + else + { + foreach($_COOKIE as $key=>$value) + $this->_cookies->add(new THttpCookie($key,$value)); + } + } + return $this->_cookies; + } + + /** + * @return array list of uploaded files. + */ + public function getUploadedFiles() + { + return $_FILES; + } + + /** + * @return array list of server variables. + */ + public function getServerVariables() + { + return $_SERVER; + } + + /** + * @return array list of environment variables. + */ + public function getEnvironmentVariables() + { + return $_ENV; + } + + /** + * Constructs a URL that can be recognized by PRADO. + * The actual construction work is done by the URL manager module. + * This method may append session information to the generated URL if needed. + * You may provide your own URL manager module by setting {@link setUrlManager UrlManager} + * to provide your own URL scheme. + * + * Note, the constructed URL does not contain the protocol and hostname part. + * You may obtain an absolute URL by prepending the constructed URL with {@link getBaseUrl BaseUrl}. + * @param string service ID + * @param string service parameter + * @param array GET parameters, null if not needed + * @param boolean whether to encode the ampersand in URL, defaults to true. + * @param boolean whether to encode the GET parameters (their names and values), defaults to false. + * @return string URL + * @see TUrlManager::constructUrl + */ + public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true) + { + if ($this->_cookieOnly===null) + $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies'); + $url=$this->getUrlManagerModule()->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); + if(defined('SID') && SID != '' && !$this->_cookieOnly) + return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&':'&')) . SID; + else + return $url; + } + + /** + * Parses the request URL and returns an array of input parameters (excluding GET variables). + * You may override this method to support customized URL format. + * @return array list of input parameters, indexed by parameter names + * @see TUrlManager::parseUrl + */ + protected function parseUrl() + { + return $this->getUrlManagerModule()->parseUrl(); + } + + /** + * Resolves the requested service. + * This method implements a URL-based service resolution. + * A URL in the format of /index.php?sp=serviceID.serviceParameter + * will be resolved with the serviceID and the serviceParameter. + * You may override this method to provide your own way of service resolution. + * @param array list of valid service IDs + * @return string the currently requested service ID, null if no service ID is found + * @see constructUrl + */ + public function resolveRequest($serviceIDs) + { + Prado::trace("Resolving request from ".$_SERVER['REMOTE_ADDR'],'System.Web.THttpRequest'); + $getParams=$this->parseUrl(); + foreach($getParams as $name=>$value) + $_GET[$name]=$value; + $this->_items=array_merge($_GET,$_POST); + $this->_requestResolved=true; + foreach($serviceIDs as $serviceID) + { + if($this->contains($serviceID)) + { + $this->setServiceID($serviceID); + $this->setServiceParameter($this->itemAt($serviceID)); + return $serviceID; + } + } + return null; + } + + /** + * @return boolean true if request is already resolved, false otherwise. + */ + public function getRequestResolved() + { + return $this->_requestResolved; + } + + /** + * @return string requested service ID + */ + public function getServiceID() + { + return $this->_serviceID; + } + + /** + * Sets the requested service ID. + * @param string requested service ID + */ + public function setServiceID($value) + { + $this->_serviceID=$value; + } + + /** + * @return string requested service parameter + */ + public function getServiceParameter() + { + return $this->_serviceParam; + } + + /** + * Sets the requested service parameter. + * @param string requested service parameter + */ + public function setServiceParameter($value) + { + $this->_serviceParam=$value; + } + + //------ The following methods enable THttpRequest to be TMap-like ----- + + /** + * Returns an iterator for traversing the items in the list. + * This method is required by the interface IteratorAggregate. + * @return Iterator an iterator for traversing the items in the list. + */ + public function getIterator() + { + return new TMapIterator($this->_items); + } + + /** + * @return integer the number of items in the request + */ + public function getCount() + { + return count($this->_items); + } + + /** + * Returns the number of items in the request. + * This method is required by Countable interface. + * @return integer number of items in the request. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return array the key list + */ + public function getKeys() + { + return array_keys($this->_items); + } + + /** + * Returns the item with the specified key. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the key + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function itemAt($key) + { + return isset($this->_items[$key]) ? $this->_items[$key] : null; + } + + /** + * Adds an item into the request. + * Note, if the specified key already exists, the old value will be overwritten. + * @param mixed key + * @param mixed value + */ + public function add($key,$value) + { + $this->_items[$key]=$value; + } + + /** + * Removes an item from the request by its key. + * @param mixed the key of the item to be removed + * @return mixed the removed value, null if no such key exists. + * @throws TInvalidOperationException if the item cannot be removed + */ + public function remove($key) + { + if(isset($this->_items[$key]) || array_key_exists($key,$this->_items)) + { + $value=$this->_items[$key]; + unset($this->_items[$key]); + return $value; + } + else + return null; + } + + /** + * Removes all items in the request. + */ + public function clear() + { + foreach(array_keys($this->_items) as $key) + $this->remove($key); + } + + /** + * @param mixed the key + * @return boolean whether the request contains an item with the specified key + */ + public function contains($key) + { + return isset($this->_items[$key]) || array_key_exists($key,$this->_items); + } + + /** + * @return array the list of items in array + */ + public function toArray() + { + return $this->_items; + } + + /** + * Returns whether there is an element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return $this->contains($offset); + } + + /** + * Returns the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + + /** + * Sets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + + /** + * Unsets the element at the specified offset. + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } +} + +/** + * THttpCookieCollection class. + * + * THttpCookieCollection implements a collection class to store cookies. + * Besides using all functionalities from {@link TList}, you can also + * retrieve a cookie by its name using either {@link findCookieByName} or + * simply: + * + * $cookie=$collection[$cookieName]; + * + * + * @author Qiang Xue + * @version $Id: THttpRequest.php 3273 2013-02-13 21:51:21Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class THttpCookieCollection extends TList +{ + /** + * @var mixed owner of this collection + */ + private $_o; + + /** + * Constructor. + * @param mixed owner of this collection. + */ + public function __construct($owner=null) + { + $this->_o=$owner; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added THttpCookie object. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a THttpCookie object. + */ + public function insertAt($index,$item) + { + if($item instanceof THttpCookie) + { + parent::insertAt($index,$item); + if($this->_o instanceof THttpResponse) + $this->_o->addCookie($item); + } + else + throw new TInvalidDataTypeException('httpcookiecollection_httpcookie_required'); + } + + /** + * Removes an item at the specified position. + * This overrides the parent implementation by performing additional + * cleanup work when removing a TCookie object. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $item=parent::removeAt($index); + if($this->_o instanceof THttpResponse) + $this->_o->removeCookie($item); + return $item; + } + + /** + * @param integer|string index of the cookie in the collection or the cookie's name + * @return THttpCookie the cookie found + */ + public function itemAt($index) + { + if(is_integer($index)) + return parent::itemAt($index); + else + return $this->findCookieByName($index); + } + + /** + * Finds the cookie with the specified name. + * @param string the name of the cookie to be looked for + * @return THttpCookie the cookie, null if not found + */ + public function findCookieByName($name) + { + foreach($this as $cookie) + if($cookie->getName()===$name) + return $cookie; + return null; + } +} + +/** + * THttpCookie class. + * + * A THttpCookie instance stores a single cookie, including the cookie name, value, + * domain, path, expire, and secure. + * + * @author Qiang Xue + * @version $Id: THttpRequest.php 3273 2013-02-13 21:51:21Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class THttpCookie extends TComponent +{ + /** + * @var string domain of the cookie + */ + private $_domain=''; + /** + * @var string name of the cookie + */ + private $_name; + /** + * @var string value of the cookie + */ + private $_value=''; + /** + * @var integer expire of the cookie + */ + private $_expire=0; + /** + * @var string path of the cookie + */ + private $_path='/'; + /** + * @var boolean whether cookie should be sent via secure connection + */ + private $_secure=false; + /** + * @var boolean if true the cookie value will be unavailable to JavaScript + */ + private $_httpOnly=false; + + /** + * Constructor. + * @param string name of this cookie + * @param string value of this cookie + */ + public function __construct($name,$value) + { + $this->_name=$name; + $this->_value=$value; + } + + /** + * @return string the domain to associate the cookie with + */ + public function getDomain() + { + return $this->_domain; + } + + /** + * @param string the domain to associate the cookie with + */ + public function setDomain($value) + { + $this->_domain=$value; + } + + /** + * @return integer the time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch. + */ + public function getExpire() + { + return $this->_expire; + } + + /** + * @param integer the time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch. + */ + public function setExpire($value) + { + $this->_expire=TPropertyValue::ensureInteger($value); + } + + /** + * @return boolean if true the cookie value will be unavailable to JavaScript + */ + public function getHttpOnly() + { + return $this->_httpOnly; + } + + /** + * @param boolean $value if true the cookie value will be unavailable to JavaScript + */ + public function setHttpOnly($value) + { + $this->_httpOnly = TPropertyValue::ensureBoolean($value); + } + + /** + * @return string the name of the cookie + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string the name of the cookie + */ + public function setName($value) + { + $this->_name=$value; + } + + /** + * @return string the value of the cookie + */ + public function getValue() + { + return $this->_value; + } + + /** + * @param string the value of the cookie + */ + public function setValue($value) + { + $this->_value=$value; + } + + /** + * @return string the path on the server in which the cookie will be available on, default is '/' + */ + public function getPath() + { + return $this->_path; + } + + /** + * @param string the path on the server in which the cookie will be available on + */ + public function setPath($value) + { + $this->_path=$value; + } + + /** + * @return boolean whether the cookie should only be transmitted over a secure HTTPS connection + */ + public function getSecure() + { + return $this->_secure; + } + + /** + * @param boolean ether the cookie should only be transmitted over a secure HTTPS connection + */ + public function setSecure($value) + { + $this->_secure=TPropertyValue::ensureBoolean($value); + } +} + +/** + * TUri class + * + * TUri represents a URI. Given a URI + * http://joe:whatever@example.com:8080/path/to/script.php?param=value#anchor + * it will be decomposed as follows, + * - scheme: http + * - host: example.com + * - port: 8080 + * - user: joe + * - password: whatever + * - path: /path/to/script.php + * - query: param=value + * - fragment: anchor + * + * @author Qiang Xue + * @version $Id: THttpRequest.php 3273 2013-02-13 21:51:21Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class TUri extends TComponent +{ + /** + * @var array list of default ports for known schemes + */ + private static $_defaultPort=array( + 'ftp'=>21, + 'gopher'=>70, + 'http'=>80, + 'https'=>443, + 'news'=>119, + 'nntp'=>119, + 'wais'=>210, + 'telnet'=>23 + ); + /** + * @var string scheme of the URI + */ + private $_scheme; + /** + * @var string host name of the URI + */ + private $_host; + /** + * @var integer port of the URI + */ + private $_port; + /** + * @var string user of the URI + */ + private $_user; + /** + * @var string password of the URI + */ + private $_pass; + /** + * @var string path of the URI + */ + private $_path; + /** + * @var string query string of the URI + */ + private $_query; + /** + * @var string fragment of the URI + */ + private $_fragment; + /** + * @var string the URI + */ + private $_uri; + + /** + * Constructor. + * Decomposes the specified URI into parts. + * @param string URI to be represented + * @throws TInvalidDataValueException if URI is of bad format + */ + public function __construct($uri) + { + if(($ret=@parse_url($uri))!==false) + { + // decoding??? + $this->_scheme=isset($ret['scheme'])?$ret['scheme']:''; + $this->_host=isset($ret['host'])?$ret['host']:''; + $this->_port=isset($ret['port'])?$ret['port']:''; + $this->_user=isset($ret['user'])?$ret['user']:''; + $this->_pass=isset($ret['pass'])?$ret['pass']:''; + $this->_path=isset($ret['path'])?$ret['path']:''; + $this->_query=isset($ret['query'])?$ret['query']:''; + $this->_fragment=isset($ret['fragment'])?$ret['fragment']:''; + $this->_uri=$uri; + } + else + { + throw new TInvalidDataValueException('uri_format_invalid',$uri); + } + } + + /** + * @return string URI + */ + public function getUri() + { + return $this->_uri; + } + + /** + * @return string scheme of the URI, such as 'http', 'https', 'ftp', etc. + */ + public function getScheme() + { + return $this->_scheme; + } + + /** + * @return string hostname of the URI + */ + public function getHost() + { + return $this->_host; + } + + /** + * @return integer port number of the URI + */ + public function getPort() + { + return $this->_port; + } + + /** + * @return string username of the URI + */ + public function getUser() + { + return $this->_user; + } + + /** + * @return string password of the URI + */ + public function getPassword() + { + return $this->_pass; + } + + /** + * @return string path of the URI + */ + public function getPath() + { + return $this->_path; + } + + /** + * @return string query string of the URI + */ + public function getQuery() + { + return $this->_query; + } + + /** + * @return string fragment of the URI + */ + public function getFragment() + { + return $this->_fragment; + } +} + +/** + * THttpRequestUrlFormat class. + * THttpRequestUrlFormat defines the enumerable type for the possible URL formats + * that can be recognized by {@link THttpRequest}. + * + * The following enumerable values are defined: + * - Get: the URL format is like /path/to/index.php?name1=value1&name2=value2... + * - Path: the URL format is like /path/to/index.php/name1,value1/name2,value2... + * - HiddenPath: the URL format is like /path/to/name1,value1/name2,value2... + * + * @author Qiang Xue + * @version $Id: THttpRequest.php 3273 2013-02-13 21:51:21Z ctrlaltca $ + * @package System.Web + * @since 3.0.4 + */ +class THttpRequestUrlFormat extends TEnumerable +{ + const Get='Get'; + const Path='Path'; + const HiddenPath='HiddenPath'; +} + diff --git a/gui/baculum/framework/Web/THttpResponse.php b/gui/baculum/framework/Web/THttpResponse.php new file mode 100644 index 0000000000..ebcb9cc742 --- /dev/null +++ b/gui/baculum/framework/Web/THttpResponse.php @@ -0,0 +1,719 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THttpResponse.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + */ + +/** + * Includes the THttpResponse adapter. + */ +Prado::using('System.Web.THttpResponseAdapter'); + +/** + * THttpResponse class + * + * THttpResponse implements the mechanism for sending output to client users. + * + * To output a string to client, use {@link write()}. By default, the output is + * buffered until {@link flush()} is called or the application ends. The output in + * the buffer can also be cleaned by {@link clear()}. To disable output buffering, + * set BufferOutput property to false. + * + * To send cookies to client, use {@link getCookies()}. + * To redirect client browser to a new URL, use {@link redirect()}. + * To send a file to client, use {@link writeFile()}. + * + * By default, THttpResponse is registered with {@link TApplication} as the + * response module. It can be accessed via {@link TApplication::getResponse()}. + * + * THttpResponse may be configured in application configuration file as follows + * + * + * + * where {@link getCacheExpire CacheExpire}, {@link getCacheControl CacheControl} + * and {@link getBufferOutput BufferOutput} are optional properties of THttpResponse. + * + * THttpResponse sends charset header if either {@link setCharset() Charset} + * or {@link TGlobalization::setCharset() TGlobalization.Charset} is set. + * + * Since 3.1.2, HTTP status code can be set with the {@link setStatusCode StatusCode} property. + * + * Note: Some HTTP Status codes can require additional header or body information. So, if you use {@link setStatusCode StatusCode} + * in your application, be sure to add theses informations. + * E.g : to make an http authentication : + * + * public function clickAuth ($sender, $param) + * { + * $response=$this->getResponse(); + * $response->setStatusCode(401); + * $response->appendHeader('WWW-Authenticate: Basic realm="Test"'); + * } + * + * + * This event handler will sent the 401 status code (Unauthorized) to the browser, with the WWW-Authenticate header field. This + * will force the browser to ask for a username and a password. + * + * @author Qiang Xue + * @version $Id: THttpResponse.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class THttpResponse extends TModule implements ITextWriter +{ + const DEFAULT_CONTENTTYPE = 'text/html'; + const DEFAULT_CHARSET = 'UTF-8'; + + /** + * @var The differents defined status code by RFC 2616 {@link http://www.faqs.org/rfcs/rfc2616} + */ + private static $HTTP_STATUS_CODES = array( + 100 => 'Continue', 101 => 'Switching Protocols', + 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', + 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', + 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', + 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' + ); + + /** + * @var boolean whether to buffer output + */ + private $_bufferOutput=true; + /** + * @var boolean if the application is initialized + */ + private $_initialized=false; + /** + * @var THttpCookieCollection list of cookies to return + */ + private $_cookies=null; + /** + * @var integer response status code + */ + private $_status=200; + /** + * @var string reason correspond to status code + */ + private $_reason='OK'; + /** + * @var string HTML writer type + */ + private $_htmlWriterType='System.Web.UI.THtmlWriter'; + /** + * @var string content type + */ + private $_contentType=null; + /** + * @var string|boolean character set, e.g. UTF-8 or false if no character set should be send to client + */ + private $_charset=''; + /** + * @var THttpResponseAdapter adapter. + */ + private $_adapter; + /** + * @var boolean whether http response header has been sent + */ + private $_httpHeaderSent; + /** + * @var boolean whether content-type header has been sent + */ + private $_contentTypeHeaderSent; + + /** + * Destructor. + * Flushes any existing content in buffer. + */ + public function __destruct() + { + //if($this->_bufferOutput) + // @ob_end_flush(); + } + + /** + * @param THttpResponseAdapter response adapter + */ + public function setAdapter(THttpResponseAdapter $adapter) + { + $this->_adapter=$adapter; + } + + /** + * @return THttpResponseAdapter response adapter, null if not exist. + */ + public function getAdapter() + { + return $this->_adapter; + } + + /** + * @return boolean true if adapter exists, false otherwise. + */ + public function getHasAdapter() + { + return $this->_adapter!==null; + } + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * It starts output buffer if it is enabled. + * @param TXmlElement module configuration + */ + public function init($config) + { + if($this->_bufferOutput) + ob_start(); + $this->_initialized=true; + $this->getApplication()->setResponse($this); + } + + /** + * @return integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. Defaults to 180. + */ + public function getCacheExpire() + { + return session_cache_expire(); + } + + /** + * @param integer time-to-live for cached session pages in minutes, this has no effect for nocache limiter. + */ + public function setCacheExpire($value) + { + session_cache_expire(TPropertyValue::ensureInteger($value)); + } + + /** + * @return string cache control method to use for session pages + */ + public function getCacheControl() + { + return session_cache_limiter(); + } + + /** + * @param string cache control method to use for session pages. Valid values + * include none/nocache/private/private_no_expire/public + */ + public function setCacheControl($value) + { + session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public'))); + } + + /** + * @return string content type, default is text/html + */ + public function setContentType($type) + { + if ($this->_contentTypeHeaderSent) + throw new Exception('Unable to alter content-type as it has been already sent'); + $this->_contentType = $type; + } + + /** + * @return string current content type + */ + public function getContentType() + { + return $this->_contentType; + } + + /** + * @return string|boolean output charset. + */ + public function getCharset() + { + return $this->_charset; + } + + /** + * @param string|boolean output charset. + */ + public function setCharset($charset) + { + $this->_charset = (strToLower($charset) === 'false') ? false : (string)$charset; + } + + /** + * @return boolean whether to enable output buffer + */ + public function getBufferOutput() + { + return $this->_bufferOutput; + } + + /** + * @param boolean whether to enable output buffer + * @throws TInvalidOperationException if session is started already + */ + public function setBufferOutput($value) + { + if($this->_initialized) + throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); + else + $this->_bufferOutput=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer HTTP status code, defaults to 200 + */ + public function getStatusCode() + { + return $this->_status; + } + + /** + * Set the HTTP status code for the response. + * The code and its reason will be sent to client using the currently requested http protocol version (see {@link THttpRequest::getHttpProtocolVersion}) + * Keep in mind that HTTP/1.0 clients might not understand all status codes from HTTP/1.1 + * + * @param integer HTTP status code + * @param string HTTP status reason, defaults to standard HTTP reasons + */ + public function setStatusCode($status, $reason=null) + { + if ($this->_httpHeaderSent) + throw new Exception('Unable to alter response as HTTP header already sent'); + $status=TPropertyValue::ensureInteger($status); + if(isset(self::$HTTP_STATUS_CODES[$status])) { + $this->_reason=self::$HTTP_STATUS_CODES[$status]; + }else{ + if($reason===null || $reason==='') { + throw new TInvalidDataValueException("response_status_reason_missing"); + } + $reason=TPropertyValue::ensureString($reason); + if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) { + throw new TInvalidDataValueException("response_status_reason_barchars"); + } + $this->_reason=$reason; + } + $this->_status=$status; + } + + /** + * @param string HTTP status reason + */ + public function getStatusReason() { + return $this->_reason; + } + + /** + * @return THttpCookieCollection list of output cookies + */ + public function getCookies() + { + if($this->_cookies===null) + $this->_cookies=new THttpCookieCollection($this); + return $this->_cookies; + } + + /** + * Outputs a string. + * It may not be sent back to user immediately if output buffer is enabled. + * @param string string to be output + */ + public function write($str) + { + // when starting output make sure we send the headers first + if (!$this->_bufferOutput and !$this->_httpHeaderSent) + $this->ensureHeadersSent(); + echo $str; + } + + /** + * Sends a file back to user. + * Make sure not to output anything else after calling this method. + * @param string file name + * @param string content to be set. If null, the content will be read from the server file pointed to by $fileName. + * @param string mime type of the content. + * @param array list of headers to be sent. Each array element represents a header string (e.g. 'Content-Type: text/plain'). + * @param boolean force download of file, even if browser able to display inline. Defaults to 'true'. + * @param string force a specific file name on client side. Defaults to 'null' means auto-detect. + * @param integer size of file or content in bytes if already known. Defaults to 'null' means auto-detect. + * @throws TInvalidDataValueException if the file cannot be found + */ + public function writeFile($fileName,$content=null,$mimeType=null,$headers=null,$forceDownload=true,$clientFileName=null,$fileSize=null) + { + static $defaultMimeTypes=array( + 'css'=>'text/css', + 'gif'=>'image/gif', + 'png'=>'image/png', + 'jpg'=>'image/jpeg', + 'jpeg'=>'image/jpeg', + 'htm'=>'text/html', + 'html'=>'text/html', + 'js'=>'javascript/js', + 'pdf'=>'application/pdf', + 'xls'=>'application/vnd.ms-excel', + ); + + if($mimeType===null) + { + $mimeType='text/plain'; + if(function_exists('mime_content_type')) + $mimeType=mime_content_type($fileName); + else if(($ext=strrchr($fileName,'.'))!==false) + { + $ext=substr($ext,1); + if(isset($defaultMimeTypes[$ext])) + $mimeType=$defaultMimeTypes[$ext]; + } + } + + if($clientFileName===null) + $clientFileName=basename($fileName); + else + $clientFileName=basename($clientFileName); + + if($fileSize===null || $fileSize < 0) + $fileSize = ($content===null?filesize($fileName):strlen($content)); + + $this->sendHttpHeader(); + if(is_array($headers)) + { + foreach($headers as $h) + header($h); + } + else + { + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-Type: $mimeType"); + $this->_contentTypeHeaderSent = true; + } + + header('Content-Length: '.$fileSize); + header("Content-Disposition: " . ($forceDownload ? 'attachment' : 'inline') . "; filename=\"$clientFileName\""); + header('Content-Transfer-Encoding: binary'); + if($content===null) + readfile($fileName); + else + echo $content; + } + + /** + * Redirects the browser to the specified URL. + * The current application will be terminated after this method is invoked. + * @param string URL to be redirected to. If the URL is a relative one, the base URL of + * the current request will be inserted at the beginning. + */ + public function redirect($url) + { + if($this->getHasAdapter()) + $this->_adapter->httpRedirect($url); + else + $this->httpRedirect($url); + } + + /** + * Redirect the browser to another URL and exists the current application. + * This method is used internally. Please use {@link redirect} instead. + * + * @since 3.1.5 + * You can set the set {@link setStatusCode StatusCode} to a value between 300 and 399 before + * calling this function to change the type of redirection. + * If not specified, StatusCode will be 302 (Found) by default + * + * @param string URL to be redirected to. If the URL is a relative one, the base URL of + * the current request will be inserted at the beginning. + */ + public function httpRedirect($url) + { + $this->ensureHeadersSent(); + + if($url[0]==='/') + $url=$this->getRequest()->getBaseUrl().$url; + if ($this->_status >= 300 && $this->_status < 400) + // The status code has been modified to a valid redirection status, send it + header('Location: '.str_replace('&','&',$url), true, $this->_status); + else + header('Location: '.str_replace('&','&',$url)); + + if(!$this->getApplication()->getRequestCompleted()) + $this->getApplication()->onEndRequest(); + + exit(); + } + + /** + * Reloads the current page. + * The effect of this method call is the same as user pressing the + * refresh button on his browser (without post data). + **/ + public function reload() + { + $this->redirect($this->getRequest()->getRequestUri()); + } + + /** + * Flush the response contents and headers. + */ + public function flush($continueBuffering = true) + { + if($this->getHasAdapter()) + $this->_adapter->flushContent($continueBuffering); + else + $this->flushContent($continueBuffering); + } + + /** + * Ensures that HTTP response and content-type headers are sent + */ + public function ensureHeadersSent() + { + $this->ensureHttpHeaderSent(); + $this->ensureContentTypeHeaderSent(); + } + + /** + * Outputs the buffered content, sends content-type and charset header. + * This method is used internally. Please use {@link flush} instead. + * @param boolean whether to continue buffering after flush if buffering was active + */ + public function flushContent($continueBuffering = true) + { + Prado::trace("Flushing output",'System.Web.THttpResponse'); + $this->ensureHeadersSent(); + if($this->_bufferOutput) + { + // avoid forced send of http headers (ob_flush() does that) if there's no output yet + if (ob_get_length()>0) + { + if (!$continueBuffering) + { + $this->_bufferOutput = false; + ob_end_flush(); + } + else + ob_flush(); + flush(); + } + } + else + flush(); + } + + /** + * Ensures that the HTTP header with the status code and status reason are sent + */ + protected function ensureHttpHeaderSent() + { + if (!$this->_httpHeaderSent) + $this->sendHttpHeader(); + } + + /** + * Send the HTTP header with the status code (defaults to 200) and status reason (defaults to OK) + */ + protected function sendHttpHeader() + { + if (($version=$this->getRequest()->getHttpProtocolVersion())==='') + header (' ', true, $this->_status); + else + header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status); + $this->_httpHeaderSent = true; + } + + /** + * Ensures that the HTTP header with the status code and status reason are sent + */ + protected function ensureContentTypeHeaderSent() + { + if (!$this->_contentTypeHeaderSent) + $this->sendContentTypeHeader(); + } + + /** + * Sends content type header with optional charset. + */ + protected function sendContentTypeHeader() + { + $contentType=$this->_contentType===null?self::DEFAULT_CONTENTTYPE:$this->_contentType; + $charset=$this->getCharset(); + if($charset === false) { + $this->appendHeader('Content-Type: '.$contentType); + return; + } + + if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null) + $charset=$globalization->getCharset(); + + if($charset==='') $charset = self::DEFAULT_CHARSET; + $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset); + + $this->_contentTypeHeaderSent = true; + } + + /** + * Returns the content in the output buffer. + * The buffer will NOT be cleared after calling this method. + * Use {@link clear()} is you want to clear the buffer. + * @return string output that is in the buffer. + */ + public function getContents() + { + Prado::trace("Retrieving output",'System.Web.THttpResponse'); + return $this->_bufferOutput?ob_get_contents():''; + } + + /** + * Clears any existing buffered content. + */ + public function clear() + { + if($this->_bufferOutput) + ob_clean(); + Prado::trace("Clearing output",'System.Web.THttpResponse'); + } + + /** + * @param integer|null Either {@link CASE_UPPER} or {@link CASE_LOWER} or as is null (default) + * @return array + */ + public function getHeaders($case=null) + { + $result = array(); + $headers = headers_list(); + foreach($headers as $header) { + $tmp = explode(':', $header); + $key = trim(array_shift($tmp)); + $value = trim(implode(':', $tmp)); + if(isset($result[$key])) + $result[$key] .= ', ' . $value; + else + $result[$key] = $value; + } + + if($case !== null) + return array_change_key_case($result, $case); + + return $result; + } + + /** + * Sends a header. + * @param string header + * @param boolean whether the header should replace a previous similar header, or add a second header of the same type + */ + public function appendHeader($value, $replace=true) + { + Prado::trace("Sending header '$value'",'System.Web.THttpResponse'); + header($value, $replace); + } + + /** + * Writes a log message into error log. + * This method is simple wrapper of PHP function error_log. + * @param string The error message that should be logged + * @param integer where the error should go + * @param string The destination. Its meaning depends on the message parameter as described above + * @param string The extra headers. It's used when the message parameter is set to 1. This message type uses the same internal function as mail() does. + * @see http://us2.php.net/manual/en/function.error-log.php + */ + public function appendLog($message,$messageType=0,$destination='',$extraHeaders='') + { + error_log($message,$messageType,$destination,$extraHeaders); + } + + /** + * Sends a cookie. + * Do not call this method directly. Operate with the result of {@link getCookies} instead. + * @param THttpCookie cook to be sent + */ + public function addCookie($cookie) + { + $request=$this->getRequest(); + if($request->getEnableCookieValidation()) + { + $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); + setcookie( + $cookie->getName(), + $value, + $cookie->getExpire(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + else { + setcookie( + $cookie->getName(), + $cookie->getValue(), + $cookie->getExpire(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + } + + /** + * Deletes a cookie. + * Do not call this method directly. Operate with the result of {@link getCookies} instead. + * @param THttpCookie cook to be deleted + */ + public function removeCookie($cookie) + { + setcookie( + $cookie->getName(), + null, + 0, + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + + /** + * @return string the type of HTML writer to be used, defaults to THtmlWriter + */ + public function getHtmlWriterType() + { + return $this->_htmlWriterType; + } + + /** + * @param string the type of HTML writer to be used, may be the class name or the namespace + */ + public function setHtmlWriterType($value) + { + $this->_htmlWriterType=$value; + } + + /** + * Creates a new instance of HTML writer. + * If the type of the HTML writer is not supplied, {@link getHtmlWriterType HtmlWriterType} will be assumed. + * @param string type of the HTML writer to be created. If null, {@link getHtmlWriterType HtmlWriterType} will be assumed. + */ + public function createHtmlWriter($type=null) + { + if($type===null) + $type=$this->getHtmlWriterType(); + if($this->getHasAdapter()) + return $this->_adapter->createNewHtmlWriter($type, $this); + else + return $this->createNewHtmlWriter($type, $this); + } + + /** + * Create a new html writer instance. + * This method is used internally. Please use {@link createHtmlWriter} instead. + * @param string type of HTML writer to be created. + * @param ITextWriter text writer holding the contents. + */ + public function createNewHtmlWriter($type, $writer) + { + return Prado::createComponent($type, $writer); + } +} + diff --git a/gui/baculum/framework/Web/THttpResponseAdapter.php b/gui/baculum/framework/Web/THttpResponseAdapter.php new file mode 100644 index 0000000000..635752ed8e --- /dev/null +++ b/gui/baculum/framework/Web/THttpResponseAdapter.php @@ -0,0 +1,77 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web + */ + +/** + * THttpResponseAdapter class. + * + * THttpResponseAdapter allows the base http response class to change behavior + * without change the class hierarchy. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Web + * @since 3.0 + */ +class THttpResponseAdapter extends TApplicationComponent +{ + /** + * @var THttpResponse the response object the adapter is attached. + */ + private $_response; + + /** + * Constructor. Attach a response to be adapted. + * @param THttpResponse the response object the adapter is to attach to. + */ + public function __construct($response) + { + $this->_response=$response; + } + + /** + * @return THttpResponse the response object adapted. + */ + public function getResponse() + { + return $this->_response; + } + + /** + * This method is invoked when the response flushes the content and headers. + * Default implementation calls the attached response flushContent method. + */ + public function flushContent() + { + $this->_response->flushContent(); + } + + /** + * This method is invoked when the response is to redirect to another page. + * @param string new url to redirect to. + */ + public function httpRedirect($url) + { + $this->_response->httpRedirect($url); + } + + /** + * This method is invoked when a new HtmlWriter needs to be created. + * Default implementation calls the attached response createNewHtmlWriter method. + * @param string type of the HTML writer to be created. + * @param ITextWriter the writer responsible for holding the content. + */ + public function createNewHtmlWriter($type, $writer) + { + return $this->_response->createNewHtmlWriter($type,$writer); + } +} + diff --git a/gui/baculum/framework/Web/THttpSession.php b/gui/baculum/framework/Web/THttpSession.php new file mode 100644 index 0000000000..f32b7063d0 --- /dev/null +++ b/gui/baculum/framework/Web/THttpSession.php @@ -0,0 +1,730 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THttpSession.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + */ + +/** + * THttpSession class + * + * THttpSession provides session-level data management and the related configurations. + * To start the session, call {@link open}; to complete and send out session data, call {@link close}; + * to destroy the session, call {@link destroy}. If AutoStart is true, then the session + * will be started once the session module is loaded and initialized. + * + * To access data stored in session, use THttpSession like an associative array. For example, + * + * $session=new THttpSession; + * $session->open(); + * $value1=$session['name1']; // get session variable 'name1' + * $value2=$session['name2']; // get session variable 'name2' + * foreach($session as $name=>$value) // traverse all session variables + * $session['name3']=$value3; // set session variable 'name3' + * + * + * The following configurations are available for session: + * {@link setAutoStart AutoStart}, {@link setCookieMode CookieMode}, + * {@link setSavePath SavePath}, + * {@link setUseCustomStorage UseCustomStorage}, {@link setGCProbability GCProbability}, + * {@link setTimeout Timeout}. + * See the corresponding setter and getter documentation for more information. + * Note, these properties must be set before the session is started. + * + * THttpSession can be inherited with customized session storage method. + * Override {@link _open}, {@link _close}, {@link _read}, {@link _write}, {@link _destroy} and {@link _gc} + * and set {@link setUseCustomStorage UseCustomStorage} to true. + * Then, the session data will be stored using the above methods. + * + * By default, THttpSession is registered with {@link TApplication} as the + * request module. It can be accessed via {@link TApplication::getSession()}. + * + * THttpSession may be configured in application configuration file as follows, + * + * + * + * where {@link getSessionName SessionName}, {@link getSavePath SavePath}, + * {@link getCookieMode CookieMode}, {@link getUseCustomStorage + * UseCustomStorage}, {@link getAutoStart AutoStart}, {@link getGCProbability + * GCProbability}, {@link getUseTransparentSessionID UseTransparentSessionID} + * and {@link getTimeout TimeOut} are configurable properties of THttpSession. + * + * @author Qiang Xue + * @version $Id: THttpSession.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class THttpSession extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule +{ + /** + * @var boolean whether this module has been initialized + */ + private $_initialized=false; + /** + * @var boolean whether the session has started + */ + private $_started=false; + /** + * @var boolean whether the session should be started when the module is initialized + */ + private $_autoStart=false; + /** + * @var THttpCookie cookie to be used to store session ID and other data + */ + private $_cookie=null; + /** + * @var string module id + */ + private $_id; + /** + * @var boolean + */ + private $_customStorage=false; + + /** + * @return string id of this module + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of this module + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * Initializes the module. + * This method is required by IModule. + * If AutoStart is true, the session will be started. + * @param TXmlElement module configuration + */ + public function init($config) + { + if($this->_autoStart) + $this->open(); + $this->_initialized=true; + $this->getApplication()->setSession($this); + register_shutdown_function(array($this, "close")); + } + + /** + * Starts the session if it has not started yet. + */ + public function open() + { + if(!$this->_started) + { + if($this->_customStorage) + session_set_save_handler(array($this,'_open'),array($this,'_close'),array($this,'_read'),array($this,'_write'),array($this,'_destroy'),array($this,'_gc')); + if($this->_cookie!==null) + session_set_cookie_params($this->_cookie->getExpire(),$this->_cookie->getPath(),$this->_cookie->getDomain(),$this->_cookie->getSecure()); + if(ini_get('session.auto_start')!=='1') + session_start(); + $this->_started=true; + } + } + + /** + * Ends the current session and store session data. + */ + public function close() + { + if($this->_started) + { + session_write_close(); + $this->_started=false; + } + } + + /** + * Destroys all data registered to a session. + */ + public function destroy() + { + if($this->_started) + { + session_destroy(); + $this->_started=false; + } + } + + /** + * Update the current session id with a newly generated one + * + * @param boolean $deleteOld Whether to delete the old associated session or not. + * @return string old session id + * @link http://php.net/manual/en/function.session-regenerate-id.php + */ + public function regenerate($deleteOld=false) + { + $old = $this->getSessionID(); + session_regenerate_id($deleteOld); + return $old; + } + + /** + * @return boolean whether the session has started + */ + public function getIsStarted() + { + return $this->_started; + } + + /** + * @return string the current session ID + */ + public function getSessionID() + { + return session_id(); + } + + /** + * @param string the session ID for the current session + * @throws TInvalidOperationException if session is started already + */ + public function setSessionID($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_sessionid_unchangeable'); + else + session_id($value); + } + + /** + * @return string the current session name + */ + public function getSessionName() + { + return session_name(); + } + + /** + * @param string the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID + * @throws TInvalidOperationException if session is started already + */ + public function setSessionName($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_sessionname_unchangeable'); + else if(ctype_alnum($value)) + session_name($value); + else + throw new TInvalidDataValueException('httpsession_sessionname_invalid',$value); + } + + /** + * @return string the current session save path, defaults to '/tmp'. + */ + public function getSavePath() + { + return session_save_path(); + } + + /** + * @param string the current session save path + * @throws TInvalidOperationException if session is started already + */ + public function setSavePath($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_savepath_unchangeable'); + else if(is_dir($value)) + session_save_path($value); + else + throw new TInvalidDataValueException('httpsession_savepath_invalid',$value); + } + + /** + * @return boolean whether to use user-specified handlers to store session data. Defaults to false. + */ + public function getUseCustomStorage() + { + return $this->_customStorage; + } + + /** + * @param boolean whether to use user-specified handlers to store session data. + * If true, make sure the methods {@link _open}, {@link _close}, {@link _read}, + * {@link _write}, {@link _destroy}, and {@link _gc} are overridden in child + * class, because they will be used as the callback handlers. + */ + public function setUseCustomStorage($value) + { + $this->_customStorage=TPropertyValue::ensureBoolean($value); + } + + /** + * @return THttpCookie cookie that will be used to store session ID + */ + public function getCookie() + { + if($this->_cookie===null) + $this->_cookie=new THttpCookie($this->getSessionName(),$this->getSessionID()); + return $this->_cookie; + } + + /** + * @return THttpSessionCookieMode how to use cookie to store session ID. Defaults to THttpSessionCookieMode::Allow. + */ + public function getCookieMode() + { + if(ini_get('session.use_cookies')==='0') + return THttpSessionCookieMode::None; + else if(ini_get('session.use_only_cookies')==='0') + return THttpSessionCookieMode::Allow; + else + return THttpSessionCookieMode::Only; + } + + /** + * @param THttpSessionCookieMode how to use cookie to store session ID + * @throws TInvalidOperationException if session is started already + */ + public function setCookieMode($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_cookiemode_unchangeable'); + else + { + $value=TPropertyValue::ensureEnum($value,'THttpSessionCookieMode'); + if($value===THttpSessionCookieMode::None) + ini_set('session.use_cookies','0'); + else if($value===THttpSessionCookieMode::Allow) + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','0'); + } + else + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','1'); + ini_set('session.use_trans_sid', 0); + } + } + } + + /** + * @return boolean whether the session should be automatically started when the session module is initialized, defaults to false. + */ + public function getAutoStart() + { + return $this->_autoStart; + } + + /** + * @param boolean whether the session should be automatically started when the session module is initialized, defaults to false. + * @throws TInvalidOperationException if session is started already + */ + public function setAutoStart($value) + { + if($this->_initialized) + throw new TInvalidOperationException('httpsession_autostart_unchangeable'); + else + $this->_autoStart=TPropertyValue::ensureBoolean($value); + } + + /** + * @return integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. + */ + public function getGCProbability() + { + return TPropertyValue::ensureInteger(ini_get('session.gc_probability')); + } + + /** + * @param integer the probability (percentage) that the gc (garbage collection) process is started on every session initialization. + * @throws TInvalidOperationException if session is started already + * @throws TInvalidDataValueException if the value is beyond [0,100]. + */ + public function setGCProbability($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_gcprobability_unchangeable'); + else + { + $value=TPropertyValue::ensureInteger($value); + if($value>=0 && $value<=100) + { + ini_set('session.gc_probability',$value); + ini_set('session.gc_divisor','100'); + } + else + throw new TInvalidDataValueException('httpsession_gcprobability_invalid',$value); + } + } + + /** + * @return boolean whether transparent sid support is enabled or not, defaults to false. + */ + public function getUseTransparentSessionID() + { + return ini_get('session.use_trans_sid')==='1'; + } + + /** + * @param boolean whether transparent sid support is enabled or not. + */ + public function setUseTransparentSessionID($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_transid_unchangeable'); + else + { + $value=TPropertyValue::ensureBoolean($value); + if ($value && $this->getCookieMode()==THttpSessionCookieMode::Only) + throw new TInvalidOperationException('httpsession_transid_cookieonly'); + ini_set('session.use_trans_sid',$value?'1':'0'); + } + } + + /** + * @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. + */ + public function getTimeout() + { + return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime')); + } + + /** + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up + * @throws TInvalidOperationException if session is started already + */ + public function setTimeout($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable'); + else + ini_set('session.gc_maxlifetime',$value); + } + + /** + * Session open handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session save path + * @param string session name + * @return boolean whether session is opened successfully + */ + public function _open($savePath,$sessionName) + { + return true; + } + + /** + * Session close handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @return boolean whether session is closed successfully + */ + public function _close() + { + return true; + } + + /** + * Session read handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @return string the session data + */ + public function _read($id) + { + return ''; + } + + /** + * Session write handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @param string session data + * @return boolean whether session write is successful + */ + public function _write($id,$data) + { + return true; + } + + /** + * Session destroy handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param string session ID + * @return boolean whether session is destroyed successfully + */ + public function _destroy($id) + { + return true; + } + + /** + * Session GC (garbage collection) handler. + * This method should be overridden if {@link setUseCustomStorage UseCustomStorage} is set true. + * @param integer the number of seconds after which data will be seen as 'garbage' and cleaned up. + * @return boolean whether session is GCed successfully + */ + public function _gc($maxLifetime) + { + return true; + } + + //------ The following methods enable THttpSession to be TMap-like ----- + + /** + * Returns an iterator for traversing the session variables. + * This method is required by the interface IteratorAggregate. + * @return TSessionIterator an iterator for traversing the session variables. + */ + public function getIterator() + { + return new TSessionIterator; + } + + /** + * @return integer the number of session variables + */ + public function getCount() + { + return count($_SESSION); + } + + /** + * Returns the number of items in the session. + * This method is required by Countable interface. + * @return integer number of items in the session. + */ + public function count() + { + return $this->getCount(); + } + + /** + * @return array the list of session variable names + */ + public function getKeys() + { + return array_keys($_SESSION); + } + + /** + * Returns the session variable value with the session variable name. + * This method is exactly the same as {@link offsetGet}. + * @param mixed the session variable name + * @return mixed the session variable value, null if no such variable exists + */ + public function itemAt($key) + { + return isset($_SESSION[$key]) ? $_SESSION[$key] : null; + } + + /** + * Adds a session variable. + * Note, if the specified name already exists, the old value will be removed first. + * @param mixed session variable name + * @param mixed session variable value + */ + public function add($key,$value) + { + $_SESSION[$key]=$value; + } + + /** + * Removes a session variable. + * @param mixed the name of the session variable to be removed + * @return mixed the removed value, null if no such session variable. + */ + public function remove($key) + { + if(isset($_SESSION[$key])) + { + $value=$_SESSION[$key]; + unset($_SESSION[$key]); + return $value; + } + else + return null; + } + + /** + * Removes all session variables + */ + public function clear() + { + foreach(array_keys($_SESSION) as $key) + unset($_SESSION[$key]); + } + + /** + * @param mixed session variable name + * @return boolean whether there is the named session variable + */ + public function contains($key) + { + return isset($_SESSION[$key]); + } + + /** + * @return array the list of all session variables in array + */ + public function toArray() + { + return $_SESSION; + } + + /** + * This method is required by the interface ArrayAccess. + * @param mixed the offset to check on + * @return boolean + */ + public function offsetExists($offset) + { + return isset($_SESSION[$offset]); + } + + /** + * This method is required by the interface ArrayAccess. + * @param integer the offset to retrieve element. + * @return mixed the element at the offset, null if no element is found at the offset + */ + public function offsetGet($offset) + { + return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; + } + + /** + * This method is required by the interface ArrayAccess. + * @param integer the offset to set element + * @param mixed the element value + */ + public function offsetSet($offset,$item) + { + $_SESSION[$offset]=$item; + } + + /** + * This method is required by the interface ArrayAccess. + * @param mixed the offset to unset element + */ + public function offsetUnset($offset) + { + unset($_SESSION[$offset]); + } +} + +/** + * TSessionIterator class + * + * TSessionIterator implements Iterator interface. + * + * TSessionIterator is used by THttpSession. It allows THttpSession to return a new iterator + * for traversing the session variables. + * + * @author Qiang Xue + * @version $Id: THttpSession.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class TSessionIterator implements Iterator +{ + /** + * @var array list of keys in the map + */ + private $_keys; + /** + * @var mixed current key + */ + private $_key; + + /** + * Constructor. + * @param array the data to be iterated through + */ + public function __construct() + { + $this->_keys=array_keys($_SESSION); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_key=reset($this->_keys); + } + + /** + * Returns the key of the current array element. + * This method is required by the interface Iterator. + * @return mixed the key of the current array element + */ + public function key() + { + return $this->_key; + } + + /** + * Returns the current array element. + * This method is required by the interface Iterator. + * @return mixed the current array element + */ + public function current() + { + return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null; + } + + /** + * Moves the internal pointer to the next array element. + * This method is required by the interface Iterator. + */ + public function next() + { + do + { + $this->_key=next($this->_keys); + } + while(!isset($_SESSION[$this->_key]) && $this->_key!==false); + } + + /** + * Returns whether there is an element at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_key!==false; + } +} + + +/** + * THttpSessionCookieMode class. + * THttpSessionCookieMode defines the enumerable type for the possible methods of + * using cookies to store session ID. + * + * The following enumerable values are defined: + * - None: not using cookie. + * - Allow: using cookie. + * - Only: using cookie only. + * + * @author Qiang Xue + * @version $Id: THttpSession.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.0.4 + */ +class THttpSessionCookieMode extends TEnumerable +{ + const None='None'; + const Allow='Allow'; + const Only='Only'; +} + diff --git a/gui/baculum/framework/Web/THttpUtility.php b/gui/baculum/framework/Web/THttpUtility.php new file mode 100644 index 0000000000..128dee550a --- /dev/null +++ b/gui/baculum/framework/Web/THttpUtility.php @@ -0,0 +1,62 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THttpUtility.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + */ + +/** + * THttpUtility class + * + * @author Qiang Xue + * @version $Id: THttpUtility.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web + * @since 3.0 + */ +class THttpUtility +{ + private static $_encodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + private static $_decodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + private static $_stripTable=array('<'=>'','>'=>'','"'=>''); + + /** + * HTML-encodes a string. + * This method translates the following characters to their corresponding + * HTML entities: <, >, " + * Note, unlike {@link htmlspecialchars}, & is not translated. + * @param string string to be encoded + * @return string encoded string + */ + public static function htmlEncode($s) + { + return strtr($s,self::$_encodeTable); + } + + /** + * HTML-decodes a string. + * It is the inverse of {@link htmlEncode}. + * @param string string to be decoded + * @return string decoded string + */ + public static function htmlDecode($s) + { + return strtr($s,self::$_decodeTable); + } + + /** + * This method strips the following characters from a string: + * HTML entities: <, >, " + * @param string string to be encoded + * @return string encoded string + */ + public static function htmlStrip($s) + { + return strtr($s,self::$_stripTable); + } +} + diff --git a/gui/baculum/framework/Web/TUrlManager.php b/gui/baculum/framework/Web/TUrlManager.php new file mode 100644 index 0000000000..75abdcf10a --- /dev/null +++ b/gui/baculum/framework/Web/TUrlManager.php @@ -0,0 +1,161 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id $ + * @package System.Web + */ + +/** + * TUrlManager class + * + * TUrlManager is the base class for managing URLs that can be + * recognized by PRADO applications. It provides the default implementation + * for parsing and constructing URLs. + * + * Derived classes may override {@link constructUrl} and {@link parseUrl} + * to provide customized URL schemes. + * + * By default, {@link THttpRequest} uses TUrlManager as its URL manager. + * If you want to use your customized URL manager, load your manager class + * as an application module and set {@link THttpRequest::setUrlManager THttpRequest.UrlManager} + * with the ID of your URL manager module. + * + * @author Qiang Xue + * @version $Id $ + * @package System.Web + * @since 3.0.6 + */ +class TUrlManager extends TModule +{ + /** + * Constructs a URL that can be recognized by PRADO. + * + * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. + * Override this method if you want to provide your own way of URL formatting. + * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. + * + * The URL is constructed as the following format: + * /entryscript.php?serviceID=serviceParameter&get1=value1&... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', + * the following format is used instead: + * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'HiddenPath', + * then entryscript.php will be hidden and the following format is used instead: + * /serviceID/serviceParameter/get1,value1/get2,value2... + * In order to use the 'HiddenPath' format you need proper url rewrite configuration; + * here's an example for Apache's .htaccess: + * + * Options +FollowSymLinks + * RewriteEngine On + * RewriteCond %{REQUEST_FILENAME} !-d + * RewriteCond %{REQUEST_FILENAME} !-f + * RewriteRule ^(.*)$ index.php/$1 [L] + *
    + * @param string service ID + * @param string service parameter + * @param array GET parameters, null if not provided + * @param boolean whether to encode the ampersand in URL + * @param boolean whether to encode the GET parameters (their names and values) + * @return string URL + * @see parseUrl + */ + public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) + { + $url=$serviceID.'='.urlencode($serviceParam); + $amp=$encodeAmpersand?'&':'&'; + $request=$this->getRequest(); + if(is_array($getItems) || $getItems instanceof Traversable) + { + if($encodeGetItems) + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + $name=urlencode($name.'[]'); + foreach($value as $v) + $url.=$amp.$name.'='.urlencode($v); + } + else + $url.=$amp.urlencode($name).'='.urlencode($value); + } + } + else + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + foreach($value as $v) + $url.=$amp.$name.'[]='.$v; + } + else + $url.=$amp.$name.'='.$value; + } + } + } + + switch($request->getUrlFormat()) + { + case THttpRequestUrlFormat::Path: + return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); + case THttpRequestUrlFormat::HiddenPath: + return rtrim(dirname($request->getApplicationUrl()), '/').'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); + default: + return $request->getApplicationUrl().'?'.$url; + } + } + + /** + * Parses the request URL and returns an array of input parameters. + * This method is automatically invoked by {@link THttpRequest} when + * handling a user request. + * + * In general, this method should parse the path info part of the requesting URL + * and generate an array of name-value pairs according to some scheme. + * The current implementation deals with both 'Get' and 'Path' URL formats. + * + * You may override this method to support customized URL format. + * @return array list of input parameters, indexed by parameter names + * @see constructUrl + */ + public function parseUrl() + { + $request=$this->getRequest(); + $pathInfo=trim($request->getPathInfo(),'/'); + if(($request->getUrlFormat()===THttpRequestUrlFormat::Path || + $request->getUrlFormat()===THttpRequestUrlFormat::HiddenPath) && + $pathInfo!=='') + { + $separator=$request->getUrlParamSeparator(); + $paths=explode('/',$pathInfo); + $getVariables=array(); + foreach($paths as $path) + { + if(($path=trim($path))!=='') + { + if(($pos=strpos($path,$separator))!==false) + { + $name=substr($path,0,$pos); + $value=substr($path,$pos+1); + if(($pos=strpos($name,'[]'))!==false) + $getVariables[substr($name,0,$pos)][]=$value; + else + $getVariables[$name]=$value; + } + else + $getVariables[$path]=''; + } + } + return $getVariables; + } + else + return array(); + } +} + diff --git a/gui/baculum/framework/Web/TUrlMapping.php b/gui/baculum/framework/Web/TUrlMapping.php new file mode 100644 index 0000000000..929a873e09 --- /dev/null +++ b/gui/baculum/framework/Web/TUrlMapping.php @@ -0,0 +1,1041 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TUrlMapping.php 3290 2013-05-06 08:32:15Z ctrlaltca $ + * @package System.Web + */ + +Prado::using('System.Web.TUrlManager'); +Prado::using('System.Collections.TAttributeCollection'); + +/** + * TUrlMapping Class + * + * The TUrlMapping module allows PRADO to construct and recognize URLs + * based on specific patterns. + * + * TUrlMapping consists of a list of URL patterns which are used to match + * against the currently requested URL. The first matching pattern will then + * be used to decompose the URL into request parameters (accessible through + * $this->Request['paramname']). + * + * The patterns can also be used to construct customized URLs. In this case, + * the parameters in an applied pattern will be replaced with the corresponding + * GET variable values. + * + * Since it is derived from {@link TUrlManager}, it should be configured globally + * in the application configuration like the following, + * + * + * + * + * + * + * + * + * + * In the above, each <url> element specifies a URL pattern represented + * as a {@link TUrlMappingPattern} internally. You may create your own pattern classes + * by extending {@link TUrlMappingPattern} and specifying the <class> attribute + * in the element. + * + * The patterns can be also be specified in an external file using the {@link setConfigFile ConfigFile} property. + * + * The URL mapping are evaluated in order, only the first mapping that matches + * the URL will be used. Cascaded mapping can be achieved by placing the URL mappings + * in particular order. For example, placing the most specific mappings first. + * + * Only the PATH_INFO part of the URL is used to match the available patterns. The matching + * is strict in the sense that the whole pattern must match the whole PATH_INFO of the URL. + * + * From PRADO v3.1.1, TUrlMapping also provides support for constructing URLs according to + * the specified pattern. You may enable this functionality by setting {@link setEnableCustomUrl EnableCustomUrl} to true. + * When you call THttpRequest::constructUrl() (or via TPageService::constructUrl()), + * TUrlMapping will examine the available URL mapping patterns using their {@link TUrlMappingPattern::getServiceParameter ServiceParameter} + * and {@link TUrlMappingPattern::getPattern Pattern} properties. A pattern is applied if its + * {@link TUrlMappingPattern::getServiceParameter ServiceParameter} matches the service parameter passed + * to constructUrl() and every parameter in the {@link getPattern Pattern} is found + * in the GET variables. + * + * @author Wei Zhuo + * @version $Id: TUrlMapping.php 3290 2013-05-06 08:32:15Z ctrlaltca $ + * @package System.Web + * @since 3.0.5 + */ +class TUrlMapping extends TUrlManager +{ + /** + * @var TUrlMappingPattern[] list of patterns. + */ + protected $_patterns=array(); + /** + * @var TUrlMappingPattern matched pattern. + */ + private $_matched; + /** + * @var string external configuration file + */ + private $_configFile=null; + /** + * @var boolean whether to enable custom contructUrl + */ + private $_customUrl=false; + /** + * @var array rules for constructing URLs + */ + protected $_constructRules=array(); + + private $_urlPrefix=''; + + private $_defaultMappingClass='TUrlMappingPattern'; + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param mixed configuration for this module, can be null + * @throws TConfigurationException if module is configured in the global scope. + */ + public function init($config) + { + parent::init($config); + if($this->getRequest()->getRequestResolved()) + throw new TConfigurationException('urlmapping_global_required'); + if($this->_configFile!==null) + $this->loadConfigFile(); + $this->loadUrlMappings($config); + if($this->_urlPrefix==='') + { + $request=$this->getRequest(); + if($request->getUrlFormat()===THttpRequestUrlFormat::HiddenPath) + { + $this->_urlPrefix=dirname($request->getApplicationUrl()); + } else { + $this->_urlPrefix=$request->getApplicationUrl(); + } + } + $this->_urlPrefix=rtrim($this->_urlPrefix,'/'); + } + + /** + * Initialize the module from configuration file. + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + protected function loadConfigFile() + { + if(is_file($this->_configFile)) + { + if($this->getApplication()->getConfigurationType()==TApplication::CONFIG_TYPE_PHP) + { + $config = include $this->_configFile; + $this->loadUrlMappings($dom); + } + else + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadUrlMappings($dom); + } + } + else + throw new TConfigurationException('urlmapping_configfile_inexistent',$this->_configFile); + } + + /** + * Returns a value indicating whether to enable custom constructUrl. + * If true, constructUrl() will make use of the URL mapping rules to + * construct valid URLs. + * @return boolean whether to enable custom constructUrl. Defaults to false. + * @since 3.1.1 + */ + public function getEnableCustomUrl() + { + return $this->_customUrl; + } + + /** + * Sets a value indicating whether to enable custom constructUrl. + * If true, constructUrl() will make use of the URL mapping rules to + * construct valid URLs. + * @param boolean whether to enable custom constructUrl. + * @since 3.1.1 + */ + public function setEnableCustomUrl($value) + { + $this->_customUrl=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string the part that will be prefixed to the constructed URLs. Defaults to the requested script path (e.g. /path/to/index.php for a URL http://hostname/path/to/index.php) + * @since 3.1.1 + */ + public function getUrlPrefix() + { + return $this->_urlPrefix; + } + + /** + * @param string the part that will be prefixed to the constructed URLs. This is used by constructUrl() when EnableCustomUrl is set true. + * @see getUrlPrefix + * @since 3.1.1 + */ + public function setUrlPrefix($value) + { + $this->_urlPrefix=$value; + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,$this->getApplication()->getConfigurationFileExt()))===null) + throw new TConfigurationException('urlmapping_configfile_invalid',$value); + } + + /** + * @return string the default class of URL mapping patterns. Defaults to TUrlMappingPattern. + * @since 3.1.1 + */ + public function getDefaultMappingClass() + { + return $this->_defaultMappingClass; + } + + /** + * Sets the default class of URL mapping patterns. + * When a URL matching pattern does not specify "class" attribute, it will default to the class + * specified by this property. You may use either a class name or a namespace format of class (if the class needs to be included first.) + * @param string the default class of URL mapping patterns. + * @since 3.1.1 + */ + public function setDefaultMappingClass($value) + { + $this->_defaultMappingClass=$value; + } + + /** + * Load and configure each url mapping pattern. + * @param mixed configuration node + * @throws TConfigurationException if specific pattern class is invalid + */ + protected function loadUrlMappings($config) + { + $defaultClass = $this->getDefaultMappingClass(); + + if(is_array($config)) + { + if(isset($config['urls']) && is_array($config['urls'])) + { + foreach($config['urls'] as $url) + { + $class=null; + if(!isset($url['class'])) + $class=$defaultClass; + $properties = isset($url['properties'])?$url['properties']:array(); + $this->buildUrlMapping($class,$properties,$url); + } + } + } + else + { + foreach($config->getElementsByTagName('url') as $url) + { + $properties=$url->getAttributes(); + if(($class=$properties->remove('class'))===null) + $class=$defaultClass; + $this->buildUrlMapping($class,$properties,$url); + } + } + } + + private function buildUrlMapping($class, $properties, $url) + { + $pattern=Prado::createComponent($class,$this); + if(!($pattern instanceof TUrlMappingPattern)) + throw new TConfigurationException('urlmapping_urlmappingpattern_required'); + foreach($properties as $name=>$value) + $pattern->setSubproperty($name,$value); + + if($url instanceof TXmlElement) { + $text = $url -> getValue(); + if($text) { + $text = preg_replace('/(\s+)/S', '', $text); + if(($regExp = $pattern->getRegularExpression()) !== '') + trigger_error(sPrintF('%s.RegularExpression property value "%s" for ServiceID="%s" and ServiceParameter="%s" was replaced by node value "%s"', + get_class($pattern), + $regExp, + $pattern->getServiceID(), + $pattern->getServiceParameter(), + $text), + E_USER_NOTICE); + $pattern->setRegularExpression($text); + } + } + + $this->_patterns[]=$pattern; + $pattern->init($url); + + $key=$pattern->getServiceID().':'.$pattern->getServiceParameter(); + $this->_constructRules[$key][]=$pattern; + } + + /** + * Parses the request URL and returns an array of input parameters. + * This method overrides the parent implementation. + * The input parameters do not include GET and POST variables. + * This method uses the request URL path to find the first matching pattern. If found + * the matched pattern parameters are used to return as the input parameters. + * @return array list of input parameters + */ + public function parseUrl() + { + $request=$this->getRequest(); + foreach($this->_patterns as $pattern) + { + $matches=$pattern->getPatternMatches($request); + if(count($matches)>0) + { + $this->_matched=$pattern; + $params=array(); + foreach($matches as $key=>$value) + { + if(is_string($key)) + $params[$key]=$value; + } + if (!$pattern->getIsWildCardPattern()) + $params[$pattern->getServiceID()]=$pattern->getServiceParameter(); + return $params; + } + } + return parent::parseUrl(); + } + + /** + * Constructs a URL that can be recognized by PRADO. + * + * This method provides the actual implementation used by {@link THttpRequest::constructUrl}. + * Override this method if you want to provide your own way of URL formatting. + * If you do so, you may also need to override {@link parseUrl} so that the URL can be properly parsed. + * + * The URL is constructed as the following format: + * /entryscript.php?serviceID=serviceParameter&get1=value1&... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'Path', + * the following format is used instead: + * /entryscript.php/serviceID/serviceParameter/get1,value1/get2,value2... + * If {@link THttpRequest::setUrlFormat THttpRequest.UrlFormat} is 'HiddenPath', + * the following format is used instead: + * /serviceID/serviceParameter/get1,value1/get2,value2... + * @param string service ID + * @param string service parameter + * @param array GET parameters, null if not provided + * @param boolean whether to encode the ampersand in URL + * @param boolean whether to encode the GET parameters (their names and values) + * @return string URL + * @see parseUrl + * @since 3.1.1 + */ + public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) + { + if($this->_customUrl) + { + if(!(is_array($getItems) || ($getItems instanceof Traversable))) + $getItems=array(); + $key=$serviceID.':'.$serviceParam; + $wildCardKey = ($pos=strrpos($serviceParam,'.'))!==false ? + $serviceID.':'.substr($serviceParam,0,$pos).'.*' : $serviceID.':*'; + if(isset($this->_constructRules[$key])) + { + foreach($this->_constructRules[$key] as $rule) + { + if($rule->supportCustomUrl($getItems)) + return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); + } + } + elseif(isset($this->_constructRules[$wildCardKey])) + { + foreach($this->_constructRules[$wildCardKey] as $rule) + { + if($rule->supportCustomUrl($getItems)) + { + $getItems['*']= $pos ? substr($serviceParam,$pos+1) : $serviceParam; + return $rule->constructUrl($getItems,$encodeAmpersand,$encodeGetItems); + } + } + } + } + return parent::constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); + } + + /** + * @return TUrlMappingPattern the matched pattern, null if not found. + */ + public function getMatchingPattern() + { + return $this->_matched; + } +} + +/** + * TUrlMappingPattern class. + * + * TUrlMappingPattern represents a pattern used to parse and construct URLs. + * If the currently requested URL matches the pattern, it will alter + * the THttpRequest parameters. If a constructUrl() call matches the pattern + * parameters, the pattern will generate a valid URL. In both case, only the PATH_INFO + * part of a URL is parsed/constructed using the pattern. + * + * To specify the pattern, set the {@link setPattern Pattern} property. + * {@link setPattern Pattern} takes a string expression with + * parameter names enclosed between a left brace '{' and a right brace '}'. + * The patterns for each parameter can be set using {@link getParameters Parameters} + * attribute collection. For example + * + * + * + * + * In the above example, the pattern contains 3 parameters named "year", + * "month" and "day". The pattern for these parameters are, respectively, + * "\d{4}" (4 digits), "\d{2}" (2 digits) and "\d+" (1 or more digits). + * Essentially, the Parameters attribute name and values are used + * as substrings in replacing the placeholders in the Pattern string + * to form a complete regular expression string. + * + * For more complicated patterns, one may specify the pattern using a regular expression + * by {@link setRegularExpression RegularExpression}. For example, the above pattern + * is equivalent to the following regular expression-based pattern: + * + * #^articles/(?P\d{4})/(?P\d{2})\/(?P\d+)$#u + * + * The above regular expression used the "named group" feature available in PHP. + * If you intended to use the RegularExpression property or + * regular expressions in CDATA sections, notice that you need to escape the slash, + * if you are using the slash as regular expressions delimiter. + * + * Thus, only an url that matches the pattern will be valid. For example, + * a URL http://example.com/index.php/articles/2006/07/21 will match the above pattern, + * while http://example.com/index.php/articles/2006/07/hello will not + * since the "day" parameter pattern is not satisfied. + * + * The parameter values are available through the THttpRequest instance (e.g. + * $this->Request['year']). + * + * The {@link setServiceParameter ServiceParameter} and {@link setServiceID ServiceID} + * (the default ID is 'page') set the service parameter and service id respectively. + * + * Since 3.1.4 you can also use simplyfied wildcard patterns to match multiple + * ServiceParameters with a single rule. The pattern must contain the placeholder + * {*} for the ServiceParameter. For example + * + * + * + * This rule will match an URL like http://example.com/index.php/admin/edituser + * and resolve it to the page Application.pages.admin.edituser. The wildcard matching + * is non-recursive. That means you have to add a rule for every subdirectory you + * want to access pages in: + * + * + * + * It is still possible to define an explicit rule for a page in the wildcard path. + * This rule has to preceed the wildcard rule. + * + * You can also use parameters with wildcard patterns. The parameters are then + * available with every matching page: + * + * + * + * To enable automatic parameter encoding in a path format from wildcard patterns you can set + * {@setUrlFormat UrlFormat} to 'Path': + * + * + * + * This will create and parse URLs of the form + * .../index.php/admin/listuser/param1/value1/param2/value2. + * + * Use {@setUrlParamSeparator} to define another separator character between parameter + * name and value. Parameter/value pairs are always separated by a '/'. + * + * + * + * .../index.php/admin/listuser/param1-value1/param2-value2. + * + * Since 3.2.2 you can also add a list of "constants" parameters that can be used just + * like the original "parameters" parameters, except that the supplied value will be treated + * as a simple string constant instead of a regular expression. For example + * + * + * + * + * These rules, when matched by the actual request, will make the application see a "lisstype" parameter present + * (even through not supplied in the request) and equal to "detailed" or "summarized", depending on the friendly url matched. + * The constants is practically a table-based validation and translation of specified, fixed-set parameter values. + * + * @author Wei Zhuo + * @version $Id: TUrlMapping.php 3290 2013-05-06 08:32:15Z ctrlaltca $ + * @package System.Web + * @since 3.0.5 + */ +class TUrlMappingPattern extends TComponent +{ + /** + * @var string service parameter such as Page class name. + */ + private $_serviceParameter; + /** + * @var string service ID, default is 'page'. + */ + private $_serviceID='page'; + /** + * @var string url pattern to match. + */ + private $_pattern; + /** + * @var TAttributeCollection parameter regular expressions. + */ + private $_parameters; + /** + * @var TAttributeCollection of constant parameters. + */ + protected $_constants; + /** + * @var string regular expression pattern. + */ + private $_regexp=''; + + private $_customUrl=true; + + private $_manager; + + private $_caseSensitive=true; + + private $_isWildCardPattern=false; + + private $_urlFormat=THttpRequestUrlFormat::Get; + + private $_separator='/'; + + /** + * @var TUrlMappingPatternSecureConnection + * @since 3.2 + */ + private $_secureConnection = TUrlMappingPatternSecureConnection::Automatic; + + /** + * Constructor. + * @param TUrlManager the URL manager instance + */ + public function __construct(TUrlManager $manager) + { + $this->_manager=$manager; + } + + /** + * @return TUrlManager the URL manager instance + */ + public function getManager() + { + return $this->_manager; + } + + /** + * Initializes the pattern. + * @param TXmlElement configuration for this module. + * @throws TConfigurationException if service parameter is not specified + */ + public function init($config) + { + if($this->_serviceParameter===null) + throw new TConfigurationException('urlmappingpattern_serviceparameter_required', $this->getPattern()); + if(strpos($this->_serviceParameter,'*')!==false) + $this->_isWildCardPattern=true; + } + + /** + * Substitute the parameter key value pairs as named groupings + * in the regular expression matching pattern. + * @return string regular expression pattern with parameter subsitution + */ + protected function getParameterizedPattern() + { + $params=array(); + $values=array(); + if ($this->_parameters) + { + foreach($this->_parameters as $key=>$value) + { + $params[]='{'.$key.'}'; + $values[]='(?P<'.$key.'>'.$value.')'; + } + } + if ($this->getIsWildCardPattern()) + { + $params[]='{*}'; + // service parameter must not contain '=' and '/' + $values[]='(?P<'.$this->getServiceID().'>[^=/]+)'; + } + $params[]='/'; + $values[]='\\/'; + $regexp=str_replace($params,$values,trim($this->getPattern(),'/').'/'); + if ($this->_urlFormat===THttpRequestUrlFormat::Get) + $regexp='/^'.$regexp.'$/u'; + else + $regexp='/^'.$regexp.'(?P.*)$/u'; + + if(!$this->getCaseSensitive()) + $regexp.='i'; + return $regexp; + } + + /** + * @return string full regular expression mapping pattern + */ + public function getRegularExpression() + { + return $this->_regexp; + } + + /** + * @param string full regular expression mapping pattern. + */ + public function setRegularExpression($value) + { + $this->_regexp=$value; + } + + /** + * @return boolean whether the {@link getPattern Pattern} should be treated as case sensititve. Defaults to true. + */ + public function getCaseSensitive() + { + return $this->_caseSensitive; + } + + /** + * @param boolean whether the {@link getPattern Pattern} should be treated as case sensititve. + */ + public function setCaseSensitive($value) + { + $this->_caseSensitive=TPropertyValue::ensureBoolean($value); + } + + /** + * @param string service parameter, such as page class name. + */ + public function setServiceParameter($value) + { + $this->_serviceParameter=$value; + } + + /** + * @return string service parameter, such as page class name. + */ + public function getServiceParameter() + { + return $this->_serviceParameter; + } + + /** + * @param string service id to handle. + */ + public function setServiceID($value) + { + $this->_serviceID=$value; + } + + /** + * @return string service id. + */ + public function getServiceID() + { + return $this->_serviceID; + } + + /** + * @return string url pattern to match. Defaults to ''. + */ + public function getPattern() + { + return $this->_pattern; + } + + /** + * @param string url pattern to match. + */ + public function setPattern($value) + { + $this->_pattern = $value; + } + + /** + * @return TAttributeCollection parameter key value pairs. + */ + public function getParameters() + { + if (!$this->_parameters) + { + $this->_parameters=new TAttributeCollection; + $this->_parameters->setCaseSensitive(true); + } + return $this->_parameters; + } + + /** + * @param TAttributeCollection new parameter key value pairs. + */ + public function setParameters($value) + { + $this->_parameters=$value; + } + + /** + * @return TAttributeCollection constanst parameter key value pairs. + * @since 3.2.2 + */ + public function getConstants() + { + if (!$this->_constants) + { + $this->_constants = new TAttributeCollection; + $this->_constants->setCaseSensitive(true); + } + return $this->_constants; + } + + /** + * Uses URL pattern (or full regular expression if available) to + * match the given url path. + * @param THttpRequest the request module + * @return array matched parameters, empty if no matches. + */ + public function getPatternMatches($request) + { + $matches=array(); + if(($pattern=$this->getRegularExpression())!=='') + preg_match($pattern,$request->getPathInfo(),$matches); + else + preg_match($this->getParameterizedPattern(),trim($request->getPathInfo(),'/').'/',$matches); + + if($this->getIsWildCardPattern() && isset($matches[$this->_serviceID])) + $matches[$this->_serviceID]=str_replace('*',$matches[$this->_serviceID],$this->_serviceParameter); + + if (isset($matches['urlparams'])) + { + $params=explode('/',$matches['urlparams']); + if ($this->_separator==='/') + { + while($key=array_shift($params)) + $matches[$key]=($value=array_shift($params)) ? $value : ''; + } + else + { + array_pop($params); + foreach($params as $param) + { + list($key,$value)=explode($this->_separator,$param,2); + $matches[$key]=$value; + } + } + unset($matches['urlparams']); + } + + if(count($matches) > 0 && $this->_constants) + { + foreach($this->_constants->toArray() as $key=>$value) + $matches[$key] = $value; + } + + return $matches; + } + + /** + * Returns a value indicating whether to use this pattern to construct URL. + * @return boolean whether to enable custom constructUrl. Defaults to true. + * @since 3.1.1 + */ + public function getEnableCustomUrl() + { + return $this->_customUrl; + } + + /** + * Sets a value indicating whether to enable custom constructUrl using this pattern + * @param boolean whether to enable custom constructUrl. + */ + public function setEnableCustomUrl($value) + { + $this->_customUrl=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether this pattern is a wildcard pattern + * @since 3.1.4 + */ + public function getIsWildCardPattern() { + return $this->_isWildCardPattern; + } + + /** + * @return THttpRequestUrlFormat the format of URLs. Defaults to THttpRequestUrlFormat::Get. + */ + public function getUrlFormat() + { + return $this->_urlFormat; + } + + /** + * Sets the format of URLs constructed and interpreted by this pattern. + * A Get URL format is like index.php?name1=value1&name2=value2 + * while a Path URL format is like index.php/name1/value1/name2/value. + * The separating character between name and value can be configured with + * {@link setUrlParamSeparator} and defaults to '/'. + * Changing the UrlFormat will affect {@link constructUrl} and how GET variables + * are parsed. + * @param THttpRequestUrlFormat the format of URLs. + * @since 3.1.4 + */ + public function setUrlFormat($value) + { + $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); + } + + /** + * @return string separator used to separate GET variable name and value when URL format is Path. Defaults to slash '/'. + */ + public function getUrlParamSeparator() + { + return $this->_separator; + } + + /** + * @param string separator used to separate GET variable name and value when URL format is Path. + * @throws TInvalidDataValueException if the separator is not a single character + */ + public function setUrlParamSeparator($value) + { + if(strlen($value)===1) + $this->_separator=$value; + else + throw new TInvalidDataValueException('httprequest_separator_invalid'); + } + + /** + * @return TUrlMappingPatternSecureConnection the SecureConnection behavior. Defaults to {@link TUrlMappingPatternSecureConnection::Automatic Automatic} + * @since 3.2 + */ + public function getSecureConnection() + { + return $this->_secureConnection; + } + + /** + * @param TUrlMappingPatternSecureConnection the SecureConnection behavior. + * @since 3.2 + */ + public function setSecureConnection($value) + { + $this->_secureConnection = TPropertyValue::ensureEnum($value, 'TUrlMappingPatternSecureConnection'); + } + + /** + * @param array list of GET items to be put in the constructed URL + * @return boolean whether this pattern IS the one for constructing the URL with the specified GET items. + * @since 3.1.1 + */ + public function supportCustomUrl($getItems) + { + if(!$this->_customUrl || $this->getPattern()===null) + return false; + if ($this->_parameters) + { + foreach($this->_parameters as $key=>$value) + { + if(!isset($getItems[$key])) + return false; + } + } + + if ($this->_constants) + { + foreach($this->_constants->toArray() as $key=>$value) + { + if (!isset($getItems[$key])) + return false; + if ($getItems[$key]!=$value) + return false; + } + } + return true; + } + + /** + * Constructs a URL using this pattern. + * @param array list of GET variables + * @param boolean whether the ampersand should be encoded in the constructed URL + * @param boolean whether the GET variables should be encoded in the constructed URL + * @return string the constructed URL + * @since 3.1.1 + */ + public function constructUrl($getItems,$encodeAmpersand,$encodeGetItems) + { + if ($this->_constants) + { + foreach($this->_constants->toArray() as $key=>$value) + { + unset($getItems[$key]); + } + } + + $extra=array(); + $replace=array(); + // for the GET variables matching the pattern, put them in the URL path + foreach($getItems as $key=>$value) + { + if(($this->_parameters && $this->_parameters->contains($key)) || ($key==='*' && $this->getIsWildCardPattern())) + $replace['{'.$key.'}']=$encodeGetItems ? rawurlencode($value) : $value; + else + $extra[$key]=$value; + } + + $url=$this->_manager->getUrlPrefix().'/'.ltrim(strtr($this->getPattern(),$replace),'/'); + + // for the rest of the GET variables, put them in the query string + if(count($extra)>0) + { + if ($this->_urlFormat===THttpRequestUrlFormat::Path && $this->getIsWildCardPattern()) { + foreach ($extra as $name=>$value) + $url.='/'.$name.$this->_separator.($encodeGetItems?rawurlencode($value):$value); + return $url; + } + + $url2=''; + $amp=$encodeAmpersand?'&':'&'; + if($encodeGetItems) + { + foreach($extra as $name=>$value) + { + if(is_array($value)) + { + $name=rawurlencode($name.'[]'); + foreach($value as $v) + $url2.=$amp.$name.'='.rawurlencode($v); + } + else + $url2.=$amp.rawurlencode($name).'='.rawurlencode($value); + } + } + else + { + foreach($extra as $name=>$value) + { + if(is_array($value)) + { + foreach($value as $v) + $url2.=$amp.$name.'[]='.$v; + } + else + $url2.=$amp.$name.'='.$value; + } + } + $url=$url.'?'.substr($url2,strlen($amp)); + } + return $this -> applySecureConnectionPrefix($url); + } + + /** + * Apply behavior of {@link SecureConnection} property by conditionaly prefixing + * URL with {@link THttpRequest::getBaseUrl()} + * + * @param string $url + * @return string + * @since 3.2 + */ + protected function applySecureConnectionPrefix($url) + { + static $request; + if($request === null) $request = Prado::getApplication() -> getRequest(); + + static $isSecureConnection; + if($isSecureConnection === null) $isSecureConnection = $request -> getIsSecureConnection(); + + switch($this -> getSecureConnection()) + { + case TUrlMappingPatternSecureConnection::EnableIfNotSecure: + if($isSecureConnection) return $url; + return $request -> getBaseUrl(true) . $url; + break; + case TUrlMappingPatternSecureConnection::DisableIfSecure: + if(!$isSecureConnection) return $url; + return $request -> getBaseUrl(false) . $url; + break; + case TUrlMappingPatternSecureConnection::Enable: + return $request -> getBaseUrl(true) . $url; + break; + case TUrlMappingPatternSecureConnection::Disable: + return $request -> getBaseUrl(false) . $url; + break; + case TUrlMappingPatternSecureConnection::Automatic: + default: + return $url; + break; + } + } +} + +/** + * TUrlMappingPatternSecureConnection class + * + * TUrlMappingPatternSecureConnection defines the enumerable type for the possible SecureConnection + * URL prefix behavior that can be used by {@link TUrlMappingPattern::constructUrl()}. + * + * @author Yves Berkholz + * @version $Id: TUrlMapping.php 3290 2013-05-06 08:32:15Z ctrlaltca $ + * @package System.Web + * @since 3.2 + */ +class TUrlMappingPatternSecureConnection extends TEnumerable +{ + /** + * Keep current SecureConnection status + * means no prefixing + */ + const Automatic = 'Automatic'; + + /** + * Force use secured connection + * always prefixing with https://example.com/path/to/app + */ + const Enable = 'Enable'; + + /** + * Force use unsecured connection + * always prefixing with http://example.com/path/to/app + */ + const Disable = 'Disable'; + + /** + * Force use secured connection, if in unsecured mode + * prefixing with https://example.com/path/to/app + */ + const EnableIfNotSecure = 'EnableIfNotSecure'; + + /** + * Force use unsecured connection, if in secured mode + * prefixing with https://example.com/path/to/app + */ + const DisableIfSecure = 'DisableIfSecure'; +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveButton.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveButton.php new file mode 100644 index 0000000000..16ba53390b --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveButton.php @@ -0,0 +1,132 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveButton is the active control counter part to TButton. + * + * When a TActiveButton is clicked, rather than a normal post back request a + * callback request is initiated. + * + * The {@link onCallback OnCallback} event is raised during a callback request + * and it is raise after the {@link onClick OnClick} event. + * + * When the {@link TBaseActiveCallbackControl::setEnableUpdate ActiveControl.EnableUpdate} + * property is true, changing the {@link setText Text} property during callback request + * will update the button's caption upon callback response completion. + * + * @author Wei Zhuo + * @version $Id: TActiveButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveButton extends TButton implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by + * {@link ICallbackEventHandler} interface. If {@link getCausesValidation CausesValidation} + * is true, it will invoke the page's {@link TPage::validate validate} + * method first. It will raise {@link onClick OnClick} event first + * and then the {@link onCallback OnCallback} event. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->raisePostBackEvent($param); + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Updates the button text on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param string caption of the button + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'value', $value); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * @return string corresponding javascript class name for this TActiveButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveButton'; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveCheckBox.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveCheckBox.php new file mode 100644 index 0000000000..6221150d98 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveCheckBox.php @@ -0,0 +1,181 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveCheckBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveCheckBox class. + * + * The active control counter part to checkbox. The {@link setAutoPostBack AutoPostBack} + * property is set to true by default. Thus, when the checkbox is clicked a + * {@link onCallback OnCallback} event is raise after {@link OnCheckedChanged} event. + * + * The {@link setText Text} and {@link setChecked Checked} properties can be + * changed during a callback. + * + * @author Wei Zhuo + * @version $Id: TActiveCheckBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveCheckBox extends TCheckBox implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + $this->setAutoPostBack(true); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Updates the button text on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param string caption of the button + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->update( + $this->getDefaultLabelID(), $value); + } + + /** + * Sets a value indicating whether the checkbox is to be checked or not. + * Updates checkbox checked state on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param boolean whether the checkbox is to be checked or not. + */ + public function setChecked($value) + { + $value = TPropertyValue::ensureBoolean($value); + parent::setChecked($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->check($this, $value); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + * + * Since 3.1.4, the javascript code is not rendered if {@link setAutoPostBack AutoPostBack} is false + * + * @param THtmlWriter the writer for the rendering purpose + * @param string checkbox id + * @param string onclick js + */ + protected function renderInputTag($writer,$clientID,$onclick) + { + parent::renderInputTag($writer,$clientID,$onclick); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * @return string corresponding javascript class name for this TActiveCheckBox. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveCheckBox'; + } + + /** + * Overrides parent implementation to ensure label has ID. + * @return TMap list of attributes to be rendered for label beside the checkbox + */ + public function getLabelAttributes() + { + $attributes = parent::getLabelAttributes(); + $attributes['id'] = $this->getDefaultLabelID(); + return $attributes; + } + + /** + * Renders a label beside the checkbox. + * @param THtmlWriter the writer for the rendering purpose + * @param string checkbox id + * @param string label text + */ + protected function renderLabel($writer,$clientID,$text) + { + $writer->addAttribute('id', $this->getDefaultLabelID()); + parent::renderLabel($writer, $clientID, $text); + } + + /** + * @return string checkbox label ID; + */ + protected function getDefaultLabelID() + { + if($attributes=$this->getViewState('LabelAttributes',null)) + return TCheckBox::getLabelAttributes()->itemAt('id'); + else + return $this->getClientID().'_label'; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveCheckBoxList.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveCheckBoxList.php new file mode 100644 index 0000000000..a42044fb6e --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveCheckBoxList.php @@ -0,0 +1,121 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveCheckBoxList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveListControlAdapter'); + +/** + * TActiveCheckBoxList class. + * + * The active control counter part to checkbox list control. + * The {@link setAutoPostBack AutoPostBack} property is set to true by default. + * Thus, when a checkbox is clicked a {@link onCallback OnCallback} event is + * raised after {@link OnSelectedIndexChanged} event. + * + * With {@link TBaseActiveControl::setEnableUpdate() ActiveControl.EnableUpdate} + * set to true (default is true), changes to the selection will be updated + * on the client side. + * + * List items can not be changed dynamically during a callback request. + * + * @author Wei Zhuo + * @version $Id: TActiveCheckBoxList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveCheckBoxList extends TCheckBoxList implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveListControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + $this->setAdapter(new TActiveListControlAdapter($this)); + $this->setAutoPostBack(true); + parent::__construct(); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Creates a control used for repetition (used as a template). + * @return TControl the control to be repeated + */ + protected function createRepeatedControl() + { + $control = new TActiveCheckBox; + $control->getAdapter()->setBaseActiveControl($this->getActiveControl()); + return $control; + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveClientScript.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveClientScript.php new file mode 100755 index 0000000000..8fce726aa7 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveClientScript.php @@ -0,0 +1,82 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveClientScript.php 3144 2012-05-19 10:07:03Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveClientScript class + * + * This is the active counterpart of the {@link TClientScript} class. + * + * TActiveClientScript has the ability to render itself on ajax + * callbacks. This means that every variable or function declared in javascript + * code will be available to the page. + * + * Beware that when rendered on normal (postback) or ajax callbacks, some + * javascript code won't behave in the same way. + * When rendered as part of a normal/postback response, scripts will execute instantly + * where they are in the page and in a synchronous fashion. + * Instead, when they are rendered as part of a callback response, + * they will be executed when all DOM modifications are complete and any dynamic + * script file includes are loaded, out-of-band and practically all blocks at once, + * regardless of where they actually occour in the original template/markup code. + * This can potentially hurt compatibility and graceful fallback. + * + * @author Wei Zhuo + * @version $Id: TActiveClientScript.php 3144 2012-05-19 10:07:03Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.2 + */ + +class TActiveClientScript extends TClientScript +{ + /** + * Renders the custom script file. + * @param THtmLWriter the renderer + */ + protected function renderCustomScriptFile($writer) + { + if(($scriptUrl = $this->getScriptUrl())!=='') + { + if($this->getPage()->getIsCallback()) + { + $cs = $this->getPage()->getClientScript(); + $uniqueid=$this->ClientID.'_custom'; + if(!$cs->isScriptFileRegistered($uniqueid)) + $cs->registerScriptFile($uniqueid, $scriptUrl); + } else { + $writer->write("\n"); + } + } + } + + /** + * Registers the body content as javascript. + * @param THtmlWriter the renderer + */ + protected function renderCustomScript($writer) + { + if($this->getHasControls()) + { + if($this->getPage()->getIsCallback()) + { + $extWriter= $this->getPage()->getResponse()->createHtmlWriter(); + $extWriter->write("/*renderChildren($extWriter); + $extWriter->write("\n/*]]>*/"); + $this->getPage()->getCallbackClient()->appendScriptBlock($extWriter); + } else { + $writer->write("\n"); + } + } + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveControlAdapter.php new file mode 100644 index 0000000000..ab15f09105 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -0,0 +1,575 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/* + * Load common active control options. + */ +Prado::using('System.Web.UI.ActiveControls.TBaseActiveControl'); + +/** + * TActiveControlAdapter class. + * + * Customize the parent TControl class for active control classes. + * TActiveControlAdapter instantiates a common base active control class + * throught the {@link getBaseActiveControl BaseActiveControl} property. + * The type of BaseActiveControl can be provided in the second parameter in the + * constructor. Default is TBaseActiveControl or TBaseActiveCallbackControl if + * the control adapted implements ICallbackEventHandler. + * + * TActiveControlAdapter will tracking viewstate changes to update the + * corresponding client-side properties. + * + * @author Wei Zhuo + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveControlAdapter extends TControlAdapter +{ + /** + * @var string base active control class name. + */ + private $_activeControlType; + /** + * @var TBaseActiveControl base active control instance. + */ + private $_baseActiveControl; + /** + * @var TCallbackPageStateTracker view state tracker. + */ + private $_stateTracker; + + /** + * Constructor. + * @param IActiveControl active control to adapt. + * @param string Base active control class name. + */ + public function __construct(IActiveControl $control, $baseCallbackClass=null) + { + parent::__construct($control); + $this->setBaseControlClass($baseCallbackClass); + } + + /** + * @param string base active control instance + */ + protected function setBaseControlClass($type) + { + if($type===null) + { + if($this->getControl() instanceof ICallbackEventHandler) + $this->_activeControlType = 'TBaseActiveCallbackControl'; + else + $this->_activeControlType = 'TBaseActiveControl'; + } + else + $this->_activeControlType = $type; + } + + /** + * Publish the ajax script + */ + public function onPreRender($param) + { + parent::onPreRender($param); + } + + /** + * Renders the callback client scripts. + */ + public function render($writer) + { + $this->getPage()->getClientScript()->registerPradoScript('ajax'); + $this->renderCallbackClientScripts(); + if($this->_control->getVisible(false)) + { + parent::render($writer); + } else { + $writer->write("_control->getClientID()."\" style=\"display:none\">"); + } + } + + /** + * Register the callback clientscripts and sets the post loader IDs. + */ + protected function renderCallbackClientScripts() + { + $cs = $this->getPage()->getClientScript(); + $key = 'Prado.CallbackRequest.addPostLoaders'; + if(!$cs->isEndScriptRegistered($key)) + { + $data = $this->getPage()->getPostDataLoaders(); + if(count($data) > 0) + { + $options = TJavaScript::encode($data,false); + $script = "Prado.CallbackRequest.addPostLoaders({$options});"; + $cs->registerEndScript($key, $script); + } + } + } + + /** + * @param TBaseActiveControl change base active control + */ + public function setBaseActiveControl($control) + { + $this->_baseActiveControl=$control; + } + + /** + * @return TBaseActiveControl Common active control options. + */ + public function getBaseActiveControl() + { + if($this->_baseActiveControl===null) + { + $type = $this->_activeControlType; + $this->_baseActiveControl = new $type($this->getControl()); + } + return $this->_baseActiveControl; + } + + /** + * @return boolean true if the viewstate needs to be tracked. + */ + protected function getIsTrackingPageState() + { + if($this->getPage()->getIsCallback()) + { + $target = $this->getPage()->getCallbackEventTarget(); + if($target instanceof ICallbackEventHandler) + { + $client = $target->getActiveControl()->getClientSide(); + return $client->getEnablePageStateUpdate(); + } + } + return false; + } + + /** + * Starts viewstate tracking if necessary after when controls has been loaded + */ + public function onLoad($param) + { + if($this->getIsTrackingPageState()) + { + $this->_stateTracker = new TCallbackPageStateTracker($this->getControl()); + $this->_stateTracker->trackChanges(); + } + parent::onLoad($param); + } + + /** + * Saves additional persistent control state. Respond to viewstate changes + * if necessary. + */ + public function saveState() + { + if(($this->_stateTracker!==null) + && $this->getControl()->getActiveControl()->canUpdateClientSide(true)) + { + $this->_stateTracker->respondToChanges(); + } + parent::saveState(); + } + + /** + * @return TCallbackPageStateTracker state tracker. + */ + public function getStateTracker() + { + return $this->_stateTracker; + } +} + +/** + * TCallbackPageStateTracker class. + * + * Tracking changes to the page state during callback. + * + * @author Wei Zhuo + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackPageStateTracker +{ + /** + * @var TMap new view state data + */ + private $_states; + /** + * @var TMap old view state data + */ + private $_existingState; + /** + * @var TControl the control tracked + */ + private $_control; + /** + * @var object null object. + */ + private $_nullObject; + + /** + * Constructor. Add a set of default states to track. + * @param TControl control to track. + */ + public function __construct($control) + { + $this->_control = $control; + $this->_existingState = new TMap; + $this->_nullObject = new stdClass; + $this->_states = new TMap; + $this->addStatesToTrack(); + } + + /** + * Add a list of view states to track. Each state is added + * to the StatesToTrack property with the view state name as key. + * The value should be an array with two enteries. The first entery + * is the name of the class that will calculate the state differences. + * The second entry is a php function/method callback that handles + * the changes in the viewstate. + */ + protected function addStatesToTrack() + { + $states = $this->getStatesToTrack(); + $states['Visible'] = array('TScalarDiff', array($this, 'updateVisible')); + $states['Enabled'] = array('TScalarDiff', array($this, 'updateEnabled')); + $states['Attributes'] = array('TMapCollectionDiff', array($this, 'updateAttributes')); + $states['Style'] = array('TStyleDiff', array($this, 'updateStyle')); + $states['TabIndex'] = array('TScalarDiff', array($this, 'updateTabIndex')); + $states['ToolTip'] = array('TScalarDiff', array($this, 'updateToolTip')); + $states['AccessKey'] = array('TScalarDiff', array($this, 'updateAccessKey')); + } + + /** + * @return TMap list of viewstates to track. + */ + protected function getStatesToTrack() + { + return $this->_states; + } + + /** + * Start tracking view state changes. The clone function on objects are called + * for those viewstate having an object as value. + */ + public function trackChanges() + { + foreach($this->_states as $name => $value) + { + $obj = $this->_control->getViewState($name); + $this->_existingState[$name] = is_object($obj) ? clone($obj) : $obj; + } + } + + /** + * @return array list of viewstate and the changed data. + */ + protected function getChanges() + { + $changes = array(); + foreach($this->_states as $name => $details) + { + $new = $this->_control->getViewState($name); + $old = $this->_existingState[$name]; + if($new !== $old) + { + $diff = new $details[0]($new, $old, $this->_nullObject); + if(($change = $diff->getDifference()) !== $this->_nullObject) + $changes[] = array($details[1],array($change)); + } + } + return $changes; + } + + /** + * For each of the changes call the corresponding change handlers. + */ + public function respondToChanges() + { + foreach($this->getChanges() as $change) + call_user_func_array($change[0], $change[1]); + } + + /** + * @return TCallbackClientScript callback client scripting + */ + protected function client() + { + return $this->_control->getPage()->getCallbackClient(); + } + + /** + * Updates the tooltip. + * @param string new tooltip + */ + protected function updateToolTip($value) + { + $this->client()->setAttribute($this->_control, 'title', $value); + } + + /** + * Updates the tab index. + * @param integer tab index + */ + protected function updateTabIndex($value) + { + $this->client()->setAttribute($this->_control, 'tabindex', $value); + } + + /** + * Updates the modifier access key + * @param string access key + */ + protected function updateAccessKey($value) + { + $this->client()->setAttribute($this->_control, 'accesskey', $value); + } + + /** + * Hides or shows the control on the client-side. The control must be + * already rendered on the client-side. + * @param boolean true to show the control, false to hide. + */ + protected function updateVisible($visible) + { + if($visible === false) + $this->client()->replaceContent($this->_control,"_control->getClientID()."\" style=\"display:none\" >"); + else + $this->client()->replaceContent($this->_control,$this->_control); + } + + /** + * Enables or Disables the control on the client-side. + * @param boolean true to enable the control, false to disable. + */ + protected function updateEnabled($enable) + { + $this->client()->setAttribute($this->_control, 'disabled', $enable===false); + } + + /** + * Updates the CSS style on the control on the client-side. + * @param array list of new CSS style declarations. + */ + protected function updateStyle($style) + { + if($style['CssClass']!==null) + $this->client()->setAttribute($this->_control, 'class', $style['CssClass']); + if(count($style['Style']) > 0) + $this->client()->setStyle($this->_control, $style['Style']); + } + + /** + * Updates/adds a list of attributes on the control. + * @param array list of attribute name-value pairs. + */ + protected function updateAttributes($attributes) + { + foreach($attributes as $name => $value) + $this->client()->setAttribute($this->_control, $name, $value); + } +} + +/** + * Calculates the viewstate changes during the request. + * + * @author Wei Zhuo + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +abstract class TViewStateDiff +{ + /** + * @var mixed updated viewstate + */ + protected $_new; + /** + * @var mixed viewstate value at the begining of the request. + */ + protected $_old; + /** + * @var object null value. + */ + protected $_null; + + /** + * Constructor. + * @param mixed updated viewstate value. + * @param mixed viewstate value at the begining of the request. + * @param object representing the null value. + */ + public function __construct($new, $old, $null) + { + $this->_new = $new; + $this->_old = $old; + $this->_null = $null; + } + + /** + * @return mixed view state changes, nullObject if no difference. + */ + public abstract function getDifference(); +} + +/** + * TScalarDiff class. + * + * Calculate the changes to a scalar value. + * + * @author Wei Zhuo + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TScalarDiff extends TViewStateDiff +{ + /** + * @return mixed update viewstate value. + */ + public function getDifference() + { + if(gettype($this->_new) === gettype($this->_old) + && $this->_new === $this->_old) + return $this->_null; + else + return $this->_new; + } +} + +/** + * TStyleDiff class. + * + * Calculates the changes to the Style properties. + * + * @author Wei Zhuo + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TStyleDiff extends TViewStateDiff +{ + /** + * @param TStyle control style + * @return array all the style properties combined. + */ + protected function getCombinedStyle($obj) + { + if(!($obj instanceof TStyle)) + return array(); + $style = $obj->getStyleFields(); + $style = array_merge($style,$this->getStyleFromString($obj->getCustomStyle())); + if($obj->hasFont()) + $style = array_merge($style, $this->getStyleFromString($obj->getFont()->toString())); + return $style; + } + + /** + * @param string CSS custom style string. + * @param array CSS style as name-value array. + */ + protected function getStyleFromString($string) + { + $style = array(); + if(!is_string($string)) return $style; + + foreach(explode(';',$string) as $sub) + { + $arr=explode(':',$sub); + if(isset($arr[1]) && trim($arr[0])!=='') + $style[trim($arr[0])] = trim($arr[1]); + } + return $style; + } + + /** + * @return string changes to the CSS class name. + */ + protected function getCssClassDiff() + { + if($this->_old===null) + { + return ($this->_new!==null) && $this->_new->hasCssClass() + ? $this->_new->getCssClass() : null; + } + else + { + return $this->_old->getCssClass() !== $this->_new->getCssClass() ? + $this->_new->getCssClass() : null; + } + } + + /** + * @return array list of changes to the control style. + */ + protected function getStyleDiff() + { + $diff = array_diff_assoc( + $this->getCombinedStyle($this->_new), + $this->getCombinedStyle($this->_old)); + return count($diff) > 0 ? $diff : null; + } + + /** + * @return array list of changes to the control style and CSS class name. + */ + public function getDifference() + { + if($this->_new===null) + return $this->_null; + else + { + $css = $this->getCssClassDiff(); + $style = $this->getStyleDiff(); + if(($css!==null) || ($style!==null)) + return array('CssClass' => $css, 'Style' => $style); + else + $this->_null; + } + } +} + +/** + * TAttributesDiff class. + * + * Calculate the changes to attributes collection. + * + * @author Wei Zhuo + * @version $Id: TActiveControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TMapCollectionDiff extends TViewStateDiff +{ + /** + * @return array updates to the attributes collection. + */ + public function getDifference() + { + if($this->_old===null) + { + return ($this->_new!==null) ? $this->_new->toArray() : $this->_null; + } + else + { + $new = $this->_new->toArray(); + $old = $this->_old->toArray(); + $diff = array_diff_assoc($new, $old); + return count($diff) > 0 ? $diff : $this->_null; + } + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveCustomValidator.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveCustomValidator.php new file mode 100644 index 0000000000..cc2a54a118 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveCustomValidator.php @@ -0,0 +1,265 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveCustomValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TCallbackClientSide'); + +/** + * TActiveCustomValidator Class + * + * Performs custom validation using only server-side {@link onServerValidate onServerValidate} + * validation event. The client-side uses callbacks to raise + * the {@link onServerValidate onServerValidate} event. + * + * Beware that the {@link onServerValidate onServerValidate} may be + * raised when the control to validate on the client side + * changes value, that is, the server validation may be called many times. + * + * After the callback or postback, the {@link onServerValidate onServerValidate} + * is raised once more. The {@link getIsCallback IsCallback} property + * will be true when validation is made during a callback request. + * + * @author Wei Zhuo + * @version $Id: TActiveCustomValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveCustomValidator extends TCustomValidator + implements ICallbackEventHandler, IActiveControl +{ + /** + * @var boolean true if validation is made during a callback request. + */ + private $_isCallback = false; + + /** + * @return boolean true if validation is made during a callback request. + */ + public function getIsCallback() + { + return $this->_isCallback; + } + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + $this->getActiveControl()->setClientSide(new TActiveCustomValidatorClientSide); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Client validation function is NOT supported. + */ + public function setClientValidationFunction($value) + { + throw new TNotSupportedException('tactivecustomvalidator_clientfunction_unsupported', + get_class($this)); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. The {@link onServerValidate + * OnServerValidate} event is raised first and then the + * {@link onCallback OnCallback} event. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->_isCallback = true; + $result = $this->onServerValidate($param->getCallbackParameter()); + $param->setResponseData($result); + $this->onCallback($param); + } + + /** + * @param boolean whether the value is valid; this method will trigger a clientside update if needed + */ + public function setIsValid($value) + { + parent::setIsValid($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $func = 'Prado.Validation.updateActiveCustomValidator'; + $client->callClientFunction($func, array($this, $value)); + } + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options=TBaseValidator::getClientScriptOptions(); + $options['EventTarget'] = $this->getUniqueID(); + return $options; + } + + /** + * Sets the text for the error message. Updates client-side erorr message. + * @param string the error message + */ + public function setErrorMessage($value) + { + parent::setErrorMessage($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $func = 'Prado.Validation.setErrorMessage'; + $client->callClientFunction($func, array($this, $value)); + } + } + + + /** + * It's mandatory for the EnableClientScript to be activated or the TActiveCustomValidator won't work. + * @return boolean whether client-side validation is enabled. + */ + public function getEnableClientScript() + { + return true; + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + TBaseValidator::registerClientScriptValidator(); + } + + /** + * @return string corresponding javascript class name for this this. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveCustomValidator'; + } +} + +/** + * Custom Validator callback client side options class. + * + * @author Wei Zhuo + * @version $Id: TActiveCustomValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveCustomValidatorClientSide extends TCallbackClientSide +{ + /** + * @return string javascript code for client-side OnValidate event. + */ + public function getOnValidate() + { + return $this->getOption('OnValidate'); + } + + /** + * Client-side OnValidate validator event is raise before the validators + * validation functions are called. + * @param string javascript code for client-side OnValidate event. + */ + public function setOnValidate($javascript) + { + $this->setFunction('OnValidate', $javascript); + } + + /** + * Client-side OnSuccess event is raise after validation is successfull. + * This will override the default client-side validator behaviour. + * @param string javascript code for client-side OnSuccess event. + */ + public function setOnValidationSuccess($javascript) + { + $this->setFunction('OnValidationSuccess', $javascript); + } + + /** + * @return string javascript code for client-side OnSuccess event. + */ + public function getOnValidationSuccess() + { + return $this->getOption('OnValidationSuccess'); + } + + /** + * Client-side OnError event is raised after validation failure. + * This will override the default client-side validator behaviour. + * @param string javascript code for client-side OnError event. + */ + public function setOnValidationError($javascript) + { + $this->setFunction('OnValidationError', $javascript); + } + + /** + * @return string javascript code for client-side OnError event. + */ + public function getOnValidationError() + { + return $this->getOption('OnValidationError'); + } + + /** + * @param boolean true to revalidate when the control to validate changes value. + */ + public function setObserveChanges($value) + { + $this->setOption('ObserveChanges', TPropertyValue::ensureBoolean($value)); + } + + /** + * @return boolean true to observe changes. + */ + public function getObserveChanges() + { + $changes = $this->getOption('ObserveChanges'); + return ($changes===null) ? true : $changes; + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveDataGrid.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDataGrid.php new file mode 100644 index 0000000000..00c9cb49a4 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDataGrid.php @@ -0,0 +1,830 @@ + + * @link http://www.landwehr-software.de/ + * @copyright Copyright © 2009 LANDWEHR Computer und Software GmbH + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI.ActiveControls + */ + +/** + * Includes the following used classes + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton'); +Prado::using('System.Web.UI.ActiveControls.TActiveImageButton'); +Prado::using('System.Web.UI.ActiveControls.TActiveButton'); +Prado::using('System.Web.UI.ActiveControls.TActiveImage'); +Prado::using('System.Web.UI.ActiveControls.TActiveCheckBox'); +Prado::using('System.Web.UI.ActiveControls.TCallbackOptions'); +Prado::using('System.Web.UI.WebControls.TDataGrid'); +Prado::using('System.Web.UI.WebControls.TBoundColumn'); +Prado::using('System.Web.UI.WebControls.TEditCommandColumn'); +Prado::using('System.Web.UI.WebControls.TButtonColumn'); +Prado::using('System.Web.UI.WebControls.THyperLinkColumn'); +Prado::using('System.Web.UI.WebControls.TCheckBoxColumn'); + +/** + * TActiveDataGrid class + * + * TActiveDataGrid represents a data bound and updatable grid control which is the + * active counterpart to the original {@link TDataGrid} control. + * + * This component can be used in the same way as the regular datagrid, the only + * difference is that the active datagrid uses callbacks instead of postbacks + * for interaction. + * + * There are also active datagrid columns to work with the TActiveDataGrid, which are + * - {@link TActiveBoundColumn}, the active counterpart to {@link TBoundColumn}. + * - {@link TActiveLiteralColumn}, the active counterpart to {@link TLiteralColumn}. + * - {@link TActiveCheckBoxColumn}, the active counterpart to {@link TCheckBoxColumn}. + * - {@link TActiveDropDownListColumn}, the active counterpart to {@link TDropDownListColumn}. + * - {@link TActiveHyperLinkColumn}, the active counterpart to {@link THyperLinkColumn}. + * - {@link TActiveEditCommandColumn}, the active counterpart to {@link TEditCommandColumn}. + * - {@link TActiveButtonColumn}, the active counterpart to {@link TButtonColumn}. + * - {@link TActiveTemplateColumn}, the active counterpart to {@link TTemplateColumn}. + * + * Please refer to the original documentation of the regular counterparts for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveDataGrid extends TDataGrid implements IActiveControl, ISurroundable { + + /** + * @return string Name of the class used in AutoGenerateColumns mode + */ + protected function getAutoGenerateColumnName() + { + return 'TActiveBoundColumn'; + } + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. + */ + public function __construct() { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Sets the data source object associated with the datagrid control. + * In addition, the render method of all connected pagers is called so they + * get updated when the data source is changed. Also the datagrid registers + * itself for rendering in order to get it's content replaced on client side. + * @param Traversable|array|string data source object + */ + public function setDataSource($value) { + parent::setDataSource($value); + if($this->getActiveControl()->canUpdateClientSide()) { + $this->renderPager(); + $this->getPage()->getAdapter()->registerControlToRender($this,$this->getResponse()->createHtmlWriter()); + } + } + + /** + * Returns the id of the surrounding container (div). + * @return string container id + */ + public function getSurroundingTagId() { + return $this->ClientID.'_Container'; + } + + /** + * Creates a pager button. + * Depending on the button type, a TActiveLinkButton or a TActiveButton may be created. + * If it is enabled (clickable), its command name and parameter will also be set. + * It overrides the datagrid's original method to create active controls instead, thus + * the pager will do callbacks instead of the regular postbacks. + * @param mixed the container pager instance of TActiveDatagridPager + * @param string button type, either LinkButton or PushButton + * @param boolean whether the button should be enabled + * @param string caption of the button + * @param string CommandName corresponding to the OnCommand event of the button + * @param string CommandParameter corresponding to the OnCommand event of the button + * @return mixed the button instance + */ + protected function createPagerButton($pager,$buttonType,$enabled,$text,$commandName,$commandParameter) { + if($buttonType===TDataGridPagerButtonType::LinkButton) { + if($enabled) + $button=new TActiveLinkButton; + else { + $button=new TLabel; + $button->setText($text); + return $button; + } + } + else { + $button=new TActiveButton; + if(!$enabled) + $button->setEnabled(false); + } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCommandParameter($commandParameter); + $button->setCausesValidation(false); + $button->getAdapter()->getBaseActiveControl()->setClientSide( + $pager->getClientSide() + ); + return $button; + } + + protected function createPager() + { + $pager=new TActiveDataGridPager($this); + $this->buildPager($pager); + $this->onPagerCreated(new TActiveDataGridPagerEventParameter($pager)); + $this->getControls()->add($pager); + return $pager; + } + + /** + * Renders the datagrid. + * If the datagrid did not pass the prerender phase yet, it will register itself for rendering later. + * Else it will call the {@link renderDataGrid()} method which will do the rendering of the datagrid. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) { + if($this->getHasPreRendered()) { + $this->renderDataGrid($writer); + if($this->getActiveControl()->canUpdateClientSide()) $this->getPage()->getCallbackClient()->replaceContent($this->getSurroundingTagId(),$writer); + } + else { + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + } + } + + /** + * Loops through all {@link TActivePager} on the page and registers the ones which are set to paginate + * the datagrid for rendering. This is to ensure that the connected pagers are also rendered if the + * data source changed. + */ + private function renderPager() { + $pager=$this->getPage()->findControlsByType('TActivePager', false); + foreach($pager as $item) { + if($item->ControlToPaginate==$this->ID) { + $writer=$this->getResponse()->createHtmlWriter(); + $this->getPage()->getAdapter()->registerControlToRender($item,$writer); + } + } + } + + /** + * Renders the datagrid by writing a div tag with the container id obtained from {@link getSurroundingTagId()} + * which will be called by the replacement method of the client script to update it's content. + * @param THtmlWriter writer for the rendering purpose + */ + private function renderDataGrid($writer) { + $writer->write('
    '); + parent::render($writer); + $writer->write('
    '); + } +} + + +/** + * TActiveBoundColumn class + * + * TActiveBoundColumn represents a column that is bound to a field in a data source. + * The cells in the column will be displayed using the data indexed by + * {@link setDataField DataField}. You can customize the display by + * setting {@link setDataFormatString DataFormatString}. + * + * This is the active counterpart to the {@link TBoundColumn} control. For that purpose, + * if sorting is allowed, the header links/buttons are replaced by active controls. + * + * Please refer to the original documentation of the {@link TBoundColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveBoundColumn extends TBoundColumn { + protected function initializeHeaderCell($cell,$columnIndex) { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') { + $control=Prado::createComponent($classPath); + if($control instanceof IDataRenderer) { + if($control instanceof IItemDataRenderer) { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + $cell->getControls()->add($control); + } + else if($this->getAllowSorting()) { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') { + $button->setAlternateText($text); + $button->setToolTip($text); + } + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else { + if(($url=$this->getHeaderImageUrl())!=='') { + $image=Prado::createComponent('System.Web.UI.WebControls.TActiveImage'); + $image->setImageUrl($url); + if($text!=='') { + $image->setAlternateText($text); + $image->setToolTip($text); + } + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } +} + + +/** + * TActiveEditCommandColumn class + * + * TActiveEditCommandColumn contains the Edit command buttons for editing data items in each row. + * + * TActiveEditCommandColumn will create an edit button if a cell is not in edit mode. + * Otherwise an update button and a cancel button will be created within the cell. + * The button captions are specified using {@link setEditText EditText}, + * {@link setUpdateText UpdateText}, and {@link setCancelText CancelText}. + * + * This is the active counterpart to the {@link TEditCommandColumn} control. The buttons for + * interaction are replaced by active buttons. + * + * Please refer to the original documentation of the {@link TEditCommandColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveEditCommandColumn extends TEditCommandColumn { + protected function createButton($commandName,$text,$causesValidation,$validationGroup) { + if($this->getButtonType()===TButtonColumnType::LinkButton) + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + else if($this->getButtonType()===TButtonColumnType::PushButton) + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveButton'); + else // image buttons + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setToolTip($text); + if(strcasecmp($commandName,'Update')===0) + $url=$this->getUpdateImageUrl(); + else if(strcasecmp($commandName,'Cancel')===0) + $url=$this->getCancelImageUrl(); + else + $url=$this->getEditImageUrl(); + $button->setImageUrl($url); + } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCausesValidation($causesValidation); + $button->setValidationGroup($validationGroup); + return $button; + } +} + + +/** + * TActiveButtonColumn class + * + * TActiveButtonColumn contains a user-defined command button, such as Add or Remove, + * that corresponds with each row in the column. + * + * This is the active counterpart to the {@link TButtonColumn} control where the + * button is replaced by the appropriate active button control. + * + * Please refer to the original documentation of the {@link TButtonColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveButtonColumn extends TButtonColumn { + public function initializeCell($cell,$columnIndex,$itemType) { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) { + $buttonType=$this->getButtonType(); + if($buttonType===TButtonColumnType::LinkButton) + $button=new TActiveLinkButton; + else if($buttonType===TButtonColumnType::PushButton) + $button=new TActiveButton; + else // image button + { + $button=new TActiveImageButton; + $button->setImageUrl($this->getImageUrl()); + $button->setToolTip($this->getText()); + } + $button->setText($this->getText()); + $button->setCommandName($this->getCommandName()); + $button->setCausesValidation($this->getCausesValidation()); + $button->setValidationGroup($this->getValidationGroup()); + if($this->getDataTextField()!=='' || ($buttonType===TButtonColumnType::ImageButton && $this->getDataImageUrlField()!=='')) + $button->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + $cell->getControls()->add($button); + $cell->registerObject('Button',$button); + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } +} + + +/** + * TActiveTemplateColumn class + * + * TActiveTemplateColumn customizes the layout of controls in the column with templates. + * In particular, you can specify {@link setItemTemplate ItemTemplate}, + * {@link setEditItemTemplate EditItemTemplate}, {@link setHeaderTemplate HeaderTemplate} + * and {@link setFooterTemplate FooterTemplate} to customize specific + * type of cells in the column. + * + * This is the active counterpart to the {@link TTemplateColumn} control. For that purpose, + * if sorting is allowed, the header links/buttons are replaced by active controls. + * + * Please refer to the original documentation of the {@link TTemplateColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveTemplateColumn extends TTemplateColumn { + protected function initializeHeaderCell($cell,$columnIndex) { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') { + $control=Prado::createComponent($classPath); + if($control instanceof IDataRenderer) { + if($control instanceof IItemDataRenderer) { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + $cell->getControls()->add($control); + } + else if($this->getAllowSorting()) { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') + $button->setAlternateText($text); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else { + if(($url=$this->getHeaderImageUrl())!=='') { + $image=Prado::createComponent('System.Web.UI.WebControls.TActiveImage'); + $image->setImageUrl($url); + if($text!=='') + $image->setAlternateText($text); + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } +} + +/** + * TActiveHyperLinkColumn class + * + * TActiveHyperLinkColumn contains a hyperlink for each item in the column. + * + * This is the active counterpart to the {@link THyperLinkColumn} control. For that purpose, + * if sorting is allowed, the header links/buttons are replaced by active controls. + * + * Please refer to the original documentation of the {@link THyperLinkColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveHyperLinkColumn extends THyperLinkColumn +{ + + protected function initializeHeaderCell($cell,$columnIndex) + { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') + { + $control=Prado::createComponent($classPath); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + $cell->getControls()->add($control); + } + else if($this->getAllowSorting()) + { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') + $button->setAlternateText($text); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else + { + if(($url=$this->getHeaderImageUrl())!=='') + { + $image=Prado::createComponent('System.Web.UI.WebControls.TActiveImage'); + $image->setImageUrl($url); + if($text!=='') + $image->setAlternateText($text); + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } +} + +/** + * TActiveCheckBoxColumn class + * + * TActiveCheckBoxColumn represents a checkbox column that is bound to a field in a data source. + * + * This is the active counterpart to the {@link TCheckBoxColumn} control. For that purpose, + * if sorting is allowed, the header links/buttons are replaced by active controls. + * + * Please refer to the original documentation of the {@link TCheckBoxColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveCheckBoxColumn extends TCheckBoxColumn +{ + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates a checkbox inside the cell. + * If the column is read-only or if the item is not in edit mode, + * the checkbox will be set disabled. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) + { + $checkBox=new TActiveCheckBox; + if($this->getReadOnly() || $itemType!==TListItemType::EditItem) + $checkBox->setEnabled(false); + $cell->setHorizontalAlign('Center'); + $cell->getControls()->add($checkBox); + $cell->registerObject('CheckBox',$checkBox); + if($this->getDataField()!=='') + $checkBox->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + protected function initializeHeaderCell($cell,$columnIndex) + { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') + { + $control=Prado::createComponent($classPath); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + $cell->getControls()->add($control); + } + else if($this->getAllowSorting()) + { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') + $button->setAlternateText($text); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else + { + if(($url=$this->getHeaderImageUrl())!=='') + { + $image=Prado::createComponent('System.Web.UI.WebControls.TActiveImage'); + $image->setImageUrl($url); + if($text!=='') + $image->setAlternateText($text); + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } +} + +/** + * TActiveDropDownListColumn class + * + * TActiveDropDownListColumn represents a column that is bound to a field in a data source. + * + * This is the active counterpart to the {@link TDropDownListColumn} control. For that purpose, + * if sorting is allowed, the header links/buttons are replaced by active controls. + * + * Please refer to the original documentation of the {@link TDropDownListColumn} for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveDropDownListColumn extends TDropDownListColumn +{ + protected function initializeHeaderCell($cell,$columnIndex) + { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') + { + $control=Prado::createComponent($classPath); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + $cell->getControls()->add($control); + } + else if($this->getAllowSorting()) + { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') + $button->setAlternateText($text); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else + { + if(($url=$this->getHeaderImageUrl())!=='') + { + $image=Prado::createComponent('System.Web.UI.WebControls.TActiveImage'); + $image->setImageUrl($url); + if($text!=='') + $image->setAlternateText($text); + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } + +} + +/** + * TActiveLiteralColumn class + * + * TActiveLiteralColumn represents a static text column that is bound to a field in a data source. + * The cells in the column will be displayed with static texts using the data indexed by + * {@link setDataField DataField}. You can customize the display by + * setting {@link setDataFormatString DataFormatString}. + * + * If {@link setDataField DataField} is not specified, the cells will be filled + * with {@link setText Text}. + * + * If {@link setEncode Encode} is true, the static texts will be HTML-encoded. + * + * This is the active counterpart to the {@link TLiteralColumn} control. For that purpose, + * if sorting is allowed, the header links/buttons are replaced by active controls. + * + * Please refer to the original documentation of the {@link TLiteralColumn} for usage. + * + * @author Fabio Bas + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveLiteralColumn extends TLiteralColumn { + protected function initializeHeaderCell($cell,$columnIndex) { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') { + $control=Prado::createComponent($classPath); + if($control instanceof IDataRenderer) { + if($control instanceof IItemDataRenderer) { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + $cell->getControls()->add($control); + } + else if($this->getAllowSorting()) { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') { + $button->setAlternateText($text); + $button->setToolTip($text); + } + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') { + $button=Prado::createComponent('System.Web.UI.WebControls.TActiveLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else { + if(($url=$this->getHeaderImageUrl())!=='') { + $image=Prado::createComponent('System.Web.UI.WebControls.TActiveImage'); + $image->setImageUrl($url); + if($text!=='') { + $image->setAlternateText($text); + $image->setToolTip($text); + } + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } +} + +/** + * TActiveDataGridPager class. + * + * TActiveDataGridPager represents a pager in an activedatagrid. + * + * @author Fabio Bas + * @version $Id: TDataGrid.php 3057 2011-11-09 12:35:57Z ctrlaltca@gmail.com $ + * @package System.Web.UI.ActiveControls + * @since 3.2.1 + */ +class TActiveDataGridPager extends TDataGridPager +{ + protected $_callbackoptions; + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + if($this->_callbackoptions === null) + $this->_callbackoptions = new TCallbackOptions; + return $this->_callbackoptions->getClientSide(); + } +} + +/** + * TActiveDataGridPagerEventParameter class + * + * TActiveDataGridPagerEventParameter encapsulates the parameter data for + * {@link TActiveDataGrid::onPagerCreated OnPagerCreated} event of {@link TActiveDataGrid} controls. + * The {@link getPager Pager} property indicates the datagrid pager related with the event. + * + * @author Fabio Bas + * @version $Id: TActiveDataGrid.php 3057 2011-11-09 12:35:57Z ctrlaltca@gmail.com $ + * @package System.Web.UI.ActiveControls + * @since 3.2.1 + */ +class TActiveDataGridPagerEventParameter extends TDataGridPagerEventParameter +{ + /** + * Constructor. + * @param TActiveDataGridPager datagrid pager related with the corresponding event + */ + public function __construct(TActiveDataGridPager $pager) + { + $this->_pager=$pager; + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveDataList.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDataList.php new file mode 100644 index 0000000000..dbf7006758 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDataList.php @@ -0,0 +1,117 @@ + + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @package System.Web.UI.ActiveControls + * @version $Id: TActiveDataList.php 2706 2009-09-24 14:42:30Z rojaro $ + */ + +/** + * TActiveDataList class + * + * TActiveDataList represents a data bound and updatable grid control which is the + * active counterpart to the original {@link TDataList} control. + * + * This component can be used in the same way as the regular datalist, the only + * difference is that the active datalist uses callbacks instead of postbacks + * for interaction. + * + * Please refer to the original documentation of the regular counterparts for usage. + * + * @author Marcos Aurelio Nobre + * @package System.Web.UI.ActiveControls + */ +class TActiveDataList extends TDataList implements IActiveControl { + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Sets the data source object associated with the repeater control. + * In addition, the render method of all connected pagers is called so they + * get updated when the data source is changed. Also the repeater registers + * itself for rendering in order to get it's content replaced on client side. + * @param Traversable|array|string data source object + */ + public function setDataSource($value) + { + parent::setDataSource($value); + if($this->getActiveControl()->canUpdateClientSide()) { + $this->renderPager(); + $this->getPage()->getAdapter()->registerControlToRender($this,$this->getResponse()->createHtmlWriter()); + } + } + + /** + * Returns the id of the surrounding container (span). + * @return string container id + */ + protected function getContainerID() + { + return $this->ClientID.'_Container'; + } + + /** + * Renders the repeater. + * If the repeater did not pass the prerender phase yet, it will register itself for rendering later. + * Else it will call the {@link renderRepeater()} method which will do the rendering of the repeater. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + if($this->getHasPreRendered()) { + $this->renderDataList($writer); + if($this->getActiveControl()->canUpdateClientSide()) $this->getPage()->getCallbackClient()->replaceContent($this->getContainerID(),$writer); + } + else { + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + } + } + + /** + * Loops through all {@link TActivePager} on the page and registers the ones which are set to paginate + * the repeater for rendering. This is to ensure that the connected pagers are also rendered if the + * data source changed. + */ + private function renderPager() + { + $pager=$this->getPage()->findControlsByType('TActivePager', false); + foreach($pager as $item) + { + if($item->ControlToPaginate==$this->ID) { + $writer=$this->getResponse()->createHtmlWriter(); + $this->getPage()->getAdapter()->registerControlToRender($item,$writer); + } + } + } + + /** + * Renders the repeater by writing a span tag with the container id obtained from {@link getContainerID()} + * which will be called by the replacement method of the client script to update it's content. + * @param THtmlWriter writer for the rendering purpose + */ + private function renderDataList($writer) + { + $writer->write(''); + parent::render($writer); + $writer->write(''); + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveDatePicker.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDatePicker.php new file mode 100755 index 0000000000..279739a8c5 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDatePicker.php @@ -0,0 +1,201 @@ + + * @author Christophe Boulain + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveDatePicker.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveDatePicker class + * + * The active control counter part to date picker control. + * When the date selection is changed, the {@link onCallback OnCallback} event is + * raised. + * + * @author Bradley Booms + * @author Christophe Boulain + * @version $Id: TActiveDatePicker.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1.3 + */ +class TActiveDatePicker extends TDatePicker implements ICallbackEventHandler, IActiveControl +{ + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user modifies the text in the TActiveDatePicker control and + * then tabs out of the component. Defaults to true. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',true); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * modifies the text in the TActiveDatePicker control and then tabs out of the component. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Get javascript date picker options. + * @return array date picker client-side options + */ + protected function getDatePickerOptions() + { + $options = parent::getDatePickerOptions(); + $options['CausesValidation']=$this->getCausesValidation(); + $options['ValidationGroup']=$this->getValidationGroup(); + $options['EventTarget'] = $this->getUniqueID(); + $options['ShowCalendar'] = $this->getShowCalendar(); + $options['AutoPostBack'] = $this->getAutoPostBack(); + return $options; + } + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl(){ + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Client-side Text property can only be updated after the OnLoad stage. + * @param string text content for the textbox + */ + public function setText($value){ + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData()){ + $cb=$this->getPage()->getCallbackClient(); + $cb->setValue($this, $value); + if ($this->getInputMode()==TDatePickerInputMode::DropDownList) + { + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getDate($this->getTimeStampFromText()); + $id=$this->getClientID(); + $cb->select($id.TControl::CLIENT_ID_SEPARATOR.'day', 'Value', $date['mday'], 'select'); + $cb->select($id.TControl::CLIENT_ID_SEPARATOR.'month', 'Value', $date['mon']-1, 'select'); + $cb->select($id.TControl::CLIENT_ID_SEPARATOR.'year', 'Value', $date['year'], 'select'); + + } + } + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param){ + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param){ + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Registers the javascript code to initialize the date picker. + */ + + protected function registerCalendarClientScriptPre() + { + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript("activedatepicker"); + } + + protected function renderClientControlScript($writer) + { + $cs = $this->getPage()->getClientScript(); + if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) + { + $spacer = $this->getAssetUrl('spacer.gif'); + $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; + $cs->registerEndScript('TDatePicker.spacer', $code); + } + + $options = TJavaScript::encode($this->getDatePickerOptions()); + $code = "new Prado.WebUI.TActiveDatePicker($options);"; + $cs->registerEndScript("prado:".$this->getClientID(), $code); + } + + /** + * @return TActiveDatePickerClientScript javascript validator event options. + */ + protected function createClientScript() + { + return new TActiveDatePickerClientScript; + } +} + +/** + * TActiveDatePickerClientScript class. + * + * Client-side date picker event {@link setOnDateChanged OnDateChanged} + * can be modified through the {@link TActiveDatePicker::getClientSide ClientSide} + * property of a date picker. + * + * The OnDateChanged event is raise when the date picker's date + * is changed. + * The formatted date according to {@link TDatePicker::getDateFormat DateFormat} is sent + * as parameter to this event + * + * @author Fabio Bas + * @version $Id: TActiveDatePicker.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.2.1 + */ +class TActiveDatePickerClientScript extends TCallbackClientSide +{ + /** + * Javascript code to execute when the date picker's date is changed. + * @param string javascript code + */ + public function setOnDateChanged($javascript) + { + $this->setFunction('OnDateChanged', $javascript); + } + + /** + * @return string javascript code to execute when the date picker's date is changed. + */ + public function getOnDateChanged() + { + return $this->getOption('OnDateChanged'); + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveDropDownList.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDropDownList.php new file mode 100644 index 0000000000..f0f8bba2a0 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveDropDownList.php @@ -0,0 +1,145 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveDropDownList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active list control adapter + */ +Prado::using('System.Web.UI.ActiveControls.TActiveListControlAdapter'); + +/** + * TActiveDropDownList class. + * + * The active control counter part to drop down list control. + * The {@link setAutoPostBack AutoPostBack} property is set to true by default. + * Thus, when the drop down list selection is changed the {@link onCallback OnCallback} event is + * raised after {@link OnSelectedIndexChanged} event. + * + * With {@link TBaseActiveControl::setEnableUpdate() ActiveControl.EnableUpdate} + * set to true (default is true), changes to the selection, after OnLoad event has + * been raised, will be updated. + * on the client side. + * + * List items can be changed dynamically during a callback request. + * + * @author Wei Zhuo + * @version $Id: TActiveDropDownList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveDropDownList extends TDropDownList implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveListControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveListControlAdapter($this)); + $this->setAutoPostBack(true); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * No client class for this control. + * This method overrides the parent implementation. + * @return null no javascript class name. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveDropDownList'; + } + + /** + * Creates a collection object to hold list items. A specialized + * TActiveListItemCollection is created to allow the drop down list options + * to be added. + * This method may be overriden to create a customized collection. + * @return TActiveListItemCollection the collection object + */ + protected function createListItemCollection() + { + $collection = new TActiveListItemCollection; + $collection->setControl($this); + return $collection; + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Updates the client-side options if the item list has changed after the OnLoad event. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getAdapter()->updateListItems(); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveFileUpload.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveFileUpload.php new file mode 100755 index 0000000000..10aa2ed3c9 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveFileUpload.php @@ -0,0 +1,467 @@ + + * @author Christophe Boulain + * @author Gabor Berczi (issue 349 remote vulnerability fix) + * @version $Id: TActiveFileUpload.php 3232 2013-01-02 14:42:24Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load TActiveControlAdapter and TFileUpload. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.WebControls.TFileUpload'); + +/** + * TActiveFileUpload + * + * TActiveFileUpload displays a file upload field on a page. Upon postback, + * the text entered into the field will be treated as the name of the file + * that will be uploaded to the server. The property {@link getHasFile HasFile} + * indicates whether the file upload is successful. If successful, the file + * may be obtained by calling {@link saveAs} to save it at a specified place. + * You can use {@link getFileName FileName}, {@link getFileType FileType}, + * {@link getFileSize FileSize} to get the original client-side file name, + * the file mime type, and the file size information. If the upload is not + * successful, {@link getErrorCode ErrorCode} contains the error code + * describing the cause of failure. + * + * TActiveFileUpload raises {@link onFileUpload OnFileUpload} event if a file is uploaded + * (whether it succeeds or not). + * + * TActiveFileUpload actually does a postback in a hidden IFrame, and then does a callback. + * This callback then raises the {@link onFileUpload OnFileUpload} event. After the postback + * a status icon is displayed; either a green checkmark if the upload is successful, + * or a red x if there was an error. + * + * @author Bradley Booms + * @author Christophe Boulain + * @version $Id: TActiveFileUpload.php 3232 2013-01-02 14:42:24Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ +class TActiveFileUpload extends TFileUpload implements IActiveControl, ICallbackEventHandler, INamingContainer +{ + + const SCRIPT_PATH = 'prado/activefileupload'; + + /** + * @var THiddenField a flag to tell which component is doing the callback. + */ + private $_flag; + /** + * @var TImage that spins to show that the file is being uploaded. + */ + private $_busy; + /** + * @var TImage that shows a green check mark for completed upload. + */ + private $_success; + /** + * @var TImage that shows a red X for incomplete upload. + */ + private $_error; + /** + * @var TInlineFrame used to submit the data in an "asynchronous" fashion. + */ + private $_target; + + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct(){ + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + + /** + * @param string asset file in the self::SCRIPT_PATH directory. + * @return string asset file url. + */ + protected function getAssetUrl($file='') + { + $base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl(); + return $base.'/'.self::SCRIPT_PATH.'/'.$file; + } + + + /** + * This method is invoked when a file is uploaded. + * If you override this method, be sure to call the parent implementation to ensure + * the invocation of the attached event handlers. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onFileUpload($param) + { + if ($this->_flag->getValue() && $this->getPage()->getIsPostBack() && $param == $this->_target->getUniqueID()){ + // save the file so that it will persist past the end of this return. + $localName = str_replace('\\', '/', tempnam(Prado::getPathOfNamespace($this->getTempPath()),'')); + parent::saveAs($localName); + + $filename=addslashes($this->getFileName()); + + + $params = new TActiveFileUploadCallbackParams; + $params->localName = $localName; + $params->fileName = $filename; + $params->fileSize = $this->getFileSize(); + $params->fileType = $this->getFileType(); + $params->errorCode = $this->getErrorCode(); + + // return some javascript to display a completion status. + echo << + Options = new Object(); + Options.clientID = '{$this->getClientID()}'; + Options.targetID = '{$this->_target->getUniqueID()}'; + Options.fileName = '{$params->fileName}'; + Options.fileSize = '{$params->fileSize}'; + Options.fileType = '{$params->fileType}'; + Options.errorCode = '{$params->errorCode}'; + Options.callbackToken = '{$this->pushParamsAndGetToken($params)}'; + parent.Prado.WebUI.TActiveFileUpload.onFileUpload(Options); + +EOS; + + exit(); + } + } + + /** + * @return string the path where the uploaded file will be stored temporarily, in namespace format + * default "Application.runtime.*" + */ + public function getTempPath(){ + return $this->getViewState('TempPath', 'Application.runtime.*'); + } + + /** + * @param string the path where the uploaded file will be stored temporarily in namespace format + * default "Application.runtime.*" + */ + public function setTempPath($value){ + $this->setViewState('TempPath',$value,'Application.runtime.*'); + } + + /** + * @return boolean a value indicating whether an automatic callback to the server will occur whenever the user modifies the text in the TTextBox control and then tabs out of the component. Defaults to true. + * Note: When set to false, you will need to trigger the callback yourself. + */ + public function getAutoPostBack(){ + return $this->getViewState('AutoPostBack', true); + } + + /** + * @param boolean a value indicating whether an automatic callback to the server will occur whenever the user modifies the text in the TTextBox control and then tabs out of the component. Defaults to true. + * Note: When set to false, you will need to trigger the callback yourself. + */ + public function setAutoPostBack($value){ + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string A chuck of javascript that will need to be called if {{@link getAutoPostBack AutoPostBack} is set to false} + */ + public function getCallbackJavascript(){ + return "Prado.WebUI.TActiveFileUpload.fileChanged(\"{$this->getClientID()}\")"; + } + + /** + * @throws TInvalidDataValueException if the {@link getTempPath TempPath} is not writable. + */ + public function onInit($sender){ + parent::onInit($sender); + + if (!Prado::getApplication()->getCache()) + if (!Prado::getApplication()->getSecurityManager()) + throw new Exception('TActiveFileUpload needs either an application level cache or a security manager to work securely'); + + if (!is_writable(Prado::getPathOfNamespace($this->getTempPath()))){ + throw new TInvalidDataValueException("activefileupload_temppath_invalid", $this->getTempPath()); + } + } + + /** + * Raises OnFileUpload event. + * + * This method is required by {@link ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param){ + $cp = $param->getCallbackParameter(); + if ($key = $cp->targetID == $this->_target->getUniqueID()){ + + $params = $this->popParamsByToken($cp->callbackToken); + + $_FILES[$key]['name'] = $params->fileName; + $_FILES[$key]['size'] = intval($params->fileSize); + $_FILES[$key]['type'] = $params->fileType; + $_FILES[$key]['error'] = intval($params->errorCode); + $_FILES[$key]['tmp_name'] = $params->localName; + $this->loadPostData($key, null); + + $this->raiseEvent('OnFileUpload', $this, $param); + } + } + + /** + * Raises postdata changed event. + * This method calls {@link onFileUpload} method + * This method is primarily used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onFileUpload($this->getPage()->getRequest()->itemAt('TActiveFileUpload_TargetId')); + } + + protected function pushParamsAndGetToken(TActiveFileUploadCallbackParams $params) + { + if ($cache = Prado::getApplication()->getCache()) + { + // this is the most secure method, file info can't be forged from client side, no matter what + $token = md5('TActiveFileUpload::Params::'.$this->ClientID.'::'+rand(1000*1000,9999*1000)); + $cache->set($token, serialize($params), 5*60); // expire in 5 minutes - the callback should arrive back in seconds, actually + } + else + if ($mgr = Prado::getApplication()->getSecurityManager()) + { + // this is a less secure method, file info can be still forged from client side, but only if attacker knows the secret application key + $token = urlencode(base64_encode($mgr->encrypt(serialize($params)))); + } + else + throw new Exception('TActiveFileUpload needs either an application level cache or a security manager to work securely'); + + return $token; + } + + protected function popParamsByToken($token) + { + if ($cache = Prado::getApplication()->getCache()) + { + $v = $cache->get($token); + assert($v!=''); + $cache->delete($token); // remove it from cache so it can't be used again and won't take up space either + $params = unserialize($v); + } + else + if ($mgr = Prado::getApplication()->getSecurityManager()) + { + $v = $mgr->decrypt(base64_decode(urldecode($token))); + $params = unserialize($v); + } + else + throw new Exception('TActiveFileUpload needs either an application level cache or a security manager to work securely'); + + assert($params instanceof TActiveFileUploadCallbackParams); + + return $params; + } + + /** + * Publish the javascript + */ + public function onPreRender($param) + { + parent::onPreRender($param); + + if(!$this->getPage()->getIsPostBack() && isset($_GET['TActiveFileUpload_InputId']) && isset($_GET['TActiveFileUpload_TargetId']) && $_GET['TActiveFileUpload_InputId'] == $this->getClientID()) + { + // tricky workaround to intercept "uploaded file too big" error: real uploads happens in onFileUpload instead + $this->_errorCode = UPLOAD_ERR_FORM_SIZE; + $localName = str_replace('\\', '/', tempnam(Prado::getPathOfNamespace($this->getTempPath()),'')); + $fileName = addslashes($this->getFileName()); + + $params = new TActiveFileUploadCallbackParams; + $params->localName = $localName; + $params->fileName = $fileName; + $params->fileSize = $this->getFileSize(); + $params->fileType = $this->getFileType(); + $params->errorCode = $this->getErrorCode(); + + echo << + Options = new Object(); + Options.clientID = '{$_GET['TActiveFileUpload_InputId']}'; + Options.targetID = '{$_GET['TActiveFileUpload_TargetId']}'; + Options.fileName = '{$params->fileName}'; + Options.fileSize = '{$params->fileSize}'; + Options.fileType = '{$params->fileType}'; + Options.errorCode = '{$params->errorCode}'; + Options.callbackToken = '{$this->pushParamsAndGetToken($params)}'; + parent.Prado.WebUI.TactiveFileUpload.onFileUpload(Options); + +EOS; + } + } + + public function createChildControls(){ + $this->_flag = Prado::createComponent('THiddenField'); + $this->_flag->setID('Flag'); + $this->getControls()->add($this->_flag); + + $this->_busy = Prado::createComponent('TImage'); + $this->_busy->setID('Busy'); + $this->_busy->setImageUrl($this->getAssetUrl('ActiveFileUploadIndicator.gif')); + $this->_busy->setStyle("display:none"); + $this->getControls()->add($this->_busy); + + $this->_success = Prado::createComponent('TImage'); + $this->_success->setID('Success'); + $this->_success->setImageUrl($this->getAssetUrl('ActiveFileUploadComplete.png')); + $this->_success->setStyle("display:none"); + $this->getControls()->add($this->_success); + + $this->_error = Prado::createComponent('TImage'); + $this->_error->setID('Error'); + $this->_error->setImageUrl($this->getAssetUrl('ActiveFileUploadError.png')); + $this->_error->setStyle("display:none"); + $this->getControls()->add($this->_error); + + $this->_target = Prado::createComponent('TInlineFrame'); + $this->_target->setID('Target'); + $this->_target->setFrameUrl($this->getAssetUrl('ActiveFileUploadBlank.html')); + $this->_target->setStyle("width:0px; height:0px;"); + $this->_target->setShowBorder(false); + $this->getControls()->add($this->_target); + } + + + /** + * Removes localfile on ending of the callback. + */ + public function onUnload($param){ + if ($this->getPage()->getIsCallback() && + $this->getHasFile() && + file_exists($this->getLocalName())){ + unlink($this->getLocalName()); + } + parent::onUnload($param); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl(){ + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Adds ID attribute, and renders the javascript for active component. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer){ + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + + $this->getPage()->getClientScript()->registerPradoScript('activefileupload'); + $this->getActiveControl()->registerCallbackClientScript($this->getClientClassName(),$this->getClientOptions()); + } + + /** + * @return string corresponding javascript class name for this control. + */ + protected function getClientClassName(){ + return 'Prado.WebUI.TActiveFileUpload'; + } + + /** + * Gets the client side options for this control. + * @return array ( inputID => input client ID, + * flagID => flag client ID, + * targetName => target unique ID, + * formID => form client ID, + * indicatorID => upload indicator client ID, + * completeID => complete client ID, + * errorID => error client ID) + */ + protected function getClientOptions(){ + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + + $options['inputID'] = $this->getClientID(); + $options['flagID'] = $this->_flag->getClientID(); + $options['targetID'] = $this->_target->getUniqueID(); + $options['formID'] = $this->getPage()->getForm()->getClientID(); + $options['indicatorID'] = $this->_busy->getClientID(); + $options['completeID'] = $this->_success->getClientID(); + $options['errorID'] = $this->_error->getClientID(); + $options['autoPostBack'] = $this->getAutoPostBack(); + return $options; + } + + /** + * Saves the uploaded file. + * @param string the file name used to save the uploaded file + * @param boolean whether to delete the temporary file after saving. + * If true, you will not be able to save the uploaded file again. + * @return boolean true if the file saving is successful + */ + public function saveAs($fileName,$deleteTempFile=true){ + if (($this->getErrorCode()===UPLOAD_ERR_OK) && (file_exists($this->getLocalName()))){ + if ($deleteTempFile) + return rename($this->getLocalName(),$fileName); + else + return copy($this->getLocalName(),$fileName); + } else + return false; + } + + /** + * @return TImage the image displayed when an upload + * completes successfully. + */ + public function getSuccessImage(){ + $this->ensureChildControls(); + return $this->_success; + } + + /** + * @return TImage the image displayed when an upload + * does not complete successfully. + */ + public function getErrorImage(){ + $this->ensureChildControls(); + return $this->_error; + } + + /** + * @return TImage the image displayed when an upload + * is in progress. + */ + public function getBusyImage(){ + $this->ensureChildControls(); + return $this->_busy; + } +} + +/** + * TActiveFileUploadCallbackParams is an internal class used by {@link TActiveFileUpload}. + * + * @author Bradley Booms + * @author Christophe Boulain + * @version $Id: TActiveFileUpload.php 3232 2013-01-02 14:42:24Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ +class TActiveFileUploadCallbackParams +{ + public $localName; + public $fileName; + public $fileSize; + public $fileType; + public $errorCode; +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveHiddenField.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveHiddenField.php new file mode 100644 index 0000000000..0d4b3737ee --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveHiddenField.php @@ -0,0 +1,116 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +/** + * TActiveHiddenField class + * + * TActiveHiddenField displays a hidden input field on a Web page. + * The value of the input field can be accessed via {@link getValue Value} property. + * + * @author Carl G. Mathisen + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveHiddenField extends THiddenField implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Client-side Value property can only be updated after the OnLoad stage. + * @param string text content for the hidden field + */ + public function setValue($value) + { + parent::setValue($value); + if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData()) + $this->getPage()->getCallbackClient()->setValue($this, $value); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveHiddenField'; + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveHyperLink.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveHyperLink.php new file mode 100644 index 0000000000..6ba8a8015f --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveHyperLink.php @@ -0,0 +1,102 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveHyperLink.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveHyperLink class. + * + * The active control counterpart of THyperLink component. When + * {@link TBaseActiveControl::setEnableUpdate ActiveControl.EnableUpdate} + * property is true the during a callback request, setting {@link setText Text} + * property will also set the text of the label on the client upon callback + * completion. Similarly, for other properties such as {@link setImageUrl ImageUrl}, + * {@link setNavigateUrl NavigateUrl} and {@link setTarget Target}. + * + * @author Wei Zhuo + * @version $Id: TActiveHyperLink.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveHyperLink extends THyperLink implements IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl basic active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * On callback response, the inner HTMl of the label is updated. + * @param string the text value of the label + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->update($this, $value); + } + + /** + * Sets the location of image file of the THyperLink. + * @param string the image file location + */ + public function setImageUrl($value) + { + parent::setImageUrl($value); + if($this->getActiveControl()->canUpdateClientSide() && $value !== '') + { + $textWriter = new TTextWriter; + $renderer = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), $textWriter); + $this->createImage($value)->renderControl($renderer); + $this->getPage()->getCallbackClient()->update($this, $textWriter->flush()); + } + } + + /** + * Sets the URL to link to when the THyperLink component is clicked. + * @param string the URL + */ + public function setNavigateUrl($value) + { + parent::setNavigateUrl($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + //replace & with & and urldecode the url (setting the href using javascript is literal) + $url = urldecode(str_replace('&', '&', $value)); + $this->getPage()->getCallbackClient()->setAttribute($this, 'href', $url); + } + } + + /** + * Sets the target window or frame to display the Web page content linked to when the THyperLink component is clicked. + * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. + */ + public function setTarget($value) + { + parent::setTarget($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'target', $value); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveImage.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveImage.php new file mode 100644 index 0000000000..752c7a0457 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveImage.php @@ -0,0 +1,92 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveImage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveImage class. + * + * TActiveImage allows the {@link setAlternateText AlternateText}, + * {@link setImageAlign ImageAlign}, {@link setImageUrl ImageUrl}, + * and {@link setDescriptionUrl DescriptionUrl} to be updated during + * a callback request. + * + * @author Wei Zhuo + * @version $Id: TActiveImage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveImage extends TImage implements IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl basic active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Sets the alternative text to be displayed in the TImage when the image is unavailable. + * @param string the alternative text + */ + public function setAlternateText($value) + { + parent::setAlternateText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'alt', $value); + } + + /** + * Sets the alignment of the image with respective to other elements on the page. + * Possible values include: absbottom, absmiddle, baseline, bottom, left, + * middle, right, texttop, and top. If an empty string is passed in, + * imagealign attribute will not be rendered. + * @param string the alignment of the image + */ + public function setImageAlign($value) + { + parent::setImageAlign($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'align', $value); + } + + /** + * @param string the URL of the image file + */ + public function setImageUrl($value) + { + parent::setImageUrl($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'src', $value); + } + + /** + * @param string the URL to the long description of the image. + */ + public function setDescriptionUrl($value) + { + parent::setDescriptionUrl($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'longdesc', $value); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveImageButton.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveImageButton.php new file mode 100644 index 0000000000..b2aa996050 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveImageButton.php @@ -0,0 +1,168 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveImageButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveImageButton class. + * + * TActiveImageButton is the active control counter part to TImageButton. + * When a TActiveImageButton is clicked, rather than a normal post back request a + * callback request is initiated. + * + * The {@link onCallback OnCallback} event is raised during a callback request + * and it is raise after the {@link onClick OnClick} event. + * + * @author Wei Zhuo + * @version $Id: TActiveImageButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveImageButton extends TImageButton implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl basic active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Sets the alternative text to be displayed in the TImage when the image is unavailable. + * @param string the alternative text + */ + public function setAlternateText($value) + { + parent::setAlternateText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'alt', $value); + } + + /** + * Sets the alignment of the image with respective to other elements on the page. + * Possible values include: absbottom, absmiddle, baseline, bottom, left, + * middle, right, texttop, and top. If an empty string is passed in, + * imagealign attribute will not be rendered. + * @param string the alignment of the image + */ + public function setImageAlign($value) + { + parent::setImageAlign($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'align', $value); + } + + /** + * @param string the URL of the image file + */ + public function setImageUrl($value) + { + parent::setImageUrl($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'src', $value); + } + + /** + * @param string the URL to the long description of the image. + */ + public function setDescriptionUrl($value) + { + parent::setDescriptionUrl($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->setAttribute($this, 'longdesc', $value); + } + + /** + * Raises the callback event. This method is required by + * {@link ICallbackEventHandler ICallbackEventHandler} interface. If + * {@link getCausesValidation CausesValidation} is true, it will invoke the page's + * {@link TPage::validate} method first. It will raise + * {@link onClick OnClick} event first and then the {@link onCallback OnCallback} event. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->raisePostBackEvent($param); + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Register the x and y hidden input names of the position clicked. + * @param THtmlWriter the renderer. + */ + public function onPreRender($writer) + { + parent::onPreRender($writer); + $uid = $uid=$this->getUniqueID(); + $this->getPage()->registerPostDataLoader($uid.'_x'); + $this->getPage()->registerPostDataLoader($uid.'_y'); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * @return string corresponding javascript class name for this TActiveLinkButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveImageButton'; + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveLabel.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveLabel.php new file mode 100644 index 0000000000..4fd19a56e4 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveLabel.php @@ -0,0 +1,90 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveLabel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveLabel class + * + * The active control counterpart of TLabel component. When + * {@link TBaseActiveControl::setEnableUpdate ActiveControl.EnableUpdate} + * property is true the during a callback request, setting {@link setText Text} + * property will also set the text of the label on the client upon callback + * completion. Similarly, setting {@link setForControl ForControl} will also set + * the client-side "for" attribute on the label. + * + * @author Wei Zhuo + * @version $Id: TActiveLabel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveLabel extends TLabel implements IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl basic active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * On callback response, the inner HTML of the label is updated. + * @param string the text value of the label + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->update($this, $value); + } + + /** + * Sets the ID of the control that the label is associated with. + * The control must be locatable via {@link TControl::findControl} using the ID. + * On callback response, the For attribute of the label is updated. + * @param string the associated control ID + */ + public function setForControl($value) + { + parent::setForControl($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $id=$this->findControl($value)->getClientID(); + $this->getPage()->getCallbackClient()->setAttribute($this, 'for', $id); + } + } + + /** + * Adds attribute id to the renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) { + $writer->addAttribute('id',$this->getClientID()); + parent::addAttributesToRender($writer); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveLinkButton.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveLinkButton.php new file mode 100644 index 0000000000..d8a8ffcbf9 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveLinkButton.php @@ -0,0 +1,161 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveLinkButton.php 3292 2013-05-31 08:51:42Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveLinkButton is the active control counter part to TLinkButton. + * + * When a TActiveLinkButton is clicked, rather than a normal post back request a + * callback request is initiated. + * + * The {@link onCallback OnCallback} event is raised during a callback request + * and it is raise after the {@link onClick OnClick} event. + * + * When the {@link TBaseActiveCallbackControl::setEnableUpdate ActiveControl.EnableUpdate} + * property is true, changing the {@link setText Text} property during callback request + * will update the link text upon callback response completion. + * + * @author Wei Zhuo + * @version $Id: TActiveLinkButton.php 3292 2013-05-31 08:51:42Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveLinkButton extends TLinkButton implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by + * {@link ICallbackEventHandlerICallbackEventHandler} interface. If + * {@link getCausesValidation CausesValidation} is true, it will + * invoke the page's {@link TPage::validate validate} method first. It will raise + * {@link onClick OnClick} event first and then the {@link onCallback OnCallback} + * event. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->raisePostBackEvent($param); + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Updates the link text on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param string caption of the button + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->update($this, $value); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + + if($this->getEnabled(true)) + { + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + } + + /** + * Ensures that the anchor is rendered correctly when its Enabled property + * changes in a callback + * @param bool enabled + */ + public function setEnabled($value) + { + parent::setEnabled($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + if($this->getEnabled(true)) + { + $nop = "javascript:;//".$this->getClientID(); + $this->getPage()->getCallbackClient()->setAttribute($this, 'href', $nop); + + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + + } else { + $this->getPage()->getCallbackClient()->setAttribute($this, 'href', false); + } + } + } + + /** + * @return string corresponding javascript class name for this TActiveLinkButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveLinkButton'; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveListBox.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveListBox.php new file mode 100644 index 0000000000..dfb4bf6e30 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveListBox.php @@ -0,0 +1,160 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveListBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active list control adapter + */ +Prado::using('System.Web.UI.ActiveControls.TActiveListControlAdapter'); + +/** + * TActiveListBox class. + * + * List items can be added dynamically during a callback request. + * + * @author Wei Zhuo + * @version $Id: TActiveListBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveListBox extends TListBox implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveListControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveListControlAdapter($this)); + $this->setAutoPostBack(true); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Creates a collection object to hold list items. A specialized + * TActiveListItemCollection is created to allow the drop down list options + * to be added. + * This method may be overriden to create a customized collection. + * @return TActiveListItemCollection the collection object + */ + protected function createListItemCollection() + { + $collection = new TActiveListItemCollection; + $collection->setControl($this); + return $collection; + } + + /** + * Javascript client class for this control. + * This method overrides the parent implementation. + * @return null no javascript class name. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveListBox'; + } + + /** + * Sets the selection mode of the list control (Single, Multiple) + * on the client-side if the {@link setEnableUpdate EnableUpdate} + * property is set to true. + * @param string the selection mode + */ + public function setSelectionMode($value) + { + parent::setSelectionMode($value); + $multiple = $this->getIsMultiSelect(); + $id = $this->getUniqueID(); $multi_id = $id.'[]'; + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $client->setAttribute($this, 'multiple', $multiple ? 'multiple' : false); + $client->setAttribute($this, 'name', $multiple ? $multi_id : $id); + if($multiple) + $client->addPostDataLoader($multi_id); + } + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Updates the client-side options if the item list has changed after the OnLoad event. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getAdapter()->updateListItems(); + $multiple = $this->getIsMultiSelect(); + $id = $this->getUniqueID(); $multi_id = $id.'[]'; + if($multiple) + $this->getPage()->registerPostDataLoader($multi_id); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveListControlAdapter.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveListControlAdapter.php new file mode 100644 index 0000000000..e48b936489 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveListControlAdapter.php @@ -0,0 +1,258 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveListControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.WebControls.TListControl'); + +/** + * TActiveListControlAdapter class. + * + * Adapte the list controls to allows the selections on the client-side to be altered + * during callback response. + * + * @author Wei Zhuo + * @version $Id: TActiveListControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveListControlAdapter extends TActiveControlAdapter implements IListControlAdapter +{ + /** + * @return boolean true if can update client-side attributes. + */ + protected function canUpdateClientSide() + { + return $this->getControl()->getActiveControl()->canUpdateClientSide(); + } + + /** + * Selects an item based on zero-base index on the client side. + * @param integer the index (zero-based) of the item to be selected + */ + public function setSelectedIndex($index) + { + if($this->canUpdateClientSide()) + { + $this->updateListItems(); + // if a prompt is set, we mimic the postback behaviour of not counting it + // in the index. We assume the prompt is _always_ the first item (Issue #368) + $promptValue=$this->getControl()->getPromptValue(); + if($promptValue==='') + $promptValue=$this->getControl()->getPromptText(); + if($promptValue!=='') + $index++; + + if($index >= 0 && $index <= $this->getControl()->getItemCount()) + $this->getPage()->getCallbackClient()->select( + $this->getControl(), 'Index', $index); + } + } + + /** + * Selects a list of item based on zero-base indices on the client side. + * @param array list of index of items to be selected + */ + public function setSelectedIndices($indices) + { + if($this->canUpdateClientSide()) + { + $this->updateListItems(); + $n = $this->getControl()->getItemCount(); + + $promptValue=$this->getControl()->getPromptValue(); + if($promptValue==='') + $promptValue=$this->getControl()->getPromptText(); + + $list = array(); + foreach($indices as $index) + { + $index = intval($index); + if($promptValue!=='') + $index++; + if($index >= 0 && $index <= $n) + $list[] = $index; + } + if(count($list) > 0) + $this->getPage()->getCallbackClient()->select( + $this->getControl(), 'Indices', $list); + } + } + + /** + * Sets selection by item value on the client side. + * @param string the value of the item to be selected. + */ + public function setSelectedValue($value) + { + if($this->canUpdateClientSide()) + { + $this->updateListItems(); + $this->getPage()->getCallbackClient()->select( + $this->getControl(), 'Value', $value); + } + } + + /** + * Sets selection by a list of item values on the client side. + * @param array list of the selected item values + */ + public function setSelectedValues($values) + { + if($this->canUpdateClientSide()) + { + $this->updateListItems(); + $list = array(); + foreach($values as $value) + $list[] = $value; + if(count($list) > 0) + $this->getPage()->getCallbackClient()->select( + $this->getControl(), 'Values', $list); + } + } + + /** + * Clears all existing selections on the client side. + */ + public function clearSelection() + { + if($this->canUpdateClientSide()) + { + $this->updateListItems(); + if($this->getControl() instanceof TActiveDropDownList) + { + // clearing a TActiveDropDownList's selection actually doesn't select the first item; + // we mimic the postback behaviour selecting it (Issue #368) + $this->getPage()->getCallbackClient()->select($this->getControl(), 'Index', 0); + } else { + $this->getPage()->getCallbackClient()->select($this->getControl(), 'Clear'); + } + } + } + + /** + * Update the client-side list options. + */ + public function updateListItems() + { + if($this->canUpdateClientSide()) + { + $items = $this->getControl()->getItems(); + if($items instanceof TActiveListItemCollection + && $items->getListHasChanged()) + { + $items->updateClientSide(); + } + } + } +} + +/** + * TActiveListItemCollection class. + * + * Allows TActiveDropDownList and TActiveListBox to add new options + * during callback response. New options can only be added after the + * {@link TControl::onLoad OnLoad} event. + * + * The {@link getListHasChanged ListHasChanged} property is true when the + * list items has changed. The control responsible for the list needs to + * repopulate the client-side options. + * + * @author Wei Zhuo + * @version $Id: TActiveListControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveListItemCollection extends TListItemCollection +{ + /** + * @var IActiveControl control instance. + */ + private $_control; + /** + * @var boolean true if list items were changed. + */ + private $_hasChanged=false; + + /** + * @return boolean true if active controls can update client-side and + * the onLoad event has already been raised. + */ + protected function canUpdateClientSide() + { + return $this->getControl()->getActiveControl()->canUpdateClientSide() + && $this->getControl()->getHasLoaded(); + } + + /** + * @param IActiveControl a active list control. + */ + public function setControl(IActiveControl $control) + { + $this->_control = $control; + } + + /** + * @return IActiveControl active control using the collection. + */ + public function getControl() + { + return $this->_control; + } + + /** + * @return boolean true if the list has changed after onLoad event. + */ + public function getListHasChanged() + { + return $this->_hasChanged; + } + + /** + * Update client-side list items. + */ + public function updateClientSide() + { + $client = $this->getControl()->getPage()->getCallbackClient(); + $client->setListItems($this->getControl(), $this); + $this->_hasChanged=false; + } + + /** + * Inserts an item into the collection. + * The new option is added on the client-side during callback. + * @param integer the location where the item will be inserted. + * The current item at the place and the following ones will be moved backward. + * @param TListItem the item to be inserted. + * @throws TInvalidDataTypeException if the item being inserted is neither a string nor TListItem + */ + public function insertAt($index, $value) + { + parent::insertAt($index, $value); + if($this->canUpdateClientSide()) + $this->_hasChanged = true; + } + + /** + * Removes an item from at specified index. + * @param int zero based index. + */ + public function removeAt($index) + { + parent::removeAt($index); + if($this->canUpdateClientSide()) + $this->_hasChanged = true; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveMultiView.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveMultiView.php new file mode 100644 index 0000000000..5729634b79 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveMultiView.php @@ -0,0 +1,112 @@ + + * @link http://www.landwehr-software.de/ + * @copyright Copyright © 2009 LANDWEHR Computer und Software GmbH + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI.ActiveControls + */ + +/** + * Includes the following used classes + */ +Prado::using('System.Web.UI.WebControls.TMultiView'); + +/** + * TActiveMultiView class. + * + * TActiveMultiView is the active counterpart to the original {@link TMultiView} control. + * It re-renders on Callback when {@link setActiveView ActiveView} or + * {@link setActiveViewIndex ActiveViewIndex} is called. + * + * Please refer to the original documentation of the regular counterpart for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.6 + */ +class TActiveMultiView extends TMultiView implements IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Returns the id of the surrounding container (span). + * @return string container id + */ + protected function getContainerID() + { + return $this->ClientID.'_Container'; + } + + /** + * Renders the TActiveMultiView. + * If the MutliView did not pass the prerender phase yet, it will register itself for rendering later. + * Else it will call the {@link renderMultiView()} method which will do the rendering of the MultiView. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + if($this->getHasPreRendered()) { + $this->renderMultiView($writer); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this->getContainerID(),$writer); + } + else + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + } + + /** + * Renders the TActiveMultiView by writing a span tag with the container id obtained from {@link getContainerID()} + * which will be called by the replacement method of the client script to update it's content. + * @param $writer THtmlWriter writer for the rendering purpose + */ + protected function renderMultiView($writer) + { + $writer->addAttribute('id', $this->getContainerID()); + $writer->renderBeginTag('span'); + parent::render($writer); + $writer->renderEndTag(); + } + + /** + * @param integer the zero-based index of the current view in the view collection. -1 if no active view. + * @throws TInvalidDataValueException if the view index is invalid + */ + public function setActiveViewIndex($value) + { + $old=parent::getActiveViewIndex(); + parent::setActiveViewIndex($value); + if($this->getActiveControl()->canUpdateClientSide() && $old!=$value) + $this->getPage()->getAdapter()->registerControlToRender($this,$this->getResponse()->createHtmlWriter()); + } + + /** + * @param TView the view to be activated + * @throws TInvalidOperationException if the view is not in the view collection + */ + public function setActiveView($value) + { + $old=parent::getActiveView(); + parent::setActiveView($value); + if($this->getActiveControl()->canUpdateClientSide() && $old!=$value) + $this->getPage()->getAdapter()->registerControlToRender($this,$this->getResponse()->createHtmlWriter()); + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/gui/baculum/framework/Web/UI/ActiveControls/TActivePageAdapter.php new file mode 100644 index 0000000000..03fe2f1695 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -0,0 +1,416 @@ + + * @author Gabor Berczi (lazyload additions & progressive rendering) + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActivePageAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load callback response adapter class. + */ +Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter'); +Prado::using('System.Web.UI.ActiveControls.TCallbackClientScript'); +Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); + +/** + * TActivePageAdapter class. + * + * Callback request handler. + * + * @author Wei Zhuo + * @author Gabor Berczi (lazyload additions & progressive rendering) + * @version $Id: TActivePageAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActivePageAdapter extends TControlAdapter +{ + /** + * Callback response data header name. + */ + const CALLBACK_DATA_HEADER = 'X-PRADO-DATA'; + /** + * Callback response client-side action header name. + */ + const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS'; + /** + * Callback error header name. + */ + const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR'; + /** + * Callback page state header name. + */ + const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE'; + /** + * Script list header name. + */ + const CALLBACK_SCRIPTLIST_HEADER = 'X-PRADO-SCRIPTLIST'; + /** + * Stylesheet list header name. + */ + const CALLBACK_STYLESHEETLIST_HEADER = 'X-PRADO-STYLESHEETLIST'; + /** + * Stylesheet header name. + */ + const CALLBACK_STYLESHEET_HEADER = 'X-PRADO-STYLESHEET'; + /** + * Hidden field list header name. + */ + const CALLBACK_HIDDENFIELDLIST_HEADER = 'X-PRADO-HIDDENFIELDLIST'; + + /** + * Callback redirect url header name. + */ + const CALLBACK_REDIRECT = 'X-PRADO-REDIRECT'; + + /** + * @var ICallbackEventHandler callback event handler. + */ + private $_callbackEventTarget; + /** + * @var mixed callback event parameter. + */ + private $_callbackEventParameter; + /** + * @var TCallbackClientScript callback client script handler + */ + private $_callbackClient; + + private $_controlsToRender=array(); + + /** + * Constructor, trap errors and exception to let the callback response + * handle them. + */ + public function __construct(TPage $control) + { + parent::__construct($control); + + //TODO: can this be done later? + $response = $this->getApplication()->getResponse(); + $response->setAdapter(new TCallbackResponseAdapter($response)); + + $this->trapCallbackErrorsExceptions(); + } + + /** + * Process the callback request. + * @param THtmlWriter html content writer. + */ + public function processCallbackEvent($writer) + { + Prado::trace("ActivePage raiseCallbackEvent()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + $this->raiseCallbackEvent(); + } + + /** + * Register a control for defered render() call. + * @param TControl control for defered rendering + * @param THtmlWriter the renderer + */ + public function registerControlToRender($control,$writer) + { + $id = $control->getUniqueID(); + if(!isset($this->_controlsToRender[$id])) + $this->_controlsToRender[$id] = array($control,$writer); + } + + /** + * Trap errors and exceptions to be handled by TCallbackErrorHandler. + */ + protected function trapCallbackErrorsExceptions() + { + $this->getApplication()->setErrorHandler(new TCallbackErrorHandler); + } + + /** + * Render the callback response. + * @param THtmlWriter html content writer. + */ + public function renderCallbackResponse($writer) + { + Prado::trace("ActivePage renderCallbackResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + if(($url = $this->getResponse()->getAdapter()->getRedirectedUrl())===null) + $this->renderResponse($writer); + else + $this->redirect($url); + } + + /** + * Redirect url on the client-side using javascript. + * @param string new url to load. + */ + protected function redirect($url) + { + Prado::trace("ActivePage redirect()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + $this->appendContentPart($this->getResponse(), self::CALLBACK_REDIRECT, $url); + //$this->getResponse()->appendHeader(self::CALLBACK_REDIRECT.': '.$url); + } + + /** + * Renders the callback response by adding additional callback data and + * javascript actions in the header and page state if required. + * @param THtmlWriter html content writer. + */ + protected function renderResponse($writer) + { + Prado::trace("ActivePage renderResponse()",'System.Web.UI.ActiveControls.TActivePageAdapter'); + //renders all the defered render() calls. + foreach($this->_controlsToRender as $rid => $forRender) + $forRender[0]->render($forRender[1]); + + $response = $this->getResponse(); + + //send response data in header + if($response->getHasAdapter()) + { + $responseData = $response->getAdapter()->getResponseData(); + if($responseData!==null) + { + $data = TJavaScript::jsonEncode($responseData); + + $this->appendContentPart($response, self::CALLBACK_DATA_HEADER, $data); + //$response->appendHeader(self::CALLBACK_DATA_HEADER.': '.$data); + } + } + + //sends page state in header + if(($handler = $this->getCallbackEventTarget()) !== null) + { + if($handler->getActiveControl()->getClientSide()->getEnablePageStateUpdate()) + { + $pagestate = $this->getPage()->getClientState(); + $this->appendContentPart($response, self::CALLBACK_PAGESTATE_HEADER, $pagestate); + //$response->appendHeader(self::CALLBACK_PAGESTATE_HEADER.': '.$pagestate); + } + } + + //safari must receive at least 1 byte of data. + $writer->write(" "); + + //output the end javascript + if($this->getPage()->getClientScript()->hasEndScripts()) + { + $writer = $response->createHtmlWriter(); + $this->getPage()->getClientScript()->renderEndScripts($writer); + $this->getPage()->getCallbackClient()->evaluateScript($writer); + } + + //output the actions + $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute(); + $actions = TJavaScript::jsonEncode($executeJavascript); + $this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions); + //$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); + + + $cs = $this->Page->getClientScript(); + + // collect all stylesheet file references + $stylesheets = $cs->getStyleSheetUrls(); + if (count($stylesheets)>0) + $this->appendContentPart($response, self::CALLBACK_STYLESHEETLIST_HEADER, TJavaScript::jsonEncode($stylesheets)); + + // collect all stylesheet snippets references + $stylesheets = $cs->getStyleSheetCodes(); + if (count($stylesheets)>0) + $this->appendContentPart($response, self::CALLBACK_STYLESHEET_HEADER, TJavaScript::jsonEncode($stylesheets)); + + // collect all script file references + $scripts = $cs->getScriptUrls(); + if (count($scripts)>0) + $this->appendContentPart($response, self::CALLBACK_SCRIPTLIST_HEADER, TJavaScript::jsonEncode($scripts)); + + // collect all hidden field references + $fields = $cs->getHiddenFields(); + if (count($fields)>0) + $this->appendContentPart($response, self::CALLBACK_HIDDENFIELDLIST_HEADER, TJavaScript::jsonEncode($fields)); + } + + /** + * Appends data or javascript code to the body content surrounded with delimiters + */ + private function appendContentPart($response, $delimiter, $data) + { + $content = $response->createHtmlWriter(); + $content->getWriter()->setBoundary($delimiter); + $content->write($data); + } + + /** + * Trys to find the callback event handler and raise its callback event. + * @throws TInvalidCallbackException if call back target is not found. + * @throws TInvalidCallbackException if the requested target does not + * implement ICallbackEventHandler. + */ + private function raiseCallbackEvent() + { + if(($callbackHandler=$this->getCallbackEventTarget())!==null) + { + if($callbackHandler instanceof ICallbackEventHandler) + { + $param = $this->getCallbackEventParameter(); + $result = new TCallbackEventParameter($this->getResponse(), $param); + $callbackHandler->raiseCallbackEvent($result); + } + else + { + throw new TInvalidCallbackException( + 'callback_invalid_handler', $callbackHandler->getUniqueID()); + } + } + else + { + $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); + throw new TInvalidCallbackException('callback_invalid_target', $target); + } + } + + /** + * @return TControl the control responsible for the current callback event, + * null if nonexistent + */ + public function getCallbackEventTarget() + { + if($this->_callbackEventTarget===null) + { + $eventTarget=$this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); + if(!empty($eventTarget)) + $this->_callbackEventTarget=$this->getPage()->findControl($eventTarget); + } + return $this->_callbackEventTarget; + } + + /** + * Registers a control to raise callback event in the current request. + * @param TControl control registered to raise callback event. + */ + public function setCallbackEventTarget(TControl $control) + { + $this->_callbackEventTarget=$control; + } + + /** + * Gets callback parameter. JSON encoding is assumed. + * @return string postback event parameter + */ + public function getCallbackEventParameter() + { + if($this->_callbackEventParameter===null) + { + $param = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_PARAMETER); + if(strlen($param) > 0) + $this->_callbackEventParameter=TJavaScript::jsonDecode((string)$param); + } + return $this->_callbackEventParameter; + } + + /** + * @param mixed postback event parameter + */ + public function setCallbackEventParameter($value) + { + $this->_callbackEventParameter=$value; + } + + /** + * Gets the callback client script handler. It handlers the javascript functions + * to be executed during the callback response. + * @return TCallbackClientScript callback client handler. + */ + public function getCallbackClientHandler() + { + if($this->_callbackClient===null) + $this->_callbackClient = new TCallbackClientScript; + return $this->_callbackClient; + } +} + +/** + * TCallbackErrorHandler class. + * + * Captures errors and exceptions and send them back during callback response. + * When the application is in debug mode, the error and exception stack trace + * are shown. A TJavascriptLogger must be present on the client-side to view + * the error stack trace. + * + * @author Wei Zhuo + * @version $Id: TActivePageAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackErrorHandler extends TErrorHandler +{ + /** + * Displays the exceptions to the client-side TJavascriptLogger. + * A HTTP 500 status code is sent and the stack trace is sent as JSON encoded. + * @param Exception exception details. + */ + protected function displayException($exception) + { + if($this->getApplication()->getMode()===TApplication::STATE_DEBUG) + { + $response = $this->getApplication()->getResponse(); + $trace = $this->getExceptionStackTrace($exception); + try { + $trace = TJavaScript::jsonEncode($trace); + } catch (Exception $e) { + // strip everythin not 7bit ascii + $trace = preg_replace('/[^(\x20-\x7F)]*/','', serialize($trace)); + } + $response->setStatusCode(500, 'Internal Server Error'); + $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace); + } + else + { + error_log("Error happened while processing an existing error:\n".$exception->__toString()); + header('HTTP/1.0 500 Internal Server Error', true, 500); + } + $this->getApplication()->getResponse()->flush(); + } + + /** + * @param Exception exception details. + * @return array exception stack trace details. + */ + private function getExceptionStackTrace($exception) + { + $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500; + $data['file']=$exception->getFile(); + $data['line']=$exception->getLine(); + $data['trace']=$exception->getTrace(); + if($exception instanceof TPhpErrorException) + { + // if PHP exception, we want to show the 2nd stack level context + // because the 1st stack level is of little use (it's in error handler) + if(isset($trace[0]) && isset($trace[0]['file']) && isset($trace[0]['line'])) + { + $data['file']=$trace[0]['file']; + $data['line']=$trace[0]['line']; + } + } + $data['type']=get_class($exception); + $data['message']=$exception->getMessage(); + $data['version']=$_SERVER['SERVER_SOFTWARE'].' '.Prado::getVersion(); + $data['time']=@strftime('%Y-%m-%d %H:%M',time()); + return $data; + } +} + +/** + * TInvalidCallbackException class. + * + * @author Wei Zhuo + * @version $Id: TActivePageAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TInvalidCallbackException extends TException +{ +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActivePager.php b/gui/baculum/framework/Web/UI/ActiveControls/TActivePager.php new file mode 100644 index 0000000000..7dcd321298 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActivePager.php @@ -0,0 +1,207 @@ +after the {@link onPageIndexChanged OnPageIndexChanged} event. + * + * @author "gevik" (forum contributor) and Christophe Boulain (Christophe.Boulain@gmail.com) + * @version $Id: TActivePager.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1.2 + */ +class TActivePager extends TPager implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Builds a dropdown list pager + * Override parent implementation to build Active dropdown lists. + */ + protected function buildListPager() + { + $list=new TActiveDropDownList; + + $list->getAdapter()->getBaseActiveControl()->setClientSide( + $this->getClientSide() + ); + + $this->getControls()->add($list); + $list->setDataSource(range(1,$this->getPageCount())); + $list->dataBind(); + $list->setSelectedIndex($this->getCurrentPageIndex()); + $list->setAutoPostBack(true); + $list->attachEventHandler('OnSelectedIndexChanged',array($this,'listIndexChanged')); + $list->attachEventHandler('OnCallback', array($this, 'handleCallback')); + } + + /** + * Creates a pager button. + * Override parent implementation to create, depending on the button type, a TActiveLinkButton, + * a TActiveButton or a TActiveImageButton may be created. + * + * @param string button type, either LinkButton or PushButton + * @param boolean whether the button should be enabled + * @param string caption of the button + * @param string CommandName corresponding to the OnCommand event of the button + * @param string CommandParameter corresponding to the OnCommand event of the button + * @return mixed the button instance + */ + protected function createPagerButton($buttonType,$enabled,$text,$commandName,$commandParameter) + { + if($buttonType===TPagerButtonType::LinkButton) + { + if($enabled) + $button=new TActiveLinkButton; + else + { + $button=new TLabel; + $button->setText($text); + return $button; + } + } + else if($buttonType===TPagerButtonType::ImageButton) + { + $button = new TActiveImageButton; + $button->setImageUrl($this->getPageImageUrl($text,$commandName)); + if($enabled) + $button->Visible = true; + else + $button->Visible = false; + } + else + { + $button=new TActiveButton; + if(!$enabled) + $button->setEnabled(false); + } + + if($buttonType===TPagerButtonType::ImageButton) + { + $button->ImageUrl = $text; + } + + $button->setText($text); + $button->setCommandName($commandName); + $button->setCommandParameter($commandParameter); + $button->setCausesValidation(false); + + $button->attachEventHandler('OnCallback', array($this, 'handleCallback')); + $button->getAdapter()->getBaseActiveControl()->setClientSide( + $this->getClientSide() + ); + + return $button; + } + + /** + * Event handler to the OnCallback active buttons or active dropdownlist. + * This handler will raise the {@link onCallback OnCallback} event + * + * @param mixed $sender + * @param TCallbackEventParameter $param + */ + public function handleCallback ($sender,$param) + { + // Update all the buttons pagers attached to the same control. + // Dropdown pagers doesn't need to be re-rendered. + $controlToPaginate=$this->getControlToPaginate(); + foreach ($this->getNamingContainer()->findControlsByType('TActivePager', false) as $control) + { + if ($control->getMode() !== TPagerMode::DropDownList && $control->getControlToPaginate()===$controlToPaginate) + { + $control->render($param->getNewWriter()); + // FIXME : With some very fast machine, the getNewWriter() consecutive calls are in the same microsecond, resulting + // of getting the same boundaries in ajax response. Wait 1 microsecond to avoid this. + usleep(1); + } + } + // Raise callback event + $this->onCallback($param); + } + + public function render ($writer) + { + if($this->getHasPreRendered()) + { + $this->setDisplay(($this->getPageCount()==1)?TDisplayStyle::None:TDisplayStyle::Dynamic); + TWebControl::render($writer); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this,$writer); + } + else + { + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + } + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActivePanel.php b/gui/baculum/framework/Web/UI/ActiveControls/TActivePanel.php new file mode 100644 index 0000000000..39636cad02 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActivePanel.php @@ -0,0 +1,100 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActivePanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActivePanel is the TPanel active control counterpart. + * + * TActivePanel allows the client-side panel contents to be updated during a + * callback response using the {@link render} method. + * + * Example: Assume $param is an instance of TCallbackEventParameter attached to + * the OnCallback event of a TCallback with ID "callback1", and + * "panel1" is the ID of a TActivePanel. + * + * function callback1_requested($sender, $param) + * { + * $this->panel1->render($param->getNewWriter()); + * } + * + * + * @author Wei Zhuo + * @version $Id: TActivePanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActivePanel extends TPanel implements IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Adds attribute id to the renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) { + $writer->addAttribute('id',$this->getClientID()); + parent::addAttributesToRender($writer); + } + + /** + * Renders and replaces the panel's content on the client-side. + * When render() is called before the OnPreRender event, such as when render() + * is called during a callback event handler, the rendering + * is defered until OnPreRender event is raised. + * @param THtmlWriter html writer + */ + public function render($writer) + { + if($this->getHasPreRendered()) + { + parent::render($writer); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this,$writer); + } + else + { + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + if ($this->getHasControls()) + { + // If we update a TActivePanel on callback, + // We shouldn't update all childs, because the whole content will be replaced by + // the parent + foreach ($this->findControlsByType('IActiveControl', false) as $control) + { + $control->getActiveControl()->setEnableUpdate(false); + } + } + } + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveRadioButton.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRadioButton.php new file mode 100644 index 0000000000..d192536a7d --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRadioButton.php @@ -0,0 +1,176 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRadioButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveRadioButton class. + * + * The active control counter part to radio button. The {@link setAutoPostBack AutoPostBack} + * property is set to true by default. Thus, when the radio button is clicked a + * {@link onCallback OnCallback} event is raise after {@link OnCheckedChanged} event. + * + * The {@link setText Text} and {@link setChecked Checked} properties can be + * changed during a callback. + * + * The {@link setGroupName GroupName} property may NOT be changed + * during callback because the client-side name attribute is read-only + * and can not be changed using javascript. + * + * @author Wei Zhuo + * @version $Id: TActiveRadioButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveRadioButton extends TRadioButton implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + $this->setAutoPostBack(true); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Updates the button text on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param string caption of the button + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->update( + $this->getDefaultLabelID(), $value); + } + + /** + * Checks the radio button. + * Updates radio button checked state on the client-side if the + * {@link setEnableUpdate EnableUpdate} property is set to true. + * @param boolean whether the radio button is to be checked or not. + */ + public function setChecked($value) + { + $value = TPropertyValue::ensureBoolean($value); + parent::setChecked($value); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->check($this, $value); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + * Since 3.1.4, the javascript code is not rendered if {@link setAutoPostBack AutoPostBack} is false + * + */ + protected function renderInputTag($writer,$clientID,$onclick) + { + parent::renderInputTag($writer,$clientID,$onclick); + if ($this->getAutoPostBack()) + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * @return string corresponding javascript class name for this TActiveRadioButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveRadioButton'; + } + + /** + * Overrides parent implementation to ensure label has ID. + * @return TMap list of attributes to be rendered for label beside the radio button + */ + public function getLabelAttributes() + { + $attributes = parent::getLabelAttributes(); + $attributes['id'] = $this->getDefaultLabelID(); + return $attributes; + } + + /** + * Renders a label beside the radio button. + * @param THtmlWriter the writer for the rendering purpose + * @param string radio button id + * @param string label text + */ + protected function renderLabel($writer,$clientID,$text) + { + $writer->addAttribute('id', $this->getDefaultLabelID()); + parent::renderLabel($writer, $clientID, $text); + } + + /** + * @return string radio button label ID; + */ + protected function getDefaultLabelID() + { + if($attributes=$this->getViewState('LabelAttributes',null)) + return $this->getLabelAttributes()->itemAt('id'); + else + return $this->getClientID().'_label'; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveRadioButtonList.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRadioButtonList.php new file mode 100644 index 0000000000..3244ce11c2 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRadioButtonList.php @@ -0,0 +1,121 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveRadioButtonList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter and active radio button. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveListControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TActiveRadioButton'); + +/** + * TActiveRadioButtonList class. + * + * The active control counter part to radio button list control. + * The {@link setAutoPostBack AutoPostBack} property is set to true by default. + * Thus, when a radio button is clicked a {@link onCallback OnCallback} event is + * raised after {@link OnSelectedIndexChanged} event. + * + * With {@link TBaseActiveControl::setEnableUpdate() ActiveControl.EnableUpdate} + * set to true (default is true), changes to the selection will be updated + * on the client side. + * + * List items can not be changed dynamically during a callback request. + * + * @author Wei Zhuo + * @version $Id: TActiveRadioButtonList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveRadioButtonList extends TRadioButtonList implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveListControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + $this->setAdapter(new TActiveListControlAdapter($this)); + $this->setAutoPostBack(true); + parent::__construct(); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Creates a control used for repetition (used as a template). + * @return TControl the control to be repeated + */ + protected function createRepeatedControl() + { + $control = new TActiveRadioButton; + $control->getAdapter()->setBaseActiveControl($this->getActiveControl()); + return $control; + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveRatingList.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRatingList.php new file mode 100644 index 0000000000..022efe6538 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRatingList.php @@ -0,0 +1,144 @@ + + * @author Bradley Booms + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * TActiveRatingList Class + * + * Displays clickable images that represent a TRadioButtonList + * + * @author Wei Zhuo + * @author Bradley Booms + * @version $Id$ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveRatingList extends TRatingList implements IActiveControl, ICallbackEventHandler +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveListControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + $this->setAdapter(new TActiveListControlAdapter($this)); + $this->setAutoPostBack(true); + parent::__construct(); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * @param boolean whether the items in the column can be edited + */ + public function setReadOnly($value) + { + parent::setReadOnly($value); + $value = $this->getReadOnly(); + $this->callClientFunction('setReadOnly',$value); + } + + /** + * @param float rating value, also sets the selected Index + */ + public function setRating($value) + { + parent::setRating($value); + $value = $this->getRating(); + $this->callClientFunction('setRating',$value); + } + + /** + * Calls the client-side static method for this control class. + * @param string static method name + * @param mixed method parmaeter + */ + protected function callClientFunction($func,$value) + { + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $code = parent::getClientClassName().'.'.$func; + $client->callClientFunction($code,array($this,$value)); + } + } + + /** + * @param string caption text + */ + public function setCaption($value) + { + parent::setCaption($value); + // if it's an active control, this should not be needed. + $this->callClientFunction('setCaption',$value); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveRatingList'; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveRepeater.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRepeater.php new file mode 100644 index 0000000000..08e6f93ad7 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveRepeater.php @@ -0,0 +1,112 @@ + + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + * @version $Id: TActiveRepeater.php 2707 2009-09-29 10:33:30Z Christophe.Boulain $ + */ + +/** + * TActiveRepeater class + * + * TActiveRepeater represents a data bound and updatable grid control which is the + * active counterpart to the original {@link TRepeater} control. + * + * This component can be used in the same way as the regular datagrid, the only + * difference is that the active repeater uses callbacks instead of postbacks + * for interaction. + * + * Please refer to the original documentation of the regular counterparts for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveRepeater extends TRepeater implements IActiveControl, ISurroundable { + +/** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. + */ + public function __construct() { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * Sets the data source object associated with the repeater control. + * In addition, the render method of all connected pagers is called so they + * get updated when the data source is changed. Also the repeater registers + * itself for rendering in order to get it's content replaced on client side. + * @param Traversable|array|string data source object + */ + public function setDataSource($value) { + parent::setDataSource($value); + if($this->getActiveControl()->canUpdateClientSide()) { + $this->renderPager(); + $this->getPage()->getAdapter()->registerControlToRender($this,$this->getResponse()->createHtmlWriter()); + } + } + + /** + * Returns the id of the surrounding container (span). + * @return string container id + */ + public function getSurroundingTagID() { + return $this->ClientID.'_Container'; + } + + /** + * Renders the repeater. + * If the repeater did not pass the prerender phase yet, it will register itself for rendering later. + * Else it will call the {@link renderRepeater()} method which will do the rendering of the repeater. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) { + if($this->getHasPreRendered()) { + $this->renderRepeater($writer); + if($this->getActiveControl()->canUpdateClientSide()) $this->getPage()->getCallbackClient()->replaceContent($this->getSurroundingTagId(),$writer); + } + else { + $this->getPage()->getAdapter()->registerControlToRender($this,$writer); + } + } + + /** + * Loops through all {@link TActivePager} on the page and registers the ones which are set to paginate + * the repeater for rendering. This is to ensure that the connected pagers are also rendered if the + * data source changed. + */ + private function renderPager() { + $pager=$this->getPage()->findControlsByType('TActivePager', false); + foreach($pager as $item) { + if($item->ControlToPaginate==$this->ID) { + $writer=$this->getResponse()->createHtmlWriter(); + $this->getPage()->getAdapter()->registerControlToRender($item,$writer); + } + } + } + + /** + * Renders the repeater by writing a span tag with the container id obtained from {@link getSurroundingTagID()} + * which will be called by the replacement method of the client script to update it's content. + * @param THtmlWriter writer for the rendering purpose + */ + private function renderRepeater($writer) { + $writer->addAttribute('id',$this->getSurroundingTagID()); + $writer->renderBeginTag('span'); + parent::render($writer); + $writer->renderEndTag(); + } + +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveTableCell.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveTableCell.php new file mode 100644 index 0000000000..73e9896754 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveTableCell.php @@ -0,0 +1,252 @@ + + * @link http://www.landwehr-software.de/ + * @copyright Copyright © 2009 LANDWEHR Computer und Software GmbH + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI.ActiveControls + * @version $Id$ + */ + +/** + * Includes the following used classes + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); + +/** + * TActiveTableCell class. + * + * TActiveTableCell is the active counterpart to the original {@link TTableCell} control + * and displays a table cell. The horizontal and vertical alignments of the cell + * are specified via {@link setHorizontalAlign HorizontalAlign} and + * {@link setVerticalAlign VerticalAlign} properties, respectively. + * + * TActiveTableCell allows the contents of the table cell to be changed during callback. When + * {@link onCellSelected CellSelected} property is set, selecting (clicking on) the cell will + * perform a callback request causing {@link onCellSelected OnCellSelected} event to be fired. + * + * It will also bubble the {@link onCellSelected OnCellSelected} event up to it's parent + * {@link TActiveTableRow} control which will fire up the event handlers if implemented. + * + * TActiveTableCell allows the client-side cell contents to be updated during a + * callback response by getting a new writer, invoking the render method and flushing the + * output, similar to a {@link TActivePanel} control. + * + * function callback_request($sender, $param) + * { + * $this->active_cell->render($param->getNewWriter()); + * } + * + * + * Please refer to the original documentation of the regular counterpart for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @version $Id$ + * @since 3.1.9 + */ +class TActiveTableCell extends TTableCell implements ICallbackEventHandler, IActiveControl +{ + + /** + * @var TTable parent row control containing the cell + */ + private $_row; + + /** + * Creates a new callback control, sets the adapter to TActiveControlAdapter. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * @return string corresponding javascript class name for this TActiveTableCell. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveTableCell'; + } + + /** + * Raises the callback event. This method is required by {@link ICallbackEventHandler} + * interface. It will raise {@link onCellSelected OnCellSelected} event with a + * {@link TActiveTableCellEventParameter} containing the zero-based index of the + * TActiveTableCell. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $parameter = new TActiveTableCellEventParameter($this->getResponse(), $param->getCallbackParameter(), $this->getCellIndex()); + $this->onCellSelected($parameter); + $this->raiseBubbleEvent($this, $parameter); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCellSelected' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TActiveTableCellEventParameter event parameter to be passed to the event handlers + */ + public function onCellSelected($param) + { + $this->raiseEvent('OnCellSelected', $this, $param); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control if the event handler for the + * {@link onCellSelected OnCellSelected} event is set. + * @param THtmlWriter the writer responsible for rendering + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id', $this->getClientID()); + if ($this->hasEventHandler('OnCellSelected')) + $this->getActiveControl()->registerCallbackClientScript($this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Renders and replaces the cell's content on the client-side. When render() is + * called before the OnPreRender event, such as when render() is called during + * a callback event handler, the rendering is defered until OnPreRender event + * is raised. + * @param THtmlWriter html writer + */ + public function render($writer) + { + if ($this->getHasPreRendered()) + { + parent::render($writer); + if ($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this, $writer); + } + else { + $this->getPage()->getAdapter()->registerControlToRender($this, $writer); + // If we update a TActiveTableCell on callback, we shouldn't update all childs, + // because the whole content will be replaced by the parent. + if ($this->getHasControls()) + { + foreach ($this->findControlsByType('IActiveControl', false) as $control) + $control->getActiveControl()->setEnableUpdate(false); + } + } + } + + /** + * Returns postback specifications for the table cell. + * This method is used by framework and control developers. + * @return array parameters about how the row defines its postback behavior. + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + return $options; + } + + /** + * Returns the zero-based index of the TActiveTableCell within the {@link TTableCellCollection} + * of the parent {@link TTableRow} control. Raises a {@link TConfigurationException} if the cell + * is no member of the cell collection. + * @return integer the zero-based index of the cell + */ + public function getCellIndex() + { + foreach ($this->getRow()->getCells() as $key => $row) + if ($row == $this) return $key; + throw new TConfigurationException('tactivetablecell_control_notincollection', get_class($this), $this->getUniqueID()); + } + + /** + * Returns the parent {@link TTableRow} control by looping through all parents until a {@link TTableRow} + * is found. Raises a {@link TConfigurationException} if no row control is found. + * @return TTableRow the parent row control + */ + public function getRow() + { + if ($this->_row === null) + { + $row = $this->getParent(); + while (!($row instanceof TTableRow) && $row !== null) + { + $row = $row->getParent(); + } + if ($row instanceof TTableRow) $this->_row = $row; + else throw new TConfigurationException('tactivetablecell_control_outoftable', get_class($this), $this->getUniqueID()); + } + return $this->_row; + } + +} + +/** + * TActiveTableCellEventParameter class. + * + * The TActiveTableCellEventParameter provides the parameter passed during the callback + * requestion in the {@link getCallbackParameter CallbackParameter} property. The + * callback response content (e.g. new HTML content) must be rendered + * using an THtmlWriter obtained from the {@link getNewWriter NewWriter} + * property, which returns a NEW instance of TCallbackResponseWriter. + * + * The {@link getSelectedCellIndex SelectedCellIndex} is a zero-based index of the + * TActiveTableCell , -1 if the cell is not part of the cell collection (this shouldn't + * happen though since an exception is thrown before). + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveTableCellEventParameter extends TCallbackEventParameter +{ + + /** + * @var integer the zero-based index of the cell. + */ + private $_selectedCellIndex = -1; + + /** + * Creates a new TActiveTableRowEventParameter. + */ + public function __construct($response, $parameter, $index=-1) + { + parent::__construct($response, $parameter); + $this->_selectedCellIndex = $index; + } + + /** + * Returns the zero-based index of the {@link TActiveTableCell} within the + * {@link TTableCellCollection} of the parent {@link TTableRow} control. + * @return integer the zero-based index of the cell. + */ + public function getSelectedCellIndex() + { + return $this->_selectedCellIndex; + } + +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveTableRow.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveTableRow.php new file mode 100644 index 0000000000..6beeb27e3e --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveTableRow.php @@ -0,0 +1,270 @@ + + * @link http://www.landwehr-software.de/ + * @copyright Copyright © 2009 LANDWEHR Computer und Software GmbH + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI.ActiveControls + * @version $Id$ + */ + +/** + * Includes the following used classes + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); + +/** + * TActiveTableRow class. + * + * TActiveTableRow is the active counterpart to the original {@link TTableRow} control + * and displays a table row. The table cells in the row can be accessed + * via {@link getCells Cells}. The horizontal and vertical alignments of the row + * are specified via {@link setHorizontalAlign HorizontalAlign} and + * {@link setVerticalAlign VerticalAlign} properties, respectively. + * + * TActiveTableRow allows the contents of the table row to be changed during callback. When + * {@link onRowSelected RowSelected} property is set, selecting (clicking on) the row will + * perform a callback request causing {@link onRowSelected OnRowSelected} event to be fired. + * + * It will also respond to a bubbled {@link onCellSelected OnCellSelected} event of a + * {@link TActiveTableCell} child control and fire a {@link onRowSelected OnRowSelected} event. + * + * TActiveTableRow allows the client-side row contents to be updated during a + * callback response by getting a new writer, invoking the render method and flushing the + * output, similar to a {@link TActivePanel} control. + * + * function callback_request($sender, $param) + * { + * $this->active_row->render($param->getNewWriter()); + * } + * + * + * Please refer to the original documentation of the regular counterpart for usage. + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @version $Id$ + * @since 3.1.9 + */ +class TActiveTableRow extends TTableRow implements ICallbackEventHandler, IActiveControl +{ + + /** + * @var TTable parent table control containing the row + */ + private $_table; + + /** + * Creates a new callback control, sets the adapter to TActiveControlAdapter. + * */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * @return string corresponding javascript class name for this TActiveTableRow. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveTableRow'; + } + + /** + * Raises the callback event. This method is required by {@link ICallbackEventHandler} + * interface. It will raise {@link onRowSelected OnRowSelected} event with a + * {@link TActiveTableRowEventParameter} containing the zero-based index of the + * TActiveTableRow. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $parameter = new TActiveTableRowEventParameter($this->getResponse(), $param->getCallbackParameter(), $this->getRowIndex()); + $this->onRowSelected($parameter); + } + + /** + * This method overrides parent's implementation and raises the control's + * callback event. This will fire the {@link onRowSelected OnRowSelected} + * event if an appropriate event handler is implemented. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender, $param) + { + if ($param instanceof TActiveTableCellEventParameter) + { + $this->raiseCallbackEvent($param); + return true; + } + else return false; + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnRowSelected' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TActiveTableRowEventParameter event parameter to be passed to the event handlers + */ + public function onRowSelected($param) + { + $this->raiseEvent('OnRowSelected', $this, $param); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control if the event handler for the + * {@link onRowSelected OnRowSelected} event is set. + * @param THtmlWriter the writer responsible for rendering + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id', $this->getClientID()); + if ($this->hasEventHandler('OnRowSelected')) + $this->getActiveControl()->registerCallbackClientScript($this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Renders and replaces the row's content on the client-side. When render() is + * called before the OnPreRender event, such as when render() is called during + * a callback event handler, the rendering is defered until OnPreRender event + * is raised. + * @param THtmlWriter html writer + */ + public function render($writer) + { + if ($this->getHasPreRendered()) + { + parent::render($writer); + if ($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this, $writer); + } + else + { + $this->getPage()->getAdapter()->registerControlToRender($this, $writer); + // If we update a TActiveTableRow on callback, we shouldn't update all childs, + // because the whole content will be replaced by the parent. + if ($this->getHasControls()) + { + foreach ($this->findControlsByType('IActiveControl', false) as $control) + $control->getActiveControl()->setEnableUpdate(false); + } + } + } + + /** + * Returns postback specifications for the table row. + * This method is used by framework and control developers. + * @return array parameters about how the row defines its postback behavior. + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + return $options; + } + + /** + * Returns the zero-based index of the TActiveTableRow within the {@link TTableRowCollection} + * of the parent {@link TTable} control. Raises a {@link TConfigurationException} if the row + * is no member of the row collection. + * @return integer the zero-based index of the row + */ + public function getRowIndex() + { + foreach ($this->getTable()->getRows() as $key => $row) + if ($row == $this) return $key; + throw new TConfigurationException('tactivetablerow_control_notincollection', get_class($this), $this->getUniqueID()); + } + + /** + * Returns the parent {@link TTable} control by looping through all parents until a {@link TTable} + * is found. Raises a {@link TConfigurationException} if no table control is found. + * @return TTable the parent table control + */ + public function getTable() + { + if ($this->_table === null) + { + $table = $this->getParent(); + while (!($table instanceof TTable) && $table !== null) + { + $table = $table->getParent(); + } + if ($table instanceof TTable) $this->_table = $table; + else throw new TConfigurationException('tactivetablerow_control_outoftable', get_class($this), $this->getUniqueID()); + } + return $this->_table; + } + +} + +/** + * TActiveTableRowEventParameter class. + * + * The TActiveTableRowEventParameter provides the parameter passed during the callback + * requestion in the {@link getCallbackParameter CallbackParameter} property. The + * callback response content (e.g. new HTML content) must be rendered + * using an THtmlWriter obtained from the {@link getNewWriter NewWriter} + * property, which returns a NEW instance of TCallbackResponseWriter. + * + * The {@link getSelectedRowIndex SelectedRowIndex} is a zero-based index of the + * TActiveTableRow , -1 if the row is not part of the row collection (this shouldn't + * happen though since an exception is thrown before). + * + * @author LANDWEHR Computer und Software GmbH + * @package System.Web.UI.ActiveControls + * @since 3.1.9 + */ +class TActiveTableRowEventParameter extends TCallbackEventParameter +{ + /** + * @var integer the zero-based index of the row. + */ + private $_selectedRowIndex = -1; + + /** + * Creates a new TActiveTableRowEventParameter. + */ + public function __construct($response, $parameter, $index=-1) + { + parent::__construct($response, $parameter); + $this->_selectedRowIndex = $index; + } + + /** + * Returns the zero-based index of the {@link TActiveTableRow} within the + * {@link TTableRowCollection} of the parent {@link TTable} control. + * @return integer the zero-based index of the row. + */ + public function getSelectedRowIndex() + { + return $this->_selectedRowIndex; + } + +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TActiveTextBox.php b/gui/baculum/framework/Web/UI/ActiveControls/TActiveTextBox.php new file mode 100644 index 0000000000..5f96ff291f --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TActiveTextBox.php @@ -0,0 +1,125 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TActiveTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TActiveTextBox class. + * + * TActiveTextBox allows the {@link setText Text} property of the textbox to + * be changed during callback. When {@link setAutoPostBack AutoPostBack} property + * is true, changes to the textbox contents will perform a callback request causing + * {@link onTextChanged OnTextChanged} to be fired first followed by {@link onCallback OnCallback} + * event. + * + * @author Wei Zhuo + * @version $Id: TActiveTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TActiveTextBox extends TTextBox implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Client-side Text property can only be updated after the OnLoad stage. + * @param string text content for the textbox + */ + public function setText($value) + { + parent::setText($value); + if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData()) + $this->getPage()->getCallbackClient()->setValue($this, $value); + } + + /** + * Raises the callback event. This method is required by {@link + * ICallbackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TActiveTextBox'; + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TAutoComplete.php b/gui/baculum/framework/Web/UI/ActiveControls/TAutoComplete.php new file mode 100644 index 0000000000..5e14404010 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -0,0 +1,441 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TAutoComplete.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active text box. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); +Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter'); + +/** + * TAutoComplete class. + * + * TAutoComplete is a textbox that provides a list of suggestion on + * the current partial word typed in the textbox. The suggestions are + * requested using callbacks, and raises the {@link onSuggestion OnSuggestion} + * event. The events of the TActiveText (from which TAutoComplete is extended from) + * and {@link onSuggestion OnSuggestion} are mutually exculsive. That is, + * if {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} + * events are raise, then {@link onSuggestion OnSuggestion} will not be raise, and + * vice versa. + * + * The list of suggestions should be set in the {@link onSuggestion OnSuggestion} + * event handler. The partial word to match the suggestion is in the + * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter} + * property. The datasource of the TAutoComplete must be set using {@link setDataSource} + * method. This sets the datasource for the suggestions repeater, available through + * the {@link getSuggestions Suggestions} property. Header, footer templates and + * other properties of the repeater can be access via the {@link getSuggestions Suggestions} + * property and its sub-properties. + * + * The {@link setTextCssClass TextCssClass} property if set is used to find + * the element within the Suggestions.ItemTemplate and Suggestions.AlternatingItemTemplate + * that contains the actual text for the suggestion selected. That is, + * only text inside elements with CSS class name equal to {@link setTextCssClass TextCssClass} + * will be used as suggestions. + * + * To return the list of suggestions back to the browser, supply a non-empty data source + * and call databind. For example, + * + * function autocomplete_suggestion($sender, $param) + * { + * $token = $param->getToken(); //the partial word to match + * $sender->setDataSource($this->getSuggestionsFor($token)); //set suggestions + * $sender->dataBind(); + * } + * + * + * The suggestion will be rendered when the {@link dataBind()} method is called + * during a callback request. + * + * When an suggestion is selected, that is, when the use has clicked, pressed + * the "Enter" key, or pressed the "Tab" key, the {@link onSuggestionSelected OnSuggestionSelected} + * event is raised. The + * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter} + * property contains the index of the selected suggestion. + * + * TAutoComplete allows multiple suggestions within one textbox with each + * word or phrase separated by any characters specified in the + * {@link setSeparator Separator} property. The {@link setFrequency Frequency} + * and {@link setMinChars MinChars} properties sets the delay and minimum number + * of characters typed, respectively, before requesting for sugggestions. + * + * Use {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} events + * to handle post backs due to {@link setAutoPostBack AutoPostBack}. + * + * In the {@link getSuggestions Suggestions} TRepater item template, all HTML text elements + * are considered as text for the suggestion. Text within HTML elements with CSS class name + * "informal" are ignored as text for suggestions. + * + * @author Wei Zhuo + * @version $Id: TAutoComplete.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TAutoComplete extends TActiveTextBox implements INamingContainer +{ + /** + * @var ITemplate template for repeater items + */ + private $_repeater=null; + /** + * @var TPanel result panel holding the suggestion items. + */ + private $_resultPanel=null; + + /** + * @return string word or token separators (delimiters). + */ + public function getSeparator() + { + return $this->getViewState('tokens', ''); + } + + /** + * @return string word or token separators (delimiters). + */ + public function setSeparator($value) + { + $this->setViewState('tokens', TPropertyValue::ensureString($value), ''); + } + + /** + * @return float maximum delay (in seconds) before requesting a suggestion. + */ + public function getFrequency() + { + return $this->getViewState('frequency', ''); + } + + /** + * @param float maximum delay (in seconds) before requesting a suggestion. + * Default is 0.4. + */ + public function setFrequency($value) + { + $this->setViewState('frequency', TPropertyValue::ensureFloat($value),''); + } + + /** + * @return integer minimum number of characters before requesting a suggestion. + */ + public function getMinChars() + { + return $this->getViewState('minChars',''); + } + + /** + * @param integer minimum number of characters before requesting a suggestion. + */ + public function setMinChars($value) + { + $this->setViewState('minChars', TPropertyValue::ensureInteger($value), ''); + } + + /** + * @param string Css class name of the element to use for suggestion. + */ + public function setTextCssClass($value) + { + $this->setViewState('TextCssClass', $value); + } + + /** + * @return string Css class name of the element to use for suggestion. + */ + public function getTextCssClass() + { + return $this->getViewState('TextCssClass'); + } + + /** + * Raises the callback event. This method is overrides the parent implementation. + * If {@link setAutoPostBack AutoPostBack} is enabled it will raise + * {@link onTextChanged OnTextChanged} event event and then the + * {@link onCallback OnCallback} event. The {@link onSuggest OnSuggest} event is + * raise if the request is to find sugggestions, the {@link onTextChanged OnTextChanged} + * and {@link onCallback OnCallback} events are NOT raised. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $token = $param->getCallbackParameter(); + if(is_array($token) && count($token) == 2) + { + if($token[1] === '__TAutoComplete_onSuggest__') + { + $parameter = new TAutoCompleteEventParameter($this->getResponse(), $token[0]); + $this->onSuggest($parameter); + } + else if($token[1] === '__TAutoComplete_onSuggestionSelected__') + { + $parameter = new TAutoCompleteEventParameter($this->getResponse(), null, $token[0]); + $this->onSuggestionSelected($parameter); + } + } + else if($this->getAutoPostBack()) + parent::raiseCallbackEvent($param); + } + + /** + * This method is invoked when an autocomplete suggestion is requested. + * The method raises 'OnSuggest' event. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onSuggest($param) + { + $this->raiseEvent('OnSuggest', $this, $param); + } + + /** + * This method is invoked when an autocomplete suggestion is selected. + * The method raises 'OnSuggestionSelected' event. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onSuggestionSelected($param) + { + $this->raiseEvent('OnSuggestionSelected', $this, $param); + } + + /** + * @param array data source for suggestions. + */ + public function setDataSource($data) + { + $this->getSuggestions()->setDataSource($data); + } + + /** + * Overrides parent implementation. Callback {@link renderSuggestions()} when + * page's IsCallback property is true. + */ + public function dataBind() + { + parent::dataBind(); + if($this->getPage()->getIsCallback()) + $this->renderSuggestions($this->getResponse()->createHtmlWriter()); + } + + /** + * @return TPanel suggestion results panel. + */ + public function getResultPanel() + { + if($this->_resultPanel===null) + $this->_resultPanel = $this->createResultPanel(); + return $this->_resultPanel; + } + + /** + * @return TPanel new instance of result panel. Default uses TPanel. + */ + protected function createResultPanel() + { + $panel = Prado::createComponent('System.Web.UI.WebControls.TPanel'); + $this->getControls()->add($panel); + $panel->setID('result'); + return $panel; + } + + /** + * @return TRepeater suggestion list repeater + */ + public function getSuggestions() + { + if($this->_repeater===null) + $this->_repeater = $this->createRepeater(); + return $this->_repeater; + } + + /** + * @return TRepeater new instance of TRepater to render the list of suggestions. + */ + protected function createRepeater() + { + $repeater = Prado::createComponent('System.Web.UI.WebControls.TRepeater'); + $repeater->setHeaderTemplate(new TAutoCompleteTemplate('
      ')); + $repeater->setFooterTemplate(new TAutoCompleteTemplate('
    ')); + $repeater->setItemTemplate(new TTemplate('
  • <%# $this->DataItem %>
  • ',null)); + $repeater->setEmptyTemplate(new TAutoCompleteTemplate('
      ')); + $this->getControls()->add($repeater); + return $repeater; + } + + /** + * Renders the end tag and registers javascript effects library. + */ + public function renderEndTag($writer) + { + $this->getPage()->getClientScript()->registerPradoScript('effects'); + parent::renderEndTag($writer); + $this->renderResultPanel($writer); + } + + /** + * Renders the result panel. + * @param THtmlWriter the renderer. + */ + protected function renderResultPanel($writer) + { + $this->getResultPanel()->render($writer); + } + + /** + * Renders the suggestions during a callback respones. + * @param THtmlWriter the renderer. + */ + public function renderCallback($writer) + { + $this->renderSuggestions($writer); + } + + /** + * Renders the suggestions repeater. + * @param THtmlWriter the renderer. + */ + public function renderSuggestions($writer) + { + if($this->getActiveControl()->canUpdateClientSide()) + { + $this->getSuggestions()->render($writer); + $boundary = $writer->getWriter()->getBoundary(); + $this->getResponse()->getAdapter()->setResponseData($boundary); + } + } + + /** + * @return array list of callback options. + */ + protected function getPostBackOptions() + { + //disallow page state update ? + //$this->getActiveControl()->getClientSide()->setEnablePageStateUpdate(false); + $options = array(); + if(strlen($string = $this->getSeparator())) + { + $string = strtr($string,array('\t'=>"\t",'\n'=>"\n",'\r'=>"\r")); + $token = preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY); + $options['tokens'] = $token; + } + if($this->getAutoPostBack()) + { + $options = array_merge($options,parent::getPostBackOptions()); + $options['AutoPostBack'] = true; + } + if(strlen($select = $this->getTextCssClass())) + $options['select'] = $select; + $options['ResultPanel'] = $this->getResultPanel()->getClientID(); + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + if(($minchars=$this->getMinChars())!=='') + $options['minChars'] = $minchars; + if(($frequency=$this->getFrequency())!=='') + $options['frequency'] = $frequency; + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + return $options; + } + + /** + * Override parent implementation, no javascript is rendered here instead + * the javascript required for active control is registered in {@link addAttributesToRender}. + */ + protected function renderClientControlScript($writer) + { + } + + /** + * @return string corresponding javascript class name for this TActiveButton. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TAutoComplete'; + } +} + +/** + * TAutCompleteEventParameter contains the {@link getToken Token} requested by + * the user for a partial match of the suggestions. + * + * The {@link getSelectedIndex SelectedIndex} is a zero-based index of the + * suggestion selected by the user, -1 if not suggestion is selected. + * + * @author Wei Zhuo + * @version $Id: TAutoComplete.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TAutoCompleteEventParameter extends TCallbackEventParameter +{ + private $_selectedIndex=-1; + + /** + * Creates a new TCallbackEventParameter. + */ + public function __construct($response, $parameter, $index=-1) + { + parent::__construct($response, $parameter); + $this->_selectedIndex=$index; + } + + /** + * @return int selected suggestion zero-based index, -1 if not selected. + */ + public function getSelectedIndex() + { + return $this->_selectedIndex; + } + + /** + * @return string token for matching a list of suggestions. + */ + public function getToken() + { + return $this->getCallbackParameter(); + } +} + +/** + * TAutoCompleteTemplate class. + * + * TAutoCompleteTemplate is the default template for TAutoCompleteTemplate + * item template. + * + * @author Wei Zhuo + * @version $Id: TAutoComplete.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TAutoCompleteTemplate extends TComponent implements ITemplate +{ + private $_template; + + public function __construct($template) + { + $this->_template = $template; + } + /** + * Instantiates the template. + * It creates a {@link TDataList} control. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $parent->getControls()->add($this->_template); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TBaseActiveControl.php b/gui/baculum/framework/Web/UI/ActiveControls/TBaseActiveControl.php new file mode 100644 index 0000000000..61230719ff --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TBaseActiveControl.php @@ -0,0 +1,394 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TBaseActiveControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TCallbackClientSide'); + +/** + * TBaseActiveControl class provided additional basic property for every + * active control. An instance of TBaseActiveControl or its decendent + * TBaseActiveCallbackControl is created by {@link TActiveControlAdapter::getBaseActiveControl()} + * method. + * + * The {@link setEnableUpdate EnableUpdate} property determines wether the active + * control is allowed to update the contents of the client-side when the callback + * response returns. + * + * @author Wei Zhuo + * @version $Id: TBaseActiveControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TBaseActiveControl extends TComponent +{ + /** + * @var TMap map of active control options. + */ + private $_options; + /** + * @var TControl attached control. + */ + private $_control; + + /** + * Constructor. Attach a base active control to an active control instance. + * @param TControl active control + */ + public function __construct($control) + { + $this->_control = $control; + $this->_options = new TMap; + } + + /** + * Sets a named options with a value. Options are used to store and retrive + * named values for the base active controls. + * @param string option name. + * @param mixed new value. + * @param mixed default value. + * @return mixed options value. + */ + protected function setOption($name,$value,$default=null) + { + $value = ($value===null) ? $default : $value; + if($value!==null) + $this->_options->add($name,$value); + } + + /** + * Gets an option named value. Options are used to store and retrive + * named values for the base active controls. + * @param string option name. + * @param mixed default value. + * @return mixed options value. + */ + protected function getOption($name,$default=null) + { + $item = $this->_options->itemAt($name); + return ($item===null) ? $default : $item; + } + + /** + * @return TMap active control options + */ + protected function getOptions() + { + return $this->_options; + } + + /** + * @return TPage the page containing the attached control. + */ + protected function getPage() + { + return $this->_control->getPage(); + } + + /** + * @return TControl the attached control. + */ + protected function getControl() + { + return $this->_control; + } + + /** + * @param boolean true to allow fine grain callback updates. + */ + public function setEnableUpdate($value) + { + $this->setOption('EnableUpdate', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return boolean true to allow fine grain callback updates. + */ + public function getEnableUpdate() + { + return $this->getOption('EnableUpdate', true); + } + + /** + * Returns true if callback response is allowed to update the browser contents. + * Is is true if the control is initilized, and is a callback request and + * the {@link setEnableUpdate EnableUpdate} property is true and + * the page is not loading post data. + * @return boolean true if the callback response is allowed update + * client-side contents. + */ + public function canUpdateClientSide($bDontRequireVisibility=false) + { + return $this->getControl()->getHasChildInitialized() + && $this->getPage()->getIsLoadingPostData() == false + && $this->getPage()->getIsCallback() + && $this->getEnableUpdate() + && ($bDontRequireVisibility || $this->getControl()->getVisible()); + } +} + +/** + * TBaseActiveCallbackControl is a common set of options and functionality for + * active controls that can perform callback requests. + * + * The properties of TBaseActiveCallbackControl can be accessed and changed from + * each individual active controls' {@link getActiveControl ActiveControl} + * property. + * + * The following example sets the validation group property of a TCallback component. + * + * + * + * + * Additional client-side options and events can be set using the + * {@link getClientSide ClientSide} property. The following example shows + * an alert box when a TCallback component response returns successfully. + * + * + * + * + * @author Wei Zhuo + * @version $Id: TBaseActiveControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TBaseActiveCallbackControl extends TBaseActiveControl +{ + /** + * Callback client-side options can be set by setting the properties of + * the ClientSide property. E.g. + * See {@link TCallbackClientSide} for details on the properties of ClientSide. + * @return TCallbackClientSide client-side callback options. + */ + public function getClientSide() + { + if(($client = $this->getOption('ClientSide'))===null) + { + $client = $this->createClientSide(); + $this->setOption('ClientSide', $client); + } + return $client; + } + + /** + * Sets the client side options. Can only be set when client side is null. + * @param TCallbackClientSide client side options. + */ + public function setClientSide($client) + { + if( $this->getOption('ClientSide')===null) + $this->setOption('ClientSide', $client); + else + throw new TConfigurationException( + 'active_controls_client_side_exists', $this->getControl()->getID()); + } + + /** + * @return TCallbackClientSide callback client-side options. + */ + protected function createClientSide() + { + return new TCallbackClientSide; + } + + /** + * Sets default callback options. Takes the ID of a TCallbackOptions + * component to duplicate the client-side + * options for this control. The {@link getClientSide ClientSide} + * subproperties takes precedence over the CallbackOptions property. + * @param string ID of a TCallbackOptions control from which ClientSide + * options are cloned. + */ + public function setCallbackOptions($value) + { + $this->setOption('CallbackOptions', $value, ''); + } + + /** + * @return string ID of a TCallbackOptions control from which ClientSide + * options are duplicated. + */ + public function getCallbackOptions() + { + return $this->getOption('CallbackOptions', ''); + } + + /** + * Returns an array of default callback client-side options. The default options + * are obtained from the client-side options of a TCallbackOptions control with + * ID specified by {@link setCallbackOptions CallbackOptions}. + * @return array list of default callback client-side options. + */ + protected function getDefaultClientSideOptions() + { + if(($id=$this->getCallbackOptions())!=='') + { + if(($pos=strrpos($id,'.'))!==false) + { + $control=$this->getControl()->getSubProperty(substr($id,0,$pos)); + $newid=substr($id,$pos+1); + if ($control!==null) + $control=$control->$newid; + } + else + { + // TCheckBoxList overrides findControl() with a fake implementation + // but accepts a second parameter to use the standard one + $control=$this->getControl()->findControl($id, true); + } + + if($control instanceof TCallbackOptions) + return $control->getClientSide()->getOptions()->toArray(); + else + throw new TConfigurationException('callback_invalid_callback_options', $this->getControl()->getID(), $id); + } + + return array(); + } + + /** + * @return boolean whether callback event trigger by this button will cause + * input validation, default is true + */ + public function getCausesValidation() + { + return $this->getOption('CausesValidation',true); + } + + /** + * @param boolean whether callback event trigger by this button will cause + * input validation + */ + public function setCausesValidation($value) + { + $this->setOption('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the button causes validation + * upon callback + */ + public function getValidationGroup() + { + return $this->getOption('ValidationGroup',''); + } + + /** + * @param string the group of validators which the button causes validation + * upon callback + */ + public function setValidationGroup($value) + { + $this->setOption('ValidationGroup',$value,''); + } + + /** + * @return boolean whether to perform validation if the callback is + * requested. + */ + public function canCauseValidation() + { + if($this->getCausesValidation()) + { + $group=$this->getValidationGroup(); + return $this->getPage()->getValidators($group)->getCount()>0; + } + else + return false; + } + + /** + * @param mixed callback parameter value. + */ + public function setCallbackParameter($value) + { + $this->setOption('CallbackParameter', $value, ''); + } + + /** + * @return mixed callback parameter value. + */ + public function getCallbackParameter() + { + return $this->getOption('CallbackParameter', ''); + } + + + /** + * @return array list of callback javascript options. + */ + protected function getClientSideOptions() + { + $default = $this->getDefaultClientSideOptions(); + $options = array_merge($default,$this->getClientSide()->getOptions()->toArray()); + $validate = $this->getCausesValidation(); + $options['CausesValidation']= $validate ? '' : false; + $options['ValidationGroup']=$this->getValidationGroup(); + $options['CallbackParameter'] = $this->getCallbackParameter(); + return $options; + } + + /** + * Registers the callback control javascript code. Client-side options are + * merged and passed to the javascript code. This method should be called by + * Active component developers wanting to register the javascript to initialize + * the active component with additional options offered by the + * {@link getClientSide ClientSide} property. + * @param string client side javascript class name. + * @param array additional callback options. + */ + public function registerCallbackClientScript($class,$options=null) + { + $cs = $this->getPage()->getClientScript(); + if(is_array($options)) + $options = array_merge($this->getClientSideOptions(),$options); + else + $options = $this->getClientSideOptions(); + + //remove true as default to save bytes + if($options['CausesValidation']===true) + $options['CausesValidation']=''; + $cs->registerCallbackControl($class, $options); + } + + /** + * Returns the javascript callback request instance. To invoke a callback + * request for this control call the dispatch() method on the + * request instance. Example code in javascript + * + * var request = <%= $this->mycallback->ActiveControl->Javascript %>; + * request.setParameter('hello'); + * request.dispatch(); //make the callback request. + * + * + * Alternatively, + * + * //dispatches immediately + * Prado.Callback("<%= $this->mycallback->UniqueID %>", + * $this->mycallback->ActiveControl->JsCallbackOptions); + * + * @return string javascript client-side callback request object (javascript + * code) + */ + public function getJavascript() + { + $client = $this->getPage()->getClientScript(); + return $client->getCallbackReference($this->getControl(),$this->getClientSideOptions()); + } + + /** + * @param string callback requestion options as javascript code. + */ + public function getJsCallbackOptions() + { + return TJavaScript::encode($this->getClientSideOptions()); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TCallback.php b/gui/baculum/framework/Web/UI/ActiveControls/TCallback.php new file mode 100644 index 0000000000..2c20567cb9 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TCallback.php @@ -0,0 +1,101 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active control adapter. + */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + +/** + * TCallback component class. + * + * The TCallback provides a basic callback handler that can be invoked from the + * client side by running the javascript code obtained from the + * {@link TBaseActiveCallbackControl::getJavascript ActiveControl.Javascript} property. + * The event {@link onCallback OnCallback} is raised when a callback is requested made. + * + * Example usage: + * + * + * + *
      Click Me!
      + *
      + * + * @author Wei Zhuo + * @version $Id: TCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallback extends TControl implements ICallbackEventHandler, IActiveControl +{ + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, call this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveCallbackControl standard callback options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Raises the callback event. This method is required by + * {@link ICallbackEventHandler ICallbackEventHandler} interface. If + * {@link getCausesValidation ActiveControl.CausesValidation} is true, + * it will invoke the page's {@link TPage::validate validate} method first. + * It will raise {@link onCallback OnCallback} event. This method is mainly + * used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + if($this->getActiveControl()->canCauseValidation()) + $this->getPage()->validate($this->getActiveControl()->getValidationGroup()); + $this->onCallback($param); + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackClientScript.php new file mode 100644 index 0000000000..40051f7686 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -0,0 +1,705 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCallbackClientScript.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackClientScript class. + * + * The TCallbackClientScript class provides corresponding methods that can be + * executed on the client-side (i.e. the browser client that is viewing + * the page) during a callback response. + * + * The avaiable methods includes setting/clicking input elements, changing Css + * styles, hiding/showing elements, and adding visual effects to elements on the + * page. The client-side methods can be access through the CallbackClient + * property available in TPage. + * + * For example, to hide "$myTextBox" element during callback response, do + * + * $this->getPage()->getCallbackClient()->hide($myTextBox); + * + * + * @author Wei Zhuo + * @version $Id: TCallbackClientScript.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackClientScript extends TApplicationComponent +{ + /** + * @var TList list of client functions to execute. + */ + private $_actions; + + /** + * Constructor. + */ + public function __construct() + { + $this->_actions = new TList; + } + + /** + * @return array list of client function to be executed during callback + * response. + */ + public function getClientFunctionsToExecute() + { + return $this->_actions->toArray(); + } + + /** + * Executes a client-side statement. + * @param string javascript function name + * @param array list of arguments for the function + */ + public function callClientFunction($function, $params=null) + { + if(!is_array($params)) + $params = array($params); + + if(count($params) > 0) + { + if($params[0] instanceof TControl) + $params[0] = $params[0]->getClientID(); + } + $this->_actions->add(array($function => $params)); + } + + /** + * Client script to set the value of a particular input element. + * @param TControl control element to set the new value + * @param string new value + */ + public function setValue($input, $text) + { + $this->callClientFunction('Prado.Element.setValue', array($input, $text)); + } + + /** + * Client script to select/clear/check a drop down list, check box list, + * or radio button list. + * The second parameter determines the selection method. Valid methods are + * - Value, select or check by value + * - Values, select or check by a list of values + * - Index, select or check by index (zero based index) + * - Indices, select or check by a list of index (zero based index) + * - Clear, clears or selections or checks in the list + * - All, select all + * - Invert, invert the selection. + * @param TControl list control + * @param string selection method + * @param string|int the value or index to select/check. + * @param string selection control type, either 'check' or 'select' + */ + public function select($control, $method='Value', $value=null, $type=null) + { + $method = TPropertyValue::ensureEnum($method, + 'Value', 'Index', 'Clear', 'Indices', 'Values', 'All', 'Invert'); + $type = ($type===null) ? $this->getSelectionControlType($control) : $type; + $total = $this->getSelectionControlIsListType($control) ? $control->getItemCount() : 1; + $this->callClientFunction('Prado.Element.select', + array($control, $type.$method, $value, $total)); + } + + private function getSelectionControlType($control) + { + if(is_string($control)) return 'check'; + if($control instanceof TCheckBoxList) + return 'check'; + if($control instanceof TCheckBox) + return 'check'; + return 'select'; + } + + private function getSelectionControlIsListType($control) + { + return $control instanceof TListControl; + } + + /** + * Client script to click on an element. This client-side function is unpredictable. + * + * @param TControl control element or element id + */ + public function click($control) + { + $this->callClientFunction('Prado.Element.click', $control); + } + + /** + * Client script to check or uncheck a checkbox or radio button. + * @param TControl control element or element id + * @param boolean check or uncheck the checkbox or radio button. + */ + public function check($checkbox, $checked=true) + { + $this->select($checkbox, "Value", $checked); + } + + /** + * Raise the client side event (given by $eventName) on a particular element. + * @param TControl control element or element id + * @param string Event name, e.g. "click" + */ + public function raiseClientEvent($control, $eventName) + { + $this->callClientFunction('Event.fireEvent', + array($control, strtolower($eventName))); + } + + /** + * Sets the attribute of a particular control. + * @param TControl control element or element id + * @param string attribute name + * @param string attribute value + */ + public function setAttribute($control, $name, $value) + { + // Attributes should be applied on Surrounding tag, except for 'disabled' attribute + if ($control instanceof ISurroundable && strtolower($name)!=='disabled') + $control=$control->getSurroundingTagID(); + $this->callClientFunction('Prado.Element.setAttribute',array($control, $name, $value)); + } + + /** + * Sets the options of a select input element. + * @param TControl control element or element id + * @param TCollection a list of new options + */ + public function setListItems($control, $items) + { + $options = array(); + if($control instanceof TListControl) + { + $promptText = $control->getPromptText(); + $promptValue = $control->getPromptValue(); + + if($promptValue==='') + $promptValue = $promptText; + + if($promptValue!=='') + $options[] = array($promptText, $promptValue); + } + + foreach($items as $item) + { + if($item->getHasAttributes()) + $options[] = array($item->getText(),$item->getValue(), $item->getAttributes()->itemAt('Group')); + else + $options[] = array($item->getText(),$item->getValue()); + } + $this->callClientFunction('Prado.Element.setOptions', array($control, $options)); + } + + /** + * Shows an element by changing its CSS display style as empty. + * @param TControl control element or element id + */ + public function show($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.show', $element); + } + + /** + * Hides an element by changing its CSS display style to "none". + * @param TControl control element or element id + */ + public function hide($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.hide', $element); + } + + /** + * Toggles the visibility of the element. + * @param TControl control element or element id + * @param string visual effect, such as, 'appear' or 'slide' or 'blind'. + * @param array additional options. + */ + public function toggle($element, $effect=null, $options=array()) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.toggle', array($element,$effect,$options)); + } + + /** + * Removes an element from the HTML page. + * @param TControl control element or element id + */ + public function remove($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.remove', $element); + } + + public function addPostDataLoader($name) + { + $this->callClientFunction('Prado.CallbackRequest.addPostLoaders', $name); + } + + /** + * Update the element's innerHTML with new content. + * @param TControl control element or element id + * @param TControl new HTML content, if content is of a TControl, the + * controls render method is called. + */ + public function update($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Element.update'); + } + + /** + * Add a Css class name to the element. + * @param TControl control element or element id + * @param string CssClass name to add. + */ + public function addCssClass($element, $cssClass) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.addClassName', array($element, $cssClass)); + } + + /** + * Remove a Css class name from the element. + * @param TControl control element or element id + * @param string CssClass name to remove. + */ + public function removeCssClass($element, $cssClass) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.removeClassName', array($element, $cssClass)); + } + + /** + * Sets the CssClass of an element. + * @param TControl control element or element id + * @param string new CssClass name for the element. + */ + /*public function setCssClass($element, $cssClass) + { + $this->callClientFunction('Prado.Element.CssClass.set', array($element, $cssClass)); + }*/ + + /** + * Scroll the top of the browser viewing area to the location of the + * element. + * @param TControl control element or element id + */ + public function scrollTo($element) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Element.scrollTo', $element); + } + + /** + * Focus on a particular element. + * @param TControl control element or element id. + */ + public function focus($element) + { + $this->callClientFunction('Prado.Element.focus', $element); + } + + /** + * Sets the style of element. The style must be a key-value array where the + * key is the style property and the value is the style value. + * @param TControl control element or element id + * @param array list of key-value pairs as style property and style value. + */ + public function setStyle($element, $styles) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction('Prado.Element.setStyle', array($element, $styles)); + } + + /** + * Append a HTML fragement to the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function appendContent($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.append'); + } + + /** + * Prepend a HTML fragement to the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function prependContent($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.prepend'); + } + + /** + * Insert a HTML fragement after the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function insertContentAfter($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.after'); + } + + /** + * Insert a HTML fragement in before the element. + * @param TControl control element or element id + * @param string HTML fragement or the control to be rendered + */ + public function insertContentBefore($element, $content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content, 'Prado.Element.Insert.before'); + } + + /** + * Replace the content of an element with new content. The new content can + * be a string or a TControl component. If the content parameter is + * a TControl component, its rendered method will be called and its contents + * will be used for replacement. + * @param TControl control element or HTML element id. + * @param string HTML fragement or the control to be rendered + * @param string replacement method, default is to replace the outter + * html content. + * @param string provide a custom boundary. + * @see insertAbout + * @see insertBelow + * @see insertBefore + * @see insertAfter + */ + protected function replace($element, $content, $method="Element.replace", $boundary=null) + { + if($content instanceof TControl) + { + $boundary = $this->getRenderedContentBoundary($content); + $content = null; + } + else if($content instanceof THtmlWriter) + { + $boundary = $this->getResponseContentBoundary($content); + $content = null; + } + + $this->callClientFunction('Prado.Element.replace', + array($element, $method, $content, $boundary)); + } + + /** + * Replace the content of an element with new content contained in writer. + * @param TControl control element or HTML element id. + * @param string HTML fragement or the control to be rendered + */ + public function replaceContent($element,$content) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->replace($element, $content); + } + + /** + * Evaluate a block of javascript enclosed in a boundary. + * @param THtmlWriter writer for the content. + */ + public function evaluateScript($writer) + { + $this->replace(null, $writer, 'Prado.Element.evaluateScript'); + } + + /** + * Appends a block of inline javascript enclosed in a boundary. + * Similar to to evaluateScript(), but functions declared in the + * inline block will be available to page elements. + * @param THtmlWriter writer for the content. + */ + public function appendScriptBlock($content) + { + if($content instanceof TControl) + { + $boundary = $this->getRenderedContentBoundary($content); + } + else if($content instanceof THtmlWriter) + { + $boundary = $this->getResponseContentBoundary($content); + } + + $this->callClientFunction('Prado.Element.appendScriptBlock', array($boundary)); + } + + /** + * Renders the control and return the content boundary from + * TCallbackResponseWriter. This method should only be used by framework + * component developers. The render() method is defered to be called in the + * TActivePageAdapter class. + * @param TControl control to be rendered on callback response. + * @return string the boundary for which the rendered content is wrapped. + */ + private function getRenderedContentBoundary($control) + { + $writer = $this->getResponse()->createHtmlWriter(); + $adapter = $control->getPage()->getAdapter(); + $adapter->registerControlToRender($control, $writer); + return $writer->getWriter()->getBoundary(); + } + + /** + * @param THtmlWriter the writer responsible for rendering html content. + * @return string content boundary. + */ + private function getResponseContentBoundary($html) + { + if($html instanceof THtmlWriter) + { + if($html->getWriter() instanceof TCallbackResponseWriter) + return $html->getWriter()->getBoundary(); + } + return null; + } + + /** + * Add a visual effect the element. + * @param string visual effect function name. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function visualEffect($type, $element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->callClientFunction($type, array($element, $options)); + } + + /** + * Visual Effect: Gradually make the element appear. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function appear($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Appear', $element, $options); + } + + /** + * Visual Effect: Blind down. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function blindDown($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.BlindDown', $element, $options); + } + + /** + * Visual Effect: Blind up. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function blindUp($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.BlindUp', $element, $options); + + } + + /** + * Visual Effect: Drop out. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function dropOut($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.DropOut', $element, $options); + } + + /** + * Visual Effect: Gradually fade the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function fade($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Fade', $element, $options); + } + + /** + * Visual Effect: Fold. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function fold($element, $options = null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Fold', $element, $options); + } + + /** + * Visual Effect: Gradually make an element grow to a predetermined size. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function grow($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Grow', $element, $options); + } + + /** + * Visual Effect: Gradually grow and fade the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function puff($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Puff', $element, $options); + } + + /** + * Visual Effect: Pulsate. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function pulsate($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Pulsate', $element, $options); + } + + /** + * Visual Effect: Shake the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function shake($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Shake', $element, $options); + } + + /** + * Visual Effect: Shrink the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function shrink($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Shrink', $element, $options); + } + + /** + * Visual Effect: Slide down. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function slideDown($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.SlideDown', $element, $options); + } + + /** + * Visual Effect: Side up. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function slideUp($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.SlideUp', $element, $options); + } + + /** + * Visual Effect: Squish the element. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function squish($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.Squish', $element, $options); + } + + /** + * Visual Effect: Switch Off effect. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function switchOff($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Effect.SwitchOff', $element, $options); + } + + /** + * Visual Effect: High light the element for about 2 seconds. + * @param TControl control element or element id + * @param array visual effect key-value pair options. + */ + public function highlight($element, $options=null) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $this->visualEffect('Prado.Effect.Highlight', $element, $options); + } + + /** + * Set the opacity on a html element or control. + * @param TControl control element or element id + * @param float opacity value between 1 and 0 + */ + public function setOpacity($element, $value) + { + if ($element instanceof ISurroundable) + $element=$element->getSurroundingTagID(); + $value = TPropertyValue::ensureFloat($value); + $this->callClientFunction('Element.setOpacity', array($element,$value)); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TCallbackClientSide.php b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackClientSide.php new file mode 100644 index 0000000000..ec993c1497 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackClientSide.php @@ -0,0 +1,322 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCallbackClientSide.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackClientSide class. + * + * The following client side events are executing in order if the callback + * request and response are send and received successfuly. + * + * - onPreDispatch executed before a request is dispatched. + * - onUninitialized executed when callback request is uninitialized. + * - onLoading* executed when callback request is initiated + * - onLoaded* executed when callback request begins. + * - onInteractive executed when callback request is in progress. + * - onCompleteexecuted when callback response returns. + * - onSuccess executed when callback request returns and is successful. + * - onFailure executed when callback request returns and fails. + * - onException raised when callback request fails due to request/response errors. + * + * * Note that theses 2 events are not fired correctly by Opera. To make + * them work in this browser, Prado will fire them just after onPreDispatch. + * + * In a general way, onUninitialized, onLoading, onLoaded and onInteractive events + * are not implemented consistently in all browsers.When cross browser compatibility is + * needed, it is best to avoid use them + * + * The OnSuccess and OnFailure events are raised when the + * response is returned. A successful request/response will raise + * OnSuccess event otherwise OnFailure will be raised. + * + * - PostState true to collect the form inputs and post them during callback, default is true. + * - RequestTimeOut The request timeout in milliseconds. + * - HasPriority true to ensure that the callback request will be sent + * immediately and will abort existing prioritized requests. It does not affect + * callbacks that are not prioritized. + * - EnablePageStateUpdate enable the callback response to enable the + * viewstate update. This will automatically set HasPriority to true when enabled. + * + * @author Wei Zhuo + * @version $Id: TCallbackClientSide.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackClientSide extends TClientSideOptions +{ + /** + * Returns javascript statement enclosed within a javascript function. + * @param string javascript statement + * @return string javascript statement wrapped in a javascript function + */ + protected function ensureFunction($javascript) + { + return "function(sender, parameter){ {$javascript} }"; + } + + /** + * @param string javascript code to be executed before a request is dispatched. + */ + public function setOnPreDispatch($javascript) + { + $this->setFunction('onPreDispatch', $javascript); + } + + /** + * @return string javascript code to be executed before a request is dispatched. + */ + public function getOnPreDispatch() + { + return $this->getOption('onPreDispatch'); + } + + /** + * @return string javascript code for client-side onUninitialized event + */ + public function getOnUninitialized() + { + return $this->getOption('onUninitialized'); + } + + /** + * @param string javascript code for client-side onUninitialized event. + */ + public function setOnUninitialized($javascript) + { + $this->setFunction('onUninitialized', $javascript); + } + + /** + * @return string javascript code for client-side onLoading event + */ + public function getOnLoading() + { + return $this->getOption('onLoading'); + } + + /** + * @param string javascript code for client-side onLoading event. + */ + public function setOnLoading($javascript) + { + $this->setFunction('onLoading', $javascript); + } + + /** + * @return string javascript code for client-side onLoaded event + */ + public function getOnLoaded() + { + return $this->getOption('onLoaded'); + } + + /** + * @param string javascript code for client-side onLoaded event. + */ + public function setOnLoaded($javascript) + { + $this->setFunction('onLoaded', $javascript); + } + /** + * @return string javascript code for client-side onInteractive event + */ + public function getOnInteractive() + { + return $this->getOption('onInteractive'); + } + + /** + * @param string javascript code for client-side onInteractive event. + */ + public function setOnInteractive($javascript) + { + $this->setFunction('onInteractive', $javascript); + } + /** + * @return string javascript code for client-side onComplete event + */ + public function getOnComplete() + { + return $this->getOption('onComplete'); + } + + /** + * @param string javascript code for client-side onComplete event. + */ + public function setOnComplete($javascript) + { + $this->setFunction('onComplete', $javascript); + } + /** + * @return string javascript code for client-side onSuccess event + */ + public function getOnSuccess() + { + return $this->getOption('onSuccess'); + } + + /** + * @param string javascript code for client-side onSuccess event. + */ + public function setOnSuccess($javascript) + { + $this->setFunction('onSuccess', $javascript); + } + + /** + * @return string javascript code for client-side onFailure event + */ + public function getOnFailure() + { + return $this->getOption('onFailure'); + } + + /** + * @param string javascript code for client-side onFailure event. + */ + public function setOnFailure($javascript) + { + $this->setFunction('onFailure', $javascript); + } + + /** + * @return string javascript code for client-side onException event + */ + public function getOnException() + { + return $this->getOption('onException'); + } + + /** + * @param string javascript code for client-side onException event. + */ + public function setOnException($javascript) + { + $this->setFunction('onException', $javascript); + } + + /** + * @return boolean true to post the inputs of the form on callback, default + * is post the inputs on callback. + */ + public function getPostState() + { + return $this->getOption('PostInputs'); + } + + /** + * @param boolean true to post the inputs of the form with callback + * requests. Default is to post the inputs. + */ + public function setPostState($value) + { + $this->setOption('PostInputs', TPropertyValue::ensureBoolean($value)); + } + + /** + * @return integer callback request timeout. + */ + public function getRequestTimeOut() + { + return $this->getOption('RequestTimeOut'); + } + + /** + * @param integer callback request timeout + */ + public function setRequestTimeOut($value) + { + $this->setOption('RequestTimeOut', TPropertyValue::ensureInteger($value)); + } + + /** + * @return boolean true if the callback request has priority and will abort + * existing prioritized request in order to send immediately. It does not + * affect callbacks that are not prioritized. Default is true. + */ + public function getHasPriority() + { + $option = $this->getOption('HasPriority'); + return ($option===null) ? true : $option; + } + + /** + * @param boolean true to ensure that the callback request will be sent + * immediately and will abort existing prioritized requests. It does not + * affect callbacks that are not prioritized. + */ + public function setHasPriority($value) + { + $hasPriority = TPropertyValue::ensureBoolean($value); + $this->setOption('HasPriority', $hasPriority); + if(!$hasPriority) + $this->setEnablePageStateUpdate(false); + } + + /** + * Set to true to enable the callback response to enable the viewstate + * update. This will automatically set HasPrority to true. + * @param boolean true enables the callback response to update the + * viewstate. + */ + public function setEnablePageStateUpdate($value) + { + $enabled = TPropertyValue::ensureBoolean($value); + $this->setOption('EnablePageStateUpdate', $enabled); + if($enabled) + $this->setHasPriority(true); + } + + /** + * @return boolean client-side viewstate will be updated on callback + * response if true. Default is true. + */ + public function getEnablePageStateUpdate() + { + $option = $this->getOption('EnablePageStateUpdate'); + return ($option===null) ? true : $option; + } + + /** + * @return string post back target ID + */ + public function getPostBackTarget() + { + return $this->getOption('EventTarget'); + } + + /** + * @param string post back target ID + */ + public function setPostBackTarget($value) + { + if($value instanceof TControl) + $value = $value->getUniqueID(); + $this->setOption('EventTarget', $value); + } + + /** + * @return string post back event parameter. + */ + public function getPostBackParameter() + { + return $this->getOption('EventParameter'); + } + + /** + * @param string post back event parameter. + */ + public function setPostBackParameter($value) + { + $this->setOption('EventParameter', $value); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TCallbackEventParameter.php b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackEventParameter.php new file mode 100644 index 0000000000..728ebbbae9 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackEventParameter.php @@ -0,0 +1,87 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackEventParameter class. + * + * The TCallbackEventParameter provides the parameter passed during the callback + * request in the {@link getCallbackParameter CallbackParameter} property. The + * callback response content (e.g. new HTML content) must be rendered + * using an THtmlWriter obtained from the {@link getNewWriter NewWriter} + * property, which returns a NEW instance of TCallbackResponseWriter. + * + * Each instance TCallbackResponseWriter is associated with a unique + * boundary delimited. By default each panel only renders its own content. + * To replace the content of ONE panel with that of rendered from multiple panels + * use the same writer instance for the panels to be rendered. + * + * The response data (i.e., passing results back to the client-side + * callback handler function) can be set using {@link setResponseData ResponseData} property. + * + * @author Wei Zhuo + * @version $Id: TActivePageAdapter.php 1648 2007-01-24 05:52:22Z wei $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackEventParameter extends TEventParameter +{ + /** + * @var THttpResponse output content. + */ + private $_response; + /** + * @var mixed callback request parameter. + */ + private $_parameter; + + /** + * Creates a new TCallbackEventParameter. + */ + public function __construct($response, $parameter) + { + $this->_response = $response; + $this->_parameter = $parameter; + } + + /** + * @return TCallbackResponseWriter holds the response content. + */ + public function getNewWriter() + { + return $this->_response->createHtmlWriter(null); + } + + /** + * @return mixed callback request parameter. + */ + public function getCallbackParameter() + { + return $this->_parameter; + } + + /** + * @param mixed callback response data. + */ + public function setResponseData($value) + { + $this->_response->getAdapter()->setResponseData($value); + } + + /** + * @return mixed callback response data. + */ + public function getResponseData() + { + return $this->_response->getAdapter()->getResponseData(); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TCallbackOptions.php b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackOptions.php new file mode 100644 index 0000000000..19d53aeb84 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackOptions.php @@ -0,0 +1,53 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCallbackOptions.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackOptions class. + * + * TCallbackOptions allows common set of callback client-side options + * to be attached to other active controls. + * + * @author Wei Zhuo + * @version $Id: TCallbackOptions.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackOptions extends TControl +{ + /** + * @var TCallbackClientSide client side callback options. + */ + private $_clientSide; + + /** + * Callback client-side options can be set by setting the properties of + * the ClientSide property. E.g. + * See {@link TCallbackClientSide} for details on the properties of + * ClientSide. + * @return TCallbackClientSide client-side callback options. + */ + public function getClientSide() + { + if($this->_clientSide===null) + $this->_clientSide = $this->createClientSide(); + return $this->_clientSide; + } + + /** + * @return TCallbackClientSide callback client-side options. + */ + protected function createClientSide() + { + return Prado::createComponent('System.Web.UI.ActiveControls.TCallbackClientSide'); + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php new file mode 100755 index 0000000000..47b5fae472 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php @@ -0,0 +1,161 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCallbackResponseAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * TCallbackResponseAdapter alters the THttpResponse's outputs. + * + * A TCallbackResponseWriter is used instead of the TTextWrite when + * createHtmlWriter is called. Each call to createHtmlWriter will create + * a new TCallbackResponseWriter. When flushContent() is called each + * instance of TCallbackResponseWriter's content is flushed. + * + * The callback response data can be set using the {@link setResponseData ResponseData} + * property. + * + * @author Wei Zhuo + * @version $Id: TCallbackResponseAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackResponseAdapter extends THttpResponseAdapter +{ + /** + * @var TCallbackResponseWriter[] list of writers. + */ + private $_writers=array(); + /** + * @var mixed callback response data. + */ + private $_data; + + private $_redirectUrl=null; + + /** + * Returns a new instance of THtmlWriter. + * An instance of TCallbackResponseWriter is created to hold the content. + * @param string writer class name. + * @param THttpResponse http response handler. + */ + public function createNewHtmlWriter($type,$response) + { + $writer = new TCallbackResponseWriter(); + $this->_writers[] = $writer; + return parent::createNewHtmlWriter($type,$writer); + } + + /** + * Flushes the contents in the writers. + */ + public function flushContent() + { + foreach($this->_writers as $writer) + echo $writer->flush(); + parent::flushContent(); + } + + /** + * @param mixed callback response data. + */ + public function setResponseData($data) + { + $this->_data = $data; + } + + /** + * @return mixed callback response data. + */ + public function getResponseData() + { + return $this->_data; + } + + /** + * Delay the redirect until we process the rest of the page. + * @param string new url to redirect to. + */ + public function httpRedirect($url) + { + if($url[0]==='/') + $url=$this->getRequest()->getBaseUrl().$url; + $this->_redirectUrl=str_replace('&','&',$url); + } + + /** + * @return string new url for callback response to redirect to. + */ + public function getRedirectedUrl() + { + return $this->_redirectUrl; + } +} + +/** + * TCallbackResponseWriter class. + * + * TCallbackResponseWriter class enclosed a chunck of content within a + * html comment boundary. This allows multiple chuncks of content to return + * in the callback response and update multiple HTML elements. + * + * The {@link setBoundary Boundary} property sets boundary identifier in the + * HTML comment that forms the boundary. By default, the boundary identifier + * is generated using microtime. + * + * @author Wei Zhuo + * @version $Id: TCallbackResponseAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TCallbackResponseWriter extends TTextWriter +{ + /** + * @var string boundary ID + */ + private $_boundary; + + /** + * Constructor. Generates unique boundary ID using microtime. + */ + public function __construct() + { + $this->_boundary = sprintf('%x',crc32(microtime())); + } + + /** + * @return string boundary identifier. + */ + public function getBoundary() + { + return $this->_boundary; + } + + /** + * @param string boundary identifier. + */ + public function setBoundary($value) + { + $this->_boundary = $value; + } + + /** + * Returns the text content wrapped within a HTML comment with boundary + * identifier as its comment content. + * @return string text content chunck. + */ + public function flush() + { + $content = parent::flush(); + if(empty($content)) + return ""; + return ''.$content.''; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TDraggable.php b/gui/baculum/framework/Web/UI/ActiveControls/TDraggable.php new file mode 100755 index 0000000000..df92bec5fc --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TDraggable.php @@ -0,0 +1,251 @@ +{@link setGhosting Ghosting} : If set to "Ghosting" or "True", the dragged element will be cloned, and the clone will be dragged. + * If set to "SuperGhosting", the element will be cloned, and attached to body, so it can be dragged outside of its parent. + * If set to "None" of "False" (default), the element itself is dragged + * {@link setRevert Revert}: Set to True if you want your dragged element to revert to its initial position if not dropped on a valid area. + * {@link setConstraint Constraint}: Set this to Horizontal or Vertical if you want to constraint your move in one direction. + * {@link setHandle Handle}: + * + * @author Christophe BOULAIN (Christophe.Boulain@gmail.com) + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @package System.Web.UI.ActiveControls + * @version $Id: TDraggable.php 3285 2013-04-11 07:28:07Z ctrlaltca $ + */ +class TDraggable extends TPanel +{ + /** + * Set the handle id or css class + * @param string + */ + public function setHandle ($value) + { + $this->setViewState('DragHandle', TPropertyValue::ensureString($value), null); + } + + /** + * Get the handle id or css class + * @return string + */ + public function getHandle () + { + return $this->getViewState('DragHandle', null); + } + + /** + * Determine if draggable element should revert to it orginal position + * upon release in an non-droppable container. + * Since 3.2, Revert property can be set to one of the value of {@link TDraggableRevertOption} enumeration. + * o 'True' or 'Revert' : The draggable will revert to it's original position + * o 'False' or 'None' : The draggable won't revert to it's original position + * o 'Failure' : The draggable will only revert if it's dropped on a non droppable area + * @return TDraggableRevertOption true to revert + */ + public function getRevert() + { + return $this->getViewState('Revert', TDraggableRevertOptions::Revert); + } + + /** + * Sets whether the draggable element should revert to it orginal position + * upon release in an non-droppable container. + * Since 3.2, Revert property can be set to one of the value of {@link TDraggableRevertOption} enumeration. + * o 'True' or 'Revert' : The draggable will revert to it's original position + * o 'False' or 'None' : The draggable won't revert to it's original position + * o 'Failure' : The draggable will only revert if it's dropped on a non droppable area + * @param boolean true to revert + */ + public function setRevert($value) + { + if (strcasecmp($value,'true')==0 || $value===true) + $value=TDraggableRevertOptions::Revert; + elseif (strcasecmp($value,'false')==0 || $value===false) + $value=TDraggableRevertOptions::None; + $this->setViewState('Revert', TPropertyValue::ensureEnum($value, 'TDraggableRevertOptions'), true); + } + + /** + * Determine if the element should be cloned when dragged + * If true, Clones the element and drags the clone, leaving the original in place until the clone is dropped. + * Defaults to false + * Since 3.2, Ghosting can be set to one of the value of {@link TDraggableGhostingOptions} enumeration. + * o "True" or "Ghosting" means standard pre-3.2 ghosting mechanism + * o "SuperGhosting" use the Superghosting patch by Christopher Williams, which allow elements to be dragged from an + * scrollable list + * o "False" or "None" means no Ghosting options + * + * @return TDraggableGhostingOption to clone the element + */ + public function getGhosting () + { + return $this->getViewState('Ghosting', TDraggableGhostingOptions::None); + } + + /** + * Sets wether the element should be cloned when dragged + * If true, Clones the element and drags the clone, leaving the original in place until the clone is dropped. + * Defaults to false + * + * Since 3.2, Ghosting can be set to one of the value of {@link TDraggableGhostingOptions} enumeration. + * o "True" or "Ghosting" means standard pre-3.2 ghosting mechanism + * o "SuperGhosting" use the Superghosting patch by Christopher Williams, which allow elements to be dragged from an + * scrollable list + * o "False" or "None" means no Ghosting options + * + */ + public function setGhosting ($value) + { + if (strcasecmp($value,'true')==0 || $value===true) + $value=TDraggableGhostingOptions::Ghosting; + elseif (strcasecmp($value,'false')==0 || $value===false) + $value=TDraggableGhostingOptions::None; + $this->setViewState('Ghosting', TPropertyValue::ensureEnum($value, 'TDraggableGhostingOptions'), TDraggableGhostingOptions::None); + } + + /** + * Determine if the element should be constrainted in one direction or not + * @return CDraggableConstraint + */ + public function getConstraint() + { + return $this->getViewState('Constraint', TDraggableConstraint::None); + } + + /** + * Set wether the element should be constrainted in one direction + * @param CDraggableConstraint + */ + public function setConstraint($value) + { + $this->setViewState('Constraint', TPropertyValue::ensureEnum($value, 'TDraggableConstraint'), TDraggableConstraint::None); + } + + /** + * Registers clientscripts + * + * This method overrides the parent implementation and is invoked before render. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + + $cs=$this->getPage()->getClientScript(); + if ($this->getGhosting()==TDraggableGhostingOptions::SuperGhosting) + $cs->registerPradoScript('dragdropextra'); + else + $cs->registerPradoScript('dragdrop'); + $writer->addAttribute('id',$this->getClientID()); + $options=TJavascript::encode($this->getPostBackOptions()); + $class=$this->getClientClassName(); + $code="new {$class}('{$this->getClientId()}', {$options}) "; + $cs->registerEndScript(sprintf('%08X', crc32($code)), $code); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName () + { + return 'Draggable'; + } + + /** + * Gets the post back options for this textbox. + * @return array + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + + if (($handle=$this->getHandle())!== null) $options['handle']=$handle; + if (($revert=$this->getRevert())===TDraggableRevertOptions::None) + $options['revert']=false; + elseif ($revert==TDraggableRevertOptions::Revert) + $options['revert']=true; + else + $options['revert']=strtolower($revert); + if (($constraint=$this->getConstraint())!==TDraggableConstraint::None) $options['constraint']=strtolower($constraint); + switch ($this->getGhosting()) + { + case TDraggableGhostingOptions::SuperGhosting: + $options['superghosting']=true; + break; + case TDraggableGhostingOptions::Ghosting: + $options['ghosting']=true; + break; + } + + return $options; + } + +} + +/** + * @author Christophe BOULAIN (Christophe.Boulain@gmail.com) + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @package System.Web.UI.ActiveControls + * @version $Id: TDraggable.php 3285 2013-04-11 07:28:07Z ctrlaltca $ + */ +class TDraggableConstraint extends TEnumerable +{ + const None='None'; + const Horizontal='Horizontal'; + const Vertical='Vertical'; +} + +/** + * @author Christophe BOULAIN (Christophe.Boulain@gmail.com) + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @package System.Web.UI.ActiveControls + * @version $Id: TDraggable.php 3285 2013-04-11 07:28:07Z ctrlaltca $ + */ +class TDraggableGhostingOptions extends TEnumerable +{ + const None='None'; + const Ghosting='Ghosting'; + const SuperGhosting='SuperGhosting'; +} + +/** + * @author Christophe BOULAIN (Christophe.Boulain@gmail.com) + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @package System.Web.UI.ActiveControls + * @version $Id: TDraggable.php 3285 2013-04-11 07:28:07Z ctrlaltca $ + */ +class TDraggableRevertOptions extends TEnumerable +{ + const None='None'; + const Revert='Revert'; + const Failure='Failure'; +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TDropContainer.php b/gui/baculum/framework/Web/UI/ActiveControls/TDropContainer.php new file mode 100755 index 0000000000..43e8017c3e --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TDropContainer.php @@ -0,0 +1,321 @@ +{@link setAcceptCssClass AcceptCssClass} : a coma delimited classname of elements that the drop container can accept. + * {@link setHoverCssClass HoverCssClass}: CSS classname of the container when a draggable element hovers over the container. + * + * Events: + * + * {@link OnDrop OnDrop} : raised when a TDraggable control is dropped. The dropped control id is encapsulated in the event parameter, + * as well as mouse coordinates and key modifiers status + * + * + * @author Christophe BOULAIN (Christophe.Boulain@gmail.com) + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @version $Id: TDropContainer.php 3285 2013-04-11 07:28:07Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ +class TDropContainer extends TPanel implements IActiveControl, ICallbackEventHandler +{ + private $_container=null; + + /** + * Creates a new callback control, sets the adapter to + * TActiveControlAdapter. If you override this class, be sure to set the + * adapter appropriately by, for example, by calling this constructor. + */ + public function __construct() + { + parent::__construct(); + $this->setAdapter(new TActiveControlAdapter($this)); + } + + /** + * @return TBaseActiveControl standard active control options. + */ + public function getActiveControl() + { + return $this->getAdapter()->getBaseActiveControl(); + } + + /** + * @return TCallbackClientSide client side request options. + */ + public function getClientSide() + { + return $this->getAdapter()->getBaseActiveControl()->getClientSide(); + } + + /** + * Gets the Css class name that this container can accept. + * @return string + */ + public function getAcceptCssClass() + { + return $this->getViewState('Accepts', ''); + } + + /** + * Sets the Css class name that this container can accept. + * @param string comma delimited css class names. + */ + public function setAcceptCssClass($value) + { + $this->setViewState('Accepts', TPropertyValue::ensureArray($value), ''); + } + + /** + * Sets the Css class name used when a draggble element is hovering + * over this container. + * @param string css class name during draggable hover. + */ + public function setHoverCssClass($value) + { + $this->setViewState('HoverClass', $value, ''); + } + + /** + * Gets the Css class name used when a draggble element is hovering + * over this container. + * @return string css class name during draggable hover. + */ + public function getHoverCssClass() + { + return $this->getViewState('HoverClass', ''); + } + + + /** + * Raises callback event. This method is required bu {@link ICallbackEventHandler} + * interface. + * It raises the {@link onDrop OnDrop} event, then, the {@link onCallback OnCallback} event + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the parameter associated with the callback event + */ + public function raiseCallbackEvent($param) + { + $this->onDrop($param->getCallbackParameter()); + $this->onCallback($param); + } + + /** + * Raises the onDrop event. + * The drop parameters are encapsulated into a {@link TDropContainerEventParameter} + * + * @param object $dropControlId + */ + public function onDrop ($dropParams) + { + $this->raiseEvent('OnDrop', $this, new TDropContainerEventParameter ($dropParams)); + + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Gets the post back options for this textbox. + * @return array + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + + $options['accept'] = $this->getAcceptCssClass(); + $options['hoverclass'] = $this->getHoverCssClass(); + return $options; + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.DropContainer'; + } + + /** + * Registers clientscripts + * + * This method overrides the parent implementation and is invoked before render. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + + $this->getPage()->getClientScript()->registerPradoScript('dragdrop'); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Creates child control + * Override parent implementation to create a container which will contain all + * child controls. This container will be a TActivePanel, in order to allow user + * to update its content on callback. + */ + public function createChildControls () + { + if ($this->_container===null) + { + $this->_container=Prado::CreateComponent('System.Web.UI.ActiveControls.TActivePanel'); + $this->_container->setId($this->getId(false).'_content'); + parent::getControls()->add($this->_container); + } + } + + /** + * Override parent implementation to return the container control collection. + * + * @return TControlCollection + */ + public function getControls() + { + $this->ensureChildControls(); + return $this->_container->getControls(); + } + + /** + * Renders and replaces the panel's content on the client-side. + * When render() is called before the OnPreRender event, such as when render() + * is called during a callback event handler, the rendering + * is defered until OnPreRender event is raised. + * @param THtmlWriter html writer + */ + public function render ($writer) + { + if($this->getHasPreRendered()) + { + parent::render($writer); + if($this->getActiveControl()->canUpdateClientSide()) + $this->getPage()->getCallbackClient()->replaceContent($this->_container,$writer); + } + else + { + $this->getPage()->getAdapter()->registerControlToRender($this->_container,$writer); + } + } + +} + +/** + * TDropContainerEventParameter class + * + * TDropContainerEventParameter encapsulate the parameter + * data for OnDrop event of TDropContainer components + * + * @author Christophe BOULAIN (Christophe.Boulain@ceram.fr) + * @copyright Copyright © 2008, PradoSoft + * @license http://www.pradosoft.com/license + * @version $Id: TDropContainer.php 3285 2013-04-11 07:28:07Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ +class TDropContainerEventParameter extends TEventParameter +{ + private $_dragElementId; + private $_screenX; + private $_screenY; + private $_offsetX; + private $_offsetY; + private $_clientX; + private $_clientY; + private $_shiftKey; + private $_ctrlKey; + private $_altKey; + + public function __construct($dropParams) + { + $this->_dragElementId = $dropParams->DragElementID; + $this->_screenX = $dropParams->ScreenX; + $this->_screenY = $dropParams->ScreenY; + $this->_offsetX = isset($dropParams->OffsetX) ? $dropParams->OffsetX : false; + $this->_offsetY = isset($dropParams->OffsetY) ? $dropParams->OffsetY : false; + $this->_clientX = $dropParams->ClientX; + $this->_clientY = $dropParams->ClientY; + $this->_shiftKey = TPropertyValue::ensureBoolean($dropParams->ShiftKey); + $this->_ctrlKey = TPropertyValue::ensureBoolean($dropParams->CtrlKey); + $this->_altKey = TPropertyValue::ensureBoolean($dropParams->AltKey); + } + + public function getDragElementId() { return $this->_dragElementId; } + public function getScreenX() { return $this->_screenX; } + public function getScreenY() { return $this->_screenY; } + public function getOffsetX() { return $this->_offsetX; } + public function geOffsetY() { return $this->_offsetY; } + public function getClientX() { return $this->_clientX; } + public function getClientY() { return $this->_clientY; } + public function getShiftKey() { return $this->_shiftKey; } + public function getCtrlKey() { return $this->_ctrlKey; } + public function getAltKey() { return $this->_altKey; } + + /** + * GetDroppedControl + * + * Compatibility method to get the dropped control + * @return TControl dropped control, or null if not found + */ + public function getDroppedControl () + { + $control=null; + $service=prado::getApplication()->getService(); + if ($service instanceof TPageService) + { + // Find the control + // Warning, this will not work if you have a '_' in your control Id ! + $dropControlId=str_replace(TControl::CLIENT_ID_SEPARATOR,TControl::ID_SEPARATOR,$this->_dragElementId); + $control=$service->getRequestedPage()->findControl($dropControlId); + } + return $control; + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php b/gui/baculum/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php new file mode 100644 index 0000000000..4f1dbbf846 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TEventTriggeredCallback.php @@ -0,0 +1,95 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TEventTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TTriggeredCallback'); + +/** + * TEventTriggeredCallback Class + * + * Triggers a new callback request when a particular {@link setEventName EventName} + * on a control with ID given by {@link setControlID ControlID} is raised. + * + * The default action of the event on the client-side can be prevented when + * {@link setPreventDefaultAction PreventDefaultAction} is set to true. + * + * @author Wei Zhuo + * @version $Id: TEventTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TEventTriggeredCallback extends TTriggeredCallback +{ + /** + * @return string The client-side event name the trigger listens to. + */ + public function getEventName() + { + return $this->getViewState('EventName', ''); + } + + /** + * Sets the client-side event name that fires the callback request. + * @param string The client-side event name the trigger listens to. + */ + public function setEventName($value) + { + $this->setViewState('EventName', $value, ''); + } + + /** + * @param boolean true to prevent/stop default event action. + */ + public function setPreventDefaultAction($value) + { + $this->setViewState('StopEvent', TPropertyValue::ensureBoolean($value), false); + } + + /** + * @return boolean true to prevent/stop default event action. + */ + public function getPreventDefaultAction() + { + return $this->getViewState('StopEvent', false); + } + + /** + * @return array list of timer options for client-side. + */ + protected function getTriggerOptions() + { + $options = parent::getTriggerOptions(); + $name = preg_replace('/^on/', '', $this->getEventName()); + $options['EventName'] = strtolower($name); + $options['StopEvent'] = $this->getPreventDefaultAction(); + return $options; + } + + /** + * Registers the javascript code for initializing the active control. + * @param THtmlWriter the renderer. + */ + public function render($writer) + { + parent::render($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getTriggerOptions()); + } + + /** + * @return string corresponding javascript class name for TEventTriggeredCallback. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TEventTriggeredCallback'; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TInPlaceTextBox.php b/gui/baculum/framework/Web/UI/ActiveControls/TInPlaceTextBox.php new file mode 100644 index 0000000000..5309b7e872 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TInPlaceTextBox.php @@ -0,0 +1,290 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TInPlaceTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); + +/** + * TInPlaceTextBox Class + * * + * TInPlaceTextBox is a component rendered as a label and allows its + * contents to be edited by changing the label to a textbox when + * the label is clicked or when another control or html element with + * ID given by {@link setEditTriggerControlID EditTriggerControlID} is clicked. + * + * If the {@link OnLoadingText} event is handled, a callback request is + * made when the label is clicked, while the request is being made the + * textbox is disabled from editing. The {@link OnLoadingText} event allows + * you to update the content of the textbox before the client is allowed + * to edit the content. After the callback request returns successfully, + * the textbox is enabled and the contents is then allowed to be edited. + * + * Once the textbox loses focus, if {@link setAutoPostBack AutoPostBack} + * is true and the textbox content has changed, a callback request is made and + * the {@link OnTextChanged} event is raised like that of the TActiveTextBox. + * During the request, the textbox is disabled. + * + * After the callback request returns sucessfully, the textbox is enabled. + * If the {@link setAutoHideTextBox AutoHideTextBox} property is true, then + * the textbox will be hidden and the label is then shown. + * + * Since 3.1.2, you can set the {@link setReadOnly ReadOnly} property to make + * the control not editable. This property can be also changed on callback + * + * @author Wei Zhuo + * @version $Id: TInPlaceTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TInPlaceTextBox extends TActiveTextBox +{ + /** + * Sets the auto post back to true by default. + */ + public function __construct() + { + parent::__construct(); + $this->setAutoPostBack(true); + } + + /** + * @param boolean true to hide the textbox after losing focus. + */ + public function setAutoHideTextBox($value) + { + $this->setViewState('AutoHide', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return boolean true will hide the textbox after losing focus. + */ + public function getAutoHideTextBox() + { + return $this->getViewState('AutoHide', true); + } + + /** + * @param boolean true to display the edit textbox + */ + public function setDisplayTextBox($value) + { + $value = TPropertyValue::ensureBoolean($value); + $this->setViewState('DisplayTextBox', $value,false); + if($this->getActiveControl()->canUpdateClientSide()) + $this->callClientFunction('setDisplayTextBox',$value); + } + + /** + * @return boolean true to display the edit textbox + */ + public function getDisplayTextBox() + { + return $this->getViewState('DisplayTextBox', false); + } + + /** + * Calls the client-side static method for this control class. + * @param string static method name + * @param mixed method parmaeter + */ + protected function callClientFunction($func,$value) + { + $client = $this->getPage()->getCallbackClient(); + $code = $this->getClientClassName().'.'.$func; + $client->callClientFunction($code,array($this,$value)); + } + + /** + * @param string ID of the control that can trigger to edit the textbox + */ + public function setEditTriggerControlID($value) + { + $this->setViewState('EditTriggerControlID', $value); + } + + /** + * @return string ID of the control that can trigger to edit the textbox + */ + public function getEditTriggerControlID() + { + return $this->getViewState('EditTriggerControlID'); + } + + /** + * @return string edit trigger control client ID. + */ + protected function getExternalControlID() + { + $extID = $this->getEditTriggerControlID(); + if($extID===null) return ''; + if(($control = $this->findControl($extID))!==null) + return $control->getClientID(); + return $extID; + } + + /** + * On callback response, the inner HTMl of the label and the + * value of the textbox is updated + * @param string the text value of the label + */ + public function setText($value) + { + TTextBox::setText($value); + if($this->getActiveControl()->canUpdateClientSide()) + { + $client = $this->getPage()->getCallbackClient(); + $client->update($this->getLabelClientID(), $value); + $client->setValue($this, $value); + } + } + + /** + * Update ClientSide Readonly property + * @param boolean value + * @since 3.1.2 + */ + public function setReadOnly ($value) + { + $value=TPropertyValue::ensureBoolean($value); + TTextBox::setReadOnly($value); + if ($this->getActiveControl()->canUpdateClientSide()) + { + $this->callClientFunction('setReadOnly', $value); + } + } + + /** + * @return string tag name of the label. + */ + protected function getTagName() + { + return 'span'; + } + + /** + * Renders the body content of the label. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='') + parent::renderContents($writer); + else + $writer->write($text); + } + + /** + * @return string label client ID + */ + protected function getLabelClientID() + { + return $this->getClientID().'__label'; + } + + /** + * This method is invoked when a callback is requested. The method raises + * 'OnCallback' event to fire up the event handlers. If you override this + * method, be sure to call the parent implementation so that the event + * handler can be invoked. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onCallback($param) + { + $action = $param->getCallbackParameter(); + if(is_array($action) && $action[0] === '__InlineEditor_loadExternalText__') + { + $parameter = new TCallbackEventParameter($this->getResponse(), $action[1]); + $this->onLoadingText($parameter); + } + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * @return array callback options. + */ + protected function getPostBackOptions() + { + $options = parent::getPostBackOptions(); + $options['ID'] = $this->getLabelClientID(); + $options['TextBoxID'] = $this->getClientID(); + $options['ExternalControl'] = $this->getExternalControlID(); + $options['AutoHide'] = $this->getAutoHideTextBox() == false ? '' : true; + $options['AutoPostBack'] = $this->getAutoPostBack() == false ? '' : true; + $options['Columns'] = $this->getColumns(); + if($this->getTextMode()==='MultiLine') + { + $options['Rows'] = $this->getRows(); + $options['Wrap'] = $this->getWrap()== false ? '' : true; + } + else + { + $length = $this->getMaxLength(); + $options['MaxLength'] = $length > 0 ? $length : ''; + } + + if($this->hasEventHandler('OnLoadingText')) + $options['LoadTextOnEdit'] = true; + + $options['ReadOnly']=$this->getReadOnly(); + return $options; + } + + /** + * Raised when editing the content is requsted to be loaded from the + * server side. + * @param TCallbackEventParameter event parameter to be passed to the event handlers + */ + public function onLoadingText($param) + { + $this->raiseEvent('OnLoadingText',$this,$param); + } + + /** + * @return string corresponding javascript class name for this TInPlaceTextBox + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TInPlaceTextBox'; + } + + /** + * Ensure that the ID attribute is rendered and registers the javascript code + * for initializing the active control. + */ + protected function addAttributesToRender($writer) + { + //calls the TWebControl to avoid rendering other attribute normally render for a textbox. + TWebControl::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getLabelClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } + + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->registerClientScript(); + } + + /** + * Registers the relevant JavaScript. + */ + protected function registerClientScript() + { + $cs=$this->getPage()->getClientScript(); + $cs->registerPradoScript('inlineeditor'); + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TTimeTriggeredCallback.php b/gui/baculum/framework/Web/UI/ActiveControls/TTimeTriggeredCallback.php new file mode 100644 index 0000000000..46df7a2239 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TTimeTriggeredCallback.php @@ -0,0 +1,128 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTimeTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +/** + * Load active callback control. + */ +Prado::using('System.Web.UI.ActiveControls.TCallback'); + +/** + * TTimeTriggeredCallback class. + * + * TTimeTriggeredCallback sends callback request every {@link setInterval Interval} seconds. + * Upon each callback request, the {@link onCallback OnCallback} event is raised. + * + * The timer can be started by calling {@link startTimer()} and stopped using + * {@link stopTimer()}. The timer can be automatically started when + * {@link setStartTimerOnLoad StartTimerOnLoad} is true. + * + * @author Wei Zhuo + * @version $Id: TTimeTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TTimeTriggeredCallback extends TCallback +{ + /** + * @return float seconds between callback requests. Default is 1 second. + */ + public function getInterval() + { + return $this->getViewState('Interval', 1); + } + + /** + * @param float seconds between callback requests, must be a positive number, default is 1 second. + */ + public function setInterval($value) + { + $interval = TPropertyValue::ensureFloat($value); + if($interval <= 0) + throw new TConfigurationException('callback_interval_be_positive', $this->getID()); + $this->setViewState('Interval', $interval, 1); + if ($this->getActiveControl()->canUpdateClientSide()){ + $client = $this->getPage()->getCallbackClient(); + $client->callClientFunction('Prado.WebUI.TTimeTriggeredCallback.setTimerInterval', array($this, $interval)); + } + } + + /** + * Registers the javascript code to start the timer. + */ + public function startTimer() + { + $client = $this->getPage()->getCallbackClient(); + $client->callClientFunction('Prado.WebUI.TTimeTriggeredCallback.start', array($this)); + } + + /** + * Registers the javascript code to stop the timer. + */ + public function stopTimer() + { + $client = $this->getPage()->getCallbackClient(); + $client->callClientFunction('Prado.WebUI.TTimeTriggeredCallback.stop', array($this)); + } + + /** + * @param boolean true to start the timer when page loads. + */ + public function setStartTimerOnLoad($value) + { + $this->setViewState('StartTimerOnLoad', + TPropertyValue::ensureBoolean($value), false); + } + + /** + * @return boolean true to start the timer when page loads. + */ + public function getStartTimerOnLoad() + { + return $this->getViewState('StartTimerOnLoad', false); + } + + /** + * @return array list of timer options for client-side. + */ + protected function getTriggerOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget']= $this->getUniqueID(); + $options['Interval'] = $this->getInterval(); + return $options; + } + + /** + * Registers the javascript code for initializing the active control. + * @param THtmlWriter the renderer. + */ + public function render($writer) + { + parent::render($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getTriggerOptions()); + if($this->getStartTimerOnLoad()){ + $id = $this->getClientID(); + $code = "Prado.WebUI.TTimeTriggeredCallback.start('{$id}');"; + $cs = $this->getPage()->getClientScript(); + $cs->registerEndScript("{$id}:start", $code); + } + } + + /** + * @return string corresponding javascript class name for TTimeTriggeredCallback. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TTimeTriggeredCallback'; + } +} diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TTriggeredCallback.php b/gui/baculum/framework/Web/UI/ActiveControls/TTriggeredCallback.php new file mode 100644 index 0000000000..2365326bc9 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TTriggeredCallback.php @@ -0,0 +1,71 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TCallback'); +/** + * TTriggeredCallback abstract Class + * + * Base class for triggered callback controls. The {@link setControlID ControlID} + * property sets the control ID to observe the trigger. + * + * @author Wei Zhuo + * @version $Id: TTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +abstract class TTriggeredCallback extends TCallback +{ + /** + * @return string The ID of the server control the trigger is bounded to. + */ + public function getControlID() + { + return $this->getViewState('ControlID', ''); + } + + /** + * @param string The ID of the server control the trigger is bounded to. + */ + public function setControlID($value) + { + $this->setViewState('ControlID', $value, ''); + } + + /** + * @return string target control client ID or html element ID if + * control is not found in hierarchy. + */ + protected function getTargetControl() + { + $id = $this->getControlID(); + if(($control=$this->findControl($id)) instanceof TControl) + return $control->getClientID(); + if($id==='') + { + throw new TConfigurationException( + 'ttriggeredcallback_invalid_controlid', get_class($this)); + } + return $id; + } + + /** + * @return array list of trigger callback options. + */ + protected function getTriggerOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['ControlID'] = $this->getTargetControl(); + return $options; + } +} + diff --git a/gui/baculum/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php b/gui/baculum/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php new file mode 100644 index 0000000000..e9db1bed89 --- /dev/null +++ b/gui/baculum/framework/Web/UI/ActiveControls/TValueTriggeredCallback.php @@ -0,0 +1,120 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TValueTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + */ + +Prado::using('System.Web.UI.ActiveControls.TTriggeredCallback'); + +/** + * TValueTriggeredCallback Class + * + * Observes the value with {@link setPropertyName PropertyName} of a + * control with {@link setControlID ControlID}. Changes to the observed + * property value will trigger a new callback request. The property value is checked + * for changes every{@link setInterval Interval} seconds. + * + * A {@link setDecayRate DecayRate} can be set to increase the polling + * interval linearly if no changes are observed. Once a change is + * observed, the polling interval is reset to the original value. + * + * @author Wei Zhuo + * @version $Id: TValueTriggeredCallback.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.ActiveControls + * @since 3.1 + */ +class TValueTriggeredCallback extends TTriggeredCallback +{ + /** + * @return string The control property name to observe value changes. + */ + public function getPropertyName() + { + return $this->getViewState('PropertyName', ''); + } + + /** + * Sets the control property name to observe value changes that fires the callback request. + * @param string The control property name to observe value changes. + */ + public function setPropertyName($value) + { + $this->setViewState('PropertyName', $value, ''); + } + + /** + * Sets the polling interval in seconds to observe property changes. + * Default is 1 second. + * @param float polling interval in seconds. + */ + public function setInterval($value) + { + $this->setViewState('Interval', TPropertyValue::ensureFloat($value), 1); + } + + /** + * @return float polling interval, 1 second default. + */ + public function getInterval() + { + return $this->getViewState('Interval', 1); + } + + /** + * Gets the decay rate between callbacks. Default is 0; + * @return float decay rate between callbacks. + */ + public function getDecayRate() + { + return $this->getViewState('Decay', 0); + } + + /** + * Sets the decay rate between callback. Default is 0; + * @param float decay rate between callbacks. + */ + public function setDecayRate($value) + { + $decay = TPropertyValue::ensureFloat($value); + if($decay < 0) + throw new TConfigurationException('callback_decay_be_not_negative', $this->getID()); + $this->setViewState('Decay', $decay); + } + + /** + * @return array list of timer options for client-side. + */ + protected function getTriggerOptions() + { + $options = parent::getTriggerOptions(); + $options['PropertyName'] = $this->getPropertyName(); + $options['Interval'] = $this->getInterval(); + $options['Decay'] = $this->getDecayRate(); + return $options; + } + + /** + * Registers the javascript code for initializing the active control. + * @param THtmlWriter the renderer. + */ + public function render($writer) + { + parent::render($writer); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getTriggerOptions()); + } + + /** + * @return string corresponding javascript class name for TEventTriggeredCallback. + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TValueTriggeredCallback'; + } +} diff --git a/gui/baculum/framework/Web/UI/TCachePageStatePersister.php b/gui/baculum/framework/Web/UI/TCachePageStatePersister.php new file mode 100644 index 0000000000..4f3d60ccb8 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TCachePageStatePersister.php @@ -0,0 +1,200 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCachePageStatePersister.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TCachePageStatePersister class + * + * TCachePageStatePersister implements a page state persistent method based on cache. + * Page state are stored in cache (e.g. memcache, DB cache, etc.), and only a small token + * is passed to the client side to identify the state. This greatly reduces the size of + * the page state that needs to be transmitted between the server and the client. Of course, + * this is at the cost of using server side resource. + * + * A cache module has to be loaded in order to use TCachePageStatePersister. + * By default, TCachePageStatePersister will use the primary cache module. + * A non-primary cache module can be used by setting {@link setCacheModuleID CacheModuleID}. + * Any cache module, as long as it implements the interface {@link ICache}, may be used. + * For example, one can use {@link TDbCache}, {@link TMemCache}, {@link TAPCCache}, etc. + * + * TCachePageStatePersister uses {@link setCacheTimeout CacheTimeout} to limit the data + * that stores in cache. + * + * Since server resource is often limited, be cautious if you plan to use TCachePageStatePersister + * for high-traffic Web pages. You may consider using a small {@link setCacheTimeout CacheTimeout}. + * + * There are a couple of ways to use TCachePageStatePersister. + * One can override the page's {@link TPage::getStatePersister()} method and + * create a TCachePageStatePersister instance there. + * Or one can configure the pages to use TCachePageStatePersister in page configurations + * as follows, + * + * + * + * Note in the above, we use StatePersister.CacheModuleID to configure the cache module ID + * for the TCachePageStatePersister instance. + * + * The above configuration will affect the pages under the directory containing + * this configuration and all its subdirectories. + * To configure individual pages to use TCachePageStatePersister, use + * + * + * + * + * + * + * @author Qiang Xue + * @version $Id: TCachePageStatePersister.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.1.1 + */ +class TCachePageStatePersister extends TComponent implements IPageStatePersister +{ + private $_prefix='statepersister'; + private $_page; + private $_cache=null; + private $_cacheModuleID=''; + private $_timeout=1800; + + /** + * @param TPage the page that this persister works for + */ + public function getPage() + { + return $this->_page; + } + + /** + * @param TPage the page that this persister works for. + */ + public function setPage(TPage $page) + { + $this->_page=$page; + } + + /** + * @return string the ID of the cache module. + */ + public function getCacheModuleID() + { + return $this->_cacheModuleID; + } + + /** + * @param string the ID of the cache module. If not set, the primary cache module will be used. + */ + public function setCacheModuleID($value) + { + $this->_cacheModuleID=$value; + } + + /** + * @return ICache the cache module being used for data storage + */ + public function getCache() + { + if($this->_cache===null) + { + if($this->_cacheModuleID!=='') + $cache=Prado::getApplication()->getModule($this->_cacheModuleID); + else + $cache=Prado::getApplication()->getCache(); + if($cache===null || !($cache instanceof ICache)) + { + if($this->_cacheModuleID!=='') + throw new TConfigurationException('cachepagestatepersister_cachemoduleid_invalid',$this->_cacheModuleID); + else + throw new TConfigurationException('cachepagestatepersister_cache_required'); + } + $this->_cache=$cache; + } + return $this->_cache; + } + + /** + * @return integer the number of seconds in which the cached state will expire. Defaults to 1800. + */ + public function getCacheTimeout() + { + return $this->_timeout; + } + + /** + * @param integer the number of seconds in which the cached state will expire. 0 means never expire. + * @throws TInvalidDataValueException if the number is smaller than 0. + */ + public function setCacheTimeout($value) + { + if(($value=TPropertyValue::ensureInteger($value))>=0) + $this->_timeout=$value; + else + throw new TInvalidDataValueException('cachepagestatepersister_timeout_invalid'); + } + + /** + * @return string prefix of cache variable name to avoid conflict with other cache data. Defaults to 'statepersister'. + */ + public function getKeyPrefix() + { + return $this->_prefix; + } + + /** + * @param string prefix of cache variable name to avoid conflict with other cache data + */ + public function setKeyPrefix($value) + { + $this->_prefix=$value; + } + + /** + * @param string micro timestamp when saving state occurs + * @return string a key that is unique per user request + */ + protected function calculateKey($timestamp) + { + return $this->getKeyPrefix().':' + . $this->_page->getRequest()->getUserHostAddress() + . $this->_page->getPagePath() + . $timestamp; + } + + /** + * Saves state in cache. + * @param mixed state to be stored + */ + public function save($data) + { + $timestamp=(string)microtime(true); + $key=$this->calculateKey($timestamp); + $this->getCache()->add($key,$data,$this->_timeout); + $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$timestamp)); + } + + /** + * Loads page state from cache. + * @return mixed the restored state + * @throws THttpException if page state is corrupted + */ + public function load() + { + if(($timestamp=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) + { + $key=$this->calculateKey($timestamp); + if(($data=$this->getCache()->get($key))!==false) + return $data; + } + throw new THttpException(400,'cachepagestatepersister_pagestate_corrupted'); + } +} + diff --git a/gui/baculum/framework/Web/UI/TClientScriptManager.php b/gui/baculum/framework/Web/UI/TClientScriptManager.php new file mode 100644 index 0000000000..ea828187f2 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TClientScriptManager.php @@ -0,0 +1,838 @@ + + * @author Gabor Berczi (lazyload additions & progressive rendering) + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TClientScriptManager.php 3280 2013-03-13 20:19:30Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TClientScriptManager class. + * + * TClientScriptManager manages javascript and CSS stylesheets for a page. + * + * @author Qiang Xue + * @author Gabor Berczi (lazyload additions & progressive rendering) + * @version $Id: TClientScriptManager.php 3280 2013-03-13 20:19:30Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TClientScriptManager extends TApplicationComponent +{ + /** + * directory containing Prado javascript files + */ + const SCRIPT_PATH='Web/Javascripts/source'; + /** + * file containing javascript packages and their cross dependencies + */ + const PACKAGES_FILE='Web/Javascripts/packages.php'; + /** + * @var TPage page who owns this manager + */ + private $_page; + /** + * @var array registered hidden fields, indexed by hidden field names + */ + private $_hiddenFields=array(); + /** + * @var array javascript blocks to be rendered at the beginning of the form + */ + private $_beginScripts=array(); + /** + * @var array javascript blocks to be rendered at the end of the form + */ + private $_endScripts=array(); + /** + * @var array javascript files to be rendered in the form + */ + private $_scriptFiles=array(); + /** + * @var array javascript files to be rendered in page head section + */ + private $_headScriptFiles=array(); + /** + * @var array javascript blocks to be rendered in page head section + */ + private $_headScripts=array(); + /** + * @var array CSS files + */ + private $_styleSheetFiles=array(); + /** + * @var array CSS declarations + */ + private $_styleSheets=array(); + /** + * @var array registered PRADO script libraries + */ + private $_registeredPradoScripts=array(); + /** + * Client-side javascript library dependencies, loads from PACKAGES_FILE; + * @var array + */ + private static $_pradoScripts; + /** + * Client-side javascript library packages, loads from PACKAGES_FILE; + * @var array + */ + private static $_pradoPackages; + + private $_renderedHiddenFields; + + private $_renderedScriptFiles=array(); + + private $_expandedPradoScripts; + + /** + * Constructor. + * @param TPage page that owns this client script manager + */ + public function __construct(TPage $owner) + { + $this->_page=$owner; + } + + /** + * @return boolean whether THead is required in order to render CSS and js within head + * @since 3.1.1 + */ + public function getRequiresHead() + { + return count($this->_styleSheetFiles) || count($this->_styleSheets) + || count($this->_headScriptFiles) || count($this->_headScripts); + } + + public static function getPradoPackages() + { + return self::$_pradoPackages; + } + + public static function getPradoScripts() + { + return self::$_pradoScripts; + } + + /** + * Registers Prado javascript by library name. See "Web/Javascripts/packages.php" + * for library names. + * @param string script library name. + */ + public function registerPradoScript($name) + { + $this->registerPradoScriptInternal($name); + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerPradoScript',$params); + } + + /** + * Registers a Prado javascript library to be loaded. + */ + protected function registerPradoScriptInternal($name) + { + // $this->checkIfNotInRender(); + if(!isset($this->_registeredPradoScripts[$name])) + { + if(self::$_pradoScripts === null) + { + $packageFile = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::PACKAGES_FILE; + list($packages,$deps)= include($packageFile); + self::$_pradoScripts = $deps; + self::$_pradoPackages = $packages; + } + + if (isset(self::$_pradoScripts[$name])) + $this->_registeredPradoScripts[$name]=true; + else + throw new TInvalidOperationException('csmanager_pradoscript_invalid',$name); + + if(($packages=array_keys($this->_registeredPradoScripts))!==array()) + { + $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; + list($path,$baseUrl)=$this->getPackagePathUrl($base); + $packagesUrl=array(); + $isDebug=$this->getApplication()->getMode()===TApplicationMode::Debug; + foreach ($packages as $p) + { + foreach (self::$_pradoScripts[$p] as $dep) + { + foreach (self::$_pradoPackages[$dep] as $script) + if (!isset($this->_expandedPradoScripts[$script])) + { + $this->_expandedPradoScripts[$script] = true; + if($isDebug) + { + if (!in_array($url=$baseUrl.'/'.$script,$packagesUrl)) + $packagesUrl[]=$url; + } else { + if (!in_array($url=$baseUrl.'/min/'.$script,$packagesUrl)) + { + if(!is_file($filePath=$path.'/min/'.$script)) + { + $dirPath=dirname($filePath); + if(!is_dir($dirPath)) + mkdir($dirPath, PRADO_CHMOD, true); + file_put_contents($filePath, TJavaScript::JSMin(file_get_contents($base.'/'.$script))); + chmod($filePath, PRADO_CHMOD); + } + $packagesUrl[]=$url; + } + } + } + } + } + foreach($packagesUrl as $url) + $this->registerScriptFile($url,$url); + } + } + } + + /** + * @return string Prado javascript library base asset url. + */ + public function getPradoScriptAssetUrl() + { + $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; + $assets = Prado::getApplication()->getAssetManager(); + return $assets->getPublishedUrl($base); + } + + /** + * Returns the URLs of all script files referenced on the page + * @return array Combined list of all script urls used in the page + */ + public function getScriptUrls() + { + $scripts = array_values($this->_headScriptFiles); + $scripts = array_merge($scripts, array_values($this->_scriptFiles)); + $scripts = array_unique($scripts); + + return $scripts; + } + + /** + * @param string javascript package path. + * @return array tuple($path,$url). + */ + protected function getPackagePathUrl($base) + { + $assets = Prado::getApplication()->getAssetManager(); + if(strpos($base, $assets->getBaseUrl())===false) + { + if(($dir = Prado::getPathOfNameSpace($base)) !== null) { + $base = $dir; + } + return array($assets->getPublishedPath($base), $assets->publishFilePath($base)); + } + else + { + return array($assets->getBasePath().str_replace($assets->getBaseUrl(),'',$base), $base); + } + } + + /** + * Returns javascript statement that create a new callback request object. + * @param ICallbackEventHandler callback response handler + * @param array additional callback options + * @return string javascript statement that creates a new callback request. + */ + public function getCallbackReference(ICallbackEventHandler $callbackHandler, $options=null) + { + $options = !is_array($options) ? array() : $options; + $class = new ReflectionClass($callbackHandler); + $clientSide = $callbackHandler->getActiveControl()->getClientSide(); + $options = array_merge($options, $clientSide->getOptions()->toArray()); + $optionString = TJavaScript::encode($options); + $this->registerPradoScriptInternal('ajax'); + $id = $callbackHandler->getUniqueID(); + return "new Prado.CallbackRequest('{$id}',{$optionString})"; + } + + /** + * Registers callback javascript for a control. + * @param string javascript class responsible for the control being registered for callback + * @param array callback options + */ + public function registerCallbackControl($class, $options) + { + $optionString=TJavaScript::encode($options); + $code="new {$class}({$optionString});"; + $this->_endScripts[sprintf('%08X', crc32($code))]=$code; + $this->registerPradoScriptInternal('ajax'); + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerCallbackControl',$params); + } + + /** + * Registers postback javascript for a control. A null class parameter will prevent + * the javascript code registration. + * @param string javascript class responsible for the control being registered for postback + * @param array postback options + */ + public function registerPostBackControl($class,$options) + { + if($class === null) { + return; + } + if(!isset($options['FormID']) && ($form=$this->_page->getForm())!==null) + $options['FormID']=$form->getClientID(); + $optionString=TJavaScript::encode($options); + $code="new {$class}({$optionString});"; + + $this->_endScripts[sprintf('%08X', crc32($code))]=$code; + $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]=''; + $this->_hiddenFields[TPage::FIELD_POSTBACK_PARAMETER]=''; + $this->registerPradoScriptInternal('prado'); + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerPostBackControl',$params); + } + + /** + * Register a default button to panel. When the $panel is in focus and + * the 'enter' key is pressed, the $button will be clicked. + * @param TControl|string panel (or its unique ID) to register the default button action + * @param TControl|string button (or its unique ID) to trigger a postback + */ + public function registerDefaultButton($panel, $button) + { + $panelID=is_string($panel)?$panel:$panel->getUniqueID(); + + if(is_string($button)) + $buttonID=$button; + else + { + $button->setIsDefaultButton(true); + $buttonID=$button->getUniqueID(); + } + $options = TJavaScript::encode($this->getDefaultButtonOptions($panelID, $buttonID)); + $code = "new Prado.WebUI.DefaultButton($options);"; + + $this->_endScripts['prado:'.$panelID]=$code; + $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]=''; + $this->registerPradoScriptInternal('prado'); + + $params=array($panelID,$buttonID); + $this->_page->registerCachingAction('Page.ClientScript','registerDefaultButton',$params); + } + + /** + * @param string the unique ID of the container control + * @param string the unique ID of the button control + * @return array default button options. + */ + protected function getDefaultButtonOptions($panelID, $buttonID) + { + $options['ID'] = TControl::convertUniqueIdToClientId($panelID); + $options['Panel'] = TControl::convertUniqueIdToClientId($panelID); + $options['Target'] = TControl::convertUniqueIdToClientId($buttonID); + $options['EventTarget'] = $buttonID; + $options['Event'] = 'click'; + return $options; + } + + /** + * Registers the control to receive default focus. + * @param string the client ID of the control to receive default focus + */ + public function registerFocusControl($target) + { + $this->registerPradoScriptInternal('effects'); + if($target instanceof TControl) + $target=$target->getClientID(); + $id = TJavaScript::quoteString($target); + $this->_endScripts['prado:focus'] = 'new Effect.ScrollTo('.$id.'); Prado.Element.focus('.$id.');'; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerFocusControl',$params); + } + + /** + * Registers a CSS file to be rendered in the page head + * + * The CSS files in themes are registered in {@link OnPreRenderComplete onPreRenderComplete} if you want to override + * CSS styles in themes you need to register it after this event is completed. + * + * Example: + * + * Page->ClientScript->registerStyleSheetFile($url, $url); + * } + * } + * ?> + * + * + * @param string a unique key identifying the file + * @param string URL to the CSS file + * @param string media type of the CSS (such as 'print', 'screen', etc.). Defaults to empty, meaning the CSS applies to all media types. + */ + public function registerStyleSheetFile($key,$url,$media='') + { + if($media==='') + $this->_styleSheetFiles[$key]=$url; + else + $this->_styleSheetFiles[$key]=array($url,$media); + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheetFile',$params); + } + + /** + * Registers a CSS block to be rendered in the page head + * @param string a unique key identifying the CSS block + * @param string CSS block + */ + public function registerStyleSheet($key,$css,$media='') + { + $this->_styleSheets[$key]=$css; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheet',$params); + } + + /** + * Returns the URLs of all stylesheet files referenced on the page + * @return array List of all stylesheet urls used in the page + */ + public function getStyleSheetUrls() + { + $stylesheets = array_values( + array_map( + create_function('$e', 'return is_array($e) ? $e[0] : $e;'), + $this->_styleSheetFiles) + ); + + foreach(Prado::getApplication()->getAssetManager()->getPublished() as $path=>$url) + if (substr($url,strlen($url)-4)=='.css') + $stylesheets[] = $url; + + $stylesheets = array_unique($stylesheets); + + return $stylesheets; + } + + /** + * Returns all the stylesheet code snippets referenced on the page + * @return array List of all stylesheet snippets used in the page + */ + public function getStyleSheetCodes() + { + return array_unique(array_values($this->_styleSheets)); + } + + /** + * Registers a javascript file in the page head + * @param string a unique key identifying the file + * @param string URL to the javascript file + */ + public function registerHeadScriptFile($key,$url) + { + $this->checkIfNotInRender(); + $this->_headScriptFiles[$key]=$url; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerHeadScriptFile',$params); + } + + /** + * Registers a javascript block in the page head. + * @param string a unique key identifying the script block + * @param string javascript block + */ + public function registerHeadScript($key,$script) + { + $this->checkIfNotInRender(); + $this->_headScripts[$key]=$script; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerHeadScript',$params); + } + + /** + * Registers a javascript file to be rendered within the form + * @param string a unique key identifying the file + * @param string URL to the javascript file to be rendered + */ + public function registerScriptFile($key, $url) + { + $this->_scriptFiles[$key]=$url; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerScriptFile',$params); + } + + /** + * Registers a javascript script block at the beginning of the form + * @param string a unique key identifying the script block + * @param string javascript block + */ + public function registerBeginScript($key,$script) + { + $this->checkIfNotInRender(); + $this->_beginScripts[$key]=$script; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerBeginScript',$params); + } + + /** + * Registers a javascript script block at the end of the form + * @param string a unique key identifying the script block + * @param string javascript block + */ + public function registerEndScript($key,$script) + { + $this->_endScripts[$key]=$script; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerEndScript',$params); + } + + /** + * Registers a hidden field to be rendered in the form. + * @param string a unique key identifying the hidden field + * @param string|array hidden field value, if the value is an array, every element + * in the array will be rendered as a hidden field value. + */ + public function registerHiddenField($name,$value) + { + $this->_hiddenFields[$name]=$value; + + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerHiddenField',$params); + } + + /** + * @param string a unique key + * @return boolean whether there is a CSS file registered with the specified key + */ + public function isStyleSheetFileRegistered($key) + { + return isset($this->_styleSheetFiles[$key]); + } + + /** + * @param string a unique key + * @return boolean whether there is a CSS block registered with the specified key + */ + public function isStyleSheetRegistered($key) + { + return isset($this->_styleSheets[$key]); + } + + /** + * @param string a unique key + * @return boolean whether there is a head javascript file registered with the specified key + */ + public function isHeadScriptFileRegistered($key) + { + return isset($this->_headScriptFiles[$key]); + } + + /** + * @param string a unique key + * @return boolean whether there is a head javascript block registered with the specified key + */ + public function isHeadScriptRegistered($key) + { + return isset($this->_headScripts[$key]); + } + + /** + * @param string a unique key + * @return boolean whether there is a javascript file registered with the specified key + */ + public function isScriptFileRegistered($key) + { + return isset($this->_scriptFiles[$key]); + } + + /** + * @param string a unique key + * @return boolean whether there is a beginning javascript block registered with the specified key + */ + public function isBeginScriptRegistered($key) + { + return isset($this->_beginScripts[$key]); + } + + /** + * @param string a unique key + * @return boolean whether there is an ending javascript block registered with the specified key + */ + public function isEndScriptRegistered($key) + { + return isset($this->_endScripts[$key]); + } + + /** + * @return boolean true if any end scripts are registered. + */ + public function hasEndScripts() + { + return count($this->_endScripts) > 0; + } + + /** + * @return boolean true if any begin scripts are registered. + */ + public function hasBeginScripts() + { + return count($this->_beginScripts) > 0; + } + + /** + * @param string a unique key + * @return boolean whether there is a hidden field registered with the specified key + */ + public function isHiddenFieldRegistered($key) + { + return isset($this->_hiddenFields[$key]); + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderStyleSheetFiles($writer) + { + $str=''; + foreach($this->_styleSheetFiles as $url) + { + if(is_array($url)) + $str.="\n"; + else + $str.="\n"; + } + $writer->write($str); + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderStyleSheets($writer) + { + if(count($this->_styleSheets)) + $writer->write("\n"); + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderHeadScriptFiles($writer) + { + $this->renderScriptFiles($writer,$this->_headScriptFiles); + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderHeadScripts($writer) + { + $writer->write(TJavaScript::renderScriptBlocks($this->_headScripts)); + } + + public function renderScriptFilesBegin($writer) + { + $this->renderAllPendingScriptFiles($writer); + } + + public function renderScriptFilesEnd($writer) + { + $this->renderAllPendingScriptFiles($writer); + } + + public function markScriptFileAsRendered($url) + { + $this->_renderedScriptFiles[$url] = $url; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','markScriptFileAsRendered',$params); + } + + protected function renderScriptFiles($writer, Array $scripts) + { + foreach($scripts as $script) + { + $writer->write(TJavaScript::renderScriptFile($script)); + $this->markScriptFileAsRendered($script); + } + } + + protected function getRenderedScriptFiles() + { + return $this->_renderedScriptFiles; + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderAllPendingScriptFiles($writer) + { + if(!empty($this->_scriptFiles)) + { + $addedScripts = array_diff($this->_scriptFiles,$this->getRenderedScriptFiles()); + $this->renderScriptFiles($writer,$addedScripts); + } + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderBeginScripts($writer) + { + $writer->write(TJavaScript::renderScriptBlocks($this->_beginScripts)); + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + public function renderEndScripts($writer) + { + $writer->write(TJavaScript::renderScriptBlocks($this->_endScripts)); + } + + public function renderHiddenFieldsBegin($writer) + { + $this->renderHiddenFieldsInt($writer,true); + } + + public function renderHiddenFieldsEnd($writer) + { + $this->renderHiddenFieldsInt($writer,false); + } + + /** + * Flushes all pending script registrations + * @param THtmlWriter writer for the rendering purpose + * @param TControl the control forcing the flush (used only in error messages) + */ + public function flushScriptFiles($writer, $control=null) + { + $this->_page->ensureRenderInForm($control); + $this->renderAllPendingScriptFiles($writer); + } + + /** + * @param THtmlWriter writer for the rendering purpose + */ + protected function renderHiddenFieldsInt($writer, $initial) + { + if ($initial) $this->_renderedHiddenFields = array(); + $str=''; + foreach($this->_hiddenFields as $name=>$value) + { + if (in_array($name,$this->_renderedHiddenFields)) continue; + $id=strtr($name,':','_'); + if(is_array($value)) + { + foreach($value as $v) + $str.='\n"; + } + else + { + $str.='\n"; + } + $this->_renderedHiddenFields[] = $name; + } + if($str!=='') + $writer->write("
      \n".$str."
      \n"); + } + + public function getHiddenFields() + { + return $this->_hiddenFields; + } + + /** + * Checks whether page rendering has not begun yet + */ + protected function checkIfNotInRender() + { + if ($form = $this->_page->InFormRender) + throw new Exception('Operation invalid when page is already rendering'); + } +} + +/** + * TClientSideOptions abstract class. + * + * TClientSideOptions manages client-side options for components that have + * common client-side javascript behaviours and client-side events such as + * between ActiveControls and validators. + * + * @author + * @version $Id: TClientScriptManager.php 3280 2013-03-13 20:19:30Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +abstract class TClientSideOptions extends TComponent +{ + /** + * @var TMap list of client-side options. + */ + private $_options; + + /** + * Adds on client-side event handler by wrapping the code within a + * javascript function block. If the code begins with "javascript:", the + * code is assumed to be a javascript function block rather than arbiturary + * javascript statements. + * @param string option name + * @param string javascript statements. + */ + protected function setFunction($name, $code) + { + if(!TJavaScript::isJsLiteral($code)) + $code = TJavaScript::quoteJsLiteral($this->ensureFunction($code)); + $this->setOption($name, $code); + } + + /** + * @return string gets a particular option, null if not set. + */ + protected function getOption($name) + { + if ($this->_options) + return $this->_options->itemAt($name); + else + return null; + } + + /** + * @param string option name + * @param mixed option value. + */ + protected function setOption($name, $value) + { + $this->getOptions()->add($name, $value); + } + + /** + * @return TMap gets the list of options as TMap + */ + public function getOptions() + { + if (!$this->_options) + $this->_options = Prado::createComponent('System.Collections.TMap'); + return $this->_options; + } + + /** + * Ensure that the javascript statements are wrapped in a javascript + * function block as function(sender, parameter){ //code }. + */ + protected function ensureFunction($javascript) + { + return "function(sender, parameter){ {$javascript} }"; + } +} + diff --git a/gui/baculum/framework/Web/UI/TCompositeControl.php b/gui/baculum/framework/Web/UI/TCompositeControl.php new file mode 100644 index 0000000000..21e4c46fff --- /dev/null +++ b/gui/baculum/framework/Web/UI/TCompositeControl.php @@ -0,0 +1,38 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TCompositeControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TCompositeControl class. + * TCompositeControl is the base class for controls that are composed + * by other controls. + * + * @author Qiang Xue + * @version $Id: TCompositeControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TCompositeControl extends TControl implements INamingContainer +{ + /** + * Performs the OnInit step for the control and all its child controls. + * This method overrides the parent implementation + * by ensuring child controls are created first. + * Only framework developers should use this method. + * @param TControl the naming container control + */ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + parent::initRecursive($namingContainer); + } +} + diff --git a/gui/baculum/framework/Web/UI/TControl.php b/gui/baculum/framework/Web/UI/TControl.php new file mode 100644 index 0000000000..5ee20d61de --- /dev/null +++ b/gui/baculum/framework/Web/UI/TControl.php @@ -0,0 +1,2394 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * Includes TAttributeCollection and TControlAdapter class + */ +Prado::using('System.Collections.TAttributeCollection'); +Prado::using('System.Web.UI.TControlAdapter'); + +/** + * TControl class + * + * TControl is the base class for all components on a page hierarchy. + * It implements the following features for UI-related functionalities: + * - databinding feature + * - parent and child relationship + * - naming container and containee relationship + * - viewstate and controlstate features + * - rendering scheme + * - control lifecycles + * + * A property can be data-bound with an expression. By calling {@link dataBind}, + * expressions bound to properties will be evaluated and the results will be + * set to the corresponding properties. + * + * Parent and child relationship determines how the presentation of controls are + * enclosed within each other. A parent will determine where to place + * the presentation of its child controls. For example, a TPanel will enclose + * all its child controls' presentation within a div html tag. A control's parent + * can be obtained via {@link getParent Parent} property, and its + * {@link getControls Controls} property returns a list of the control's children, + * including controls and static texts. The property can be manipulated + * like an array for adding or removing a child (see {@link TList} for more details). + * + * A naming container control implements INamingContainer and ensures that + * its containee controls can be differentiated by their ID property values. + * Naming container and containee realtionship specifies a protocol to uniquely + * identify an arbitrary control on a page hierarchy by an ID path (concatenation + * of all naming containers' IDs and the target control's ID). + * + * Viewstate and controlstate are two approaches to preserve state across + * page postback requests. ViewState is mainly related with UI specific state + * and can be disabled if not needed. ControlState represents crucial logic state + * and cannot be disabled. + * + * A control is rendered via its {@link render()} method (the method is invoked + * by the framework.) Descendant control classes may override this method for + * customized rendering. By default, {@link render()} invokes {@link renderChildren()} + * which is responsible for rendering of children of the control. + * Control's {@link getVisible Visible} property governs whether the control + * should be rendered or not. + * + * Each control on a page will undergo a series of lifecycles, including + * control construction, Init, Load, PreRender, Render, and OnUnload. + * They work together with page lifecycles to process a page request. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TControl extends TApplicationComponent implements IRenderable, IBindable +{ + /** + * format of control ID + */ + const ID_FORMAT='/^[a-zA-Z_]\\w*$/'; + /** + * separator char between IDs in a UniqueID + */ + const ID_SEPARATOR='$'; + /** + * separator char between IDs in a ClientID + */ + const CLIENT_ID_SEPARATOR='_'; + /** + * prefix to an ID automatically generated + */ + const AUTOMATIC_ID_PREFIX='ctl'; + + /** + * the stage of lifecycles that the control is currently at + */ + const CS_CONSTRUCTED=0; + const CS_CHILD_INITIALIZED=1; + const CS_INITIALIZED=2; + const CS_STATE_LOADED=3; + const CS_LOADED=4; + const CS_PRERENDERED=5; + + /** + * State bits. + */ + const IS_ID_SET=0x01; + const IS_DISABLE_VIEWSTATE=0x02; + const IS_SKIN_APPLIED=0x04; + const IS_STYLESHEET_APPLIED=0x08; + const IS_DISABLE_THEMING=0x10; + const IS_CHILD_CREATED=0x20; + const IS_CREATING_CHILD=0x40; + + /** + * Indexes for the rare fields. + * In order to save memory, rare fields will only be created if they are needed. + */ + const RF_CONTROLS=0; // child controls + const RF_CHILD_STATE=1; // child state field + const RF_NAMED_CONTROLS=2; // list of controls whose namingcontainer is this control + const RF_NAMED_CONTROLS_ID=3; // counter for automatic id + const RF_SKIN_ID=4; // skin ID + const RF_DATA_BINDINGS=5; // data bindings + const RF_EVENTS=6; // event handlers + const RF_CONTROLSTATE=7; // controlstate + const RF_NAMED_OBJECTS=8; // controls declared with ID on template + const RF_ADAPTER=9; // adapter + const RF_AUTO_BINDINGS=10; // auto data bindings + + /** + * @var string control ID + */ + private $_id=''; + /** + * @var string control unique ID + */ + private $_uid; + /** + * @var TControl parent of the control + */ + private $_parent; + /** + * @var TPage page that the control resides in + */ + private $_page; + /** + * @var TControl naming container of the control + */ + private $_namingContainer; + /** + * @var TTemplateControl control whose template contains the control + */ + private $_tplControl; + /** + * @var array viewstate data + */ + private $_viewState=array(); + /** + * @var array temporary state (usually set in template) + */ + private $_tempState=array(); + /** + * @var boolean whether we should keep state in viewstate + */ + private $_trackViewState=true; + /** + * @var integer the current stage of the control lifecycles + */ + private $_stage=0; + /** + * @var integer representation of the state bits + */ + private $_flags=0; + /** + * @var array a collection of rare control data + */ + private $_rf=array(); + + /** + * Constructor. + */ + public function __construct() + { + } + + /** + * Returns a property value by name or a control by ID. + * This overrides the parent implementation by allowing accessing + * a control via its ID using the following syntax, + * + * $menuBar=$this->menuBar; + * + * Note, the control must be configured in the template + * with explicit ID. If the name matches both a property and a control ID, + * the control ID will take the precedence. + * + * @param string the property name or control ID + * @return mixed the property value or the target control + * @throws TInvalidOperationException if the property is not defined. + * @see registerObject + */ + public function __get($name) + { + if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) + return $this->_rf[self::RF_NAMED_OBJECTS][$name]; + else + return parent::__get($name); + } + + /** + * @return boolean whether there is an adapter for this control + */ + public function getHasAdapter() + { + return isset($this->_rf[self::RF_ADAPTER]); + } + + /** + * @return TControlAdapter control adapter. Null if not exists. + */ + public function getAdapter() + { + return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null; + } + + /** + * @param TControlAdapter control adapter + */ + public function setAdapter(TControlAdapter $adapter) + { + $this->_rf[self::RF_ADAPTER]=$adapter; + } + + /** + * @return TControl the parent of this control + */ + public function getParent() + { + return $this->_parent; + } + + /** + * @return TControl the naming container of this control + */ + public function getNamingContainer() + { + if(!$this->_namingContainer && $this->_parent) + { + if($this->_parent instanceof INamingContainer) + $this->_namingContainer=$this->_parent; + else + $this->_namingContainer=$this->_parent->getNamingContainer(); + } + return $this->_namingContainer; + } + + /** + * @return TPage the page that contains this control + */ + public function getPage() + { + if(!$this->_page) + { + if($this->_parent) + $this->_page=$this->_parent->getPage(); + else if($this->_tplControl) + $this->_page=$this->_tplControl->getPage(); + } + return $this->_page; + } + + /** + * Sets the page for a control. + * Only framework developers should use this method. + * @param TPage the page that contains this control + */ + public function setPage($page) + { + $this->_page=$page; + } + + /** + * Sets the control whose template contains this control. + * Only framework developers should use this method. + * @param TTemplateControl the control whose template contains this control + */ + public function setTemplateControl($control) + { + $this->_tplControl=$control; + } + + /** + * @return TTemplateControl the control whose template contains this control + */ + public function getTemplateControl() + { + if(!$this->_tplControl && $this->_parent) + $this->_tplControl=$this->_parent->getTemplateControl(); + return $this->_tplControl; + } + + /** + * @return TTemplateControl the control whose template is loaded from + * some external storage, such as file, db, and whose template ultimately + * contains this control. + */ + public function getSourceTemplateControl() + { + $control=$this; + while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null) + { + if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl()) + return $control; + } + return $this->getPage(); + } + + /** + * Gets the lifecycle step the control is currently at. + * This method should only be used by control developers. + * @return integer the lifecycle step the control is currently at. + * The value can be CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED, + * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED. + */ + protected function getControlStage() + { + return $this->_stage; + } + + /** + * Sets the lifecycle step the control is currently at. + * This method should only be used by control developers. + * @param integer the lifecycle step the control is currently at. + * Valid values include CS_CONSTRUCTED, CS_CHILD_INITIALIZED, CS_INITIALIZED, + * CS_STATE_LOADED, CS_LOADED, CS_PRERENDERED. + */ + protected function setControlStage($value) + { + $this->_stage=$value; + } + + /** + * Returns the id of the control. + * Control ID can be either manually set or automatically generated. + * If $hideAutoID is true, automatically generated ID will be returned as an empty string. + * @param boolean whether to hide automatically generated ID + * @return string the ID of the control + */ + public function getID($hideAutoID=true) + { + if($hideAutoID) + return ($this->_flags & self::IS_ID_SET) ? $this->_id : ''; + else + return $this->_id; + } + + /** + * @param string the new control ID. The value must consist of word characters [a-zA-Z0-9_] only + * @throws TInvalidDataValueException if ID is in a bad format + */ + public function setID($id) + { + if(!preg_match(self::ID_FORMAT,$id)) + throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id); + $this->_id=$id; + $this->_flags |= self::IS_ID_SET; + $this->clearCachedUniqueID($this instanceof INamingContainer); + if($this->_namingContainer) + $this->_namingContainer->clearNameTable(); + } + + /** + * Returns a unique ID that identifies the control in the page hierarchy. + * A unique ID is the contenation of all naming container controls' IDs and the control ID. + * These IDs are separated by '$' character. + * Control users should not rely on the specific format of UniqueID, however. + * @return string a unique ID that identifies the control in the page hierarchy + */ + public function getUniqueID() + { + if($this->_uid==='' || $this->_uid===null) // need to build the UniqueID + { + $this->_uid=''; // set to not-null, so that clearCachedUniqueID() may take action + if($namingContainer=$this->getNamingContainer()) + { + if($this->getPage()===$namingContainer) + return ($this->_uid=$this->_id); + else if(($prefix=$namingContainer->getUniqueID())==='') + return $this->_id; + else + return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id); + } + else // no naming container + return $this->_id; + } + else + return $this->_uid; + } + + /** + * Sets input focus to this control. + */ + public function focus() + { + $this->getPage()->setFocus($this); + } + + /** + * Returns the client ID of the control. + * The client ID can be used to uniquely identify + * the control in client-side scripts (such as JavaScript). + * Do not rely on the explicit format of the return ID. + * @return string the client ID of the control + */ + public function getClientID() + { + return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); + } + + /** + * Converts a unique ID to a client ID. + * @param string the unique ID of a control + * @return string the client ID of the control + */ + public static function convertUniqueIdToClientId($uniqueID) + { + return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); + } + + /** + * @return string the skin ID of this control, '' if not set + */ + public function getSkinID() + { + return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:''; + } + + /** + * @param string the skin ID of this control + * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already. + */ + public function setSkinID($value) + { + if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED) + throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this)); + else + $this->_rf[self::RF_SKIN_ID]=$value; + } + + /** + * @param string the skin ID of this control + * @throws TInvalidOperationException if the SkinID is set in a stage later than PreInit, or if the skin is applied already. + */ + public function getIsSkinApplied() + { + return ($this->_flags & self::IS_SKIN_APPLIED); + } + + /** + * @return boolean whether theming is enabled for this control. + * The theming is enabled if the control and all its parents have it enabled. + */ + public function getEnableTheming() + { + if($this->_flags & self::IS_DISABLE_THEMING) + return false; + else + return $this->_parent?$this->_parent->getEnableTheming():true; + } + + /** + * @param boolean whether to enable theming + * @throws TInvalidOperationException if this method is invoked after OnPreInit + */ + public function setEnableTheming($value) + { + if($this->_stage>=self::CS_CHILD_INITIALIZED) + throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID()); + else if(TPropertyValue::ensureBoolean($value)) + $this->_flags &= ~self::IS_DISABLE_THEMING; + else + $this->_flags |= self::IS_DISABLE_THEMING; + } + + /** + * Returns custom data associated with this control. + * A control may be associated with some custom data for various purposes. + * For example, a button may be associated with a string to identify itself + * in a generic OnClick event handler. + * @return mixed custom data associated with this control. Defaults to null. + */ + public function getCustomData() + { + return $this->getViewState('CustomData',null); + } + + /** + * Associates custom data with this control. + * Note, the custom data must be serializable and unserializable. + * @param mixed custom data + */ + public function setCustomData($value) + { + $this->setViewState('CustomData',$value,null); + } + + /** + * @return boolean whether the control has child controls + */ + public function getHasControls() + { + return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0; + } + + /** + * @return TControlCollection the child control collection + */ + public function getControls() + { + if(!isset($this->_rf[self::RF_CONTROLS])) + $this->_rf[self::RF_CONTROLS]=$this->createControlCollection(); + return $this->_rf[self::RF_CONTROLS]; + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TControlCollection control collection + * @see getControls + */ + protected function createControlCollection() + { + return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this); + } + + /** + * Checks if a control is visible. + * If parent check is required, then a control is visible only if the control + * and all its ancestors are visible. + * @param boolean whether the parents should also be checked if visible + * @return boolean whether the control is visible (default=true). + */ + public function getVisible($checkParents=true) + { + if($checkParents) + { + for($control=$this;$control;$control=$control->_parent) + if(!$control->getVisible(false)) + return false; + return true; + } + else + return $this->getViewState('Visible',true); + } + + /** + * @param boolean whether the control is visible + */ + public function setVisible($value) + { + $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Returns a value indicating whether the control is enabled. + * A control is enabled if it allows client user interaction. + * If $checkParents is true, all parent controls will be checked, + * and unless they are all enabled, false will be returned. + * The property Enabled is mainly used for {@link TWebControl} + * derived controls. + * @param boolean whether the parents should also be checked enabled + * @return boolean whether the control is enabled. + */ + public function getEnabled($checkParents=false) + { + if($checkParents) + { + for($control=$this;$control;$control=$control->_parent) + if(!$control->getViewState('Enabled',true)) + return false; + return true; + } + else + return $this->getViewState('Enabled',true); + } + + /** + * @param boolean whether the control is to be enabled. + */ + public function setEnabled($value) + { + $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the control has custom attributes + */ + public function getHasAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->getCount()>0; + else + return false; + } + + /** + * Returns the list of custom attributes. + * Custom attributes are name-value pairs that may be rendered + * as HTML tags' attributes. + * @return TAttributeCollection the list of custom attributes + */ + public function getAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes; + else + { + $attributes=new TAttributeCollection; + $this->setViewState('Attributes',$attributes,null); + return $attributes; + } + } + + /** + * @return boolean whether the named attribute exists + */ + public function hasAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->contains($name); + else + return false; + } + + /** + * @return string attribute value, null if attribute does not exist + */ + public function getAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->itemAt($name); + else + return null; + } + + /** + * Sets a custom control attribute. + * @param string attribute name + * @param string value of the attribute + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + + /** + * Removes the named attribute. + * @param string the name of the attribute to be removed. + * @return string attribute value removed, null if attribute does not exist. + */ + public function removeAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->remove($name); + else + return null; + } + + /** + * @return boolean whether viewstate is enabled + */ + public function getEnableViewState($checkParents=false) + { + if($checkParents) + { + for($control=$this;$control!==null;$control=$control->getParent()) + if($control->_flags & self::IS_DISABLE_VIEWSTATE) + return false; + return true; + } + else + return !($this->_flags & self::IS_DISABLE_VIEWSTATE); + } + + /** + * @param boolean set whether to enable viewstate + */ + public function setEnableViewState($value) + { + if(TPropertyValue::ensureBoolean($value)) + $this->_flags &= ~self::IS_DISABLE_VIEWSTATE; + else + $this->_flags |= self::IS_DISABLE_VIEWSTATE; + } + + /** + * Returns a controlstate value. + * + * This function is mainly used in defining getter functions for control properties + * that must be kept in controlstate. + * @param string the name of the controlstate value to be returned + * @param mixed the default value. If $key is not found in controlstate, $defaultValue will be returned + * @return mixed the controlstate value corresponding to $key + */ + protected function getControlState($key,$defaultValue=null) + { + return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue; + } + + /** + * Sets a controlstate value. + * + * This function is very useful in defining setter functions for control properties + * that must be kept in controlstate. + * Make sure that the controlstate value must be serializable and unserializable. + * @param string the name of the controlstate value + * @param mixed the controlstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from controlstate + */ + protected function setControlState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_rf[self::RF_CONTROLSTATE][$key]); + else + $this->_rf[self::RF_CONTROLSTATE][$key]=$value; + } + + /** + * Clears a controlstate value. + * @param string the name of the controlstate value to be cleared + */ + protected function clearControlState($key) + { + unset($this->_rf[self::RF_CONTROLSTATE][$key]); + } + + /** + * Sets a value indicating whether we should keep data in viewstate. + * When it is false, data saved via setViewState() will not be persisted. + * By default, it is true, meaning data will be persisted across postbacks. + * @param boolean whether data should be persisted + */ + public function trackViewState($enabled) + { + $this->_trackViewState=TPropertyValue::ensureBoolean($enabled); + } + + /** + * Returns a viewstate value. + * + * This function is very useful in defining getter functions for component properties + * that must be kept in viewstate. + * @param string the name of the viewstate value to be returned + * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned + * @return mixed the viewstate value corresponding to $key + */ + public function getViewState($key,$defaultValue=null) + { + if(isset($this->_viewState[$key])) + return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue; + else if(isset($this->_tempState[$key])) + { + if(is_object($this->_tempState[$key]) && $this->_trackViewState) + $this->_viewState[$key]=$this->_tempState[$key]; + return $this->_tempState[$key]; + } + else + return $defaultValue; + } + + /** + * Sets a viewstate value. + * + * This function is very useful in defining setter functions for control properties + * that must be kept in viewstate. + * Make sure that the viewstate value must be serializable and unserializable. + * @param string the name of the viewstate value + * @param mixed the viewstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. + */ + public function setViewState($key,$value,$defaultValue=null) + { + if($this->_trackViewState) + { + $this->_viewState[$key]=$value; + unset($this->_tempState[$key]); + } + else + { + unset($this->_viewState[$key]); + $this->_tempState[$key]=$value; + } + } + + /** + * Clears a viewstate value. + * @param string the name of the viewstate value to be cleared + */ + public function clearViewState($key) + { + unset($this->_viewState[$key]); + unset($this->_tempState[$key]); + } + + /** + * Sets up the binding between a property (or property path) and an expression. + * The context of the expression is the template control (or the control itself if it is a page). + * @param string the property name, or property path + * @param string the expression + */ + public function bindProperty($name,$expression) + { + $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression; + } + + /** + * Breaks the binding between a property (or property path) and an expression. + * @param string the property name (or property path) + */ + public function unbindProperty($name) + { + unset($this->_rf[self::RF_DATA_BINDINGS][$name]); + } + + /** + * Sets up the binding between a property (or property path) and an expression. + * Unlike regular databinding, the expression bound by this method + * is automatically evaluated during {@link prerenderRecursive()}. + * The context of the expression is the template control (or the control itself if it is a page). + * @param string the property name, or property path + * @param string the expression + */ + public function autoBindProperty($name,$expression) + { + $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression; + } + + /** + * Performs the databinding for this control. + */ + public function dataBind() + { + $this->dataBindProperties(); + $this->onDataBinding(null); + $this->dataBindChildren(); + } + + /** + * Databinding properties of the control. + */ + protected function dataBindProperties() + { + Prado::trace("Data bind properties",'System.Web.UI.TControl'); + if(isset($this->_rf[self::RF_DATA_BINDINGS])) + { + if(($context=$this->getTemplateControl())===null) + $context=$this; + foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression) + $this->setSubProperty($property,$context->evaluateExpression($expression)); + } + } + + /** + * Auto databinding properties of the control. + */ + protected function autoDataBindProperties() + { + if(isset($this->_rf[self::RF_AUTO_BINDINGS])) + { + if(($context=$this->getTemplateControl())===null) + $context=$this; + foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression) + $this->setSubProperty($property,$context->evaluateExpression($expression)); + } + } + + /** + * Databinding child controls. + */ + protected function dataBindChildren() + { + Prado::trace("dataBindChildren()",'System.Web.UI.TControl'); + if(isset($this->_rf[self::RF_CONTROLS])) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof IBindable) + $control->dataBind(); + } + } + + /** + * @return boolean whether child controls have been created + */ + final protected function getChildControlsCreated() + { + return ($this->_flags & self::IS_CHILD_CREATED)!==0; + } + + /** + * Sets a value indicating whether child controls are created. + * If false, any existing child controls will be cleared up. + * @param boolean whether child controls are created + */ + final protected function setChildControlsCreated($value) + { + if($value) + $this->_flags |= self::IS_CHILD_CREATED; + else + { + if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED)) + $this->getControls()->clear(); + $this->_flags &= ~self::IS_CHILD_CREATED; + } + } + + /** + * Ensures child controls are created. + * If child controls are not created yet, this method will invoke + * {@link createChildControls} to create them. + */ + public function ensureChildControls() + { + if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD)) + { + try + { + $this->_flags |= self::IS_CREATING_CHILD; + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->createChildControls(); + else + $this->createChildControls(); + $this->_flags &= ~self::IS_CREATING_CHILD; + $this->_flags |= self::IS_CHILD_CREATED; + } + catch(Exception $e) + { + $this->_flags &= ~self::IS_CREATING_CHILD; + $this->_flags |= self::IS_CHILD_CREATED; + throw $e; + } + } + } + + /** + * Creates child controls. + * This method can be overriden for controls who want to have their controls. + * Do not call this method directly. Instead, call {@link ensureChildControls} + * to ensure child controls are created only once. + */ + public function createChildControls() + { + } + + /** + * Finds a control by ID path within the current naming container. + * The current naming container is either the control itself + * if it implements {@link INamingContainer} or the control's naming container. + * The ID path is an ID sequence separated by {@link TControl::ID_SEPARATOR}. + * For example, 'Repeater1.Item1.Button1' looks for a control with ID 'Button1' + * whose naming container is 'Item1' whose naming container is 'Repeater1'. + * @param string ID of the control to be looked up + * @return TControl|null the control found, null if not found + * @throws TInvalidDataValueException if a control's ID is found not unique within its naming container. + */ + public function findControl($id) + { + $id=strtr($id,'.',self::ID_SEPARATOR); + $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer(); + if(!$container || !$container->getHasControls()) + return null; + if(!isset($container->_rf[self::RF_NAMED_CONTROLS])) + { + $container->_rf[self::RF_NAMED_CONTROLS]=array(); + $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]); + } + if(($pos=strpos($id,self::ID_SEPARATOR))===false) + return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null; + else + { + $cid=substr($id,0,$pos); + $sid=substr($id,$pos+1); + if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid])) + return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid); + else + return null; + } + } + + /** + * Finds all child and grand-child controls that are of the specified type. + * @param string the class name + * @param boolean whether the type comparison is strict or not. If false, controls of the parent classes of the specified class will also be returned. + * @return array list of controls found + */ + public function findControlsByType($type,$strict=true) + { + $controls=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type)))) + $controls[]=$control; + if(($control instanceof TControl) && $control->getHasControls()) + $controls=array_merge($controls,$control->findControlsByType($type,$strict)); + } + } + return $controls; + } + + /** + * Finds all child and grand-child controls with the specified ID. + * Note, this method is different from {@link findControl} in that + * it searches through all controls that have this control as the ancestor + * while {@link findcontrol} only searches through controls that have this + * control as the direct naming container. + * @param string the ID being looked for + * @return array list of controls found + */ + public function findControlsByID($id) + { + $controls=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + if($control->_id===$id) + $controls[]=$control; + $controls=array_merge($controls,$control->findControlsByID($id)); + } + } + } + return $controls; + } + + /** + * Resets the control as a naming container. + * Only framework developers should use this method. + */ + public function clearNamingContainer() + { + unset($this->_rf[self::RF_NAMED_CONTROLS_ID]); + $this->clearNameTable(); + } + + /** + * Registers an object by a name. + * A registered object can be accessed like a public member variable. + * This method should only be used by framework and control developers. + * @param string name of the object + * @param object object to be declared + * @see __get + */ + public function registerObject($name,$object) + { + if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) + throw new TInvalidOperationException('control_object_reregistered',$name); + $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object; + } + + /** + * Unregisters an object by name. + * @param string name of the object + * @see registerObject + */ + public function unregisterObject($name) + { + unset($this->_rf[self::RF_NAMED_OBJECTS][$name]); + } + + /** + * @return boolean whether an object has been registered with the name + * @see registerObject + */ + public function isObjectRegistered($name) + { + return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]); + } + + /** + * @return boolean true if the child control has been initialized. + */ + public function getHasChildInitialized() + { + return $this->getControlStage() >= self::CS_CHILD_INITIALIZED; + } + + /** + * @return boolean true if the onInit event has raised. + */ + public function getHasInitialized() + { + return $this->getControlStage() >= self::CS_INITIALIZED; + } + + /** + * @return boolean true if the control has loaded post data. + */ + public function getHasLoadedPostData() + { + return $this->getControlStage() >= self::CS_STATE_LOADED; + } + + /** + * @return boolean true if the onLoad event has raised. + */ + public function getHasLoaded() + { + return $this->getControlStage() >= self::CS_LOADED; + } + + /** + * @return boolean true if onPreRender event has raised. + */ + public function getHasPreRendered() + { + return $this->getControlStage() >= self::CS_PRERENDERED; + } + + /** + * Returns the named registered object. + * A component with explicit ID on a template will be registered to + * the template owner. This method allows you to obtain this component + * with the ID. + * @return mixed the named registered object. Null if object is not found. + */ + public function getRegisteredObject($name) + { + return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null; + } + + /** + * @return boolean whether body contents are allowed for this control. Defaults to true. + */ + public function getAllowChildControls() + { + return true; + } + + /** + * Adds the object instantiated on a template to the child control collection. + * This method overrides the parent implementation. + * Only framework developers and control developers should use this method. + * @param string|TComponent text string or component parsed and instantiated in template + * @see createdOnTemplate + */ + public function addParsedObject($object) + { + $this->getControls()->add($object); + } + + /** + * Clears up the child state data. + * After a control loads its state, those state that do not belong to + * any existing child controls are stored as child state. + * This method will remove these state. + * Only frameworker developers and control developers should use this method. + */ + final protected function clearChildState() + { + unset($this->_rf[self::RF_CHILD_STATE]); + } + + /** + * @param TControl the potential ancestor control + * @return boolean if the control is a descendent (parent, parent of parent, etc.) + * of the specified control + */ + final protected function isDescendentOf($ancestor) + { + $control=$this; + while($control!==$ancestor && $control->_parent) + $control=$control->_parent; + return $control===$ancestor; + } + + /** + * Adds a control into the child collection of the control. + * Control lifecycles will be caught up during the addition. + * Only framework developers should use this method. + * @param TControl the new child control + */ + public function addedControl($control) + { + if($control->_parent) + $control->_parent->getControls()->remove($control); + $control->_parent=$this; + $control->_page=$this->getPage(); + $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer; + if($namingContainer) + { + $control->_namingContainer=$namingContainer; + if($control->_id==='') + $control->generateAutomaticID(); + else + $namingContainer->clearNameTable(); + $control->clearCachedUniqueID($control instanceof INamingContainer); + } + + if($this->_stage>=self::CS_CHILD_INITIALIZED) + { + $control->initRecursive($namingContainer); + if($this->_stage>=self::CS_STATE_LOADED) + { + if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id])) + { + $state=$this->_rf[self::RF_CHILD_STATE][$control->_id]; + unset($this->_rf[self::RF_CHILD_STATE][$control->_id]); + } + else + $state=null; + $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE)); + if($this->_stage>=self::CS_LOADED) + { + $control->loadRecursive(); + if($this->_stage>=self::CS_PRERENDERED) + $control->preRenderRecursive(); + } + } + } + } + + /** + * Removes a control from the child collection of the control. + * Only framework developers should use this method. + * @param TControl the child control removed + */ + public function removedControl($control) + { + if($this->_namingContainer) + $this->_namingContainer->clearNameTable(); + $control->unloadRecursive(); + $control->_parent=null; + $control->_page=null; + $control->_namingContainer=null; + $control->_tplControl=null; + //$control->_stage=self::CS_CONSTRUCTED; + if(!($control->_flags & self::IS_ID_SET)) + $control->_id=''; + else + unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]); + $control->clearCachedUniqueID(true); + } + + /** + * Performs the Init step for the control and all its child controls. + * Only framework developers should use this method. + * @param TControl the naming container control + */ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + if($this->getHasControls()) + { + if($this instanceof INamingContainer) + $namingContainer=$this; + $page=$this->getPage(); + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + $control->_namingContainer=$namingContainer; + $control->_page=$page; + if($control->_id==='' && $namingContainer) + $control->generateAutomaticID(); + $control->initRecursive($namingContainer); + } + } + } + if($this->_stage_stage=self::CS_CHILD_INITIALIZED; + if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED)) + { + $page->applyControlSkin($this); + $this->_flags |= self::IS_SKIN_APPLIED; + } + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onInit(null); + else + $this->onInit(null); + $this->_stage=self::CS_INITIALIZED; + } + } + + /** + * Performs the Load step for the control and all its child controls. + * Only framework developers should use this method. + */ + protected function loadRecursive() + { + if($this->_stage_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onLoad(null); + else + $this->onLoad(null); + } + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->loadRecursive(); + } + } + if($this->_stage_stage=self::CS_LOADED; + } + + /** + * Performs the PreRender step for the control and all its child controls. + * Only framework developers should use this method. + */ + protected function preRenderRecursive() + { + $this->autoDataBindProperties(); + + if($this->getVisible(false)) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onPreRender(null); + else + $this->onPreRender(null); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->preRenderRecursive(); + else if($control instanceof TCompositeLiteral) + $control->evaluateDynamicContent(); + } + } + $this->addToPostDataLoader(); + } + $this->_stage=self::CS_PRERENDERED; + } + + /** + * Add controls implementing IPostBackDataHandler to post data loaders. + */ + protected function addToPostDataLoader() + { + if($this instanceof IPostBackDataHandler) + $this->getPage()->registerPostDataLoader($this); + } + + /** + * Performs the Unload step for the control and all its child controls. + * Only framework developers should use this method. + */ + protected function unloadRecursive() + { + if(!($this->_flags & self::IS_ID_SET)) + $this->_id=''; + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof TControl) + $control->unloadRecursive(); + } + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onUnload(null); + else + $this->onUnload(null); + } + + /** + * This method is invoked when the control enters 'OnInit' stage. + * The method raises 'OnInit' event. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onInit($param) + { + $this->raiseEvent('OnInit',$this,$param); + } + + /** + * This method is invoked when the control enters 'OnLoad' stage. + * The method raises 'OnLoad' event. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onLoad($param) + { + $this->raiseEvent('OnLoad',$this,$param); + } + + /** + * Raises 'OnDataBinding' event. + * This method is invoked when {@link dataBind} is invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onDataBinding($param) + { + Prado::trace("onDataBinding()",'System.Web.UI.TControl'); + $this->raiseEvent('OnDataBinding',$this,$param); + } + + + /** + * This method is invoked when the control enters 'OnUnload' stage. + * The method raises 'OnUnload' event. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onUnload($param) + { + $this->raiseEvent('OnUnload',$this,$param); + } + + /** + * This method is invoked when the control enters 'OnPreRender' stage. + * The method raises 'OnPreRender' event. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onPreRender($param) + { + $this->raiseEvent('OnPreRender',$this,$param); + } + + /** + * Invokes the parent's bubbleEvent method. + * A control who wants to bubble an event must call this method in its onEvent method. + * @param TControl sender of the event + * @param TEventParameter event parameter + * @see bubbleEvent + */ + protected function raiseBubbleEvent($sender,$param) + { + $control=$this; + while($control=$control->_parent) + { + if($control->bubbleEvent($sender,$param)) + break; + } + } + + /** + * This method responds to a bubbled event. + * This method should be overriden to provide customized response to a bubbled event. + * Check the type of event parameter to determine what event is bubbled currently. + * @param TControl sender of the event + * @param TEventParameter event parameters + * @return boolean true if the event bubbling is handled and no more bubbling. + * @see raiseBubbleEvent + */ + public function bubbleEvent($sender,$param) + { + return false; + } + + /** + * Broadcasts an event. + * The event will be sent to all controls on the current page hierarchy. + * If a control defines the event, the event will be raised for the control. + * If a control implements {@link IBroadcastEventReceiver}, its + * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} method will + * be invoked which gives the control a chance to respond to the event. + * For example, when broadcasting event 'OnClick', all controls having 'OnClick' + * event will have this event raised, and all controls implementing + * {@link IBroadcastEventReceiver} will also have its + * {@link IBroadcastEventReceiver::broadcastEventReceived broadcastEventReceived()} + * invoked. + * @param string name of the broadcast event + * @param TControl sender of this event + * @param TEventParameter event parameter + */ + public function broadcastEvent($name,$sender,$param) + { + $rootControl=(($page=$this->getPage())===null)?$this:$page; + $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param)); + } + + /** + * Recursively broadcasts an event. + * This method should only be used by framework developers. + * @param string name of the broadcast event + * @param TControl sender of the event + * @param TBroadcastEventParameter event parameter + */ + private function broadcastEventInternal($name,$sender,$param) + { + if($this->hasEvent($name)) + $this->raiseEvent($name,$sender,$param->getParameter()); + if($this instanceof IBroadcastEventReceiver) + $this->broadcastEventReceived($sender,$param); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->broadcastEventInternal($name,$sender,$param); + } + } + } + + /** + * Traverse the whole control hierarchy rooted at this control. + * Callback function may be invoked for each control being visited. + * A pre-callback is invoked before traversing child controls; + * A post-callback is invoked after traversing child controls. + * Callback functions can be global functions or class methods. + * They must be of the following signature: + * + * function callback_func($control,$param) {...} + * + * where $control refers to the control being visited and $param + * is the parameter that is passed originally when calling this traverse function. + * + * @param mixed parameter to be passed to callbacks for each control + * @param callback callback invoked before traversing child controls. If null, it is ignored. + * @param callback callback invoked after traversing child controls. If null, it is ignored. + */ + protected function traverseChildControls($param,$preCallback=null,$postCallback=null) + { + if($preCallback!==null) + call_user_func($preCallback,$this,$param); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + $control->traverseChildControls($param,$preCallback,$postCallback); + } + } + } + if($postCallback!==null) + call_user_func($postCallback,$this,$param); + } + + /** + * Renders the control. + * Only when the control is visible will the control be rendered. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderControl($writer) + { + if($this instanceof IActiveControl || $this->getVisible(false)) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->render($writer); + else + $this->render($writer); + } + } + + /** + * Renders the control. + * This method is invoked by {@link renderControl} when the control is visible. + * You can override this method to provide customized rendering of the control. + * By default, the control simply renders all its child contents. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + $this->renderChildren($writer); + } + + /** + * Renders the children of the control. + * This method iterates through all child controls and static text strings + * and renders them in order. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderChildren($writer) + { + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if(is_string($control)) + $writer->write($control); + else if($control instanceof TControl) + $control->renderControl($writer); + else if($control instanceof IRenderable) + $control->render($writer); + } + } + } + + /** + * This method is invoked when control state is to be saved. + * You can override this method to do last step state saving. + * Parent implementation must be invoked. + */ + public function saveState() + { + } + + /** + * This method is invoked right after the control has loaded its state. + * You can override this method to initialize data from the control state. + * Parent implementation must be invoked. + */ + public function loadState() + { + } + + /** + * Loads state (viewstate and controlstate) into a control and its children. + * This method should only be used by framework developers. + * @param array the collection of the state + * @param boolean whether the viewstate should be loaded + */ + protected function loadStateRecursive(&$state,$needViewState=true) + { + if(is_array($state)) + { + // A null state means the stateful properties all take default values. + // So if the state is enabled, we have to assign the null value. + $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); + if(isset($state[1])) + { + $this->_rf[self::RF_CONTROLSTATE]=&$state[1]; + unset($state[1]); + } + else + unset($this->_rf[self::RF_CONTROLSTATE]); + if($needViewState) + { + if(isset($state[0])) + $this->_viewState=&$state[0]; + else + $this->_viewState=array(); + } + unset($state[0]); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + if(isset($state[$control->_id])) + { + $control->loadStateRecursive($state[$control->_id],$needViewState); + unset($state[$control->_id]); + } + } + } + } + if(!empty($state)) + $this->_rf[self::RF_CHILD_STATE]=&$state; + } + $this->_stage=self::CS_STATE_LOADED; + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->loadState(); + else + $this->loadState(); + } + + /** + * Saves all control state (viewstate and controlstate) as a collection. + * This method should only be used by framework developers. + * @param boolean whether the viewstate should be saved + * @return array the collection of the control state (including its children's state). + */ + protected function &saveStateRecursive($needViewState=true) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->saveState(); + else + $this->saveState(); + $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); + $state=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $state[$control->_id]=&$control->saveStateRecursive($needViewState); + } + } + if($needViewState && !empty($this->_viewState)) + $state[0]=&$this->_viewState; + if(isset($this->_rf[self::RF_CONTROLSTATE])) + $state[1]=&$this->_rf[self::RF_CONTROLSTATE]; + return $state; + } + + /** + * Applies a stylesheet skin to a control. + * @param TPage the page containing the control + * @throws TInvalidOperationException if the stylesheet skin is applied already + */ + public function applyStyleSheetSkin($page) + { + if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED)) + { + $page->applyControlStyleSheet($this); + $this->_flags |= self::IS_STYLESHEET_APPLIED; + } + else if($this->_flags & self::IS_STYLESHEET_APPLIED) + throw new TInvalidOperationException('control_stylesheet_applied',get_class($this)); + } + + /** + * Clears the cached UniqueID. + * If $recursive=true, all children's cached UniqueID will be cleared as well. + * @param boolean whether the clearing is recursive. + */ + private function clearCachedUniqueID($recursive) + { + if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS])) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof TControl) + $control->clearCachedUniqueID($recursive); + } + $this->_uid=null; + } + + /** + * Generates an automatic ID for the control. + */ + private function generateAutomaticID() + { + $this->_flags &= ~self::IS_ID_SET; + if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID])) + $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0; + $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++; + $this->_id=self::AUTOMATIC_ID_PREFIX . $id; + $this->_namingContainer->clearNameTable(); + } + + /** + * Clears the list of the controls whose IDs are managed by the specified naming container. + */ + private function clearNameTable() + { + unset($this->_rf[self::RF_NAMED_CONTROLS]); + } + + /** + * Updates the list of the controls whose IDs are managed by the specified naming container. + * @param TControl the naming container + * @param TControlCollection list of controls + * @throws TInvalidDataValueException if a control's ID is not unique within its naming container. + */ + private function fillNameTable($container,$controls) + { + foreach($controls as $control) + { + if($control instanceof TControl) + { + if($control->_id!=='') + { + if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id])) + throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id); + else + $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control; + } + if(!($control instanceof INamingContainer) && $control->getHasControls()) + $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]); + } + } + } +} + + +/** + * TControlCollection class + * + * TControlCollection implements a collection that enables + * controls to maintain a list of their child controls. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TControlCollection extends TList +{ + /** + * the control that owns this collection. + * @var TControl + */ + private $_o; + + /** + * Constructor. + * @param TControl the control that owns this collection. + * @param boolean whether the list is read-only + */ + public function __construct(TControl $owner,$readOnly=false) + { + $this->_o=$owner; + parent::__construct(null,$readOnly); + } + + /** + * @return TControl the control that owns this collection. + */ + protected function getOwner() + { + return $this->_o; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added child control. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl. + */ + public function insertAt($index,$item) + { + if($item instanceof TControl) + { + parent::insertAt($index,$item); + $this->_o->addedControl($item); + } + else if(is_string($item) || ($item instanceof IRenderable)) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('controlcollection_control_required'); + } + + /** + * Removes an item at the specified position. + * This overrides the parent implementation by performing additional + * cleanup work when removing a child control. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $item=parent::removeAt($index); + if($item instanceof TControl) + $this->_o->removedControl($item); + return $item; + } + + /** + * Overrides the parent implementation by invoking {@link TControl::clearNamingContainer} + */ + public function clear() + { + parent::clear(); + if($this->_o instanceof INamingContainer) + $this->_o->clearNamingContainer(); + } +} + +/** + * TEmptyControlCollection class + * + * TEmptyControlCollection implements an empty control list that prohibits adding + * controls to it. This is useful for controls that do not allow child controls. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TEmptyControlCollection extends TControlCollection +{ + /** + * Constructor. + * @param TControl the control that owns this collection. + */ + public function __construct(TControl $owner) + { + parent::__construct($owner,true); + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by ignoring new addition. + * @param integer the speicified position. + * @param mixed new item + */ + public function insertAt($index,$item) + { + if(!is_string($item)) // string is possible if property tag is used. we simply ignore it in this case + parent::insertAt($index,$item); // this will generate an exception in parent implementation + } +} + +/** + * INamingContainer interface. + * INamingContainer marks a control as a naming container. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface INamingContainer +{ +} + +/** + * IPostBackEventHandler interface + * + * If a control wants to respond to postback event, it must implement this interface. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface IPostBackEventHandler +{ + /** + * Raises postback event. + * The implementation of this function should raise appropriate event(s) (e.g. OnClick, OnCommand) + * indicating the component is responsible for the postback event. + * @param string the parameter associated with the postback event + */ + public function raisePostBackEvent($param); +} + +/** + * IPostBackDataHandler interface + * + * If a control wants to load post data, it must implement this interface. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface IPostBackDataHandler +{ + /** + * Loads user input data. + * The implementation of this function can use $values[$key] to get the user input + * data that are meant for the particular control. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the control has been changed + */ + public function loadPostData($key,$values); + /** + * Raises postdata changed event. + * The implementation of this function should raise appropriate event(s) (e.g. OnTextChanged) + * indicating the control data is changed. + */ + public function raisePostDataChangedEvent(); + /** + * @return boolean whether postback causes the data change. Defaults to false for non-postback state. + */ + public function getDataChanged(); +} + + +/** + * IValidator interface + * + * If a control wants to validate user input, it must implement this interface. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface IValidator +{ + /** + * Validates certain data. + * The implementation of this function should validate certain data + * (e.g. data entered into TTextBox control). + * @return boolean whether the data passes the validation + */ + public function validate(); + /** + * @return boolean whether the previous {@link validate()} is successful. + */ + public function getIsValid(); + /** + * @param boolean whether the validator validates successfully + */ + public function setIsValid($value); + /** + * @return string error message during last validate + */ + public function getErrorMessage(); + /** + * @param string error message for the validation + */ + public function setErrorMessage($value); +} + + +/** + * IValidatable interface + * + * If a control wants to be validated by a validator, it must implement this interface. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface IValidatable +{ + /** + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue(); + /** + * @return boolean wether this control's validators validated successfully (must default to true) + */ + public function getIsValid(); + /** + * @return boolean wether this control's validators validated successfully + */ + public function setIsValid($value); +} + +/** + * IBroadcastEventReceiver interface + * + * If a control wants to check broadcast event, it must implement this interface. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface IBroadcastEventReceiver +{ + /** + * Handles broadcast event. + * This method is invoked automatically when an event is broadcasted. + * Within this method, you may check the event name given in + * the event parameter to determine whether you should respond to + * this event. + * @param TControl sender of the event + * @param TBroadCastEventParameter event parameter + */ + public function broadcastEventReceived($sender,$param); +} + +/** + * ITheme interface. + * + * This interface must be implemented by theme. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface ITheme +{ + /** + * Applies this theme to the specified control. + * @param TControl the control to be applied with this theme + */ + public function applySkin($control); +} + +/** + * ITemplate interface + * + * ITemplate specifies the interface for classes encapsulating + * parsed template structures. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface ITemplate +{ + /** + * Instantiates the template. + * Content in the template will be instantiated as components and text strings + * and passed to the specified parent control. + * @param TControl the parent control + */ + public function instantiateIn($parent); +} + +/** + * IButtonControl interface + * + * IButtonControl specifies the common properties and events that must + * be implemented by a button control, such as {@link TButton}, {@link TLinkButton}, + * {@link TImageButton}. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +interface IButtonControl +{ + /** + * @return string caption of the button + */ + public function getText(); + + /** + * @param string caption of the button + */ + public function setText($value); + + /** + * @return boolean whether postback event trigger by this button will cause input validation + */ + public function getCausesValidation(); + + /** + * @param boolean whether postback event trigger by this button will cause input validation + */ + public function setCausesValidation($value); + + /** + * @return string the command name associated with the {@link onCommand OnCommand} event. + */ + public function getCommandName(); + + /** + * @param string the command name associated with the {@link onCommand OnCommand} event. + */ + public function setCommandName($value); + + /** + * @return string the parameter associated with the {@link onCommand OnCommand} event + */ + public function getCommandParameter(); + + /** + * @param string the parameter associated with the {@link onCommand OnCommand} event. + */ + public function setCommandParameter($value); + + /** + * @return string the group of validators which the button causes validation upon postback + */ + public function getValidationGroup(); + + /** + * @param string the group of validators which the button causes validation upon postback + */ + public function setValidationGroup($value); + + /** + * Raises OnClick event. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param); + + /** + * Raises OnCommand event. + * @param TCommandEventParameter event parameter to be passed to the event handlers + */ + public function onCommand($param); + + /** + * @param boolean set by a panel to register this button as the default button for the panel. + */ + public function setIsDefaultButton($value); + + /** + * @return boolean true if this button is registered as a default button for a panel. + */ + public function getIsDefaultButton(); +} + +/** + * ISurroundable interface + * + * Identifies controls that may create an additional surrounding tag. The id of the + * tag can be obtained with {@link getSurroundingTagID}. + * + * @package System.Web.UI + * @since 3.1.2 + */ +interface ISurroundable +{ + /** + * @return string the id of the embedding tag of the control or the control's clientID if not surrounded + */ + public function getSurroundingTagID(); +} + +/** + * TBroadcastEventParameter class + * + * TBroadcastEventParameter encapsulates the parameter data for + * events that are broadcasted. The name of of the event is specified via + * {@link setName Name} property while the event parameter is via + * {@link setParameter Parameter} property. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TBroadcastEventParameter extends TEventParameter +{ + private $_name; + private $_param; + + /** + * Constructor. + * @param string name of the broadcast event + * @param mixed parameter of the broadcast event + */ + public function __construct($name='',$parameter=null) + { + $this->_name=$name; + $this->_param=$parameter; + } + + /** + * @return string name of the broadcast event + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string name of the broadcast event + */ + public function setName($value) + { + $this->_name=$value; + } + + /** + * @return mixed parameter of the broadcast event + */ + public function getParameter() + { + return $this->_param; + } + + /** + * @param mixed parameter of the broadcast event + */ + public function setParameter($value) + { + $this->_param=$value; + } +} + +/** + * TCommandEventParameter class + * + * TCommandEventParameter encapsulates the parameter data for Command + * event of button controls. You can access the name of the command via + * {@link getCommandName CommandName} property, and the parameter carried + * with the command via {@link getCommandParameter CommandParameter} property. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TCommandEventParameter extends TEventParameter +{ + private $_name; + private $_param; + + /** + * Constructor. + * @param string name of the command + * @param string parameter of the command + */ + public function __construct($name='',$parameter='') + { + $this->_name=$name; + $this->_param=$parameter; + } + + /** + * @return string name of the command + */ + public function getCommandName() + { + return $this->_name; + } + + /** + * @return string parameter of the command + */ + public function getCommandParameter() + { + return $this->_param; + } +} + + +/** + * TCompositeLiteral class + * + * TCompositeLiteral is used internally by {@link TTemplate} for representing + * consecutive static strings, expressions and statements. + * + * @author Qiang Xue + * @version $Id: TControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TCompositeLiteral extends TComponent implements IRenderable, IBindable +{ + const TYPE_EXPRESSION=0; + const TYPE_STATEMENTS=1; + const TYPE_DATABINDING=2; + private $_container=null; + private $_items=array(); + private $_expressions=array(); + private $_statements=array(); + private $_bindings=array(); + + /** + * Constructor. + * @param array list of items to be represented by TCompositeLiteral + */ + public function __construct($items) + { + $this->_items=array(); + $this->_expressions=array(); + $this->_statements=array(); + foreach($items as $id=>$item) + { + if(is_array($item)) + { + if($item[0]===self::TYPE_EXPRESSION) + $this->_expressions[$id]=$item[1]; + else if($item[0]===self::TYPE_STATEMENTS) + $this->_statements[$id]=$item[1]; + else if($item[0]===self::TYPE_DATABINDING) + $this->_bindings[$id]=$item[1]; + $this->_items[$id]=''; + } + else + $this->_items[$id]=$item; + } + } + + /** + * @return TComponent container of this component. It serves as the evaluation context of expressions and statements. + */ + public function getContainer() + { + return $this->_container; + } + + /** + * @param TComponent container of this component. It serves as the evaluation context of expressions and statements. + */ + public function setContainer(TComponent $value) + { + $this->_container=$value; + } + + /** + * Evaluates the expressions and/or statements in the component. + */ + public function evaluateDynamicContent() + { + $context=$this->_container===null?$this:$this->_container; + foreach($this->_expressions as $id=>$expression) + $this->_items[$id]=$context->evaluateExpression($expression); + foreach($this->_statements as $id=>$statement) + $this->_items[$id]=$context->evaluateStatements($statement); + } + + /** + * Performs databindings. + * This method is required by {@link IBindable} + */ + public function dataBind() + { + $context=$this->_container===null?$this:$this->_container; + foreach($this->_bindings as $id=>$binding) + $this->_items[$id]=$context->evaluateExpression($binding); + } + + /** + * Renders the content stored in this component. + * This method is required by {@link IRenderable} + * @param ITextWriter + */ + public function render($writer) + { + $writer->write(implode('',$this->_items)); + } +} + diff --git a/gui/baculum/framework/Web/UI/TControlAdapter.php b/gui/baculum/framework/Web/UI/TControlAdapter.php new file mode 100644 index 0000000000..1ef2d1fb0b --- /dev/null +++ b/gui/baculum/framework/Web/UI/TControlAdapter.php @@ -0,0 +1,143 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TControlAdapter class + * + * TControlAdapter is the base class for adapters that customize + * various behaviors for the control to which the adapter is attached. + * + * @author Qiang Xue + * @version $Id: TControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TControlAdapter extends TApplicationComponent +{ + /** + * @var TControl the control to which the adapter is attached + */ + protected $_control; + + /** + * Constructor. + * @param TControl the control to which the adapter is attached + */ + public function __construct($control) + { + $this->_control=$control; + } + + /** + * @return TControl the control to which this adapter is attached + */ + public function getControl() + { + return $this->_control; + } + + /** + * @return TPage the page that contains the attached control + */ + public function getPage() + { + return $this->_control?$this->_control->getPage():null; + } + + /** + * Creates child controls for the attached control. + * Default implementation calls the attached control's corresponding method. + */ + public function createChildControls() + { + $this->_control->createChildControls(); + } + + /** + * Loads additional persistent control state. + * Default implementation calls the attached control's corresponding method. + */ + public function loadState() + { + $this->_control->loadState(); + } + + /** + * Saves additional persistent control state. + * Default implementation calls the attached control's corresponding method. + */ + public function saveState() + { + $this->_control->saveState(); + } + + /** + * This method is invoked when the control enters 'OnInit' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onInit($param) + { + $this->_control->onInit($param); + } + + /** + * This method is invoked when the control enters 'OnLoad' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onLoad($param) + { + $this->_control->onLoad($param); + } + + /** + * This method is invoked when the control enters 'OnPreRender' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onPreRender($param) + { + $this->_control->onPreRender($param); + } + + /** + * This method is invoked when the control enters 'OnUnload' stage. + * Default implementation calls the attached control's corresponding method. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onUnload($param) + { + $this->_control->onUnload($param); + } + + /** + * This method is invoked when the control renders itself. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + $this->_control->render($writer); + } + + /** + * Renders the control's children. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderChildren($writer) + { + $this->_control->renderChildren($writer); + } +} + diff --git a/gui/baculum/framework/Web/UI/TForm.php b/gui/baculum/framework/Web/UI/TForm.php new file mode 100644 index 0000000000..9db8682c7d --- /dev/null +++ b/gui/baculum/framework/Web/UI/TForm.php @@ -0,0 +1,171 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TForm.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TForm class + * + * TForm displays an HTML form. Besides regular body content, + * it displays hidden fields, javascript blocks and files that are registered + * through {@link TClientScriptManager}. + * + * A TForm is required for a page that needs postback. + * Each page can contain at most one TForm. If multiple HTML forms are needed, + * please use regular HTML form tags for those forms that post to different + * URLs. + * + * @author Qiang Xue + * @version $Id: TForm.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TForm extends TControl +{ + /** + * Registers the form with the page. + * @param mixed event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $this->getPage()->setForm($this); + } + + /** + * Adds form specific attributes to renderer. + * @param THtmlWriter writer + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $writer->addAttribute('method',$this->getMethod()); + $uri=$this->getRequest()->getRequestURI(); + $writer->addAttribute('action',str_replace('&','&',str_replace('&','&',$uri))); + if(($enctype=$this->getEnctype())!=='') + $writer->addAttribute('enctype',$enctype); + + $attributes=$this->getAttributes(); + $attributes->remove('action'); + $writer->addAttributes($attributes); + + if(($butt=$this->getDefaultButton())!=='') + { + if(($button=$this->findControl($butt))!==null) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + else + throw new TInvalidDataValueException('form_defaultbutton_invalid',$butt); + } + } + + /** + * Renders the form. + * @param THtmlWriter writer + */ + public function render($writer) + { + $page=$this->getPage(); + + $this->addAttributesToRender($writer); + $writer->renderBeginTag('form'); + + $cs=$page->getClientScript(); + if($page->getClientSupportsJavaScript()) + { + $cs->renderHiddenFieldsBegin($writer); + $cs->renderScriptFilesBegin($writer); + $cs->renderBeginScripts($writer); + + $page->beginFormRender($writer); + $this->renderChildren($writer); + $cs->renderHiddenFieldsEnd($writer); + $page->endFormRender($writer); + + $cs->renderScriptFilesEnd($writer); + $cs->renderEndScripts($writer); + } + else + { + $cs->renderHiddenFieldsBegin($writer); + + $page->beginFormRender($writer); + $this->renderChildren($writer); + $page->endFormRender($writer); + + $cs->renderHiddenFieldsEnd($writer); + } + + $writer->renderEndTag(); + } + + /** + * @return string id path to the default button control. + */ + public function getDefaultButton() + { + return $this->getViewState('DefaultButton',''); + } + + /** + * Sets a button to be default one in a form. + * A default button will be clicked if a user presses 'Enter' key within + * the form. + * @param string id path to the default button control. + */ + public function setDefaultButton($value) + { + $this->setViewState('DefaultButton',$value,''); + } + + /** + * @return string form submission method. Defaults to 'post'. + */ + public function getMethod() + { + return $this->getViewState('Method','post'); + } + + /** + * @param string form submission method. Valid values include 'post' and 'get'. + */ + public function setMethod($value) + { + $this->setViewState('Method',TPropertyValue::ensureEnum($value,'post','get'),'post'); + } + + /** + * @return string the encoding type a browser uses to post data back to the server + */ + public function getEnctype() + { + return $this->getViewState('Enctype',''); + } + + /** + * @param string the encoding type a browser uses to post data back to the server. + * Commonly used types include + * - application/x-www-form-urlencoded : Form data is encoded as name/value pairs. This is the standard encoding format. + * - multipart/form-data : Form data is encoded as a message with a separate part for each control on the page. + * - text/plain : Form data is encoded in plain text, without any control or formatting characters. + */ + public function setEnctype($value) + { + $this->setViewState('Enctype',$value,''); + } + + /** + * @return string form name, which is equal to {@link getUniqueID UniqueID}. + */ + public function getName() + { + return $this->getUniqueID(); + } +} + diff --git a/gui/baculum/framework/Web/UI/THtmlWriter.php b/gui/baculum/framework/Web/UI/THtmlWriter.php new file mode 100644 index 0000000000..9f070d5f9b --- /dev/null +++ b/gui/baculum/framework/Web/UI/THtmlWriter.php @@ -0,0 +1,229 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THtmlWriter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * THtmlWriter class + * + * THtmlWriter is a writer that renders valid XHTML outputs. + * It provides functions to render tags, their attributes and stylesheet fields. + * Attribute and stylesheet values will be automatically HTML-encoded if + * they require so. For example, the 'value' attribute in an input tag + * will be encoded. + * + * A common usage of THtmlWriter is as the following sequence: + * + * $writer->addAttribute($name1,$value1); + * $writer->addAttribute($name2,$value2); + * $writer->renderBeginTag($tagName); + * // ... render contents enclosed within the tag here + * $writer->renderEndTag(); + * + * Make sure each invocation of {@link renderBeginTag} is accompanied with + * a {@link renderEndTag} and they are properly nested, like nesting + * tags in HTML and XHTML. + * + * @author Qiang Xue + * @version $Id: THtmlWriter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class THtmlWriter extends TApplicationComponent implements ITextWriter +{ + /** + * @var array list of tags are do not need a closing tag + */ + private static $_simpleTags=array( + 'area'=>true, + 'base'=>true, + 'basefont'=>true, + 'bgsound'=>true, + 'col'=>true, + 'embed'=>true, + 'frame'=>true, + 'hr'=>true, + 'img'=>true, + 'input'=>true, + 'isindex'=>true, + 'link'=>true, + 'meta'=>true, + 'wbr'=>true, + ); + /** + * @var array list of attributes to be rendered for a tag + */ + private $_attributes=array(); + /** + * @var array list of openning tags + */ + private $_openTags=array(); + /** + * @var array list of style attributes + */ + private $_styles=array(); + /** + * @var ITextWriter writer + */ + private $_writer=null; + + /** + * Constructor. + * @param ITextWriter a writer that THtmlWriter will pass its rendering result to + */ + public function __construct($writer) + { + $this->_writer=$writer; + } + + public function getWriter() + { + return $this->_writer; + } + + public function setWriter($writer) + { + $this->_writer = $writer; + } + /** + * Adds a list of attributes to be rendered. + * @param array list of attributes to be rendered + */ + public function addAttributes($attrs) + { + foreach($attrs as $name=>$value) + $this->_attributes[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Adds an attribute to be rendered. + * @param string name of the attribute + * @param string value of the attribute + */ + public function addAttribute($name,$value) + { + $this->_attributes[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Removes the named attribute from rendering + * @param string name of the attribute to be removed + */ + public function removeAttribute($name) + { + unset($this->_attributes[THttpUtility::htmlStrip($name)]); + } + + /** + * Adds a list of stylesheet attributes to be rendered. + * @param array list of stylesheet attributes to be rendered + */ + public function addStyleAttributes($attrs) + { + foreach($attrs as $name=>$value) + $this->_styles[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Adds a stylesheet attribute to be rendered + * @param string stylesheet attribute name + * @param string stylesheet attribute value + */ + public function addStyleAttribute($name,$value) + { + $this->_styles[THttpUtility::htmlStrip($name)]=THttpUtility::htmlEncode($value); + } + + /** + * Removes the named stylesheet attribute from rendering + * @param string name of the stylesheet attribute to be removed + */ + public function removeStyleAttribute($name) + { + unset($this->_styles[THttpUtility::htmlStrip($name)]); + } + + /** + * Flushes the rendering result. + * This will invoke the underlying writer's flush method. + * @return string the content being flushed + */ + public function flush() + { + return $this->_writer->flush(); + } + + /** + * Renders a string. + * @param string string to be rendered + */ + public function write($str) + { + $this->_writer->write($str); + } + + /** + * Renders a string and appends a newline to it. + * @param string string to be rendered + */ + public function writeLine($str='') + { + $this->_writer->write($str."\n"); + } + + /** + * Renders an HTML break. + */ + public function writeBreak() + { + $this->_writer->write('
      '); + } + + /** + * Renders the openning tag. + * @param string tag name + */ + public function renderBeginTag($tagName) + { + $str='<'.$tagName; + foreach($this->_attributes as $name=>$value) + $str.=' '.$name.'="'.$value.'"'; + if(!empty($this->_styles)) + { + $str.=' style="'; + foreach($this->_styles as $name=>$value) + $str.=$name.':'.$value.';'; + $str.='"'; + } + if(isset(self::$_simpleTags[$tagName])) + { + $str.=' />'; + $this->_openTags[] = ''; + } + else + { + $str.='>'; + $this->_openTags[] = $tagName; + } + $this->_writer->write($str); + $this->_attributes=array(); + $this->_styles=array(); + } + + /** + * Renders the closing tag. + */ + public function renderEndTag() + { + if(!empty($this->_openTags) && ($tagName=array_pop($this->_openTags))!=='') + $this->_writer->write(''); + } +} + diff --git a/gui/baculum/framework/Web/UI/TPage.php b/gui/baculum/framework/Web/UI/TPage.php new file mode 100644 index 0000000000..39656e13d6 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TPage.php @@ -0,0 +1,1341 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +Prado::using('System.Web.UI.WebControls.*'); +Prado::using('System.Web.UI.TControl'); +Prado::using('System.Web.UI.WebControls.TWebControl'); +Prado::using('System.Web.UI.TCompositeControl'); +Prado::using('System.Web.UI.TTemplateControl'); +Prado::using('System.Web.UI.TForm'); +Prado::using('System.Web.UI.TClientScriptManager'); + +/** + * TPage class + * + * @author Qiang Xue + * @version $Id: TPage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TPage extends TTemplateControl +{ + /** + * system post fields + */ + const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; + const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; + const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; + const FIELD_PAGESTATE='PRADO_PAGESTATE'; + const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; + const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; + + /** + * @var array system post fields + */ + private static $_systemPostFields=array( + 'PRADO_POSTBACK_TARGET'=>true, + 'PRADO_POSTBACK_PARAMETER'=>true, + 'PRADO_LASTFOCUS'=>true, + 'PRADO_PAGESTATE'=>true, + 'PRADO_CALLBACK_TARGET'=>true, + 'PRADO_CALLBACK_PARAMETER'=>true + ); + /** + * @var TForm form instance + */ + private $_form; + /** + * @var THead head instance + */ + private $_head; + /** + * @var array list of registered validators + */ + private $_validators=array(); + /** + * @var boolean if validation has been performed + */ + private $_validated=false; + /** + * @var TTheme page theme + */ + private $_theme; + /** + * @var string page title set when Head is not in page yet + */ + private $_title; + /** + * @var TTheme page stylesheet theme + */ + private $_styleSheet; + /** + * @var TClientScriptManager client script manager + */ + private $_clientScript; + /** + * @var TMap data post back by user + */ + protected $_postData; + /** + * @var TMap postback data that is not handled during first invocation of LoadPostData. + */ + protected $_restPostData; + /** + * @var array list of controls whose data have been changed due to the postback + */ + protected $_controlsPostDataChanged=array(); + /** + * @var array list of controls that need to load post data in the current request + */ + protected $_controlsRequiringPostData=array(); + /** + * @var array list of controls that need to load post data in the next postback + */ + protected $_controlsRegisteredForPostData=array(); + /** + * @var TControl control that needs to raise postback event + */ + private $_postBackEventTarget; + /** + * @var string postback event parameter + */ + private $_postBackEventParameter; + /** + * @var boolean whether the form has been rendered + */ + protected $_formRendered=false; + /** + * @var boolean whether the current rendering is within a form + */ + protected $_inFormRender=false; + /** + * @var TControl|string the control or the ID of the element on the page to be focused when the page is sent back to user + */ + private $_focus; + /** + * @var string page path to this page + */ + private $_pagePath=''; + /** + * @var boolean whether page state should be HMAC validated + */ + private $_enableStateValidation=true; + /** + * @var boolean whether page state should be encrypted + */ + private $_enableStateEncryption=false; + /** + * @var boolean whether page state should be compressed + * @since 3.1.6 + */ + private $_enableStateCompression=true; + /** + * @var string page state persister class name + */ + private $_statePersisterClass='System.Web.UI.TPageStatePersister'; + /** + * @var mixed page state persister + */ + private $_statePersister; + /** + * @var TStack stack used to store currently active caching controls + */ + private $_cachingStack; + /** + * @var string state string to be stored on the client side + */ + private $_clientState=''; + /** + * @var array post data loader IDs. + */ + protected $_postDataLoaders=array(); + /** + * @var boolean true if loading post data. + */ + protected $_isLoadingPostData=false; + /** + * @var boolean whether client supports javascript + */ + private $_enableJavaScript=true; + /** + * @var THtmlWriter current html render writer + */ + private $_writer; + + /** + * Constructor. + * Sets the page object to itself. + * Derived classes must call parent implementation. + */ + public function __construct() + { + $this->setPage($this); + } + + /** + * Runs through the page lifecycles. + * @param THtmlTextWriter the HTML writer + */ + public function run($writer) + { + Prado::trace("Running page life cycles",'System.Web.UI.TPage'); + $this->_writer = $writer; + + $this->determinePostBackMode(); + + if($this->getIsPostBack()) + { + if($this->getIsCallback()) + $this->processCallbackRequest($writer); + else + $this->processPostBackRequest($writer); + } + else + $this->processNormalRequest($writer); + + $this->_writer = null; + } + + protected function processNormalRequest($writer) + { + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + protected function processPostBackRequest($writer) + { + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + $this->_restPostData=new TMap; + Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); + $this->loadPageState(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_postData,true); + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_restPostData,false); + Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); + $this->raiseChangedEvents(); + Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); + $this->raisePostBackEvent(); + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + protected static function decodeUTF8($data, $enc) + { + if(is_array($data)) + { + foreach($data as $k=>$v) + $data[$k]=self::decodeUTF8($v, $enc); + return $data; + } elseif(is_string($data)) { + return iconv('UTF-8',$enc.'//IGNORE',$data); + } else { + return $data; + } + } + + /** + * Sets Adapter to TActivePageAdapter and calls apter to process the + * callback request. + */ + protected function processCallbackRequest($writer) + { + Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); + + $this->setAdapter(new TActivePageAdapter($this)); + + // Decode Callback postData from UTF-8 to current Charset + if (($g=$this->getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + foreach ($this->_postData as $k=>$v) + $this->_postData[$k]=self::decodeUTF8($v, $enc); + + Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); + $this->onPreInit(null); + + Prado::trace("Page initRecursive()",'System.Web.UI.TPage'); + $this->initRecursive(); + + Prado::trace("Page onInitComplete()",'System.Web.UI.TPage'); + $this->onInitComplete(null); + + $this->_restPostData=new TMap; + Prado::trace("Page loadPageState()",'System.Web.UI.TPage'); + $this->loadPageState(); + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_postData,true); + Prado::trace("Page onPreLoad()",'System.Web.UI.TPage'); + $this->onPreLoad(null); + Prado::trace("Page loadRecursive()",'System.Web.UI.TPage'); + $this->loadRecursive(); + + Prado::trace("Page processPostData()",'System.Web.UI.TPage'); + $this->processPostData($this->_restPostData,false); + + Prado::trace("Page raiseChangedEvents()",'System.Web.UI.TPage'); + $this->raiseChangedEvents(); + + + $this->getAdapter()->processCallbackEvent($writer); + +/* + Prado::trace("Page raisePostBackEvent()",'System.Web.UI.TPage'); + $this->raisePostBackEvent(); +*/ + Prado::trace("Page onLoadComplete()",'System.Web.UI.TPage'); + $this->onLoadComplete(null); + + Prado::trace("Page preRenderRecursive()",'System.Web.UI.TPage'); + $this->preRenderRecursive(); + Prado::trace("Page onPreRenderComplete()",'System.Web.UI.TPage'); + $this->onPreRenderComplete(null); + + Prado::trace("Page savePageState()",'System.Web.UI.TPage'); + $this->savePageState(); + Prado::trace("Page onSaveStateComplete()",'System.Web.UI.TPage'); + $this->onSaveStateComplete(null); + +/* + Prado::trace("Page renderControl()",'System.Web.UI.TPage'); + $this->renderControl($writer); +*/ + $this->getAdapter()->renderCallbackResponse($writer); + + Prado::trace("Page unloadRecursive()",'System.Web.UI.TPage'); + $this->unloadRecursive(); + } + + /** + * Gets the callback client script handler that allows javascript functions + * to be executed during the callback response. + * @return TCallbackClientScript interface to client-side javascript code. + */ + public function getCallbackClient() + { + if($this->getAdapter() !== null) + return $this->getAdapter()->getCallbackClientHandler(); + else + return new TCallbackClientScript(); + } + + /** + * Set a new callback client handler. + * @param TCallbackClientScript new callback client script handler. + */ + public function setCallbackClient($client) + { + $this->getAdapter()->setCallbackClientHandler($client); + } + + /** + * @return TControl the control responsible for the current callback event, + * null if nonexistent + */ + public function getCallbackEventTarget() + { + return $this->getAdapter()->getCallbackEventTarget(); + } + + /** + * Registers a control to raise callback event in the current request. + * @param TControl control registered to raise callback event. + */ + public function setCallbackEventTarget(TControl $control) + { + $this->getAdapter()->setCallbackEventTarget($control); + } + + /** + * Callback parameter is decoded assuming JSON encoding. + * @return string callback event parameter + */ + public function getCallbackEventParameter() + { + return $this->getAdapter()->getCallbackEventParameter(); + } + + /** + * @param mixed callback event parameter + */ + public function setCallbackEventParameter($value) + { + $this->getAdapter()->setCallbackEventParameter($value); + } + + /** + * Register post data loaders for Callback to collect post data. + * This method should only be called by framework developers. + * @param TControl control that requires post data. + * @see TControl::preRenderRecursive(); + */ + public function registerPostDataLoader($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_postDataLoaders[$id] = true; + } + + /** + * Get a list of IDs of controls that are enabled and require post data. + * @return array list of IDs implementing IPostBackDataHandler + */ + public function getPostDataLoaders() + { + return array_keys($this->_postDataLoaders); + } + + /** + * @return TForm the form on the page + */ + public function getForm() + { + return $this->_form; + } + + /** + * Registers a TForm instance to the page. + * Note, a page can contain at most one TForm instance. + * @param TForm the form on the page + * @throws TInvalidOperationException if this method is invoked twice or more. + */ + public function setForm(TForm $form) + { + if($this->_form===null) + $this->_form=$form; + else + throw new TInvalidOperationException('page_form_duplicated'); + } + + /** + * Returns a list of registered validators. + * If validation group is specified, only the validators in that group will be returned. + * @param string validation group + * @return TList registered validators in the requested group. If the group is null, all validators will be returned. + */ + public function getValidators($validationGroup=null) + { + if(!$this->_validators) + $this->_validators=new TList; + if(empty($validationGroup) === true) + return $this->_validators; + else + { + $list=new TList; + foreach($this->_validators as $validator) + if($validator->getValidationGroup()===$validationGroup) + $list->add($validator); + return $list; + } + } + + /** + * Performs input validation. + * This method will invoke the registered validators to perform the actual validation. + * If validation group is specified, only the validators in that group will be invoked. + * @param string validation group. If null, all validators will perform validation. + */ + public function validate($validationGroup=null) + { + Prado::trace("Page validate()",'System.Web.UI.TPage'); + $this->_validated=true; + if($this->_validators && $this->_validators->getCount()) + { + if($validationGroup===null) + { + foreach($this->_validators as $validator) + $validator->validate(); + } + else + { + foreach($this->_validators as $validator) + { + if($validator->getValidationGroup()===$validationGroup) + $validator->validate(); + } + } + } + } + + /** + * Returns whether user input is valid or not. + * This method must be invoked after {@link validate} is called. + * @return boolean whether the user input is valid or not. + * @throws TInvalidOperationException if {@link validate} is not invoked yet. + */ + public function getIsValid() + { + if($this->_validated) + { + if($this->_validators && $this->_validators->getCount()) + { + foreach($this->_validators as $validator) + if(!$validator->getIsValid()) + return false; + } + return true; + } + else + throw new TInvalidOperationException('page_isvalid_unknown'); + } + + /** + * @return TTheme the theme used for the page. Defaults to null. + */ + public function getTheme() + { + if(is_string($this->_theme)) + $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); + return $this->_theme; + } + + /** + * Sets the theme to be used for the page. + * @param string|TTheme the theme name or the theme object to be used for the page. + */ + public function setTheme($value) + { + $this->_theme=empty($value)?null:$value; + } + + + /** + * @return TTheme the stylesheet theme used for the page. Defaults to null. + */ + public function getStyleSheetTheme() + { + if(is_string($this->_styleSheet)) + $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); + return $this->_styleSheet; + } + + /** + * Sets the stylesheet theme to be used for the page. + * @param string|TTheme the stylesheet theme name or the stylesheet theme object to be used for the page. + */ + public function setStyleSheetTheme($value) + { + $this->_styleSheet=empty($value)?null:$value; + } + + /** + * Applies a skin in the current theme to a control. + * This method should only be used by framework developers. + * @param TControl a control to be applied skin with + */ + public function applyControlSkin($control) + { + if(($theme=$this->getTheme())!==null) + $theme->applySkin($control); + } + + /** + * Applies a stylesheet skin in the current theme to a control. + * This method should only be used by framework developers. + * @param TControl a control to be applied stylesheet skin with + */ + public function applyControlStyleSheet($control) + { + if(($theme=$this->getStyleSheetTheme())!==null) + $theme->applySkin($control); + } + + /** + * @return TClientScriptManager client script manager + */ + public function getClientScript() + { + if(!$this->_clientScript) { + $className = $classPath = $this->getService()->getClientScriptManagerClass(); + Prado::using($className); + if(($pos=strrpos($className,'.'))!==false) + $className=substr($className,$pos+1); + + if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) + throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); + + $this->_clientScript=new $className($this); + } + return $this->_clientScript; + } + + /** + * Raises OnPreInit event. + * This method is invoked right before {@link onInit OnInit} stage. + * You may override this method to provide additional initialization that + * should be done before {@link onInit OnInit} (e.g. setting {@link setTheme Theme} or + * {@link setStyleSheetTheme StyleSheetTheme}). + * Remember to call the parent implementation to ensure OnPreInit event is raised. + * @param mixed event parameter + */ + public function onPreInit($param) + { + $this->raiseEvent('OnPreInit',$this,$param); + } + + /** + * Raises OnInitComplete event. + * This method is invoked right after {@link onInit OnInit} stage and before {@link onLoad OnLoad} stage. + * You may override this method to provide additional initialization that + * should be done after {@link onInit OnInit}. + * Remember to call the parent implementation to ensure OnInitComplete event is raised. + * @param mixed event parameter + */ + public function onInitComplete($param) + { + $this->raiseEvent('OnInitComplete',$this,$param); + } + + /** + * Raises OnPreLoad event. + * This method is invoked right before {@link onLoad OnLoad} stage. + * You may override this method to provide additional page loading logic that + * should be done before {@link onLoad OnLoad}. + * Remember to call the parent implementation to ensure OnPreLoad event is raised. + * @param mixed event parameter + */ + public function onPreLoad($param) + { + $this->raiseEvent('OnPreLoad',$this,$param); + } + + /** + * Raises OnLoadComplete event. + * This method is invoked right after {@link onLoad OnLoad} stage. + * You may override this method to provide additional page loading logic that + * should be done after {@link onLoad OnLoad}. + * Remember to call the parent implementation to ensure OnLoadComplete event is raised. + * @param mixed event parameter + */ + public function onLoadComplete($param) + { + $this->raiseEvent('OnLoadComplete',$this,$param); + } + + /** + * Raises OnPreRenderComplete event. + * This method is invoked right after {@link onPreRender OnPreRender} stage. + * You may override this method to provide additional preparation for page rendering + * that should be done after {@link onPreRender OnPreRender}. + * Remember to call the parent implementation to ensure OnPreRenderComplete event is raised. + * @param mixed event parameter + */ + public function onPreRenderComplete($param) + { + $this->raiseEvent('OnPreRenderComplete',$this,$param); + $cs=$this->getClientScript(); + $theme=$this->getTheme(); + if($theme instanceof ITheme) + { + foreach($theme->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($theme->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + $styleSheet=$this->getStyleSheetTheme(); + if($styleSheet instanceof ITheme) + { + foreach($styleSheet->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($styleSheet->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + + if($cs->getRequiresHead() && $this->getHead()===null) + throw new TConfigurationException('page_head_required'); + } + + /** + * Determines the media type of the CSS file. + * The media type is determined according to the following file name pattern: + * xxx.media-type.extension + * For example, 'mystyle.print.css' means its media type is 'print'. + * @param string CSS URL + * @return string media type of the CSS file + */ + private function getCssMediaType($url) + { + $segs=explode('.',basename($url)); + if(isset($segs[2])) + return $segs[count($segs)-2]; + else + return ''; + } + + /** + * Raises OnSaveStateComplete event. + * This method is invoked right after {@link onSaveState OnSaveState} stage. + * You may override this method to provide additional logic after page state is saved. + * Remember to call the parent implementation to ensure OnSaveStateComplete event is raised. + * @param mixed event parameter + */ + public function onSaveStateComplete($param) + { + $this->raiseEvent('OnSaveStateComplete',$this,$param); + } + + /** + * Determines whether the current page request is a postback. + * Call {@link getIsPostBack} to get the result. + */ + private function determinePostBackMode() + { + $postData=$this->getRequest(); + if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) + $this->_postData=$postData; + } + + /** + * @return boolean whether the current page request is a postback + */ + public function getIsPostBack() + { + return $this->_postData!==null; + } + + /** + * @return boolean whether this is a callback request + */ + public function getIsCallback() + { + return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); + } + + /** + * This method is invoked when control state is to be saved. + * You can override this method to do last step state saving. + * Parent implementation must be invoked. + */ + public function saveState() + { + parent::saveState(); + $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); + } + + /** + * This method is invoked right after the control has loaded its state. + * You can override this method to initialize data from the control state. + * Parent implementation must be invoked. + */ + public function loadState() + { + parent::loadState(); + $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); + } + + /** + * Loads page state from persistent storage. + */ + protected function loadPageState() + { + Prado::trace("Loading state",'System.Web.UI.TPage'); + $state=$this->getStatePersister()->load(); + $this->loadStateRecursive($state,$this->getEnableViewState()); + } + + /** + * Saves page state from persistent storage. + */ + protected function savePageState() + { + Prado::trace("Saving state",'System.Web.UI.TPage'); + $state=&$this->saveStateRecursive($this->getEnableViewState()); + $this->getStatePersister()->save($state); + } + + /** + * @param string the field name + * @return boolean whether the specified field is a system field in postback data + */ + protected function isSystemPostField($field) + { + return isset(self::$_systemPostFields[$field]); + } + + /** + * Registers a control for loading post data in the next postback. + * This method needs to be invoked if the control to load post data + * may not have a post variable in some cases. For example, a checkbox, + * if not checked, will not have a post value. + * @param TControl control registered for loading post data + */ + public function registerRequiresPostData($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_controlsRegisteredForPostData[$id]=true; + $this->registerPostDataLoader($id); + $params=func_get_args(); + foreach($this->getCachingStack() as $item) + $item->registerAction('Page','registerRequiresPostData',array($id)); + } + + /** + * @return TControl the control responsible for the current postback event, null if nonexistent + */ + public function getPostBackEventTarget() + { + if($this->_postBackEventTarget===null && $this->_postData!==null) + { + $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); + if(!empty($eventTarget)) + $this->_postBackEventTarget=$this->findControl($eventTarget); + } + return $this->_postBackEventTarget; + } + + /** + * Registers a control to raise postback event in the current request. + * @param TControl control registered to raise postback event. + */ + public function setPostBackEventTarget(TControl $control) + { + $this->_postBackEventTarget=$control; + } + + /** + * @return string postback event parameter + */ + public function getPostBackEventParameter() + { + if($this->_postBackEventParameter===null && $this->_postData!==null) + { + if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) + $this->_postBackEventParameter=''; + } + return $this->_postBackEventParameter; + } + + /** + * @param string postback event parameter + */ + public function setPostBackEventParameter($value) + { + $this->_postBackEventParameter=$value; + } + + /** + * Processes post data. + * @param TMap post data to be processed + * @param boolean whether this method is invoked before {@link onLoad OnLoad}. + */ + protected function processPostData($postData,$beforeLoad) + { + $this->_isLoadingPostData=true; + if($beforeLoad) + $this->_restPostData=new TMap; + foreach($postData as $key=>$value) + { + if($this->isSystemPostField($key)) + continue; + else if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$postData)) + $this->_controlsPostDataChanged[]=$control; + } + else if($control instanceof IPostBackEventHandler && + empty($this->_postData[self::FIELD_POSTBACK_TARGET])) + { + $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); // not calling setPostBackEventTarget() because the control may be removed later + } + unset($this->_controlsRequiringPostData[$key]); + } + else if($beforeLoad) + $this->_restPostData->add($key,$value); + } + + foreach($this->_controlsRequiringPostData as $key=>$value) + { + if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$this->_postData)) + $this->_controlsPostDataChanged[]=$control; + } + else + throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); + unset($this->_controlsRequiringPostData[$key]); + } + } + $this->_isLoadingPostData=false; + } + + /** + * @return boolean true if loading post data. + */ + public function getIsLoadingPostData() + { + return $this->_isLoadingPostData; + } + + /** + * Raises OnPostDataChangedEvent for controls whose data have been changed due to the postback. + */ + protected function raiseChangedEvents() + { + foreach($this->_controlsPostDataChanged as $control) + $control->raisePostDataChangedEvent(); + } + + /** + * Raises PostBack event. + */ + protected function raisePostBackEvent() + { + if(($postBackHandler=$this->getPostBackEventTarget())===null) + $this->validate(); + else if($postBackHandler instanceof IPostBackEventHandler) + $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); + } + + /** + * @return boolean Whether form rendering is in progress + */ + public function getInFormRender() + { + return $this->_inFormRender; + } + + /** + * Ensures the control is rendered within a form. + * @param TControl the control to be rendered + * @throws TConfigurationException if the control is outside of the form + */ + public function ensureRenderInForm($control) + { + if(!$this->getIsCallback() && !$this->_inFormRender) + throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); + } + + /** + * @internal This method is invoked by TForm at the beginning of its rendering + */ + public function beginFormRender($writer) + { + if($this->_formRendered) + throw new TConfigurationException('page_form_duplicated'); + $this->_formRendered=true; + $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); + $this->_inFormRender=true; + } + + /** + * @internal This method is invoked by TForm at the end of its rendering + */ + public function endFormRender($writer) + { + if($this->_focus) + { + if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) + $focus=$this->_focus->getClientID(); + else + $focus=$this->_focus; + $this->getClientScript()->registerFocusControl($focus); + } + else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) + $this->getClientScript()->registerFocusControl($lastFocus); + $this->_inFormRender=false; + } + + /** + * Sets input focus on a control after the page is rendered to users. + * @param TControl|string control to receive focus, or the ID of the element on the page to receive focus + */ + public function setFocus($value) + { + $this->_focus=$value; + } + + /** + * @return boolean whether client supports javascript. Defaults to true. + */ + public function getClientSupportsJavaScript() + { + return $this->_enableJavaScript; + } + + /** + * @param boolean whether client supports javascript. If false, javascript will not be generated for controls. + */ + public function setClientSupportsJavaScript($value) + { + $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); + } + + /** + * @return THead page head, null if not available + */ + public function getHead() + { + return $this->_head; + } + + /** + * @param THead page head + * @throws TInvalidOperationException if a head already exists + */ + public function setHead(THead $value) + { + if($this->_head) + throw new TInvalidOperationException('page_head_duplicated'); + $this->_head=$value; + if($this->_title!==null) + { + $this->_head->setTitle($this->_title); + $this->_title=null; + } + } + + /** + * @return string page title. + */ + public function getTitle() + { + if($this->_head) + return $this->_head->getTitle(); + else + return $this->_title===null ? '' : $this->_title; + } + + /** + * Sets the page title. + * Note, a {@link THead} control needs to place on the page + * in order that this title be rendered. + * @param string page title. This will override the title set in {@link getHead Head}. + */ + public function setTitle($value) + { + if($this->_head) + $this->_head->setTitle($value); + else + $this->_title=$value; + } + + /** + * Returns the state to be stored on the client side. + * This method should only be used by framework and control developers. + * @return string the state to be stored on the client side + */ + public function getClientState() + { + return $this->_clientState; + } + + /** + * Sets the state to be stored on the client side. + * This method should only be used by framework and control developers. + * @param string the state to be stored on the client side + */ + public function setClientState($state) + { + $this->_clientState=$state; + } + + /** + * @return string the state postback from client side + */ + public function getRequestClientState() + { + return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); + } + + /** + * @return string class name of the page state persister. Defaults to TPageStatePersister. + */ + public function getStatePersisterClass() + { + return $this->_statePersisterClass; + } + + /** + * @param string class name of the page state persister. + */ + public function setStatePersisterClass($value) + { + $this->_statePersisterClass=$value; + } + + /** + * @return IPageStatePersister page state persister + */ + public function getStatePersister() + { + if($this->_statePersister===null) + { + $this->_statePersister=Prado::createComponent($this->_statePersisterClass); + if(!($this->_statePersister instanceof IPageStatePersister)) + throw new TInvalidDataTypeException('page_statepersister_invalid'); + $this->_statePersister->setPage($this); + } + return $this->_statePersister; + } + + /** + * @return boolean whether page state should be HMAC validated. Defaults to true. + */ + public function getEnableStateValidation() + { + return $this->_enableStateValidation; + } + + /** + * @param boolean whether page state should be HMAC validated. + */ + public function setEnableStateValidation($value) + { + $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether page state should be encrypted. Defaults to false. + */ + public function getEnableStateEncryption() + { + return $this->_enableStateEncryption; + } + + /** + * @param boolean whether page state should be encrypted. + */ + public function setEnableStateEncryption($value) + { + $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether page state should be compressed. Defaults to true. + * @since 3.1.6 + */ + public function getEnableStateCompression() + { + return $this->_enableStateCompression; + } + + /** + * @param boolean whether page state should be compressed. + * @since 3.1.6 + */ + public function setEnableStateCompression($value) + { + $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string the requested page path for this page + */ + public function getPagePath() + { + return $this->_pagePath; + } + + /** + * @param string the requested page path for this page + */ + public function setPagePath($value) + { + $this->_pagePath=$value; + } + + /** + * Registers an action associated with the content being cached. + * The registered action will be replayed if the content stored + * in the cache is served to end-users. + * @param string context of the action method. This is a property-path + * referring to the context object (e.g. Page, Page.ClientScript). + * @param string method name of the context object + * @param array list of parameters to be passed to the action method + */ + public function registerCachingAction($context,$funcName,$funcParams) + { + if($this->_cachingStack) + { + foreach($this->_cachingStack as $cache) + $cache->registerAction($context,$funcName,$funcParams); + } + } + + /** + * @return TStack stack of {@link TOutputCache} objects + */ + public function getCachingStack() + { + if(!$this->_cachingStack) + $this->_cachingStack=new TStack; + return $this->_cachingStack; + } + + /** + * Flushes output + */ + public function flushWriter() + { + if ($this->_writer) + $this->Response->write($this->_writer->flush()); + } +} + +/** + * IPageStatePersister interface. + * + * IPageStatePersister interface is required for all page state persister + * classes. + * + * @author Qiang Xue + * @version $Id: TPage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.1 + */ +interface IPageStatePersister +{ + /** + * @param TPage the page that this persister works for + */ + public function getPage(); + /** + * @param TPage the page that this persister works for + */ + public function setPage(TPage $page); + /** + * Saves state to persistent storage. + * @param mixed state to be stored + */ + public function save($state); + /** + * Loads page state from persistent storage + * @return mixed the restored state + */ + public function load(); +} + + +/** + * TPageStateFormatter class. + * + * TPageStateFormatter is a utility class to transform the page state + * into and from a string that can be properly saved in persistent storage. + * + * Depending on the {@link TPage::getEnableStateValidation() EnableStateValidation} + * and {@link TPage::getEnableStateEncryption() EnableStateEncryption}, + * TPageStateFormatter may do HMAC validation and encryption to prevent + * the state data from being tampered or viewed. + * The private keys and hashing/encryption methods are determined by + * {@link TApplication::getSecurityManager() SecurityManager}. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System.Web.UI + * @since 3.1 + */ +class TPageStateFormatter +{ + /** + * @param TPage + * @param mixed state data + * @return string serialized data + */ + public static function serialize($page,$data) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateValidation()) + $str=$sm->hashData(Prado::serialize($data)); + else + $str=Prado::serialize($data); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=gzcompress($str); + if($page->getEnableStateEncryption()) + $str=$sm->encrypt($str); + return base64_encode($str); + } + + /** + * @param TPage + * @param string serialized data + * @return mixed unserialized state data, null if data is corrupted + */ + public static function unserialize($page,$data) + { + $str=base64_decode($data); + if($str==='') + return null; + if($str!==false) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateEncryption()) + $str=$sm->decrypt($str); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=@gzuncompress($str); + if($page->getEnableStateValidation()) + { + if(($str=$sm->validateData($str))!==false) + return Prado::unserialize($str); + } + else + return Prado::unserialize($str); + } + return null; + } +} diff --git a/gui/baculum/framework/Web/UI/TPageStatePersister.php b/gui/baculum/framework/Web/UI/TPageStatePersister.php new file mode 100644 index 0000000000..7d2330b750 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TPageStatePersister.php @@ -0,0 +1,71 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TPageStatePersister.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TPageStatePersister class + * + * TPageStatePersister implements a page state persistent method based on + * form hidden fields. + * + * Since page state can be very big for complex pages, consider using + * alternative persisters, such as {@link TSessionPageStatePersister}, + * which store page state on the server side and thus reduce the network + * traffic for transmitting bulky page state. + * + * @author Qiang Xue + * @version $Id: TPageStatePersister.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TPageStatePersister extends TComponent implements IPageStatePersister +{ + private $_page; + + /** + * @return TPage the page that this persister works for + */ + public function getPage() + { + return $this->_page; + } + + /** + * @param TPage the page that this persister works for + */ + public function setPage(TPage $page) + { + $this->_page=$page; + } + + /** + * Saves state in hidden fields. + * @param mixed state to be stored + */ + public function save($state) + { + $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$state)); + } + + /** + * Loads page state from hidden fields. + * @return mixed the restored state + * @throws THttpException if page state is corrupted + */ + public function load() + { + if(($data=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) + return $data; + else + throw new THttpException(400,'pagestatepersister_pagestate_corrupted'); + } +} + diff --git a/gui/baculum/framework/Web/UI/TSessionPageStatePersister.php b/gui/baculum/framework/Web/UI/TSessionPageStatePersister.php new file mode 100644 index 0000000000..5448fd229a --- /dev/null +++ b/gui/baculum/framework/Web/UI/TSessionPageStatePersister.php @@ -0,0 +1,131 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSessionPageStatePersister.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * TSessionPageStatePersister class + * + * TSessionPageStatePersister implements a page state persistent method based on + * sessions. Page state are stored in user sessions and therefore, this persister + * requires session to be started and available. + * + * TSessionPageStatePersister keeps limited number of history states in session, + * mainly to preserve the precious server storage. The number is specified + * by {@link setHistorySize HistorySize}, which defaults to 10. + * + * There are a couple of ways to use TSessionPageStatePersister. + * One can override the page's {@link TPage::getStatePersister()} method and + * create a TSessionPageStatePersister instance there. + * Or one can configure the pages to use TSessionPageStatePersister in page configurations + * as follows, + * + * + * + * The above configuration will affect the pages under the directory containing + * this configuration and all its subdirectories. + * To configure individual pages to use TSessionPageStatePersister, use + * + * + * + * + * + * + * @author Qiang Xue + * @version $Id: TSessionPageStatePersister.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.1 + */ +class TSessionPageStatePersister extends TComponent implements IPageStatePersister +{ + const STATE_SESSION_KEY='PRADO_SESSION_PAGESTATE'; + const QUEUE_SESSION_KEY='PRADO_SESSION_STATEQUEUE'; + + private $_page; + private $_historySize=10; + + /** + * @param TPage the page that this persister works for + */ + public function getPage() + { + return $this->_page; + } + + /** + * @param TPage the page that this persister works for. + */ + public function setPage(TPage $page) + { + $this->_page=$page; + } + + /** + * @return integer maximum number of page states that should be kept in session. Defaults to 10. + */ + public function getHistorySize() + { + return $this->_historySize; + } + + /** + * @param integer maximum number of page states that should be kept in session + * @throws TInvalidDataValueException if the number is smaller than 1. + */ + public function setHistorySize($value) + { + if(($value=TPropertyValue::ensureInteger($value))>0) + $this->_historySize=$value; + else + throw new TInvalidDataValueException('sessionpagestatepersister_historysize_invalid'); + } + /** + * Saves state in session. + * @param mixed state to be stored + */ + public function save($state) + { + $session=$this->_page->getSession(); + $session->open(); + $data=serialize($state); + $timestamp=(string)microtime(true); + $key=self::STATE_SESSION_KEY.$timestamp; + $session->add($key,$data); + if(($queue=$session->itemAt(self::QUEUE_SESSION_KEY))===null) + $queue=array(); + $queue[]=$key; + if(count($queue)>$this->getHistorySize()) + { + $expiredKey=array_shift($queue); + $session->remove($expiredKey); + } + $session->add(self::QUEUE_SESSION_KEY,$queue); + $this->_page->setClientState(TPageStateFormatter::serialize($this->_page,$timestamp)); + } + + /** + * Loads page state from session. + * @return mixed the restored state + * @throws THttpException if page state is corrupted + */ + public function load() + { + if(($timestamp=TPageStateFormatter::unserialize($this->_page,$this->_page->getRequestClientState()))!==null) + { + $session=$this->_page->getSession(); + $session->open(); + $key=self::STATE_SESSION_KEY.$timestamp; + if(($data=$session->itemAt($key))!==null) + return unserialize($data); + } + throw new THttpException(400,'sessionpagestatepersister_pagestate_corrupted'); + } +} + diff --git a/gui/baculum/framework/Web/UI/TTemplateControl.php b/gui/baculum/framework/Web/UI/TTemplateControl.php new file mode 100644 index 0000000000..f5780928e1 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TTemplateControl.php @@ -0,0 +1,243 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTemplateControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * Includes TCompositeControl class + */ +Prado::using('System.Web.UI.TCompositeControl'); + +/** + * TTemplateControl class. + * TTemplateControl is the base class for all controls that use templates. + * By default, a control template is assumed to be in a file under the same + * directory with the control class file. They have the same file name and + * different extension name. For template file, the extension name is ".tpl". + * + * @author Qiang Xue + * @version $Id: TTemplateControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TTemplateControl extends TCompositeControl +{ + /** + * template file extension. + */ + const EXT_TEMPLATE='.tpl'; + + /** + * @var ITemplate the parsed template structure shared by the same control class + */ + private static $_template=array(); + /** + * @var ITemplate the parsed template structure specific for this control instance + */ + private $_localTemplate=null; + /** + * @var TTemplateControl the master control if any + */ + private $_master=null; + /** + * @var string master control class name + */ + private $_masterClass=''; + /** + * @var array list of TContent controls + */ + private $_contents=array(); + /** + * @var array list of TContentPlaceHolder controls + */ + private $_placeholders=array(); + + /** + * Returns the template object associated with this control object. + * @return ITemplate|null the parsed template, null if none + */ + public function getTemplate() + { + if($this->_localTemplate===null) + { + $class=get_class($this); + if(!isset(self::$_template[$class])) + self::$_template[$class]=$this->loadTemplate(); + return self::$_template[$class]; + } + else + return $this->_localTemplate; + } + + /** + * Sets the parsed template. + * Note, the template will be applied to the whole control class. + * This method should only be used by framework and control developers. + * @param ITemplate the parsed template + */ + public function setTemplate($value) + { + $this->_localTemplate=$value; + } + + /** + * @return boolean whether this control is a source template control. + * A source template control loads its template from external storage, + * such as file, db, rather than from within another template. + */ + public function getIsSourceTemplateControl() + { + if(($template=$this->getTemplate())!==null) + return $template->getIsSourceTemplate(); + else + return false; + } + + /** + * @return string the directory containing the template. Empty if no template available. + */ + public function getTemplateDirectory() + { + if(($template=$this->getTemplate())!==null) + return $template->getContextPath(); + else + return ''; + } + + /** + * Loads the template associated with this control class. + * @return ITemplate the parsed template structure + */ + protected function loadTemplate() + { + Prado::trace("Loading template ".get_class($this),'System.Web.UI.TTemplateControl'); + $template=$this->getService()->getTemplateManager()->getTemplateByClassName(get_class($this)); + return $template; + } + + /** + * Creates child controls. + * This method is overridden to load and instantiate control template. + * This method should only be used by framework and control developers. + */ + public function createChildControls() + { + if($tpl=$this->getTemplate()) + { + foreach($tpl->getDirective() as $name=>$value) + { + if(is_string($value)) + $this->setSubProperty($name,$value); + else + throw new TConfigurationException('templatecontrol_directive_invalid',get_class($this),$name); + } + $tpl->instantiateIn($this); + } + } + + /** + * Registers a content control. + * @param string ID of the content + * @param TContent + */ + public function registerContent($id,TContent $object) + { + if(isset($this->_contents[$id])) + throw new TConfigurationException('templatecontrol_contentid_duplicated',$id); + else + $this->_contents[$id]=$object; + } + + /** + * Registers a content placeholder to this template control. + * This method should only be used by framework and control developers. + * @param string placeholder ID + * @param TContentPlaceHolder placeholder control + */ + public function registerContentPlaceHolder($id,TContentPlaceHolder $object) + { + if(isset($this->_placeholders[$id])) + throw new TConfigurationException('templatecontrol_placeholderid_duplicated',$id); + else + $this->_placeholders[$id]=$object; + } + + /** + * @return string master class name (in namespace form) + */ + public function getMasterClass() + { + return $this->_masterClass; + } + + /** + * @param string master control class name (in namespace form) + */ + public function setMasterClass($value) + { + $this->_masterClass=$value; + } + + /** + * @return TTemplateControl|null master control associated with this control, null if none + */ + public function getMaster() + { + return $this->_master; + } + + /** + * Injects all content controls (and their children) to the corresponding content placeholders. + * This method should only be used by framework and control developers. + * @param string ID of the content control + * @param TContent the content to be injected + */ + public function injectContent($id,$content) + { + if(isset($this->_placeholders[$id])) + { + $placeholder=$this->_placeholders[$id]; + $controls=$placeholder->getParent()->getControls(); + $loc=$controls->remove($placeholder); + $controls->insertAt($loc,$content); + } + else + throw new TConfigurationException('templatecontrol_placeholder_inexistent',$id); + } + + /** + * Performs the OnInit step for the control and all its child controls. + * This method overrides the parent implementation + * by ensuring child controls are created first, + * and if master class is set, master will be applied. + * Only framework developers should use this method. + * @param TControl the naming container control + */ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + if($this->_masterClass!=='') + { + $master=Prado::createComponent($this->_masterClass); + if(!($master instanceof TTemplateControl)) + throw new TInvalidDataValueException('templatecontrol_mastercontrol_invalid'); + $this->_master=$master; + $this->getControls()->clear(); + $this->getControls()->add($master); + $master->ensureChildControls(); + foreach($this->_contents as $id=>$content) + $master->injectContent($id,$content); + } + else if(!empty($this->_contents)) + throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this)); + parent::initRecursive($namingContainer); + } +} + diff --git a/gui/baculum/framework/Web/UI/TTemplateControlInheritable.php b/gui/baculum/framework/Web/UI/TTemplateControlInheritable.php new file mode 100644 index 0000000000..f5b03805a4 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TTemplateControlInheritable.php @@ -0,0 +1,116 @@ + + * @link http://www.schlaue-kids.net/ + * @copyright Copyright © 2010 Schlaue-Kids.net + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Web.UI + */ + +Prado::using('System.Web.UI.TTemplateControl'); + +/** + * TTemplateControlInheritable class. + * TTemplateControlInheritable is an extension to the base class for all controls that use templates. + * By default, a control template is assumed to be in a file under the same + * directory with the control class file. They have the same file name and + * different extension name. For template file, the extension name is ".tpl". + * If a TTemplateControlInheritable is inherited it uses the base class template unless the + * inheriting control defines an own. + * + * @author Schlaue-Kids.net + * @author Kyle Caine + * @version $Id$ + * @package System.Web.UI + * @since 3.1.8 + */ +class TTemplateControlInheritable extends TTemplateControl +{ + // methods + + /** + * Creates child controls. + * This method is overridden to load and instantiate control template. + * This method should only be used by framework and control developers. + * Uses the controls template if available or the base class template otherwise. + * + * @return void + * @throws TConfigurationException if a template control directive is invalid + */ + public function createChildControls() + { + if(null === ($_template = $this->getTemplate())) { + return $this->doCreateChildControlsFor(get_class($this)); + } + + foreach($_template->getDirective() as $_name => $_value) { + if(!is_string($_value)) { + throw new TConfigurationException('templatecontrol_directive_invalid', get_class($this), $name); + } + + $this->setSubProperty($_name, $_value); + } + + $_template->instantiateIn($this); + } + + /** + * This method creates the cild controls for the given class + * + * @param string $parentClass The class to generate the child controls for + * @return void + */ + public function doCreateChildControlsFor($parentClass) + { + if(false !== ($_parentClass = get_parent_class($parentClass)) && 'TTemplateControl' != $_parentClass) { + $this->doCreateChildControlsFor($_parentClass); + } + + $this->doTemplateForClass($parentClass); + } + + /** + * This method creates the template object for the given class + * + * @param string $p_class The class to create the template from + * @return void + * @throws TConfigurationException if a template control directive is invalid + */ + public function doTemplateForClass($parentClass) + { + if(null !== ($_template = $this->getService()->getTemplateManager()->getTemplateByClassName($parentClass))) { + foreach($_template->getDirective() as $_name => $_value) { + if(!is_string($_value)) { + throw new TConfigurationException('templatecontrol_directive_invalid', get_class(this), $_name); + } + + $this->setSubProperty($_name, $_value); + } + + $_template->instantiateIn($this); + } + } + + // getter/setter + + /** + * A source template control loads its template from external storage, + * such as file, db, rather than from within another template. + * + * @return boolean whether the current control is a source template control + */ + public function getIsSourceTemplateControl() + { + if(null !== ($_template = $this->getTemplate())) { + return $_template->getIsSourceTemplate(); + } + + return ($_template = $this->getService()->getTemplateManager()->getTemplateByClassName(get_parent_class($this))) + ? $_template->getIsSourceTemplate() + : false; + } +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/UI/TTemplateManager.php b/gui/baculum/framework/Web/UI/TTemplateManager.php new file mode 100644 index 0000000000..91630241b1 --- /dev/null +++ b/gui/baculum/framework/Web/UI/TTemplateManager.php @@ -0,0 +1,1075 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTemplateManager.php 3251 2013-01-15 18:37:04Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * Includes TOutputCache class file + */ +Prado::using('System.Web.UI.WebControls.TOutputCache'); + +/** + * TTemplateManager class + * + * TTemplateManager manages the loading and parsing of control templates. + * + * There are two ways of loading a template, either by the associated template + * control class name, or the template file name. + * The former is via calling {@link getTemplateByClassName}, which tries to + * locate the corresponding template file under the directory containing + * the class file. The name of the template file is the class name with + * the extension '.tpl'. To load a template from a template file path, + * call {@link getTemplateByFileName}. + * + * By default, TTemplateManager is registered with {@link TPageService} as the + * template manager module that can be accessed via {@link TPageService::getTemplateManager()}. + * + * @author Qiang Xue + * @version $Id: TTemplateManager.php 3251 2013-01-15 18:37:04Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TTemplateManager extends TModule +{ + /** + * Template file extension + */ + const TEMPLATE_FILE_EXT='.tpl'; + /** + * Prefix of the cache variable name for storing parsed templates + */ + const TEMPLATE_CACHE_PREFIX='prado:template:'; + + /** + * Initializes the module. + * This method is required by IModule and is invoked by application. + * It starts output buffer if it is enabled. + * @param TXmlElement module configuration + */ + public function init($config) + { + $this->getService()->setTemplateManager($this); + } + + /** + * Loads the template corresponding to the specified class name. + * @return ITemplate template for the class name, null if template doesn't exist. + */ + public function getTemplateByClassName($className) + { + $class=new ReflectionClass($className); + $tplFile=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$className.self::TEMPLATE_FILE_EXT; + return $this->getTemplateByFileName($tplFile); + } + + /** + * Loads the template from the specified file. + * @return ITemplate template parsed from the specified file, null if the file doesn't exist. + */ + public function getTemplateByFileName($fileName) + { + if(($fileName=$this->getLocalizedTemplate($fileName))!==null) + { + Prado::trace("Loading template $fileName",'System.Web.UI.TTemplateManager'); + if(($cache=$this->getApplication()->getCache())===null) + return new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); + else + { + $array=$cache->get(self::TEMPLATE_CACHE_PREFIX.$fileName); + if(is_array($array)) + { + list($template,$timestamps)=$array; + if($this->getApplication()->getMode()===TApplicationMode::Performance) + return $template; + $cacheValid=true; + foreach($timestamps as $tplFile=>$timestamp) + { + if(!is_file($tplFile) || filemtime($tplFile)>$timestamp) + { + $cacheValid=false; + break; + } + } + if($cacheValid) + return $template; + } + $template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); + $includedFiles=$template->getIncludedFiles(); + $timestamps=array(); + $timestamps[$fileName]=filemtime($fileName); + foreach($includedFiles as $includedFile) + $timestamps[$includedFile]=filemtime($includedFile); + $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,$timestamps)); + return $template; + } + } + else + return null; + } + + /** + * Finds a localized template file. + * @param string template file. + * @return string|null a localized template file if found, null otherwise. + */ + protected function getLocalizedTemplate($filename) + { + if(($app=$this->getApplication()->getGlobalization(false))===null) + return is_file($filename)?$filename:null; + foreach($app->getLocalizedResource($filename) as $file) + { + if(($file=realpath($file))!==false && is_file($file)) + return $file; + } + return null; + } +} + +/** + * TTemplate implements PRADO template parsing logic. + * A TTemplate object represents a parsed PRADO control template. + * It can instantiate the template as child controls of a specified control. + * The template format is like HTML, with the following special tags introduced, + * - component tags: a component tag represents the configuration of a component. + * The tag name is in the format of com:ComponentType, where ComponentType is the component + * class name. Component tags must be well-formed. Attributes of the component tag + * are treated as either property initial values, event handler attachment, or regular + * tag attributes. + * - property tags: property tags are used to set large block of attribute values. + * The property tag name is in the format of where AttributeName + * can be a property name, an event name or a regular tag attribute name. + * - group subproperty tags: subproperties of a common property can be configured using + * + * - directive: directive specifies the property values for the template owner. + * It is in the format of <%@ property name-value pairs %>; + * - expressions: They are in the format of <%= PHP expression %> and <%% PHP statements %> + * - comments: There are two kinds of comments, regular HTML comments and special template comments. + * The former is in the format of , which will be treated as text strings. + * The latter is in the format of , which will be stripped out. + * + * Tags other than the above are not required to be well-formed. + * + * A TTemplate object represents a parsed PRADO template. To instantiate the template + * for a particular control, call {@link instantiateIn($control)}, which + * will create and intialize all components specified in the template and + * set their parent as $control. + * + * @author Qiang Xue + * @version $Id: TTemplateManager.php 3251 2013-01-15 18:37:04Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class TTemplate extends TApplicationComponent implements ITemplate +{ + /** + * '' - template comments + * '' - HTML comments + * '<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>' - component tags + * '<\/?prop:([\w\.]+)\s*>' - property tags + * '<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>' - directives + * '<%[%#~\/\\$=\\[](.*?)%>' - expressions + * ')*)\s*\/>' - group subproperty tags + */ + const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>|<%[%#~\/\\$=\\[](.*?)%>|)*)\s*\/>/msS'; + + /** + * Different configurations of component property/event/attribute + */ + const CONFIG_DATABIND=0; + const CONFIG_EXPRESSION=1; + const CONFIG_ASSET=2; + const CONFIG_PARAMETER=3; + const CONFIG_LOCALIZATION=4; + const CONFIG_TEMPLATE=5; + + /** + * @var array list of component tags and strings + */ + private $_tpl=array(); + /** + * @var array list of directive settings + */ + private $_directive=array(); + /** + * @var string context path + */ + private $_contextPath; + /** + * @var string template file path (if available) + */ + private $_tplFile=null; + /** + * @var integer the line number that parsing starts from (internal use) + */ + private $_startingLine=0; + /** + * @var string template content to be parsed + */ + private $_content; + /** + * @var boolean whether this template is a source template + */ + private $_sourceTemplate=true; + /** + * @var string hash code of the template + */ + private $_hashCode=''; + private $_tplControl=null; + private $_includedFiles=array(); + private $_includeAtLine=array(); + private $_includeLines=array(); + + + /** + * Constructor. + * The template will be parsed after construction. + * @param string the template string + * @param string the template context directory + * @param string the template file, null if no file + * @param integer the line number that parsing starts from (internal use) + * @param boolean whether this template is a source template, i.e., this template is loaded from + * some external storage rather than from within another template. + */ + public function __construct($template,$contextPath,$tplFile=null,$startingLine=0,$sourceTemplate=true) + { + $this->_sourceTemplate=$sourceTemplate; + $this->_contextPath=$contextPath; + $this->_tplFile=$tplFile; + $this->_startingLine=$startingLine; + $this->_content=$template; + $this->_hashCode=md5($template); + $this->parse($template); + $this->_content=null; // reset to save memory + } + + /** + * @return string template file path if available, null otherwise. + */ + public function getTemplateFile() + { + return $this->_tplFile; + } + + /** + * @return boolean whether this template is a source template, i.e., this template is loaded from + * some external storage rather than from within another template. + */ + public function getIsSourceTemplate() + { + return $this->_sourceTemplate; + } + + /** + * @return string context directory path + */ + public function getContextPath() + { + return $this->_contextPath; + } + + /** + * @return array name-value pairs declared in the directive + */ + public function getDirective() + { + return $this->_directive; + } + + /** + * @return string hash code that can be used to identify the template + */ + public function getHashCode() + { + return $this->_hashCode; + } + + /** + * @return array the parsed template + */ + public function &getItems() + { + return $this->_tpl; + } + + /** + * Instantiates the template. + * Content in the template will be instantiated as components and text strings + * and passed to the specified parent control. + * @param TControl the control who owns the template + * @param TControl the control who will become the root parent of the controls on the template. If null, it uses the template control. + */ + public function instantiateIn($tplControl,$parentControl=null) + { + $this->_tplControl=$tplControl; + if($parentControl===null) + $parentControl=$tplControl; + if(($page=$tplControl->getPage())===null) + $page=$this->getService()->getRequestedPage(); + $controls=array(); + $directChildren=array(); + foreach($this->_tpl as $key=>$object) + { + if($object[0]===-1) + $parent=$parentControl; + else if(isset($controls[$object[0]])) + $parent=$controls[$object[0]]; + else + continue; + if(isset($object[2])) // component + { + $component=Prado::createComponent($object[1]); + $properties=&$object[2]; + if($component instanceof TControl) + { + if($component instanceof TOutputCache) + $component->setCacheKeyPrefix($this->_hashCode.$key); + $component->setTemplateControl($tplControl); + if(isset($properties['id'])) + { + if(is_array($properties['id'])) + $properties['id']=$component->evaluateExpression($properties['id'][1]); + $tplControl->registerObject($properties['id'],$component); + } + if(isset($properties['skinid'])) + { + if(is_array($properties['skinid'])) + $component->setSkinID($component->evaluateExpression($properties['skinid'][1])); + else + $component->setSkinID($properties['skinid']); + unset($properties['skinid']); + } + + $component->trackViewState(false); + + $component->applyStyleSheetSkin($page); + foreach($properties as $name=>$value) + $this->configureControl($component,$name,$value); + + $component->trackViewState(true); + + if($parent===$parentControl) + $directChildren[]=$component; + else + $component->createdOnTemplate($parent); + if($component->getAllowChildControls()) + $controls[$key]=$component; + } + else if($component instanceof TComponent) + { + $controls[$key]=$component; + if(isset($properties['id'])) + { + if(is_array($properties['id'])) + $properties['id']=$component->evaluateExpression($properties['id'][1]); + $tplControl->registerObject($properties['id'],$component); + if(!$component->hasProperty('id')) + unset($properties['id']); + } + foreach($properties as $name=>$value) + $this->configureComponent($component,$name,$value); + if($parent===$parentControl) + $directChildren[]=$component; + else + $component->createdOnTemplate($parent); + } + } + else + { + if($object[1] instanceof TCompositeLiteral) + { + // need to clone a new object because the one in template is reused + $o=clone $object[1]; + $o->setContainer($tplControl); + if($parent===$parentControl) + $directChildren[]=$o; + else + $parent->addParsedObject($o); + } + else + { + if($parent===$parentControl) + $directChildren[]=$object[1]; + else + $parent->addParsedObject($object[1]); + } + } + } + // delay setting parent till now because the parent may cause + // the child to do lifecycle catchup which may cause problem + // if the child needs its own child controls. + foreach($directChildren as $control) + { + if($control instanceof TComponent) + $control->createdOnTemplate($parentControl); + else + $parentControl->addParsedObject($control); + } + } + + /** + * Configures a property/event of a control. + * @param TControl control to be configured + * @param string property name + * @param mixed property initial value + */ + protected function configureControl($control,$name,$value) + { + if(strncasecmp($name,'on',2)===0) // is an event + $this->configureEvent($control,$name,$value,$control); + else if(($pos=strrpos($name,'.'))===false) // is a simple property or custom attribute + $this->configureProperty($control,$name,$value); + else // is a subproperty + $this->configureSubProperty($control,$name,$value); + } + + /** + * Configures a property of a non-control component. + * @param TComponent component to be configured + * @param string property name + * @param mixed property initial value + */ + protected function configureComponent($component,$name,$value) + { + if(strpos($name,'.')===false) // is a simple property or custom attribute + $this->configureProperty($component,$name,$value); + else // is a subproperty + $this->configureSubProperty($component,$name,$value); + } + + /** + * Configures an event for a control. + * @param TControl control to be configured + * @param string event name + * @param string event handler + * @param TControl context control + */ + protected function configureEvent($control,$name,$value,$contextControl) + { + if(strpos($value,'.')===false) + $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value)); + else + $control->attachEventHandler($name,array($contextControl,$value)); + } + + /** + * Configures a simple property for a component. + * @param TComponent component to be configured + * @param string property name + * @param mixed property initial value + */ + protected function configureProperty($component,$name,$value) + { + if(is_array($value)) + { + switch($value[0]) + { + case self::CONFIG_DATABIND: + $component->bindProperty($name,$value[1]); + break; + case self::CONFIG_EXPRESSION: + if($component instanceof TControl) + $component->autoBindProperty($name,$value[1]); + else + { + $setter='set'.$name; + $component->$setter($this->_tplControl->evaluateExpression($value[1])); + } + break; + case self::CONFIG_TEMPLATE: + $setter='set'.$name; + $component->$setter($value[1]); + break; + case self::CONFIG_ASSET: // asset URL + $setter='set'.$name; + $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); + $component->$setter($url); + break; + case self::CONFIG_PARAMETER: // application parameter + $setter='set'.$name; + $component->$setter($this->getApplication()->getParameters()->itemAt($value[1])); + break; + case self::CONFIG_LOCALIZATION: + $setter='set'.$name; + $component->$setter(Prado::localize($value[1])); + break; + default: // an error if reaching here + throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); + break; + } + } + else + { + if (substr($name,0,2)=='js') + if ($value and !($value instanceof TJavaScriptLiteral)) + $value = new TJavaScriptLiteral($value); + $setter='set'.$name; + $component->$setter($value); + } + } + + /** + * Configures a subproperty for a component. + * @param TComponent component to be configured + * @param string subproperty name + * @param mixed subproperty initial value + */ + protected function configureSubProperty($component,$name,$value) + { + if(is_array($value)) + { + switch($value[0]) + { + case self::CONFIG_DATABIND: // databinding + $component->bindProperty($name,$value[1]); + break; + case self::CONFIG_EXPRESSION: // expression + if($component instanceof TControl) + $component->autoBindProperty($name,$value[1]); + else + $component->setSubProperty($name,$this->_tplControl->evaluateExpression($value[1])); + break; + case self::CONFIG_TEMPLATE: + $component->setSubProperty($name,$value[1]); + break; + case self::CONFIG_ASSET: // asset URL + $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); + $component->setSubProperty($name,$url); + break; + case self::CONFIG_PARAMETER: // application parameter + $component->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1])); + break; + case self::CONFIG_LOCALIZATION: + $component->setSubProperty($name,Prado::localize($value[1])); + break; + default: // an error if reaching here + throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); + break; + } + } + else + $component->setSubProperty($name,$value); + } + + /** + * Parses a template string. + * + * This template parser recognizes five types of data: + * regular string, well-formed component tags, well-formed property tags, directives, and expressions. + * + * The parsing result is returned as an array. Each array element can be of three types: + * - a string, 0: container index; 1: string content; + * - a component tag, 0: container index; 1: component type; 2: attributes (name=>value pairs) + * If a directive is found in the template, it will be parsed and can be + * retrieved via {@link getDirective}, which returns an array consisting of + * name-value pairs in the directive. + * + * Note, attribute names are treated as case-insensitive and will be turned into lower cases. + * Component and directive types are case-sensitive. + * Container index is the index to the array element that stores the container object. + * If an object has no container, its container index is -1. + * + * @param string the template string + * @throws TConfigurationException if a parsing error is encountered + */ + protected function parse($input) + { + $input=$this->preprocess($input); + $tpl=&$this->_tpl; + $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + $expectPropEnd=false; + $textStart=0; + $stack=array(); + $container=-1; + $matchEnd=0; + $c=0; + $this->_directive=null; + try + { + for($i=0;$i<$n;++$i) + { + $match=&$matches[$i]; + $str=$match[0][0]; + $matchStart=$match[0][1]; + $matchEnd=$matchStart+strlen($str)-1; + if(strpos($str,'$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $type=$match[1][0]; + $attributes=$this->parseAttributes($match[2][0],$match[2][1]); + $this->validateAttributes($type,$attributes); + $tpl[$c++]=array($container,$type,$attributes); + if($str[strlen($str)-2]!=='/') // open tag + { + $stack[] = $type; + $container=$c-1; + } + } + else if(strpos($str,'$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $type=$match[1][0]; + + if(empty($stack)) + throw new TConfigurationException('template_closingtag_unexpected',""); + + $name=array_pop($stack); + if($name!==$type) + { + $tag=$name[0]==='@' ? '' : ""; + throw new TConfigurationException('template_closingtag_expected',$tag); + } + $container=$tpl[$container][0]; + } + else if(strpos($str,'<%@')===0) // directive + { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + if(isset($tpl[0]) || $this->_directive!==null) + throw new TConfigurationException('template_directive_nonunique'); + $this->_directive=$this->parseAttributes($match[4][0],$match[4][1]); + } + else if(strpos($str,'<%')===0) // expression + { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $literal=trim($match[5][0]); + if($str[2]==='=') // expression + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,$literal)); + else if($str[2]==='%') // statements + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_STATEMENTS,$literal)); + else if($str[2]==='#') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_DATABINDING,$literal)); + else if($str[2]==='$') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->getApplication()->getParameters()->itemAt('$literal')")); + else if($str[2]==='~') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->publishFilePath('$this->_contextPath/$literal')")); + else if($str[2]==='/') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"rtrim(dirname(\$this->getApplication()->getRequest()->getApplicationUrl()), '/').'/$literal'")); + else if($str[2]==='[') + { + $literal=strtr(trim(substr($literal,0,strlen($literal)-1)),array("'"=>"\'","\\"=>"\\\\")); + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"Prado::localize('$literal')")); + } + } + else if(strpos($str,'')===strlen($str)-2) //subproperties + { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $prop=strtolower($match[6][0]); + $attrs=$this->parseAttributes($match[7][0],$match[7][1]); + $attributes=array(); + foreach($attrs as $name=>$value) + $attributes[$prop.'.'.$name]=$value; + $type=$tpl[$container][1]; + $this->validateAttributes($type,$attributes); + foreach($attributes as $name=>$value) + { + if(isset($tpl[$container][2][$name])) + throw new TConfigurationException('template_property_duplicated',$name); + $tpl[$container][2][$name]=$value; + } + } + else // regular property + { + $prop=strtolower($match[3][0]); + $stack[] = '@'.$prop; + if(!$expectPropEnd) + { + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $expectPropEnd=true; + } + } + } + else if(strpos($str,'"); + $name=array_pop($stack); + if($name!=='@'.$prop) + { + $tag=$name[0]==='@' ? '' : ""; + throw new TConfigurationException('template_closingtag_expected',$tag); + } + if(($last=count($stack))<1 || $stack[$last-1][0]!=='@') + { + if($matchStart>$textStart) + { + $value=substr($input,$textStart,$matchStart-$textStart); + if(substr($prop,-8,8)==='template') + $value=$this->parseTemplateProperty($value,$textStart); + else + $value=$this->parseAttribute($value); + if($container>=0) + { + $type=$tpl[$container][1]; + $this->validateAttributes($type,array($prop=>$value)); + if(isset($tpl[$container][2][$prop])) + throw new TConfigurationException('template_property_duplicated',$prop); + $tpl[$container][2][$prop]=$value; + } + else // a property for the template control + $this->_directive[$prop]=$value; + $textStart=$matchEnd+1; + } + $expectPropEnd=false; + } + } + else if(strpos($str,' + *
      + * Use the {@link setClientValidationFunction ClientValidationFunction} property + * to specify the name of the client-side validation script function associated + * with the TCustomValidator. + * + * @author Qiang Xue + * @version $Id: TCustomValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCustomValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TCustomValidator'; + } + + /** + * @return string the name of the custom client-side script function used for validation. + */ + public function getClientValidationFunction() + { + return $this->getViewState('ClientValidationFunction',''); + } + + /** + * Sets the name of the custom client-side script function used for validation. + * @param string the script function name + */ + public function setClientValidationFunction($value) + { + $this->setViewState('ClientValidationFunction',$value,''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if {@link onServerValidate} returns true. + * @return boolean whether the validation succeeds + */ + public function evaluateIsValid() + { + $value = ''; + if($this->getValidationTarget()!==null) + $value=$this->getValidationValue($this->getValidationTarget()); + return $this->onServerValidate($value); + } + + /** + * This method is invoked when the server side validation happens. + * It will raise the OnServerValidate event. + * The method also allows derived classes to handle the event without attaching a delegate. + * Note The derived classes should call parent implementation + * to ensure the OnServerValidate event is raised. + * @param string the value to be validated + * @return boolean whether the value is valid + */ + public function onServerValidate($value) + { + $param=new TServerValidateEventParameter($value,true); + $this->raiseEvent('OnServerValidate',$this,$param); + return $param->getIsValid(); + } + + /** + * @return TControl control to be validated. Null if no control is found. + */ + public function getValidationTarget() + { + if(($id=$this->getControlToValidate())!=='' && ($control=$this->findControl($id))!==null) + return $control; + else if(($id=$this->getControlToValidate())!=='') + throw new TInvalidDataTypeException('basevalidator_validatable_required',get_class($this)); + else + return null; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options=parent::getClientScriptOptions(); + if(($clientJs=$this->getClientValidationFunction())!=='') + $options['ClientValidationFunction']=$clientJs; + return $options; + } + + /** + * Only register the client-side validator if + * {@link setClientValidationFunction ClientValidationFunction} is set. + */ + protected function registerClientScriptValidator() + { + if($this->getClientValidationFunction()!=='') + parent::registerClientScriptValidator(); + } +} + +/** + * TServerValidateEventParameter class + * + * TServerValidateEventParameter encapsulates the parameter data for + * OnServerValidate event of TCustomValidator components. + * + * @author Qiang Xue + * @version $Id: TCustomValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TServerValidateEventParameter extends TEventParameter +{ + /** + * the value to be validated + * @var string + */ + private $_value=''; + /** + * whether the value is valid + * @var boolean + */ + private $_isValid=true; + + /** + * Constructor. + * @param string property value to be validated + * @param boolean whether the value is valid + */ + public function __construct($value,$isValid) + { + $this->_value=$value; + $this->setIsValid($isValid); + } + + /** + * @return string value to be validated + */ + public function getValue() + { + return $this->_value; + } + + /** + * @return boolean whether the value is valid + */ + public function getIsValid() + { + return $this->_isValid; + } + + /** + * @param boolean whether the value is valid + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataBoundControl.php b/gui/baculum/framework/Web/UI/WebControls/TDataBoundControl.php new file mode 100644 index 0000000000..08d130c135 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataBoundControl.php @@ -0,0 +1,586 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataBoundControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataSourceControl'); +Prado::using('System.Web.UI.WebControls.TDataSourceView'); +Prado::using('System.Collections.TPagedDataSource'); + +/** + * TDataBoundControl class. + * + * TDataBoundControl is the based class for controls that need to populate + * data from data sources. It provides basic properties and methods that allow + * the derived controls to associate with data sources and retrieve data from them. + * + * TBC.... + * + * TDataBoundControl is equipped with paging capabilities. By setting + * {@link setAllowPaging AllowPaging} to true, the input data will be paged + * and only one page of data is actually populated into the data-bound control. + * This saves a lot of memory when dealing with larget datasets. + * + * To specify the number of data items displayed on each page, set + * the {@link setPageSize PageSize} property, and to specify which + * page of data to be displayed, set {@link setCurrentPageIndex CurrentPageIndex}. + * + * When the size of the original data is too big to be loaded all in the memory, + * one can enable custom paging. In custom paging, the total number of data items + * is specified manually via {@link setVirtualItemCount VirtualItemCount}, + * and the data source only needs to contain the current page of data. To enable + * custom paging, set {@link setAllowCustomPaging AllowCustomPaging} to true. + * + * @author Qiang Xue + * @version $Id: TDataBoundControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataBoundControl extends TWebControl +{ + private $_initialized=false; + private $_dataSource=null; + private $_requiresBindToNull=false; + private $_requiresDataBinding=false; + private $_prerendered=false; + private $_currentView=null; + private $_currentDataSource=null; + private $_currentViewValid=false; + private $_currentDataSourceValid=false; + private $_currentViewIsFromDataSourceID=false; + private $_parameters=null; + private $_isDataBound=false; + + /** + * @return Traversable data source object, defaults to null. + */ + public function getDataSource() + { + return $this->_dataSource; + } + + /** + * Sets the data source object associated with the databound control. + * The data source must implement Traversable interface. + * If an array is given, it will be converted to xxx. + * If a string is given, it will be converted to xxx. + * @param Traversable|array|string data source object + */ + public function setDataSource($value) + { + $this->_dataSource=$this->validateDataSource($value); + $this->onDataSourceChanged(); + } + + /** + * @return string ID path to the data source control. Defaults to empty. + */ + public function getDataSourceID() + { + return $this->getViewState('DataSourceID',''); + } + + /** + * @param string ID path to the data source control. The data source + * control must be locatable via {@link TControl::findControl} call. + */ + public function setDataSourceID($value) + { + $dsid=$this->getViewState('DataSourceID',''); + if($dsid!=='' && $value==='') + $this->_requiresBindToNull=true; + $this->setViewState('DataSourceID',$value,''); + $this->onDataSourceChanged(); + } + + /** + * @return boolean if the databound control uses the data source specified + * by {@link setDataSourceID}, or it uses the data source object specified + * by {@link setDataSource}. + */ + protected function getUsingDataSourceID() + { + return $this->getDataSourceID()!==''; + } + + /** + * Sets {@link setRequiresDataBinding RequiresDataBinding} as true if the control is initialized. + * This method is invoked when either {@link setDataSource} or {@link setDataSourceID} is changed. + */ + public function onDataSourceChanged() + { + $this->_currentViewValid=false; + $this->_currentDataSourceValid=false; + if($this->getInitialized()) + $this->setRequiresDataBinding(true); + } + + /** + * @return boolean whether the databound control has been initialized. + * By default, the control is initialized after its viewstate has been restored. + */ + protected function getInitialized() + { + return $this->_initialized; + } + + /** + * Sets a value indicating whether the databound control is initialized. + * If initialized, any modification to {@link setDataSource DataSource} or + * {@link setDataSourceID DataSourceID} will set {@link setRequiresDataBinding RequiresDataBinding} + * as true. + * @param boolean a value indicating whether the databound control is initialized. + */ + protected function setInitialized($value) + { + $this->_initialized=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether databind has been invoked in the previous page request + */ + protected function getIsDataBound() + { + return $this->_isDataBound; + } + + /** + * @param boolean if databind has been invoked in this page request + */ + protected function setIsDataBound($value) + { + $this->_isDataBound=$value; + } + + /** + * @return boolean whether a databind call is required (by the data bound control) + */ + protected function getRequiresDataBinding() + { + return $this->_requiresDataBinding; + } + + /** + * @return boolean whether paging is enabled. Defaults to false. + */ + public function getAllowPaging() + { + return $this->getViewState('AllowPaging',false); + } + + /** + * @param boolean whether paging is enabled + */ + public function setAllowPaging($value) + { + $this->setViewState('AllowPaging',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether the custom paging is enabled. Defaults to false. + */ + public function getAllowCustomPaging() + { + return $this->getViewState('AllowCustomPaging',false); + } + + /** + * Sets a value indicating whether the custom paging should be enabled. + * When the pager is in custom paging mode, the {@link setVirtualItemCount VirtualItemCount} + * property is used to determine the paging, and the data items in the + * {@link setDataSource DataSource} are considered to be in the current page. + * @param boolean whether the custom paging is enabled + */ + public function setAllowCustomPaging($value) + { + $this->setViewState('AllowCustomPaging',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer the zero-based index of the current page. Defaults to 0. + */ + public function getCurrentPageIndex() + { + return $this->getViewState('CurrentPageIndex',0); + } + + /** + * @param integer the zero-based index of the current page + * @throws TInvalidDataValueException if the value is less than 0 + */ + public function setCurrentPageIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->setViewState('CurrentPageIndex',$value,0); + } + + /** + * @return integer the number of data items on each page. Defaults to 10. + */ + public function getPageSize() + { + return $this->getViewState('PageSize',10); + } + + /** + * @param integer the number of data items on each page. + * @throws TInvalidDataValueException if the value is less than 1 + */ + public function setPageSize($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('databoundcontrol_pagesize_invalid',get_class($this)); + $this->setViewState('PageSize',TPropertyValue::ensureInteger($value),10); + } + + /** + * @return integer number of pages of data items available + */ + public function getPageCount() + { + return $this->getViewState('PageCount',1); + } + + /** + * @return integer virtual number of data items in the data source. Defaults to 0. + * @see setAllowCustomPaging + */ + public function getVirtualItemCount() + { + return $this->getViewState('VirtualItemCount',0); + } + + /** + * @param integer virtual number of data items in the data source. + * @throws TInvalidDataValueException if the value is less than 0 + * @see setAllowCustomPaging + */ + public function setVirtualItemCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('databoundcontrol_virtualitemcount_invalid',get_class($this)); + $this->setViewState('VirtualItemCount',$value,0); + } + + /** + * Sets a value indicating whether a databind call is required by the data bound control. + * If true and the control has been prerendered while it uses the data source + * specified by {@link setDataSourceID}, a databind call will be called by this method. + * @param boolean whether a databind call is required. + */ + protected function setRequiresDataBinding($value) + { + $value=TPropertyValue::ensureBoolean($value); + if($value && $this->_prerendered) + { + $this->_requiresDataBinding=true; + $this->ensureDataBound(); + } + else + $this->_requiresDataBinding=$value; + } + + /** + * Ensures any pending {@link dataBind} is called. + * This method calls {@link dataBind} if the data source is specified + * by {@link setDataSourceID} or if {@link getRequiresDataBinding RequiresDataBinding} + * is true. + */ + protected function ensureDataBound() + { + if($this->_requiresDataBinding && ($this->getUsingDataSourceID() || $this->_requiresBindToNull)) + { + $this->dataBind(); + $this->_requiresBindToNull=false; + } + } + + /** + * @return TPagedDataSource creates a paged data source + */ + protected function createPagedDataSource() + { + $ds=new TPagedDataSource; + $ds->setCurrentPageIndex($this->getCurrentPageIndex()); + $ds->setPageSize($this->getPageSize()); + $ds->setAllowPaging($this->getAllowPaging()); + $ds->setAllowCustomPaging($this->getAllowCustomPaging()); + $ds->setVirtualItemCount($this->getVirtualItemCount()); + return $ds; + } + + /** + * Performs databinding. + * This method overrides the parent implementation by calling + * {@link performSelect} which fetches data from data source and does + * the actual binding work. + */ + public function dataBind() + { + $this->setRequiresDataBinding(false); + $this->dataBindProperties(); + $this->onDataBinding(null); + + if(($view=$this->getDataSourceView())!==null) + $data=$view->select($this->getSelectParameters()); + else + $data=null; + + if($data instanceof Traversable) + { + if($this->getAllowPaging()) + { + $ds=$this->createPagedDataSource(); + $ds->setDataSource($data); + $this->setViewState('PageCount',$ds->getPageCount()); + if($ds->getCurrentPageIndex()>=$ds->getPageCount()) + { + $ds->setCurrentPageIndex($ds->getPageCount()-1); + $this->setCurrentPageIndex($ds->getCurrentPageIndex()); + } + $this->performDataBinding($ds); + } + else + { + $this->clearViewState('PageCount'); + $this->performDataBinding($data); + } + } + $this->setIsDataBound(true); + $this->onDataBound(null); + } + + public function dataSourceViewChanged($sender,$param) + { + if(!$this->_ignoreDataSourceViewChanged) + $this->setRequiresDataBinding(true); + } + + protected function getDataSourceView() + { + if(!$this->_currentViewValid) + { + if($this->_currentView && $this->_currentViewIsFromDataSourceID) + $this->_currentView->detachEventHandler('DataSourceViewChanged',array($this,'dataSourceViewChanged')); + if(($dataSource=$this->determineDataSource())!==null) + { + if(($view=$dataSource->getView($this->getDataMember()))===null) + throw new TInvalidDataValueException('databoundcontrol_datamember_invalid',$this->getDataMember()); + if($this->_currentViewIsFromDataSourceID=$this->getUsingDataSourceID()) + $view->attachEventHandler('OnDataSourceViewChanged',array($this,'dataSourceViewChanged')); + $this->_currentView=$view; + } + else + $this->_currentView=null; + $this->_currentViewValid=true; + } + return $this->_currentView; + } + + protected function determineDataSource() + { + if(!$this->_currentDataSourceValid) + { + if(($dsid=$this->getDataSourceID())!=='') + { + if(($dataSource=$this->getNamingContainer()->findControl($dsid))===null) + throw new TInvalidDataValueException('databoundcontrol_datasourceid_inexistent',$dsid); + else if(!($dataSource instanceof IDataSource)) + throw new TInvalidDataValueException('databoundcontrol_datasourceid_invalid',$dsid); + else + $this->_currentDataSource=$dataSource; + } + else if(($dataSource=$this->getDataSource())!==null) + $this->_currentDataSource=new TReadOnlyDataSource($dataSource,$this->getDataMember()); + else + $this->_currentDataSource=null; + $this->_currentDataSourceValid=true; + } + return $this->_currentDataSource; + } + + abstract protected function performDataBinding($data); + + /** + * Raises OnDataBound event. + * This method should be invoked after a databind is performed. + * It is mainly used by framework and component developers. + */ + public function onDataBound($param) + { + $this->raiseEvent('OnDataBound',$this,$param); + } + + /** + * Sets page's OnPreLoad event handler as {@link pagePreLoad}. + * If viewstate is disabled and the current request is a postback, + * {@link setRequiresDataBinding RequiresDataBinding} will be set true. + * This method overrides the parent implementation. + * @param TEventParameter event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $page=$this->getPage(); + $page->attachEventHandler('OnPreLoad',array($this,'pagePreLoad')); + } + + /** + * Sets {@link getInitialized} as true. + * This method is invoked when page raises PreLoad event. + * @param mixed event sender + * @param TEventParameter event parameter + */ + public function pagePreLoad($sender,$param) + { + $this->_initialized=true; + $isPostBack=$this->getPage()->getIsPostBack(); + if(!$isPostBack || ($isPostBack && (!$this->getEnableViewState(true) || !$this->getIsDataBound()))) + $this->setRequiresDataBinding(true); + } + + /** + * Ensures any pending databind is performed. + * This method overrides the parent implementation. + * @param TEventParameter event parameter + */ + public function onPreRender($param) + { + $this->_prerendered=true; + $this->ensureDataBound(); + parent::onPreRender($param); + } + + /** + * Validates if the parameter is a valid data source. + * If it is a string or an array, it will be converted as a TList object. + * @param Traversable|array|string data source to be validated + * @return Traversable the data that is traversable + * @throws TInvalidDataTypeException if the data is neither null nor Traversable + */ + protected function validateDataSource($value) + { + if(is_string($value)) + { + $list=new TList; + foreach(TPropertyValue::ensureArray($value) as $key=>$value) + { + if(is_array($value)) + $list->add($value); + else + $list->add(array($value,is_string($key)?$key:$value)); + } + return $list; + } + else if(is_array($value)) + return new TMap($value); + else if($value instanceof TDbDataReader) { + // read array from TDbDataReader since it's forward-only stream and can only be traversed once + return $value->readAll(); + } + else if(($value instanceof Traversable) || $value===null) + return $value; + else + throw new TInvalidDataTypeException('databoundcontrol_datasource_invalid',get_class($this)); + } + + public function getDataMember() + { + return $this->getViewState('DataMember',''); + } + + public function setDataMember($value) + { + $this->setViewState('DataMember',$value,''); + } + + public function getSelectParameters() + { + if(!$this->_parameters) + $this->_parameters=new TDataSourceSelectParameters; + return $this->_parameters; + } +} + + +/** + * TListItemType class. + * TListItemType defines the enumerable type for the possible types + * that databound list items could take. + * + * The following enumerable values are defined: + * - Header: header item + * - Footer: footer item + * - Item: content item (neither header nor footer) + * - Separator: separator between items + * - AlternatingItem: alternating content item + * - EditItem: content item in edit mode + * - SelectedItem: selected content item + * - Pager: pager + * + * @author Qiang Xue + * @version $Id: TDataBoundControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TListItemType extends TEnumerable +{ + const Header='Header'; + const Footer='Footer'; + const Item='Item'; + const Separator='Separator'; + const AlternatingItem='AlternatingItem'; + const EditItem='EditItem'; + const SelectedItem='SelectedItem'; + const Pager='Pager'; +} + + +/** + * IItemDataRenderer interface. + * + * IItemDataRenderer defines the interface that an item renderer + * needs to implement. Besides the {@link getData Data} property, a list item + * renderer also needs to provide {@link getItemIndex ItemIndex} and + * {@link getItemType ItemType} property. + * + * @author Qiang Xue + * @version $Id: TDataBoundControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +interface IItemDataRenderer extends IDataRenderer +{ + /** + * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. + * If the item is not in the collection (e.g. it is a header item), it returns -1. + * @return integer zero-based index of the item. + */ + public function getItemIndex(); + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value); + + /** + * @return TListItemType the item type. + */ + public function getItemType(); + + /** + * @param TListItemType the item type. + */ + public function setItemType($value); +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataGrid.php b/gui/baculum/framework/Web/UI/WebControls/TDataGrid.php new file mode 100644 index 0000000000..249d810839 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataGrid.php @@ -0,0 +1,2258 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TBaseList, TPagedDataSource, TDummyDataSource and TTable classes + */ +Prado::using('System.Web.UI.WebControls.TBaseDataList'); +Prado::using('System.Collections.TPagedDataSource'); +Prado::using('System.Collections.TDummyDataSource'); +Prado::using('System.Web.UI.WebControls.TTable'); +Prado::using('System.Web.UI.WebControls.TPanel'); +Prado::using('System.Web.UI.WebControls.TDataGridPagerStyle'); + +/** + * TDataGrid class + * + * TDataGrid represents a data bound and updatable grid control. + * + * To populate data into the datagrid, sets its {@link setDataSource DataSource} + * to a tabular data source and call {@link dataBind()}. + * Each row of data will be represented by an item in the {@link getItems Items} + * collection of the datagrid. + * + * An item can be at one of three states: browsing, selected and edit. + * The state determines how the item will be displayed. For example, if an item + * is in edit state, it may be displayed as a table row with input text boxes + * if the columns are of type {@link TBoundColumn}; and if in browsing state, + * they are displayed as static text. + * + * To change the state of an item, set {@link setEditItemIndex EditItemIndex} + * or {@link setSelectedItemIndex SelectedItemIndex} property. + * + * Each datagrid item has a {@link TDataGridItem::getItemType type} + * which tells the position and state of the item in the datalist. An item in the header + * of the repeater is of type Header. A body item may be of either + * Item, AlternatingItem, SelectedItem or EditItem, depending whether the item + * index is odd or even, whether it is being selected or edited. + * + * A datagrid is specified with a list of columns. Each column specifies how the corresponding + * table column will be displayed. For example, the header/footer text of that column, + * the cells in that column, and so on. The following column types are currently + * provided by the framework, + * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data. + * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons + * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource. + * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state + * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource. + * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource. + * - {@link TTemplateColumn}, displaying content based on templates. + * + * There are three ways to specify columns for a datagrid. + *
        + *
      • Automatically generated based on data source. + * By setting {@link setAutoGenerateColumns AutoGenerateColumns} to true, + * a list of columns will be automatically generated based on the schema of the data source. + * Each column corresponds to a column of the data.
      • + *
      • Specified in template. For example, + * + * + * + * + * + * + *
      • + *
      • Manually created in code. Columns can be manipulated via + * the {@link setColumns Columns} property of the datagrid. For example, + * + * $column=new TBoundColumn; + * $datagrid->Columns[]=$column; + * + *
      • + *
      + * Note, automatically generated columns cannot be accessed via + * the {@link getColumns Columns} property. + * + * TDataGrid supports sorting. If the {@link setAllowSorting AllowSorting} + * is set to true, a column with nonempty {@link setSortExpression SortExpression} + * will have its header text displayed as a clickable link button. + * Clicking on the link button will raise {@link onSortCommand OnSortCommand} + * event. You can respond to this event, sort the data source according + * to the event parameter, and then invoke {@link databind()} on the datagrid + * to show to end users the sorted data. + * + * TDataGrid supports paging. If the {@link setAllowPaging AllowPaging} + * is set to true, a pager will be displayed on top and/or bottom of the table. + * How the pager will be displayed is determined by the {@link getPagerStyle PagerStyle} + * property. Clicking on a pager button will raise an {@link onPageIndexChanged OnPageIndexChanged} + * event. You can respond to this event, specify the page to be displayed by + * setting {@link setCurrentPageIndex CurrentPageIndex} property, + * and then invoke {@link databind()} on the datagrid to show to end users + * a new page of data. + * + * TDataGrid supports two kinds of paging. The first one is based on the number of data items in + * datasource. The number of pages {@link getPageCount PageCount} is calculated based + * the item number and the {@link setPageSize PageSize} property. + * The datagrid will manage which section of the data source to be displayed + * based on the {@link setCurrentPageIndex CurrentPageIndex} property. + * The second approach calculates the page number based on the + * {@link setVirtualItemCount VirtualItemCount} property and + * the {@link setPageSize PageSize} property. The datagrid will always + * display from the beginning of the datasource up to the number of + * {@link setPageSize PageSize} data items. This approach is especially + * useful when the datasource may contain too many data items to be managed by + * the datagrid efficiently. + * + * When the datagrid contains a button control that raises an {@link onCommand OnCommand} + * event, the event will be bubbled up to the datagrid control. + * If the event's command name is recognizable by the datagrid control, + * a corresponding item event will be raised. The following item events will be + * raised upon a specific command: + * - OnEditCommand, if CommandName=edit + * - OnCancelCommand, if CommandName=cancel + * - OnSelectCommand, if CommandName=select + * - OnDeleteCommand, if CommandName=delete + * - OnUpdateCommand, if CommandName=update + * - onPageIndexChanged, if CommandName=page + * - OnSortCommand, if CommandName=sort + * Note, an {@link onItemCommand OnItemCommand} event is raised in addition to + * the above specific command events. + * + * TDataGrid also raises an {@link onItemCreated OnItemCreated} event for + * every newly created datagrid item. You can respond to this event to customize + * the content or style of the newly created item. + * + * Note, the data bound to the datagrid are reset to null after databinding. + * There are several ways to access the data associated with a datagrid row: + * - Access the data in {@link onItemDataBound OnItemDataBound} event + * - Use {@link getDataKeys DataKeys} to obtain the data key associated with + * the specified datagrid row and use the key to fetch the corresponding data + * from some persistent storage such as DB. + * - Save the data in viewstate and get it back during postbacks. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGrid extends TBaseDataList implements INamingContainer +{ + /** + * datagrid item types + * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead. + */ + const IT_HEADER='Header'; + const IT_FOOTER='Footer'; + const IT_ITEM='Item'; + const IT_SEPARATOR='Separator'; + const IT_ALTERNATINGITEM='AlternatingItem'; + const IT_EDITITEM='EditItem'; + const IT_SELECTEDITEM='SelectedItem'; + const IT_PAGER='Pager'; + + /** + * Command name that TDataGrid understands. + */ + const CMD_SELECT='Select'; + const CMD_EDIT='Edit'; + const CMD_UPDATE='Update'; + const CMD_DELETE='Delete'; + const CMD_CANCEL='Cancel'; + const CMD_SORT='Sort'; + const CMD_PAGE='Page'; + const CMD_PAGE_NEXT='Next'; + const CMD_PAGE_PREV='Previous'; + const CMD_PAGE_FIRST='First'; + const CMD_PAGE_LAST='Last'; + + /** + * @var TDataGridColumnCollection manually created column collection + */ + private $_columns=null; + /** + * @var TDataGridColumnCollection automatically created column collection + */ + private $_autoColumns=null; + /** + * @var TList all columns including both manually and automatically created columns + */ + private $_allColumns=null; + /** + * @var TDataGridItemCollection datagrid item collection + */ + private $_items=null; + /** + * @var TDataGridItem header item + */ + private $_header=null; + /** + * @var TDataGridItem footer item + */ + private $_footer=null; + /** + * @var TPagedDataSource paged data source object + */ + private $_pagedDataSource=null; + private $_topPager=null; + private $_bottomPager=null; + /** + * @var ITemplate template used when empty data is bounded + */ + private $_emptyTemplate=null; + /** + * @var boolean whether empty template is effective + */ + private $_useEmptyTemplate=false; + + /** + * @return string tag name (table) of the datagrid + */ + protected function getTagName() + { + return 'table'; + } + + /** + * @return string Name of the class used in AutoGenerateColumns mode + */ + protected function getAutoGenerateColumnName() + { + return 'TBoundColumn'; + } + + /** + * Adds objects parsed in template to datagrid. + * Datagrid columns are added into {@link getColumns Columns} collection. + * @param mixed object parsed in template + */ + public function addParsedObject($object) + { + if($object instanceof TDataGridColumn) + $this->getColumns()->add($object); + else + parent::addParsedObject($object); // this is needed by EmptyTemplate + } + + /** + * @return TDataGridColumnCollection manually specified datagrid columns + */ + public function getColumns() + { + if(!$this->_columns) + $this->_columns=new TDataGridColumnCollection($this); + return $this->_columns; + } + + /** + * @return TDataGridColumnCollection automatically generated datagrid columns + */ + public function getAutoColumns() + { + if(!$this->_autoColumns) + $this->_autoColumns=new TDataGridColumnCollection($this); + return $this->_autoColumns; + } + + /** + * @return TDataGridItemCollection datagrid item collection + */ + public function getItems() + { + if(!$this->_items) + $this->_items=new TDataGridItemCollection; + return $this->_items; + } + + /** + * @return integer number of items + */ + public function getItemCount() + { + return $this->_items?$this->_items->getCount():0; + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableStyle} to be used by datagrid. + * @return TTableStyle control style to be used + */ + protected function createStyle() + { + return new TTableStyle; + } + + /** + * @return string the URL of the background image for the datagrid + */ + public function getBackImageUrl() + { + return $this->getStyle()->getBackImageUrl(); + } + + /** + * @param string the URL of the background image for the datagrid + */ + public function setBackImageUrl($value) + { + $this->getStyle()->setBackImageUrl($value); + } + + /** + * @return TTableItemStyle the style for every item + */ + public function getItemStyle() + { + if(($style=$this->getViewState('ItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('ItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for each alternating item + */ + public function getAlternatingItemStyle() + { + if(($style=$this->getViewState('AlternatingItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('AlternatingItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for selected item + */ + public function getSelectedItemStyle() + { + if(($style=$this->getViewState('SelectedItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('SelectedItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for edit item + */ + public function getEditItemStyle() + { + if(($style=$this->getViewState('EditItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('EditItemStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for header + */ + public function getHeaderStyle() + { + if(($style=$this->getViewState('HeaderStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return TTableItemStyle the style for footer + */ + public function getFooterStyle() + { + if(($style=$this->getViewState('FooterStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('FooterStyle',$style,null); + } + return $style; + } + + /** + * @return TDataGridPagerStyle the style for pager + */ + public function getPagerStyle() + { + if(($style=$this->getViewState('PagerStyle',null))===null) + { + $style=new TDataGridPagerStyle; + $this->setViewState('PagerStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style for thead element, if any + * @since 3.1.1 + */ + public function getTableHeadStyle() + { + if(($style=$this->getViewState('TableHeadStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('TableHeadStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style for tbody element, if any + * @since 3.1.1 + */ + public function getTableBodyStyle() + { + if(($style=$this->getViewState('TableBodyStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('TableBodyStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style for tfoot element, if any + * @since 3.1.1 + */ + public function getTableFootStyle() + { + if(($style=$this->getViewState('TableFootStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('TableFootStyle',$style,null); + } + return $style; + } + + /** + * @return string caption for the datagrid + */ + public function getCaption() + { + return $this->getViewState('Caption',''); + } + + /** + * @param string caption for the datagrid + */ + public function setCaption($value) + { + $this->setViewState('Caption',$value,''); + } + + /** + * @return TTableCaptionAlign datagrid caption alignment. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet); + } + + /** + * @param TTableCaptionAlign datagrid caption alignment. Valid values include + */ + public function setCaptionAlign($value) + { + $this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet); + } + + /** + * @return TDataGridItem the header item + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return TDataGridItem the footer item + */ + public function getFooter() + { + return $this->_footer; + } + + /** + * @return TDataGridPager the pager displayed at the top of datagrid. It could be null if paging is disabled. + */ + public function getTopPager() + { + return $this->_topPager; + } + + /** + * @return TDataGridPager the pager displayed at the bottom of datagrid. It could be null if paging is disabled. + */ + public function getBottomPager() + { + return $this->_bottomPager; + } + + /** + * @return TDataGridItem the selected item, null if no item is selected. + */ + public function getSelectedItem() + { + $index=$this->getSelectedItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return integer the zero-based index of the selected item in {@link getItems Items}. + * A value -1 means no item selected. + */ + public function getSelectedItemIndex() + { + return $this->getViewState('SelectedItemIndex',-1); + } + + /** + * Selects an item by its index in {@link getItems Items}. + * Previously selected item will be un-selected. + * If the item to be selected is already in edit mode, it will remain in edit mode. + * If the index is less than 0, any existing selection will be cleared up. + * @param integer the selected item index + */ + public function setSelectedItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getSelectedItemIndex())!==$value) + { + $this->setViewState('SelectedItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + { + $item=$items->itemAt($current); + if($item->getItemType()!==TListItemType::EditItem) + $item->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item); + } + if($value>=0 && $value<$itemCount) + { + $item=$items->itemAt($value); + if($item->getItemType()!==TListItemType::EditItem) + $item->setItemType(TListItemType::SelectedItem); + } + } + } + + /** + * @return TDataGridItem the edit item + */ + public function getEditItem() + { + $index=$this->getEditItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return integer the zero-based index of the edit item in {@link getItems Items}. + * A value -1 means no item is in edit mode. + */ + public function getEditItemIndex() + { + return $this->getViewState('EditItemIndex',-1); + } + + /** + * Edits an item by its index in {@link getItems Items}. + * Previously editting item will change to normal item state. + * If the index is less than 0, any existing edit item will be cleared up. + * @param integer the edit item index + */ + public function setEditItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getEditItemIndex())!==$value) + { + $this->setViewState('EditItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem:TListItemType::Item); + if($value>=0 && $value<$itemCount) + $items->itemAt($value)->setItemType(TListItemType::EditItem); + } + } + + /** + * @return boolean whether sorting is enabled. Defaults to false. + */ + public function getAllowSorting() + { + return $this->getViewState('AllowSorting',false); + } + + /** + * @param boolean whether sorting is enabled + */ + public function setAllowSorting($value) + { + $this->setViewState('AllowSorting',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether datagrid columns should be automatically generated. Defaults to true. + */ + public function getAutoGenerateColumns() + { + return $this->getViewState('AutoGenerateColumns',true); + } + + /** + * @param boolean whether datagrid columns should be automatically generated + */ + public function setAutoGenerateColumns($value) + { + $this->setViewState('AutoGenerateColumns',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the header should be displayed. Defaults to true. + */ + public function getShowHeader() + { + return $this->getViewState('ShowHeader',true); + } + + /** + * @param boolean whether the header should be displayed + */ + public function setShowHeader($value) + { + $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the footer should be displayed. Defaults to false. + */ + public function getShowFooter() + { + return $this->getViewState('ShowFooter',false); + } + + /** + * @param boolean whether the footer should be displayed + */ + public function setShowFooter($value) + { + $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return ITemplate the template applied when no data is bound to the datagrid + */ + public function getEmptyTemplate() + { + return $this->_emptyTemplate; + } + + /** + * @param ITemplate the template applied when no data is bound to the datagrid + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEmptyTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_emptyTemplate=$value; + else + throw new TInvalidDataTypeException('datagrid_template_required','EmptyTemplate'); + } + + /** + * This method overrides parent's implementation to handle + * {@link onItemCommand OnItemCommand} event which is bubbled from + * {@link TDataGridItem} child controls. + * If the event parameter is {@link TDataGridCommandEventParameter} and + * the command name is a recognized one, which includes 'select', 'edit', + * 'delete', 'update', and 'cancel' (case-insensitive), then a + * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}). + * This method should only be used by control developers. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TDataGridCommandEventParameter) + { + $this->onItemCommand($param); + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_SELECT)===0) + { + $this->setSelectedItemIndex($param->getItem()->getItemIndex()); + $this->onSelectedIndexChanged($param); + return true; + } + else if(strcasecmp($command,self::CMD_EDIT)===0) + { + $this->onEditCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_DELETE)===0) + { + $this->onDeleteCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_UPDATE)===0) + { + $this->onUpdateCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_CANCEL)===0) + { + $this->onCancelCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_SORT)===0) + { + $this->onSortCommand(new TDataGridSortCommandEventParameter($sender,$param)); + return true; + } + else if(strcasecmp($command,self::CMD_PAGE)===0) + { + $p=$param->getCommandParameter(); + if(strcasecmp($p,self::CMD_PAGE_NEXT)===0) + $pageIndex=$this->getCurrentPageIndex()+1; + else if(strcasecmp($p,self::CMD_PAGE_PREV)===0) + $pageIndex=$this->getCurrentPageIndex()-1; + else if(strcasecmp($p,self::CMD_PAGE_FIRST)===0) + $pageIndex=0; + else if(strcasecmp($p,self::CMD_PAGE_LAST)===0) + $pageIndex=$this->getPageCount()-1; + else + $pageIndex=TPropertyValue::ensureInteger($p)-1; + $this->onPageIndexChanged(new TDataGridPageChangedEventParameter($sender,$pageIndex)); + return true; + } + } + return false; + } + + /** + * Raises OnCancelCommand event. + * This method is invoked when a button control raises OnCommand event + * with cancel command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onCancelCommand($param) + { + $this->raiseEvent('OnCancelCommand',$this,$param); + } + + /** + * Raises OnDeleteCommand event. + * This method is invoked when a button control raises OnCommand event + * with delete command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onDeleteCommand($param) + { + $this->raiseEvent('OnDeleteCommand',$this,$param); + } + + /** + * Raises OnEditCommand event. + * This method is invoked when a button control raises OnCommand event + * with edit command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onEditCommand($param) + { + $this->raiseEvent('OnEditCommand',$this,$param); + } + + /** + * Raises OnItemCommand event. + * This method is invoked when a button control raises OnCommand event. + * @param TDataGridCommandEventParameter event parameter + */ + public function onItemCommand($param) + { + $this->raiseEvent('OnItemCommand',$this,$param); + } + + /** + * Raises OnSortCommand event. + * This method is invoked when a button control raises OnCommand event + * with sort command name. + * @param TDataGridSortCommandEventParameter event parameter + */ + public function onSortCommand($param) + { + $this->raiseEvent('OnSortCommand',$this,$param); + } + + /** + * Raises OnUpdateCommand event. + * This method is invoked when a button control raises OnCommand event + * with update command name. + * @param TDataGridCommandEventParameter event parameter + */ + public function onUpdateCommand($param) + { + $this->raiseEvent('OnUpdateCommand',$this,$param); + } + + /** + * Raises OnItemCreated event. + * This method is invoked right after a datagrid item is created and before + * added to page hierarchy. + * @param TDataGridItemEventParameter event parameter + */ + public function onItemCreated($param) + { + $this->raiseEvent('OnItemCreated',$this,$param); + } + + /** + * Raises OnPagerCreated event. + * This method is invoked right after a datagrid pager is created and before + * added to page hierarchy. + * @param TDataGridPagerEventParameter event parameter + */ + public function onPagerCreated($param) + { + $this->raiseEvent('OnPagerCreated',$this,$param); + } + + /** + * Raises OnItemDataBound event. + * This method is invoked for each datagrid item after it performs + * databinding. + * @param TDataGridItemEventParameter event parameter + */ + public function onItemDataBound($param) + { + $this->raiseEvent('OnItemDataBound',$this,$param); + } + + /** + * Raises OnPageIndexChanged event. + * This method is invoked when current page is changed. + * @param TDataGridPageChangedEventParameter event parameter + */ + public function onPageIndexChanged($param) + { + $this->raiseEvent('OnPageIndexChanged',$this,$param); + } + + /** + * Saves item count in viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if(!$this->getEnableViewState(true)) + return; + if($this->_items) + $this->setViewState('ItemCount',$this->_items->getCount(),0); + else + $this->clearViewState('ItemCount'); + if($this->_autoColumns) + { + $state=array(); + foreach($this->_autoColumns as $column) + $state[]=$column->saveState(); + $this->setViewState('AutoColumns',$state,array()); + } + else + $this->clearViewState('AutoColumns'); + if($this->_columns) + { + $state=array(); + foreach($this->_columns as $column) + $state[]=$column->saveState(); + $this->setViewState('Columns',$state,array()); + } + else + $this->clearViewState('Columns'); + } + + /** + * Loads item count information from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getEnableViewState(true)) + return; + if(!$this->getIsDataBound()) + { + $state=$this->getViewState('AutoColumns',array()); + if(!empty($state)) + { + $this->_autoColumns=new TDataGridColumnCollection($this); + foreach($state as $st) + { + $column=new $this->AutoGenerateColumnName; + $column->loadState($st); + $this->_autoColumns->add($column); + } + } + else + $this->_autoColumns=null; + $state=$this->getViewState('Columns',array()); + if($this->_columns && $this->_columns->getCount()===count($state)) + { + $i=0; + foreach($this->_columns as $column) + { + $column->loadState($state[$i]); + $i++; + } + } + $this->restoreGridFromViewState(); + } + } + + /** + * Clears up all items in the datagrid. + */ + public function reset() + { + $this->getControls()->clear(); + $this->getItems()->clear(); + $this->_header=null; + $this->_footer=null; + $this->_topPager=null; + $this->_bottomPager=null; + $this->_useEmptyTemplate=false; + } + + /** + * Restores datagrid content from viewstate. + */ + protected function restoreGridFromViewState() + { + $this->reset(); + + $allowPaging=$this->getAllowPaging(); + + $itemCount=$this->getViewState('ItemCount',0); + $dsIndex=$this->getViewState('DataSourceIndex',0); + + $columns=new TList($this->getColumns()); + $columns->mergeWith($this->_autoColumns); + $this->_allColumns=$columns; + + $items=$this->getItems(); + + if($columns->getCount()) + { + foreach($columns as $column) + $column->initialize(); + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + for($index=0;$index<$itemCount;++$index) + { + if($index===0) + { + if($allowPaging) + $this->_topPager=$this->createPager(); + $this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,false,null,$columns); + } + if($index===$editIndex) + $itemType=TListItemType::EditItem; + else if($index===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else if($index % 2) + $itemType=TListItemType::AlternatingItem; + else + $itemType=TListItemType::Item; + $items->add($this->createItemInternal($index,$dsIndex,$itemType,false,null,$columns)); + $dsIndex++; + } + if($index>0) + { + $this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,false,null,$columns); + if($allowPaging) + $this->_bottomPager=$this->createPager(); + } + } + if(!$dsIndex && $this->_emptyTemplate!==null) + { + $this->_useEmptyTemplate=true; + $this->_emptyTemplate->instantiateIn($this); + } + } + + /** + * Performs databinding to populate datagrid items from data source. + * This method is invoked by {@link dataBind()}. + * You may override this function to provide your own way of data population. + * @param Traversable the bound data + */ + protected function performDataBinding($data) + { + $this->reset(); + $keys=$this->getDataKeys(); + $keys->clear(); + $keyField=$this->getDataKeyField(); + + // get all columns + if($this->getAutoGenerateColumns()) + { + $columns=new TList($this->getColumns()); + $autoColumns=$this->createAutoColumns($data); + $columns->mergeWith($autoColumns); + } + else + $columns=$this->getColumns(); + $this->_allColumns=$columns; + + $items=$this->getItems(); + + $index=0; + $allowPaging=$this->getAllowPaging() && ($data instanceof TPagedDataSource); + $dsIndex=$allowPaging?$data->getFirstIndexInPage():0; + $this->setViewState('DataSourceIndex',$dsIndex,0); + if($columns->getCount()) + { + foreach($columns as $column) + $column->initialize(); + + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + foreach($data as $key=>$row) + { + if($keyField!=='') + $keys->add($this->getDataFieldValue($row,$keyField)); + else + $keys->add($key); + if($index===0) + { + if($allowPaging) + $this->_topPager=$this->createPager(); + $this->_header=$this->createItemInternal(-1,-1,TListItemType::Header,true,null,$columns); + } + if($index===$editIndex) + $itemType=TListItemType::EditItem; + else if($index===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else if($index % 2) + $itemType=TListItemType::AlternatingItem; + else + $itemType=TListItemType::Item; + $items->add($this->createItemInternal($index,$dsIndex,$itemType,true,$row,$columns)); + $index++; + $dsIndex++; + } + if($index>0) + { + $this->_footer=$this->createItemInternal(-1,-1,TListItemType::Footer,true,null,$columns); + if($allowPaging) + $this->_bottomPager=$this->createPager(); + } + } + $this->setViewState('ItemCount',$index,0); + if(!$dsIndex && $this->_emptyTemplate!==null) + { + $this->_useEmptyTemplate=true; + $this->_emptyTemplate->instantiateIn($this); + $this->dataBindChildren(); + } + } + + /** + * Merges consecutive cells who have the same text. + * @since 3.1.1 + */ + private function groupCells() + { + if(($columns=$this->_allColumns)===null) + return; + $items=$this->getItems(); + foreach($columns as $id=>$column) + { + if(!$column->getEnableCellGrouping()) + continue; + $prevCell=null; + $prevCellText=null; + foreach($items as $item) + { + $itemType=$item->getItemType(); + $cell=$item->getCells()->itemAt($id); + if(!$cell->getVisible()) + continue; + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem) + { + if(($cellText=$this->getCellText($cell))==='') + { + $prevCell=null; + $prevCellText=null; + continue; + } + if($prevCell===null || $prevCellText!==$cellText) + { + $prevCell=$cell; + $prevCellText=$cellText; + } + else + { + if(($rowSpan=$prevCell->getRowSpan())===0) + $rowSpan=1; + $prevCell->setRowSpan($rowSpan+1); + $cell->setVisible(false); + } + } + } + } + } + + private function getCellText($cell) + { + if(($data=$cell->getText())==='' && $cell->getHasControls()) + { + $controls=$cell->getControls(); + foreach($controls as $control) + { + if($control instanceof IDataRenderer) + return $control->getData(); + } + } + return $data; + } + + /** + * Creates a datagrid item instance based on the item type and index. + * @param integer zero-based item index + * @param TListItemType item type + * @return TDataGridItem created data list item + */ + protected function createItem($itemIndex,$dataSourceIndex,$itemType) + { + return new TDataGridItem($itemIndex,$dataSourceIndex,$itemType); + } + + private function createItemInternal($itemIndex,$dataSourceIndex,$itemType,$dataBind,$dataItem,$columns) + { + $item=$this->createItem($itemIndex,$dataSourceIndex,$itemType); + $this->initializeItem($item,$columns); + $param=new TDataGridItemEventParameter($item); + if($dataBind) + { + $item->setDataItem($dataItem); + $this->onItemCreated($param); + $this->getControls()->add($item); + $item->dataBind(); + $this->onItemDataBound($param); + } + else + { + $this->onItemCreated($param); + $this->getControls()->add($item); + } + return $item; + } + + /** + * Initializes a datagrid item and cells inside it + * @param TDataGrid datagrid item to be initialized + * @param TDataGridColumnCollection datagrid columns to be used to initialize the cells in the item + */ + protected function initializeItem($item,$columns) + { + $cells=$item->getCells(); + $itemType=$item->getItemType(); + $index=0; + foreach($columns as $column) + { + if($itemType===TListItemType::Header) + $cell=new TTableHeaderCell; + else + $cell=new TTableCell; + if(($id=$column->getID())!=='') + $item->registerObject($id,$cell); + $cells->add($cell); + $column->initializeCell($cell,$index,$itemType); + $index++; + } + } + + protected function createPager() + { + $pager=new TDataGridPager($this); + $this->buildPager($pager); + $this->onPagerCreated(new TDataGridPagerEventParameter($pager)); + $this->getControls()->add($pager); + return $pager; + } + + /** + * Builds the pager content based on pager style. + * @param TDataGridPager the container for the pager + */ + protected function buildPager($pager) + { + switch($this->getPagerStyle()->getMode()) + { + case TDataGridPagerMode::NextPrev: + $this->buildNextPrevPager($pager); + break; + case TDataGridPagerMode::Numeric: + $this->buildNumericPager($pager); + break; + } + } + + /** + * Creates a pager button. + * Depending on the button type, a TLinkButton or a TButton may be created. + * If it is enabled (clickable), its command name and parameter will also be set. + * Derived classes may override this method to create additional types of buttons, such as TImageButton. + * @param mixed the container pager instance of TActiveDatagridPager + * @param string button type, either LinkButton or PushButton + * @param boolean whether the button should be enabled + * @param string caption of the button + * @param string CommandName corresponding to the OnCommand event of the button + * @param string CommandParameter corresponding to the OnCommand event of the button + * @return mixed the button instance + */ + protected function createPagerButton($pager,$buttonType,$enabled,$text,$commandName,$commandParameter) + { + if($buttonType===TDataGridPagerButtonType::LinkButton) + { + if($enabled) + $button=new TLinkButton; + else + { + $button=new TLabel; + $button->setText($text); + return $button; + } + } + else + { + $button=new TButton; + if(!$enabled) + $button->setEnabled(false); + } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCommandParameter($commandParameter); + $button->setCausesValidation(false); + return $button; + } + + /** + * Builds a next-prev pager + * @param TDataGridPager the container for the pager + */ + protected function buildNextPrevPager($pager) + { + $style=$this->getPagerStyle(); + $buttonType=$style->getButtonType(); + $controls=$pager->getControls(); + $currentPageIndex=$this->getCurrentPageIndex(); + if($currentPageIndex===0) + { + if(($text=$style->getFirstPageText())!=='') + { + $label=$this->createPagerButton($pager,$buttonType,false,$text,'',''); + $controls->add($label); + $controls->add("\n"); + } + + $label=$this->createPagerButton($pager,$buttonType,false,$style->getPrevPageText(),'',''); + $controls->add($label); + } + else + { + if(($text=$style->getFirstPageText())!=='') + { + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_FIRST); + $controls->add($button); + $controls->add("\n"); + } + + $button=$this->createPagerButton($pager,$buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,self::CMD_PAGE_PREV); + $controls->add($button); + } + $controls->add("\n"); + if($currentPageIndex===$this->getPageCount()-1) + { + $label=$this->createPagerButton($pager,$buttonType,false,$style->getNextPageText(),'',''); + $controls->add($label); + if(($text=$style->getLastPageText())!=='') + { + $controls->add("\n"); + $label=$this->createPagerButton($pager,$buttonType,false,$text,'',''); + $controls->add($label); + } + } + else + { + $button=$this->createPagerButton($pager,$buttonType,true,$style->getNextPageText(),self::CMD_PAGE,self::CMD_PAGE_NEXT); + $controls->add($button); + if(($text=$style->getLastPageText())!=='') + { + $controls->add("\n"); + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_LAST); + $controls->add($button); + } + } + } + + /** + * Builds a numeric pager + * @param TDataGridPager the container for the pager + */ + protected function buildNumericPager($pager) + { + $style=$this->getPagerStyle(); + $buttonType=$style->getButtonType(); + $controls=$pager->getControls(); + $pageCount=$this->getPageCount(); + $pageIndex=$this->getCurrentPageIndex()+1; + $maxButtonCount=$style->getPageButtonCount(); + $buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount; + $startPageIndex=1; + $endPageIndex=$buttonCount; + if($pageIndex>$endPageIndex) + { + $startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1; + if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount) + $endPageIndex=$pageCount; + if($endPageIndex-$startPageIndex+1<$maxButtonCount) + { + if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1) + $startPageIndex=1; + } + } + + if($startPageIndex>1) + { + if(($text=$style->getFirstPageText())!=='') + { + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_FIRST); + $controls->add($button); + $controls->add("\n"); + } + $prevPageIndex=$startPageIndex-1; + $button=$this->createPagerButton($pager,$buttonType,true,$style->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex"); + $controls->add($button); + $controls->add("\n"); + } + + for($i=$startPageIndex;$i<=$endPageIndex;++$i) + { + if($i===$pageIndex) + { + $label=$this->createPagerButton($pager,$buttonType,false,"$i",'',''); + $controls->add($label); + } + else + { + $button=$this->createPagerButton($pager,$buttonType,true,"$i",self::CMD_PAGE,"$i"); + $controls->add($button); + } + if($i<$endPageIndex) + $controls->add("\n"); + } + + if($pageCount>$endPageIndex) + { + $controls->add("\n"); + $nextPageIndex=$endPageIndex+1; + $button=$this->createPagerButton($pager,$buttonType,true,$style->getNextPageText(),self::CMD_PAGE,"$nextPageIndex"); + $controls->add($button); + if(($text=$style->getLastPageText())!=='') + { + $controls->add("\n"); + $button=$this->createPagerButton($pager,$buttonType,true,$text,self::CMD_PAGE,self::CMD_PAGE_LAST); + $controls->add($button); + } + } + } + + /** + * Automatically generates datagrid columns based on datasource schema + * @param Traversable data source bound to the datagrid + * @return TDataGridColumnCollection + */ + protected function createAutoColumns($dataSource) + { + if(!$dataSource) + return null; + $autoColumns=$this->getAutoColumns(); + $autoColumns->clear(); + foreach($dataSource as $row) + { + foreach($row as $key=>$value) + { + $column=new $this->AutoGenerateColumnName; + if(is_string($key)) + { + $column->setHeaderText($key); + $column->setDataField($key); + $column->setSortExpression($key); + $autoColumns->add($column); + } + else + { + $column->setHeaderText(TListItemType::Item); + $column->setDataField($key); + $column->setSortExpression(TListItemType::Item); + $autoColumns->add($column); + } + } + break; + } + return $autoColumns; + } + + /** + * Applies styles to items, header, footer and separators. + * Item styles are applied in a hierarchical way. Style in higher hierarchy + * will inherit from styles in lower hierarchy. + * Starting from the lowest hierarchy, the item styles include + * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle}, + * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}. + * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, + * {@link getEditItemStyle EditItemStyle} will also have red background color + * unless it is set to a different value explicitly. + */ + protected function applyItemStyles() + { + $itemStyle=$this->getViewState('ItemStyle',null); + + $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null); + if($itemStyle!==null) + { + if($alternatingItemStyle===null) + $alternatingItemStyle=$itemStyle; + else + $alternatingItemStyle->mergeWith($itemStyle); + } + + $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); + + $editItemStyle=$this->getViewState('EditItemStyle',null); + if($selectedItemStyle!==null) + { + if($editItemStyle===null) + $editItemStyle=$selectedItemStyle; + else + $editItemStyle->mergeWith($selectedItemStyle); + } + + $headerStyle=$this->getViewState('HeaderStyle',null); + $footerStyle=$this->getViewState('FooterStyle',null); + $pagerStyle=$this->getViewState('PagerStyle',null); + $separatorStyle=$this->getViewState('SeparatorStyle',null); + + foreach($this->getControls() as $index=>$item) + { + if(!($item instanceof TDataGridItem) && !($item instanceof TDataGridPager)) + continue; + $itemType=$item->getItemType(); + switch($itemType) + { + case TListItemType::Header: + if($headerStyle) + $item->getStyle()->mergeWith($headerStyle); + if(!$this->getShowHeader()) + $item->setVisible(false); + break; + case TListItemType::Footer: + if($footerStyle) + $item->getStyle()->mergeWith($footerStyle); + if(!$this->getShowFooter()) + $item->setVisible(false); + break; + case TListItemType::Separator: + if($separatorStyle) + $item->getStyle()->mergeWith($separatorStyle); + break; + case TListItemType::Item: + if($itemStyle) + $item->getStyle()->mergeWith($itemStyle); + break; + case TListItemType::AlternatingItem: + if($alternatingItemStyle) + $item->getStyle()->mergeWith($alternatingItemStyle); + break; + case TListItemType::SelectedItem: + if($selectedItemStyle) + $item->getStyle()->mergeWith($selectedItemStyle); + if($index % 2==1) + { + if($itemStyle) + $item->getStyle()->mergeWith($itemStyle); + } + else + { + if($alternatingItemStyle) + $item->getStyle()->mergeWith($alternatingItemStyle); + } + break; + case TListItemType::EditItem: + if($editItemStyle) + $item->getStyle()->mergeWith($editItemStyle); + if($index % 2==1) + { + if($itemStyle) + $item->getStyle()->mergeWith($itemStyle); + } + else + { + if($alternatingItemStyle) + $item->getStyle()->mergeWith($alternatingItemStyle); + } + break; + case TListItemType::Pager: + if($pagerStyle) + { + $item->getStyle()->mergeWith($pagerStyle); + if($index===0) + { + if($pagerStyle->getPosition()===TDataGridPagerPosition::Bottom || !$pagerStyle->getVisible()) + $item->setVisible(false); + } + else + { + if($pagerStyle->getPosition()===TDataGridPagerPosition::Top || !$pagerStyle->getVisible()) + $item->setVisible(false); + } + } + break; + default: + break; + } + if($this->_columns && $itemType!==TListItemType::Pager) + { + $n=$this->_columns->getCount(); + $cells=$item->getCells(); + for($i=0;$i<$n;++$i) + { + $cell=$cells->itemAt($i); + $column=$this->_columns->itemAt($i); + if(!$column->getVisible()) + $cell->setVisible(false); + else + { + if($itemType===TListItemType::Header) + $style=$column->getHeaderStyle(false); + else if($itemType===TListItemType::Footer) + $style=$column->getFooterStyle(false); + else + $style=$column->getItemStyle(false); + if($style!==null) + $cell->getStyle()->mergeWith($style); + } + } + } + } + } + + /** + * Renders the openning tag for the datagrid control which will render table caption if present. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + parent::renderBeginTag($writer); + if(($caption=$this->getCaption())!=='') + { + if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet) + $writer->addAttribute('align',strtolower($align)); + $writer->renderBeginTag('caption'); + $writer->write($caption); + $writer->renderEndTag(); + } + } + + /** + * Renders the datagrid. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + if($this->getHasControls()) + { + $this->groupCells(); + if($this->_useEmptyTemplate) + { + $control=new TWebControl; + $control->setID($this->getClientID()); + $control->copyBaseAttributes($this); + if($this->getHasStyle()) + $control->getStyle()->copyFrom($this->getStyle()); + $control->renderBeginTag($writer); + $this->renderContents($writer); + $control->renderEndTag($writer); + } + else if($this->getViewState('ItemCount',0)>0) + { + $this->applyItemStyles(); + if($this->_topPager) + { + $this->_topPager->renderControl($writer); + $writer->writeLine(); + } + $this->renderTable($writer); + if($this->_bottomPager) + { + $writer->writeLine(); + $this->_bottomPager->renderControl($writer); + } + } + } + } + + /** + * Renders the tabular data. + * @param THtmlWriter writer + */ + protected function renderTable($writer) + { + $this->renderBeginTag($writer); + if($this->_header && $this->_header->getVisible()) + { + $writer->writeLine(); + if($style=$this->getViewState('TableHeadStyle',null)) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('thead'); + $this->_header->render($writer); + $writer->renderEndTag(); + } + $writer->writeLine(); + if($style=$this->getViewState('TableBodyStyle',null)) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('tbody'); + foreach($this->getItems() as $item) + $item->renderControl($writer); + $writer->renderEndTag(); + + if($this->_footer && $this->_footer->getVisible()) + { + $writer->writeLine(); + if($style=$this->getViewState('TableFootStyle',null)) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('tfoot'); + $this->_footer->render($writer); + $writer->renderEndTag(); + } + + $writer->writeLine(); + $this->renderEndTag($writer); + } +} + +/** + * TDataGridItemEventParameter class + * + * TDataGridItemEventParameter encapsulates the parameter data for + * {@link TDataGrid::onItemCreated OnItemCreated} event of {@link TDataGrid} controls. + * The {@link getItem Item} property indicates the datagrid item related with the event. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridItemEventParameter extends TEventParameter +{ + /** + * The TDataGridItem control responsible for the event. + * @var TDataGridItem + */ + private $_item=null; + + /** + * Constructor. + * @param TDataGridItem datagrid item related with the corresponding event + */ + public function __construct(TDataGridItem $item) + { + $this->_item=$item; + } + + /** + * @return TDataGridItem datagrid item related with the corresponding event + */ + public function getItem() + { + return $this->_item; + } +} + +/** + * TDataGridPagerEventParameter class + * + * TDataGridPagerEventParameter encapsulates the parameter data for + * {@link TDataGrid::onPagerCreated OnPagerCreated} event of {@link TDataGrid} controls. + * The {@link getPager Pager} property indicates the datagrid pager related with the event. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPagerEventParameter extends TEventParameter +{ + /** + * The TDataGridPager control responsible for the event. + * @var TDataGridPager + */ + protected $_pager=null; + + /** + * Constructor. + * @param TDataGridPager datagrid pager related with the corresponding event + */ + public function __construct(TDataGridPager $pager) + { + $this->_pager=$pager; + } + + /** + * @return TDataGridPager datagrid pager related with the corresponding event + */ + public function getPager() + { + return $this->_pager; + } +} + +/** + * TDataGridCommandEventParameter class + * + * TDataGridCommandEventParameter encapsulates the parameter data for + * {@link TDataGrid::onItemCommand ItemCommand} event of {@link TDataGrid} controls. + * + * The {@link getItem Item} property indicates the datagrid item related with the event. + * The {@link getCommandSource CommandSource} refers to the control that originally + * raises the Command event. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridCommandEventParameter extends TCommandEventParameter +{ + /** + * @var TDataGridItem the TDataGridItem control responsible for the event. + */ + private $_item=null; + /** + * @var TControl the control originally raises the Command event. + */ + private $_source=null; + + /** + * Constructor. + * @param TDataGridItem datagrid item responsible for the event + * @param TControl original event sender + * @param TCommandEventParameter original event parameter + */ + public function __construct($item,$source,TCommandEventParameter $param) + { + $this->_item=$item; + $this->_source=$source; + parent::__construct($param->getCommandName(),$param->getCommandParameter()); + } + + /** + * @return TDataGridItem the TDataGridItem control responsible for the event. + */ + public function getItem() + { + return $this->_item; + } + + /** + * @return TControl the control originally raises the Command event. + */ + public function getCommandSource() + { + return $this->_source; + } +} + +/** + * TDataGridSortCommandEventParameter class + * + * TDataGridSortCommandEventParameter encapsulates the parameter data for + * {@link TDataGrid::onSortCommand SortCommand} event of {@link TDataGrid} controls. + * + * The {@link getCommandSource CommandSource} property refers to the control + * that originally raises the OnCommand event, while {@link getSortExpression SortExpression} + * gives the sort expression carried with the sort command. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridSortCommandEventParameter extends TEventParameter +{ + /** + * @var string sort expression + */ + private $_sortExpression=''; + /** + * @var TControl original event sender + */ + private $_source=null; + + /** + * Constructor. + * @param TControl the control originally raises the OnCommand event. + * @param TDataGridCommandEventParameter command event parameter + */ + public function __construct($source,TDataGridCommandEventParameter $param) + { + $this->_source=$source; + $this->_sortExpression=$param->getCommandParameter(); + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } + + /** + * @return string sort expression + */ + public function getSortExpression() + { + return $this->_sortExpression; + } +} + +/** + * TDataGridPageChangedEventParameter class + * + * TDataGridPageChangedEventParameter encapsulates the parameter data for + * {@link TDataGrid::onPageIndexChanged PageIndexChanged} event of {@link TDataGrid} controls. + * + * The {@link getCommandSource CommandSource} property refers to the control + * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex} + * returns the new page index carried with the page command. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPageChangedEventParameter extends TEventParameter +{ + /** + * @var integer new page index + */ + private $_newIndex; + /** + * @var TControl original event sender + */ + private $_source=null; + + /** + * Constructor. + * @param TControl the control originally raises the OnCommand event. + * @param integer new page index + */ + public function __construct($source,$newPageIndex) + { + $this->_source=$source; + $this->_newIndex=$newPageIndex; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } + + /** + * @return integer new page index + */ + public function getNewPageIndex() + { + return $this->_newIndex; + } +} + +/** + * TDataGridItem class + * + * A TDataGridItem control represents an item in the {@link TDataGrid} control, + * such as heading section, footer section, or a data item. + * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> + * and {@link getDataItem DataItem} properties, respectively. The type of the item + * is given by {@link getItemType ItemType} property. Property {@link getDataSourceIndex DataSourceIndex} + * gives the index of the item from the bound data source. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridItem extends TTableRow implements INamingContainer +{ + /** + * @var integer index of the data item in the Items collection of datagrid + */ + private $_itemIndex=''; + /** + * @var integer index of the item from the bound data source + */ + private $_dataSourceIndex=0; + /** + * type of the TDataGridItem + * @var string + */ + private $_itemType=''; + /** + * value of the data item + * @var mixed + */ + private $_data=null; + + /** + * Constructor. + * @param integer zero-based index of the item in the item collection of datagrid + * @param TListItemType item type + */ + public function __construct($itemIndex,$dataSourceIndex,$itemType) + { + $this->_itemIndex=$itemIndex; + $this->_dataSourceIndex=$dataSourceIndex; + $this->setItemType($itemType); + if($itemType===TListItemType::Header) + $this->setTableSection(TTableRowSection::Header); + else if($itemType===TListItemType::Footer) + $this->setTableSection(TTableRowSection::Footer); + } + + /** + * @return TListItemType item type. + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * @return integer zero-based index of the item in the item collection of datagrid + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * @return integer the index of the datagrid item from the bound data source + */ + public function getDataSourceIndex() + { + return $this->_dataSourceIndex; + } + + /** + * @return mixed data associated with the item + * @since 3.1.0 + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + * @since 3.1.0 + */ + public function setData($value) + { + $this->_data=$value; + } + + /** + * This property is deprecated since v3.1.0. + * @return mixed data associated with the item + * @deprecated deprecated since v3.1.0. Use {@link getData} instead. + */ + public function getDataItem() + { + return $this->getData(); + } + + /** + * This property is deprecated since v3.1.0. + * @param mixed data to be associated with the item + * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. + */ + public function setDataItem($value) + { + return $this->setData($value); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + + +/** + * TDataGridPager class. + * + * TDataGridPager represents a datagrid pager. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPager extends TPanel implements INamingContainer +{ + private $_dataGrid; + + /** + * Constructor. + * @param TDataGrid datagrid object + */ + public function __construct($dataGrid) + { + $this->_dataGrid=$dataGrid; + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataGridCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } + + /** + * @return TDataGrid the datagrid owning this pager + */ + public function getDataGrid() + { + return $this->_dataGrid; + } + + /** + * @return string item type. + */ + public function getItemType() + { + return TListItemType::Pager; + } +} + + +/** + * TDataGridItemCollection class. + * + * TDataGridItemCollection represents a collection of data grid items. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridItemCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only TDataGridItem. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridItem. + */ + public function insertAt($index,$item) + { + if($item instanceof TDataGridItem) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('datagriditemcollection_datagriditem_required'); + } +} + +/** + * TDataGridColumnCollection class. + * + * TDataGridColumnCollection represents a collection of data grid columns. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridColumnCollection extends TList +{ + /** + * the control that owns this collection. + * @var TControl + */ + private $_o; + + /** + * Constructor. + * @param TDataGrid the control that owns this collection. + */ + public function __construct(TDataGrid $owner) + { + $this->_o=$owner; + } + + /** + * @return TDataGrid the control that owns this collection. + */ + protected function getOwner() + { + return $this->_o; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only TDataGridColumn. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TDataGridColumn. + */ + public function insertAt($index,$item) + { + if($item instanceof TDataGridColumn) + { + $item->setOwner($this->_o); + parent::insertAt($index,$item); + } + else + throw new TInvalidDataTypeException('datagridcolumncollection_datagridcolumn_required'); + } +} + +/** + * TDataGridPagerMode class. + * TDataGridPagerMode defines the enumerable type for the possible modes that a datagrid pager can take. + * + * The following enumerable values are defined: + * - NextPrev: pager buttons are displayed as next and previous pages + * - Numeric: pager buttons are displayed as numeric page numbers + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDataGridPagerMode extends TEnumerable +{ + const NextPrev='NextPrev'; + const Numeric='Numeric'; +} + + +/** + * TDataGridPagerButtonType class. + * TDataGridPagerButtonType defines the enumerable type for the possible types of datagrid pager buttons. + * + * The following enumerable values are defined: + * - LinkButton: link buttons + * - PushButton: form submit buttons + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDataGridPagerButtonType extends TEnumerable +{ + const LinkButton='LinkButton'; + const PushButton='PushButton'; +} + + +/** + * TDataGridPagerPosition class. + * TDataGridPagerPosition defines the enumerable type for the possible positions that a datagrid pager can be located at. + * + * The following enumerable values are defined: + * - Bottom: pager appears only at the bottom of the data grid. + * - Top: pager appears only at the top of the data grid. + * - TopAndBottom: pager appears on both top and bottom of the data grid. + * + * @author Qiang Xue + * @version $Id: TDataGrid.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDataGridPagerPosition extends TEnumerable +{ + const Bottom='Bottom'; + const Top='Top'; + const TopAndBottom='TopAndBottom'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataGridColumn.php b/gui/baculum/framework/Web/UI/WebControls/TDataGridColumn.php new file mode 100644 index 0000000000..ad3be81feb --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataGridColumn.php @@ -0,0 +1,567 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataGridColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Util.TDataFieldAccessor'); +Prado::using('System.Web.UI.WebControls.TDataGrid'); + +/** + * TDataGridColumn class + * + * TDataGridColumn serves as the base class for the different column types of + * the {@link TDataGrid} control. + * TDataGridColumn defines the properties and methods that are common among + * all datagrid column types. In particular, it initializes header and footer + * cells according to {@link setHeaderText HeaderText} and {@link getHeaderStyle HeaderStyle} + * {@link setFooterText FooterText} and {@link getFooterStyle FooterStyle} properties. + * If {@link setHeaderImageUrl HeaderImageUrl} is specified, the image + * will be displayed instead in the header cell. + * The {@link getItemStyle ItemStyle} is applied to cells that belong to + * non-header and -footer datagrid items. + * + * When the datagrid enables sorting, if the {@link setSortExpression SortExpression} + * is not empty, the header cell will display a button (linkbutton or imagebutton) + * that will bubble the sort command event to the datagrid. + * + * Since v3.1.0, TDataGridColumn has introduced two new properties {@link setHeaderRenderer HeaderRenderer} + * and {@link setFooterRenderer FooterRenderer} which can be used to specify + * the layout of header and footer column cells. + * A renderer refers to a control class that is to be instantiated as a control. + * For more details, see {@link TRepeater} and {@link TDataList}. + * + * Since v3.1.1, TDataGridColumn has introduced {@link setEnableCellGrouping EnableCellGrouping}. + * If a column has this property set true, consecutive cells having the same content in this + * column will be grouped into one cell. + * Note, there are some limitations to cell grouping. We determine the cell content according to + * the cell's {@link TTableCell::getText Text} property. If the text is empty and the cell has + * some child controls, we will pick up the first control who implements {@link IDataRenderer} + * and obtain its {@link IDataRenderer::getData Data} property. + * + * The following datagrid column types are provided by the framework currently, + * - {@link TBoundColumn}, associated with a specific field in datasource and displays the corresponding data. + * - {@link TEditCommandColumn}, displaying edit/update/cancel command buttons + * - {@link TDropDownListColumn}, displaying a dropdown list when the item is in edit state + * - {@link TButtonColumn}, displaying generic command buttons that may be bound to specific field in datasource. + * - {@link THyperLinkColumn}, displaying a hyperlink that may be bound to specific field in datasource. + * - {@link TCheckBoxColumn}, displaying a checkbox that may be bound to specific field in datasource. + * - {@link TTemplateColumn}, displaying content based on templates. + * + * To create your own column class, simply override {@link initializeCell()} method, + * which is the major logic for managing the data and presentation of cells in the column. + * + * @author Qiang Xue + * @version $Id: TDataGridColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataGridColumn extends TApplicationComponent +{ + private $_id=''; + private $_owner=null; + private $_viewState=array(); + + /** + * @return string the ID of the column. + */ + public function getID() + { + return $this->_id; + } + + /** + * Sets the ID of the column. + * By explicitly specifying the column ID, one can access the column + * by $templateControl->ColumnID. + * @param string the ID of the column. + * @throws TInvalidDataValueException if the ID is of bad format + */ + public function setID($value) + { + if(!preg_match(TControl::ID_FORMAT,$value)) + throw new TInvalidDataValueException('datagridcolumn_id_invalid',get_class($this),$value); + $this->_id=$value; + } + + /** + * @return string the text to be displayed in the header of this column + */ + public function getHeaderText() + { + return $this->getViewState('HeaderText',''); + } + + /** + * @param string text to be displayed in the header of this column + */ + public function setHeaderText($value) + { + $this->setViewState('HeaderText',$value,''); + } + + /** + * @return string the url of the image to be displayed in header + */ + public function getHeaderImageUrl() + { + return $this->getViewState('HeaderImageUrl',''); + } + + /** + * @param string the url of the image to be displayed in header + */ + public function setHeaderImageUrl($value) + { + $this->setViewState('HeaderImageUrl',$value,''); + } + + /** + * @return string the class name for the column header cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getHeaderRenderer() + { + return $this->getViewState('HeaderRenderer',''); + } + + /** + * Sets the column header cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the column header cell. + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the {@link getFooterText FooterText}. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setHeaderRenderer($value) + { + $this->setViewState('HeaderRenderer',$value,''); + } + + /** + * @param boolean whether to create a style if previously not existing + * @return TTableItemStyle the style for header + */ + public function getHeaderStyle($createStyle=true) + { + if(($style=$this->getViewState('HeaderStyle',null))===null && $createStyle) + { + $style=new TTableItemStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return string the text to be displayed in the footer of this column + */ + public function getFooterText() + { + return $this->getViewState('FooterText',''); + } + + /** + * @param string text to be displayed in the footer of this column + */ + public function setFooterText($value) + { + $this->setViewState('FooterText',$value,''); + } + + /** + * @return string the class name for the column footer cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getFooterRenderer() + { + return $this->getViewState('FooterRenderer',''); + } + + /** + * Sets the column footer cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the column footer cell. + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the {@link getFooterText FooterText}. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setFooterRenderer($value) + { + $this->setViewState('FooterRenderer',$value,''); + } + + /** + * @param boolean whether to create a style if previously not existing + * @return TTableItemStyle the style for footer + */ + public function getFooterStyle($createStyle=true) + { + if(($style=$this->getViewState('FooterStyle',null))===null && $createStyle) + { + $style=new TTableItemStyle; + $this->setViewState('FooterStyle',$style,null); + } + return $style; + } + + /** + * @param boolean whether to create a style if previously not existing + * @return TTableItemStyle the style for item + */ + public function getItemStyle($createStyle=true) + { + if(($style=$this->getViewState('ItemStyle',null))===null && $createStyle) + { + $style=new TTableItemStyle; + $this->setViewState('ItemStyle',$style,null); + } + return $style; + } + + /** + * @return string the name of the field or expression for sorting + */ + public function getSortExpression() + { + return $this->getViewState('SortExpression',''); + } + + /** + * @param string the name of the field or expression for sorting + */ + public function setSortExpression($value) + { + $this->setViewState('SortExpression',$value,''); + } + + /** + * @return boolean whether cells having the same content should be grouped together. Defaults to false. + * @since 3.1.1 + */ + public function getEnableCellGrouping() + { + return $this->getViewState('EnableCellGrouping',false); + } + + /** + * @param boolean whether cells having the same content should be grouped together. + * @since 3.1.1 + */ + public function setEnableCellGrouping($value) + { + $this->setViewState('EnableCellGrouping',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether the column is visible. Defaults to true. + */ + public function getVisible($checkParents=true) + { + return $this->getViewState('Visible',true); + } + + /** + * @param boolean whether the column is visible + */ + public function setVisible($value) + { + $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Returns a viewstate value. + * + * @param string the name of the viewstate value to be returned + * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned + * @return mixed the viewstate value corresponding to $key + */ + protected function getViewState($key,$defaultValue=null) + { + return isset($this->_viewState[$key])?$this->_viewState[$key]:$defaultValue; + } + + /** + * Sets a viewstate value. + * + * Make sure that the viewstate value must be serializable and unserializable. + * @param string the name of the viewstate value + * @param mixed the viewstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. + */ + protected function setViewState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_viewState[$key]); + else + $this->_viewState[$key]=$value; + } + + /** + * Loads persistent state values. + * @param mixed state values + */ + public function loadState($state) + { + $this->_viewState=$state; + } + + /** + * Saves persistent state values. + * @return mixed values to be saved + */ + public function saveState() + { + return $this->_viewState; + } + + /** + * @return TDataGrid datagrid that owns this column + */ + public function getOwner() + { + return $this->_owner; + } + + /** + * @param TDataGrid datagrid object that owns this column + */ + public function setOwner(TDataGrid $value) + { + $this->_owner=$value; + } + + /** + * Initializes the column. + * This method is invoked by {@link TDataGrid} when the column + * is about to be used to initialize datagrid items. + * Derived classes may override this method to do additional initialization. + */ + public function initialize() + { + } + + /** + * Fetches the value of the data at the specified field. + * If the data is an array, the field is used as an array key. + * If the data is an of {@link TMap}, {@link TList} or their derived class, + * the field is used as a key value. + * If the data is a component, the field is used as the name of a property. + * @param mixed data containing the field of value + * @param string the data field + * @return mixed data value at the specified field + * @throws TInvalidDataValueException if the data or the field is invalid. + */ + protected function getDataFieldValue($data,$field) + { + return TDataFieldAccessor::getDataFieldValue($data,$field); + } + + + /** + * Initializes the specified cell to its initial values. + * The default implementation sets the content of header and footer cells. + * If sorting is enabled by the grid and sort expression is specified in the column, + * the header cell will show a link/image button. Otherwise, the header/footer cell + * will only show static text/image. + * This method can be overriden to provide customized intialization to column cells. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Header) + $this->initializeHeaderCell($cell,$columnIndex); + else if($itemType===TListItemType::Footer) + $this->initializeFooterCell($cell,$columnIndex); + } + + /** + * Returns a value indicating whether this column allows sorting. + * The column allows sorting only when {@link getSortExpression SortExpression} + * is not empty and the datagrid allows sorting. + * @return boolean whether this column allows sorting + */ + public function getAllowSorting() + { + return $this->getSortExpression()!=='' && (!$this->_owner || $this->_owner->getAllowSorting()); + } + + /** + * Initializes the header cell. + * + * This method attempts to use {@link getHeaderRenderer HeaderRenderer} to + * instantiate the header cell. If that is not available, it will populate + * the cell with an image or a text string, depending on {@link getHeaderImageUrl HeaderImageUrl} + * and {@link getHeaderText HeaderText} property values. + * + * If the column allows sorting, image or text will be created as + * a button which issues Sort command upon user click. + * + * @param TTableCell the cell to be initialized + * @param integer the index to the Columns property that the cell resides in. + */ + protected function initializeHeaderCell($cell,$columnIndex) + { + $text=$this->getHeaderText(); + + if(($classPath=$this->getHeaderRenderer())!=='') + { + $control=Prado::createComponent($classPath); + $cell->getControls()->add($control); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + } + else if($this->getAllowSorting()) + { + $sortExpression=$this->getSortExpression(); + if(($url=$this->getHeaderImageUrl())!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TImageButton'); + $button->setImageUrl($url); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + if($text!=='') + $button->setAlternateText($text); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else if($text!=='') + { + $button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton'); + $button->setText($text); + $button->setCommandName(TDataGrid::CMD_SORT); + $button->setCommandParameter($sortExpression); + $button->setCausesValidation(false); + $cell->getControls()->add($button); + } + else + $cell->setText(' '); + } + else + { + if(($url=$this->getHeaderImageUrl())!=='') + { + $image=Prado::createComponent('System.Web.UI.WebControls.TImage'); + $image->setImageUrl($url); + if($text!=='') + $image->setAlternateText($text); + $cell->getControls()->add($image); + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + } + + /** + * Initializes the footer cell. + * + * This method attempts to use {@link getFooterRenderer FooterRenderer} to + * instantiate the footer cell. If that is not available, it will populate + * the cell with a text string specified by {@link getFooterImageUrl FooterImageUrl} + * + * @param TTableCell the cell to be initialized + * @param integer the index to the Columns property that the cell resides in. + */ + protected function initializeFooterCell($cell,$columnIndex) + { + $text=$this->getFooterText(); + if(($classPath=$this->getFooterRenderer())!=='') + { + $control=Prado::createComponent($classPath); + $cell->getControls()->add($control); + if($control instanceof IDataRenderer) + { + if($control instanceof IItemDataRenderer) + { + $item=$cell->getParent(); + $control->setItemIndex($item->getItemIndex()); + $control->setItemType($item->getItemType()); + } + $control->setData($text); + } + } + else if($text!=='') + $cell->setText($text); + else + $cell->setText(' '); + } + + /** + * Formats the text value according to a format string. + * If the format string is empty, the original value is converted into + * a string and returned. + * If the format string starts with '#', the string is treated as a PHP expression + * within which the token '{0}' is translated with the data value to be formated. + * Otherwise, the format string and the data value are passed + * as the first and second parameters in {@link sprintf}. + * @param string format string + * @param mixed the data to be formatted + * @return string the formatted result + */ + protected function formatDataValue($formatString,$value) + { + if($formatString==='') + return TPropertyValue::ensureString($value); + else if($formatString[0]==='#') + { + $expression=strtr(substr($formatString,1),array('{0}'=>'$value')); + try + { + if(eval("\$result=$expression;")===false) + throw new Exception(''); + return $result; + } + catch(Exception $e) + { + throw new TInvalidDataValueException('datagridcolumn_expression_invalid',get_class($this),$expression,$e->getMessage()); + } + } + else + return sprintf($formatString,$value); + } +} + + +/** + * TButtonColumnType class. + * TButtonColumnType defines the enumerable type for the possible types of buttons + * that can be used in a {@link TButtonColumn}. + * + * The following enumerable values are defined: + * - LinkButton: link buttons + * - PushButton: form buttons + * - ImageButton: image buttons + * + * @author Qiang Xue + * @version $Id: TDataGridColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TButtonColumnType extends TEnumerable +{ + const LinkButton='LinkButton'; + const PushButton='PushButton'; + const ImageButton='ImageButton'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataGridItemRenderer.php b/gui/baculum/framework/Web/UI/WebControls/TDataGridItemRenderer.php new file mode 100644 index 0000000000..6e37a73954 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataGridItemRenderer.php @@ -0,0 +1,30 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataGridItemRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataGrid'); +Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); + +/** + * TDataGridItemRenderer class + * + * TDataGridItemRenderer can be used as a convenient base class to + * define an item renderer class specific for {@link TDataGrid}. + * + * @author Qiang Xue + * @version $Id: TDataGridItemRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +class TDataGridItemRenderer extends TItemDataRenderer +{ +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataGridPagerStyle.php b/gui/baculum/framework/Web/UI/WebControls/TDataGridPagerStyle.php new file mode 100644 index 0000000000..31c0380b6d --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataGridPagerStyle.php @@ -0,0 +1,255 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataGridPagerStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataGrid'); + +/** + * TDataGridPagerStyle class. + * + * TDataGridPagerStyle specifies the styles available for a datagrid pager. + * + * @author Qiang Xue + * @version $Id: TDataGridPagerStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataGridPagerStyle extends TPanelStyle +{ + private $_mode=null; + private $_nextText=null; + private $_prevText=null; + private $_firstText=null; + private $_lastText=null; + private $_buttonCount=null; + private $_position=null; + private $_visible=null; + private $_buttonType=null; + + /** + * @return TDataGridPagerMode pager mode. Defaults to TDataGridPagerMode::NextPrev. + */ + public function getMode() + { + return $this->_mode===null?TDataGridPagerMode::NextPrev : $this->_mode; + } + + /** + * @param TDataGridPagerMode pager mode. + */ + public function setMode($value) + { + $this->_mode=TPropertyValue::ensureEnum($value,'TDataGridPagerMode'); + } + + /** + * @return TDataGridPagerButtonType the type of command button. Defaults to TDataGridPagerButtonType::LinkButton. + */ + public function getButtonType() + { + return $this->_buttonType===null?TDataGridPagerButtonType::LinkButton:$this->_buttonType; + } + + /** + * @param TDataGridPagerButtonType the type of command button + */ + public function setButtonType($value) + { + $this->_buttonType=TPropertyValue::ensureEnum($value,'TDataGridPagerButtonType'); + } + + /** + * @return string text for the next page button. Defaults to '>'. + */ + public function getNextPageText() + { + return $this->_nextText===null?'>':$this->_nextText; + } + + /** + * @param string text for the next page button. + */ + public function setNextPageText($value) + { + $this->_nextText=$value; + } + + /** + * @return string text for the previous page button. Defaults to '<'. + */ + public function getPrevPageText() + { + return $this->_prevText===null?'<':$this->_prevText; + } + + /** + * @param string text for the previous page button. + */ + public function setPrevPageText($value) + { + $this->_prevText=$value; + } + + /** + * @return string text for the first page button. Defaults to '<<'. + */ + public function getFirstPageText() + { + return $this->_firstText===null?'<<':$this->_firstText; + } + + /** + * @param string text for the first page button. + */ + public function setFirstPageText($value) + { + $this->_firstText=$value; + } + + /** + * @return string text for the last page button. Defaults to '>>'. + */ + public function getLastPageText() + { + return $this->_lastText===null?'>>':$this->_lastText; + } + + /** + * @param string text for the last page button. + */ + public function setLastPageText($value) + { + $this->_lastText=$value; + } + + /** + * @return integer maximum number of pager buttons to be displayed. Defaults to 10. + */ + public function getPageButtonCount() + { + return $this->_buttonCount===null?10:$this->_buttonCount; + } + + /** + * @param integer maximum number of pager buttons to be displayed + * @throws TInvalidDataValueException if the value is less than 1. + */ + public function setPageButtonCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<1) + throw new TInvalidDataValueException('datagridpagerstyle_pagebuttoncount_invalid'); + $this->_buttonCount=$value; + } + + /** + * @return TDataGridPagerPosition where the pager is to be displayed. Defaults to TDataGridPagerPosition::Bottom. + */ + public function getPosition() + { + return $this->_position===null?TDataGridPagerPosition::Bottom:$this->_position; + } + + /** + * @param TDataGridPagerPosition where the pager is to be displayed. + */ + public function setPosition($value) + { + $this->_position=TPropertyValue::ensureEnum($value,'TDataGridPagerPosition'); + } + + /** + * @return boolean whether the pager is visible. Defaults to true. + */ + public function getVisible() + { + return $this->_visible===null?true:$this->_visible; + } + + /** + * @param boolean whether the pager is visible. + */ + public function setVisible($value) + { + $this->_visible=TPropertyValue::ensureBoolean($value); + } + + /** + * Resets the style to the original empty state. + */ + public function reset() + { + parent::reset(); + $this->_visible=null; + $this->_position=null; + $this->_buttonCount=null; + $this->_prevText=null; + $this->_nextText=null; + $this->_mode=null; + $this->_buttonType=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TDataGridPagerStyle) + { + if($style->_visible!==null) + $this->_visible=$style->_visible; + if($style->_position!==null) + $this->_position=$style->_position; + if($style->_buttonCount!==null) + $this->_buttonCount=$style->_buttonCount; + if($style->_prevText!==null) + $this->_prevText=$style->_prevText; + if($style->_nextText!==null) + $this->_nextText=$style->_nextText; + if($style->_mode!==null) + $this->_mode=$style->_mode; + if($style->_buttonType!==null) + $this->_buttonType=$style->_buttonType; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TDataGridPagerStyle) + { + if($this->_visible===null) + $this->_visible=$style->_visible; + if($this->_position===null) + $this->_position=$style->_position; + if($this->_buttonCount===null) + $this->_buttonCount=$style->_buttonCount; + if($this->_prevText===null) + $this->_prevText=$style->_prevText; + if($this->_nextText===null) + $this->_nextText=$style->_nextText; + if($this->_mode===null) + $this->_mode=$style->_mode; + if($this->_buttonType===null) + $this->_buttonType=$style->_buttonType; + } + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataList.php b/gui/baculum/framework/Web/UI/WebControls/TDataList.php new file mode 100644 index 0000000000..6b3c4a739f --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataList.php @@ -0,0 +1,1766 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TBaseDataList class + */ +Prado::using('System.Web.UI.WebControls.TBaseDataList'); +/** + * Includes TRepeatInfo class + */ +Prado::using('System.Web.UI.WebControls.TRepeatInfo'); + +/** + * TDataList class + * + * TDataList represents a data bound and updatable list control. + * + * Like {@link TRepeater}, TDataList displays its content repeatedly based on + * the data fetched from {@link setDataSource DataSource}. + * The repeated contents in TDataList are called items, which are controls and + * can be accessed through {@link getItems Items}. When {@link dataBind()} is + * invoked, TDataList creates an item for each row of data and binds the data + * row to the item. Optionally, a TDataList can have a header, a footer and/or + * separators between items. + * + * TDataList differs from {@link TRepeater} in that it supports tiling the items + * in different manners and it maintains status of items to handle data update. + * + * The layout of the repeated contents are specified by inline templates. + * TDataList items, header, footer, etc. are being instantiated with the corresponding + * templates when data is being bound to the repeater. + * + * Since v3.1.0, the layout can also be by renderers. A renderer is a control class + * that can be instantiated as datalist items, header, etc. A renderer can thus be viewed + * as an external template (in fact, it can also be non-templated controls). + * + * A renderer can be any control class. + * - If the class implements {@link IDataRenderer}, the Data + * property will be set as the data row during databinding. Many PRADO controls + * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc. + * - If the class implements {@link IItemDataRenderer}, the ItemIndex property will be set + * as the zero-based index of the item in the datalist item collection, and + * the ItemType property as the item's type (such as TListItemType::Item). + * {@link TDataListItemRenderer} may be used as the convenient base class which + * already implements {@link IDataItemRenderer}. + * + * The following properties are used to specify different types of template and renderer + * for a datalist: + * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}: + * for each repeated row of data + * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}: + * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer} + * will be used instead. + * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}: + * for the datalist header. + * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}: + * for the datalist footer. + * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}: + * for content to be displayed between items. + * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}: + * used when data bound to the datalist is empty. + * - {@link setEditItemTemplate EditItemTemplate}, {@link setEditItemRenderer EditItemRenderer}: + * for the row being editted. + * - {@link setSelectedItemTemplate SelectedItemTemplate}, {@link setSelectedItemRenderer SelectedItemRenderer}: + * for the row being selected. + * + * If a content type is defined with both a template and a renderer, the latter takes precedence. + * + * When {@link dataBind()} is being called, TDataList undergoes the following lifecycles for each row of data: + * - create item based on templates or renderers + * - set the row of data to the item + * - raise {@link onItemCreated OnItemCreated}: + * - add the item as a child control + * - call dataBind() of the item + * - raise {@link onItemDataBound OnItemDataBound}: + * + * TDataList raises an {@link onItemCommand OnItemCommand} whenever a button control + * within some datalist item raises a OnCommand event. Therefore, + * you can handle all sorts of OnCommand event in a central place by + * writing an event handler for {@link onItemCommand OnItemCommand}. + * + * An additional event is raised if the OnCommand event has one of the following + * command names: + * - edit: user wants to edit an item. OnEditCommand event will be raised. + * - update: user wants to save the change to an item. OnUpdateCommand event will be raised. + * - select: user selects an item. OnSelectedIndexChanged event will be raised. + * - delete: user deletes an item. OnDeleteCommand event will be raised. + * - cancel: user cancels previously editting action. OnCancelCommand event will be raised. + * + * TDataList provides a few properties to support tiling the items. + * The number of columns used to display the data items is specified via + * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection} + * governs the order of the items being rendered. + * The layout of the data items in the list is specified via {@link setRepeatLayout RepeatLayout}, + * which can take one of the following values: + * - Table (default): items are organized using HTML table and cells. + * When using this layout, one can set {@link setCellPadding CellPadding} and + * {@link setCellSpacing CellSpacing} to adjust the cellpadding and cellpadding + * of the table, and {@link setCaption Caption} and {@link setCaptionAlign CaptionAlign} + * to add a table caption with the specified alignment. + * - Flow: items are organized using HTML spans and breaks. + * - Raw: TDataList does not generate any HTML tags to do the tiling. + * + * Items in TDataList can be in one of the three status: normal browsing, + * being editted and being selected. To change the status of a particular + * item, set {@link setSelectedItemIndex SelectedItemIndex} or + * {@link setEditItemIndex EditItemIndex}. The former will change + * the indicated item to selected mode, which will cause the item to + * use {@link setSelectedItemTemplate SelectedItemTemplate} or + * {@link setSelectedItemRenderer SelectedItemRenderer} for presentation. + * The latter will change the indicated item to edit mode and to use corresponding + * template or renderer. + * Note, if an item is in edit mode, then selecting this item will have no effect. + * + * Different styles may be applied to items in different status. The style + * application is performed in a hierarchical way: Style in higher hierarchy + * will inherit from styles in lower hierarchy. + * Starting from the lowest hierarchy, the item styles include + * - item's own style + * - {@link getItemStyle ItemStyle} + * - {@link getAlternatingItemStyle AlternatingItemStyle} + * - {@link getSelectedItemStyle SelectedItemStyle} + * - {@link getEditItemStyle EditItemStyle}. + * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, + * {@link getEditItemStyle EditItemStyle} will also have red background color + * unless it is set to a different value explicitly. + * + * When a page containing a datalist is post back, the datalist will restore automatically + * all its contents, including items, header, footer and separators. + * However, the data row associated with each item will not be recovered and become null. + * To access the data, use one of the following ways: + * - Use {@link getDataKeys DataKeys} to obtain the data key associated with + * the specified datalist item and use the key to fetch the corresponding data + * from some persistent storage such as DB. + * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback. + * Be aware though, if the size of your dataset is big, your page size will become big. Some + * complex data may also have serializing problem if saved in viewstate. + * + * @author Qiang Xue + * @version $Id: TDataList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataList extends TBaseDataList implements INamingContainer, IRepeatInfoUser +{ + /** + * Command name that TDataList understands. They are case-insensitive. + */ + const CMD_SELECT='Select'; + const CMD_EDIT='Edit'; + const CMD_UPDATE='Update'; + const CMD_DELETE='Delete'; + const CMD_CANCEL='Cancel'; + + /** + * @var TDataListItemCollection item list + */ + private $_items=null; + /** + * @var Itemplate various item templates + */ + private $_itemTemplate=null; + private $_emptyTemplate=null; + private $_alternatingItemTemplate=null; + private $_selectedItemTemplate=null; + private $_editItemTemplate=null; + private $_headerTemplate=null; + private $_footerTemplate=null; + private $_separatorTemplate=null; + /** + * @var TControl header item + */ + private $_header=null; + /** + * @var TControl footer item + */ + private $_footer=null; + + /** + * @return TDataListItemCollection item list + */ + public function getItems() + { + if(!$this->_items) + $this->_items=new TDataListItemCollection; + return $this->_items; + } + + /** + * @return integer number of items + */ + public function getItemCount() + { + return $this->_items?$this->_items->getCount():0; + } + + /** + * @return string the class name for datalist items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getItemRenderer() + { + return $this->getViewState('ItemRenderer',''); + } + + /** + * Sets the item renderer class. + * + * If not empty, the class will be used to instantiate as datalist items. + * This property takes precedence over {@link getItemTemplate ItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setItemTemplate + * @since 3.1.0 + */ + public function setItemRenderer($value) + { + $this->setViewState('ItemRenderer',$value,''); + } + + /** + * @return string the class name for alternative datalist items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getAlternatingItemRenderer() + { + return $this->getViewState('AlternatingItemRenderer',''); + } + + /** + * Sets the alternative item renderer class. + * + * If not empty, the class will be used to instantiate as alternative datalist items. + * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setAlternatingItemTemplate + * @since 3.1.0 + */ + public function setAlternatingItemRenderer($value) + { + $this->setViewState('AlternatingItemRenderer',$value,''); + } + + /** + * @return string the class name for the datalist item being editted. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEditItemRenderer() + { + return $this->getViewState('EditItemRenderer',''); + } + + /** + * Sets the renderer class for the datalist item being editted. + * + * If not empty, the class will be used to instantiate as the datalist item. + * This property takes precedence over {@link getEditItemTemplate EditItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setEditItemTemplate + * @since 3.1.0 + */ + public function setEditItemRenderer($value) + { + $this->setViewState('EditItemRenderer',$value,''); + } + + /** + * @return string the class name for the datalist item being selected. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getSelectedItemRenderer() + { + return $this->getViewState('SelectedItemRenderer',''); + } + + /** + * Sets the renderer class for the datalist item being selected. + * + * If not empty, the class will be used to instantiate as the datalist item. + * This property takes precedence over {@link getSelectedItemTemplate SelectedItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setSelectedItemTemplate + * @since 3.1.0 + */ + public function setSelectedItemRenderer($value) + { + $this->setViewState('SelectedItemRenderer',$value,''); + } + + /** + * @return string the class name for datalist item separators. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getSeparatorRenderer() + { + return $this->getViewState('SeparatorRenderer',''); + } + + /** + * Sets the datalist item separator renderer class. + * + * If not empty, the class will be used to instantiate as datalist item separators. + * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setSeparatorTemplate + * @since 3.1.0 + */ + public function setSeparatorRenderer($value) + { + $this->setViewState('SeparatorRenderer',$value,''); + } + + /** + * @return string the class name for datalist header item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getHeaderRenderer() + { + return $this->getViewState('HeaderRenderer',''); + } + + /** + * Sets the datalist header renderer class. + * + * If not empty, the class will be used to instantiate as datalist header item. + * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setHeaderTemplate + * @since 3.1.0 + */ + public function setHeaderRenderer($value) + { + $this->setViewState('HeaderRenderer',$value,''); + } + + /** + * @return string the class name for datalist footer item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getFooterRenderer() + { + return $this->getViewState('FooterRenderer',''); + } + + /** + * Sets the datalist footer renderer class. + * + * If not empty, the class will be used to instantiate as datalist footer item. + * This property takes precedence over {@link getFooterTemplate FooterTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setFooterTemplate + * @since 3.1.0 + */ + public function setFooterRenderer($value) + { + $this->setViewState('FooterRenderer',$value,''); + } + + /** + * @return string the class name for empty datalist item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEmptyRenderer() + { + return $this->getViewState('EmptyRenderer',''); + } + + /** + * Sets the datalist empty renderer class. + * + * The empty renderer is created as the child of the datalist + * if data bound to the datalist is empty. + * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setEmptyTemplate + * @since 3.1.0 + */ + public function setEmptyRenderer($value) + { + $this->setViewState('EmptyRenderer',$value,''); + } + + /** + * @return ITemplate the template for item + */ + public function getItemTemplate() + { + return $this->_itemTemplate; + } + + /** + * @param ITemplate the template for item + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_itemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','ItemTemplate'); + } + + /** + * @return TTableItemStyle the style for item + */ + public function getItemStyle() + { + if(($style=$this->getViewState('ItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('ItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the template for each alternating item + */ + public function getAlternatingItemTemplate() + { + return $this->_alternatingItemTemplate; + } + + /** + * @param ITemplate the template for each alternating item + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setAlternatingItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_alternatingItemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','AlternatingItemType'); + } + + /** + * @return TTableItemStyle the style for each alternating item + */ + public function getAlternatingItemStyle() + { + if(($style=$this->getViewState('AlternatingItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('AlternatingItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the selected item template + */ + public function getSelectedItemTemplate() + { + return $this->_selectedItemTemplate; + } + + /** + * @param ITemplate the selected item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setSelectedItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_selectedItemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','SelectedItemTemplate'); + } + + /** + * @return TTableItemStyle the style for selected item + */ + public function getSelectedItemStyle() + { + if(($style=$this->getViewState('SelectedItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('SelectedItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the edit item template + */ + public function getEditItemTemplate() + { + return $this->_editItemTemplate; + } + + /** + * @param ITemplate the edit item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEditItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_editItemTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','EditItemTemplate'); + } + + /** + * @return TTableItemStyle the style for edit item + */ + public function getEditItemStyle() + { + if(($style=$this->getViewState('EditItemStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('EditItemStyle',$style,null); + } + return $style; + } + + /** + * @return ITemplate the header template + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate the header template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setHeaderTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_headerTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','HeaderTemplate'); + } + + /** + * @return TTableItemStyle the style for header + */ + public function getHeaderStyle() + { + if(($style=$this->getViewState('HeaderStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return TControl the header item + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return ITemplate the footer template + */ + public function getFooterTemplate() + { + return $this->_footerTemplate; + } + + /** + * @param ITemplate the footer template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setFooterTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_footerTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','FooterTemplate'); + } + + /** + * @return TTableItemStyle the style for footer + */ + public function getFooterStyle() + { + if(($style=$this->getViewState('FooterStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('FooterStyle',$style,null); + } + return $style; + } + + /** + * @return TControl the footer item + */ + public function getFooter() + { + return $this->_footer; + } + + /** + * @return ITemplate the template applied when no data is bound to the datalist + */ + public function getEmptyTemplate() + { + return $this->_emptyTemplate; + } + + /** + * @param ITemplate the template applied when no data is bound to the datalist + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEmptyTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_emptyTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','EmptyTemplate'); + } + + /** + * @return ITemplate the separator template + */ + public function getSeparatorTemplate() + { + return $this->_separatorTemplate; + } + + /** + * @param ITemplate the separator template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setSeparatorTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_separatorTemplate=$value; + else + throw new TInvalidDataTypeException('datalist_template_required','SeparatorTemplate'); + } + + /** + * @return TTableItemStyle the style for separator + */ + public function getSeparatorStyle() + { + if(($style=$this->getViewState('SeparatorStyle',null))===null) + { + $style=new TTableItemStyle; + $this->setViewState('SeparatorStyle',$style,null); + } + return $style; + } + + /** + * @return integer the zero-based index of the selected item in {@link getItems Items}. + * A value -1 means no item selected. + */ + public function getSelectedItemIndex() + { + return $this->getViewState('SelectedItemIndex',-1); + } + + /** + * Selects an item by its index in {@link getItems Items}. + * Previously selected item will be un-selected. + * If the item to be selected is already in edit mode, it will remain in edit mode. + * If the index is less than 0, any existing selection will be cleared up. + * @param integer the selected item index + */ + public function setSelectedItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getSelectedItemIndex())!==$value) + { + $this->setViewState('SelectedItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + { + $item=$items->itemAt($current); + if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem) + $item->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item); + } + if($value>=0 && $value<$itemCount) + { + $item=$items->itemAt($value); + if(($item instanceof IItemDataRenderer) && $item->getItemType()!==TListItemType::EditItem) + $item->setItemType(TListItemType::SelectedItem); + } + } + } + + /** + * @return TControl the selected item, null if no item is selected. + */ + public function getSelectedItem() + { + $index=$this->getSelectedItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return mixed the key value of the currently selected item + * @throws TInvalidOperationException if {@link getDataKeyField DataKeyField} is empty. + */ + public function getSelectedDataKey() + { + if($this->getDataKeyField()==='') + throw new TInvalidOperationException('datalist_datakeyfield_required'); + $index=$this->getSelectedItemIndex(); + $dataKeys=$this->getDataKeys(); + if($index>=0 && $index<$dataKeys->getCount()) + return $dataKeys->itemAt($index); + else + return null; + } + + /** + * @return integer the zero-based index of the edit item in {@link getItems Items}. + * A value -1 means no item is in edit mode. + */ + public function getEditItemIndex() + { + return $this->getViewState('EditItemIndex',-1); + } + + /** + * Edits an item by its index in {@link getItems Items}. + * Previously editting item will change to normal item state. + * If the index is less than 0, any existing edit item will be cleared up. + * @param integer the edit item index + */ + public function setEditItemIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + if(($current=$this->getEditItemIndex())!==$value) + { + $this->setViewState('EditItemIndex',$value,-1); + $items=$this->getItems(); + $itemCount=$items->getCount(); + if($current>=0 && $current<$itemCount) + $items->itemAt($current)->setItemType($current%2?TListItemType::AlternatingItem : TListItemType::Item); + if($value>=0 && $value<$itemCount) + $items->itemAt($value)->setItemType(TListItemType::EditItem); + } + } + + /** + * @return TControl the edit item + */ + public function getEditItem() + { + $index=$this->getEditItemIndex(); + $items=$this->getItems(); + if($index>=0 && $index<$items->getCount()) + return $items->itemAt($index); + else + return null; + } + + /** + * @return boolean whether the header should be shown. Defaults to true. + */ + public function getShowHeader() + { + return $this->getViewState('ShowHeader',true); + } + + /** + * @param boolean whether to show header + */ + public function setShowHeader($value) + { + $this->setViewState('ShowHeader',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the footer should be shown. Defaults to true. + */ + public function getShowFooter() + { + return $this->getViewState('ShowFooter',true); + } + + /** + * @param boolean whether to show footer + */ + public function setShowFooter($value) + { + $this->setViewState('ShowFooter',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return TRepeatInfo repeat information (primarily used by control developers) + */ + protected function getRepeatInfo() + { + if(($repeatInfo=$this->getViewState('RepeatInfo',null))===null) + { + $repeatInfo=new TRepeatInfo; + $this->setViewState('RepeatInfo',$repeatInfo,null); + } + return $repeatInfo; + } + + /** + * @return string caption of the table layout + */ + public function getCaption() + { + return $this->getRepeatInfo()->getCaption(); + } + + /** + * @param string caption of the table layout + */ + public function setCaption($value) + { + $this->getRepeatInfo()->setCaption($value); + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->getRepeatInfo()->getCaptionAlign(); + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. + */ + public function setCaptionAlign($value) + { + $this->getRepeatInfo()->setCaptionAlign($value); + } + + /** + * @return integer the number of columns that the list should be displayed with. Defaults to 0 meaning not set. + */ + public function getRepeatColumns() + { + return $this->getRepeatInfo()->getRepeatColumns(); + } + + /** + * @param integer the number of columns that the list should be displayed with. + */ + public function setRepeatColumns($value) + { + $this->getRepeatInfo()->setRepeatColumns($value); + } + + /** + * @return TRepeatDirection the direction of traversing the list, defaults to TRepeatDirection::Vertical + */ + public function getRepeatDirection() + { + return $this->getRepeatInfo()->getRepeatDirection(); + } + + /** + * @param TRepeatDirection the direction of traversing the list + */ + public function setRepeatDirection($value) + { + $this->getRepeatInfo()->setRepeatDirection($value); + } + + /** + * @return TRepeatLayout how the list should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table. + */ + public function getRepeatLayout() + { + return $this->getRepeatInfo()->getRepeatLayout(); + } + + /** + * @param TRepeatLayout how the list should be displayed, using table or using line breaks + */ + public function setRepeatLayout($value) + { + $this->getRepeatInfo()->setRepeatLayout($value); + } + + /** + * This method overrides parent's implementation to handle + * {@link onItemCommand OnItemCommand} event which is bubbled from + * datalist items and their child controls. + * If the event parameter is {@link TDataListCommandEventParameter} and + * the command name is a recognized one, which includes 'select', 'edit', + * 'delete', 'update', and 'cancel' (case-insensitive), then a + * corresponding command event is also raised (such as {@link onEditCommand OnEditCommand}). + * This method should only be used by control developers. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TDataListCommandEventParameter) + { + $this->onItemCommand($param); + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_SELECT)===0) + { + if(($item=$param->getItem()) instanceof IItemDataRenderer) + $this->setSelectedItemIndex($item->getItemIndex()); + $this->onSelectedIndexChanged($param); + return true; + } + else if(strcasecmp($command,self::CMD_EDIT)===0) + { + $this->onEditCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_DELETE)===0) + { + $this->onDeleteCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_UPDATE)===0) + { + $this->onUpdateCommand($param); + return true; + } + else if(strcasecmp($command,self::CMD_CANCEL)===0) + { + $this->onCancelCommand($param); + return true; + } + } + return false; + } + + + /** + * Raises OnItemCreated event. + * This method is invoked after a data list item is created and instantiated with + * template, but before added to the page hierarchy. + * The datalist item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TDataListItemEventParameter event parameter + */ + public function onItemCreated($param) + { + $this->raiseEvent('OnItemCreated',$this,$param); + } + + /** + * Raises OnItemDataBound event. + * This method is invoked right after an item is data bound. + * The datalist item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TDataListItemEventParameter event parameter + */ + public function onItemDataBound($param) + { + $this->raiseEvent('OnItemDataBound',$this,$param); + } + + /** + * Raises OnItemCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event. + * @param TDataListCommandEventParameter event parameter + */ + public function onItemCommand($param) + { + $this->raiseEvent('OnItemCommand',$this,$param); + } + + /** + * Raises OnEditCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'edit' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onEditCommand($param) + { + $this->raiseEvent('OnEditCommand',$this,$param); + } + + /** + * Raises OnDeleteCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'delete' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onDeleteCommand($param) + { + $this->raiseEvent('OnDeleteCommand',$this,$param); + } + + /** + * Raises OnUpdateCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'update' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onUpdateCommand($param) + { + $this->raiseEvent('OnUpdateCommand',$this,$param); + } + + /** + * Raises OnCancelCommand event. + * This method is invoked when a child control of the data list + * raises an OnCommand event and the command name is 'cancel' (case-insensitive). + * @param TDataListCommandEventParameter event parameter + */ + public function onCancelCommand($param) + { + $this->raiseEvent('OnCancelCommand',$this,$param); + } + + /** + * Returns a value indicating whether this control contains header item. + * This method is required by {@link IRepeatInfoUser} interface. + * @return boolean whether the datalist has header + */ + public function getHasHeader() + { + return ($this->getShowHeader() && ($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='')); + } + + /** + * Returns a value indicating whether this control contains footer item. + * This method is required by {@link IRepeatInfoUser} interface. + * @return boolean whether the datalist has footer + */ + public function getHasFooter() + { + return ($this->getShowFooter() && ($this->_footerTemplate!==null || $this->getFooterRenderer()!=='')); + } + + /** + * Returns a value indicating whether this control contains separator items. + * This method is required by {@link IRepeatInfoUser} interface. + * @return boolean always false. + */ + public function getHasSeparators() + { + return $this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + } + + /** + * Returns a style used for rendering items. + * This method is required by {@link IRepeatInfoUser} interface. + * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) + * @param integer index of the item being rendered + * @return TStyle item style + */ + public function generateItemStyle($itemType,$index) + { + if(($item=$this->getItem($itemType,$index))!==null && ($item instanceof IStyleable) && $item->getHasStyle()) + { + $style=$item->getStyle(); + $item->clearStyle(); + return $style; + } + else + return null; + } + + /** + * Renders an item in the list. + * This method is required by {@link IRepeatInfoUser} interface. + * @param THtmlWriter writer for rendering purpose + * @param TRepeatInfo repeat information + * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) + * @param integer zero-based index of the item in the item list + */ + public function renderItem($writer,$repeatInfo,$itemType,$index) + { + $item=$this->getItem($itemType,$index); + if($repeatInfo->getRepeatLayout()===TRepeatLayout::Raw && get_class($item)==='TDataListItem') + $item->setTagName('div'); + $item->renderControl($writer); + } + + /** + * @param TListItemType item type + * @param integer item index + * @return TControl data list item with the specified item type and index + */ + private function getItem($itemType,$index) + { + switch($itemType) + { + case TListItemType::Item: + case TListItemType::AlternatingItem: + case TListItemType::SelectedItem: + case TListItemType::EditItem: + return $this->getItems()->itemAt($index); + case TListItemType::Header: + return $this->getControls()->itemAt(0); + case TListItemType::Footer: + return $this->getControls()->itemAt($this->getControls()->getCount()-1); + case TListItemType::Separator: + $i=$index+$index+1; + if($this->_headerTemplate!==null || $this->getHeaderRenderer()!=='') + $i++; + return $this->getControls()->itemAt($i); + } + return null; + } + + /** + * Creates a datalist item. + * This method invokes {@link createItem} to create a new datalist item. + * @param integer zero-based item index. + * @param TListItemType item type + * @return TControl the created item, null if item is not created + */ + private function createItemInternal($itemIndex,$itemType) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TDataListItemEventParameter($item); + $this->onItemCreated($param); + $this->getControls()->add($item); + return $item; + } + else + return null; + } + + /** + * Creates a datalist item and performs databinding. + * This method invokes {@link createItem} to create a new datalist item. + * @param integer zero-based item index. + * @param TListItemType item type + * @param mixed data to be associated with the item + * @return TControl the created item, null if item is not created + */ + private function createItemWithDataInternal($itemIndex,$itemType,$dataItem) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TDataListItemEventParameter($item); + if($item instanceof IDataRenderer) + $item->setData($dataItem); + $this->onItemCreated($param); + $this->getControls()->add($item); + $item->dataBind(); + $this->onItemDataBound($param); + return $item; + } + else + return null; + } + + private function getAlternatingItemDisplay() + { + if(($classPath=$this->getAlternatingItemRenderer())==='' && $this->_alternatingItemTemplate===null) + return array($this->getItemRenderer(),$this->_itemTemplate); + else + return array($classPath,$this->_alternatingItemTemplate); + } + + private function getSelectedItemDisplay($itemIndex) + { + if(($classPath=$this->getSelectedItemRenderer())==='' && $this->_selectedItemTemplate===null) + { + if($itemIndex%2===0) + return array($this->getItemRenderer(),$this->_itemTemplate); + else + return $this->getAlternatingItemDisplay(); + } + else + return array($classPath,$this->_selectedItemTemplate); + } + + private function getEditItemDisplay($itemIndex) + { + if(($classPath=$this->getEditItemRenderer())==='' && $this->_editItemTemplate===null) + return $this->getSelectedItemDisplay($itemIndex); + else + return array($classPath,$this->_editItemTemplate); + } + + /** + * Creates a datalist item instance based on the item type and index. + * @param integer zero-based item index + * @param TListItemType item type + * @return TControl created datalist item + */ + protected function createItem($itemIndex,$itemType) + { + $template=null; + $classPath=null; + switch($itemType) + { + case TListItemType::Item : + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + break; + case TListItemType::AlternatingItem : + list($classPath,$template)=$this->getAlternatingItemDisplay(); + break; + case TListItemType::SelectedItem: + list($classPath,$template)=$this->getSelectedItemDisplay($itemIndex); + break; + case TListItemType::EditItem: + list($classPath,$template)=$this->getEditItemDisplay($itemIndex); + break; + case TListItemType::Header : + $classPath=$this->getHeaderRenderer(); + $template=$this->_headerTemplate; + break; + case TListItemType::Footer : + $classPath=$this->getFooterRenderer(); + $template=$this->_footerTemplate; + break; + case TListItemType::Separator : + $classPath=$this->getSeparatorRenderer(); + $template=$this->_separatorTemplate; + break; + default: + throw new TInvalidDataValueException('datalist_itemtype_unknown',$itemType); + } + if($classPath!=='') + { + $item=Prado::createComponent($classPath); + if($item instanceof IItemDataRenderer) + { + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + } + } + else if($template!==null) + { + $item=new TDataListItem; + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + $template->instantiateIn($item); + } + else + $item=null; + + return $item; + } + + /** + * Creates empty datalist content. + */ + protected function createEmptyContent() + { + if(($classPath=$this->getEmptyRenderer())!=='') + $this->getControls()->add(Prado::createComponent($classPath)); + else if($this->_emptyTemplate!==null) + $this->_emptyTemplate->instantiateIn($this); + } + + /** + * Applies styles to items, header, footer and separators. + * Item styles are applied in a hierarchical way. Style in higher hierarchy + * will inherit from styles in lower hierarchy. + * Starting from the lowest hierarchy, the item styles include + * item's own style, {@link getItemStyle ItemStyle}, {@link getAlternatingItemStyle AlternatingItemStyle}, + * {@link getSelectedItemStyle SelectedItemStyle}, and {@link getEditItemStyle EditItemStyle}. + * Therefore, if background color is set as red in {@link getItemStyle ItemStyle}, + * {@link getEditItemStyle EditItemStyle} will also have red background color + * unless it is set to a different value explicitly. + */ + protected function applyItemStyles() + { + $itemStyle=$this->getViewState('ItemStyle',null); + + $alternatingItemStyle=$this->getViewState('AlternatingItemStyle',null); + if($itemStyle!==null) + { + if($alternatingItemStyle===null) + $alternatingItemStyle=$itemStyle; + else + $alternatingItemStyle->mergeWith($itemStyle); + } + + $selectedItemStyle=$this->getViewState('SelectedItemStyle',null); + + $editItemStyle=$this->getViewState('EditItemStyle',null); + if($selectedItemStyle!==null) + { + if($editItemStyle===null) + $editItemStyle=$selectedItemStyle; + else + $editItemStyle->mergeWith($selectedItemStyle); + } + + // apply header style if any + if($this->_header!==null && $this->_header instanceof IStyleable) + { + if($headerStyle=$this->getViewState('HeaderStyle',null)) + $this->_header->getStyle()->mergeWith($headerStyle); + } + + // apply footer style if any + if($this->_footer!==null && $this->_footer instanceof IStyleable) + { + if($footerStyle=$this->getViewState('FooterStyle',null)) + $this->_footer->getStyle()->mergeWith($footerStyle); + } + + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + + // apply item styles if any + foreach($this->getItems() as $index=>$item) + { + if($index===$editIndex) + $style=$editItemStyle; + else if($index===$selectedIndex) + $style=$selectedItemStyle; + else if($index%2===0) + $style=$itemStyle; + else + $style=$alternatingItemStyle; + if($style && $item instanceof IStyleable) + $item->getStyle()->mergeWith($style); + } + + // apply separator style if any + if(($separatorStyle=$this->getViewState('SeparatorStyle',null))!==null && $this->getHasSeparators()) + { + $controls=$this->getControls(); + $count=$controls->getCount(); + for($i=$this->_header?2:1;$i<$count;$i+=2) + { + if(($separator=$controls->itemAt($i)) instanceof IStyleable) + $separator->getStyle()->mergeWith($separatorStyle); + } + } + } + + /** + * Saves item count in viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if($this->_items) + $this->setViewState('ItemCount',$this->_items->getCount(),0); + else + $this->clearViewState('ItemCount'); + } + + /** + * Loads item count information from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getIsDataBound()) + $this->restoreItemsFromViewState(); + $this->clearViewState('ItemCount'); + } + + /** + * Clears up all items in the data list. + */ + public function reset() + { + $this->getControls()->clear(); + $this->getItems()->clear(); + $this->_header=null; + $this->_footer=null; + } + + /** + * Creates data list items based on viewstate information. + */ + protected function restoreItemsFromViewState() + { + $this->reset(); + if(($itemCount=$this->getViewState('ItemCount',0))>0) + { + $items=$this->getItems(); + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + $this->_header=$this->createItemInternal(-1,TListItemType::Header); + for($i=0;$i<$itemCount;++$i) + { + if($hasSeparator && $i>0) + $this->createItemInternal($i-1,TListItemType::Separator); + if($i===$editIndex) + $itemType=TListItemType::EditItem; + else if($i===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else + $itemType=$i%2?TListItemType::AlternatingItem : TListItemType::Item; + $items->add($this->createItemInternal($i,$itemType)); + } + $this->_footer=$this->createItemInternal(-1,TListItemType::Footer); + } + else + $this->createEmptyContent(); + $this->clearChildState(); + } + + /** + * Performs databinding to populate data list items from data source. + * This method is invoked by dataBind(). + * You may override this function to provide your own way of data population. + * @param Traversable the data + */ + protected function performDataBinding($data) + { + $this->reset(); + $keys=$this->getDataKeys(); + $keys->clear(); + $keyField=$this->getDataKeyField(); + $itemIndex=0; + $items=$this->getItems(); + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + $selectedIndex=$this->getSelectedItemIndex(); + $editIndex=$this->getEditItemIndex(); + foreach($data as $key=>$dataItem) + { + if($keyField!=='') + $keys->add($this->getDataFieldValue($dataItem,$keyField)); + else + $keys->add($key); + if($itemIndex===0) + $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null); + if($hasSeparator && $itemIndex>0) + $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null); + if($itemIndex===$editIndex) + $itemType=TListItemType::EditItem; + else if($itemIndex===$selectedIndex) + $itemType=TListItemType::SelectedItem; + else + $itemType=$itemIndex%2?TListItemType::AlternatingItem : TListItemType::Item; + $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem)); + $itemIndex++; + } + if($itemIndex>0) + $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null); + else + { + $this->createEmptyContent(); + $this->dataBindChildren(); + } + $this->setViewState('ItemCount',$itemIndex,0); + } + + /** + * Renders the data list control. + * This method overrides the parent implementation. + * @param THtmlWriter writer for rendering purpose. + */ + public function render($writer) + { + if($this->getHasControls()) + { + if($this->getItemCount()>0) + { + $this->applyItemStyles(); + $repeatInfo=$this->getRepeatInfo(); + $repeatInfo->renderRepeater($writer,$this); + } + else if($this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='') + parent::render($writer); + } + } +} + + +/** + * TDataListItemEventParameter class + * + * TDataListItemEventParameter encapsulates the parameter data for + * {@link TDataList::onItemCreated ItemCreated} event of {@link TDataList} controls. + * The {@link getItem Item} property indicates the DataList item related with the event. + * + * @author Qiang Xue + * @version $Id: TDataList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListItemEventParameter extends TEventParameter +{ + /** + * The datalist item control responsible for the event. + * @var TControl + */ + private $_item=null; + + /** + * Constructor. + * @param TControl DataList item related with the corresponding event + */ + public function __construct($item) + { + $this->_item=$item; + } + + /** + * @return TControl datalist item related with the corresponding event + */ + public function getItem() + { + return $this->_item; + } +} + +/** + * TDataListCommandEventParameter class + * + * TDataListCommandEventParameter encapsulates the parameter data for + * {@link TDataList::onItemCommand ItemCommand} event of {@link TDataList} controls. + * + * The {@link getItem Item} property indicates the DataList item related with the event. + * The {@link getCommandSource CommandSource} refers to the control that originally + * raises the Command event. + * + * @author Qiang Xue + * @version $Id: TDataList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListCommandEventParameter extends TCommandEventParameter +{ + /** + * @var TControl the datalist item control responsible for the event. + */ + private $_item=null; + /** + * @var TControl the control originally raises the OnCommand event. + */ + private $_source=null; + + /** + * Constructor. + * @param TControl datalist item responsible for the event + * @param TControl original event sender + * @param TCommandEventParameter original event parameter + */ + public function __construct($item,$source,TCommandEventParameter $param) + { + $this->_item=$item; + $this->_source=$source; + parent::__construct($param->getCommandName(),$param->getCommandParameter()); + } + + /** + * @return TControl the datalist item control responsible for the event. + */ + public function getItem() + { + return $this->_item; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } +} + +/** + * TDataListItem class + * + * A TDataListItem control represents an item in the {@link TDataList} control, + * such as heading section, footer section, or a data item. + * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> + * and {@link getDataItem DataItem} properties, respectively. The type of the item + * is given by {@link getItemType ItemType} property. + * + * @author Qiang Xue + * @version $Id: TDataList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListItem extends TWebControl implements INamingContainer, IItemDataRenderer +{ + /** + * index of the data item in the Items collection of DataList + */ + private $_itemIndex; + /** + * type of the TDataListItem + * @var TListItemType + */ + private $_itemType; + /** + * value of the data associated with this item + * @var mixed + */ + private $_data; + + private $_tagName='span'; + + /** + * Returns the tag name used for this control. + * @return string tag name of the control to be rendered + */ + protected function getTagName() + { + return $this->_tagName; + } + + /** + * @param string tag name of the control to be rendered + */ + public function setTagName($value) + { + $this->_tagName=$value; + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableItemStyle} to be used by a datalist item. + * @return TStyle control style to be used + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * @return TListItemType item type + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type. + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * @return integer zero-based index of the item in the item collection of datalist + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value) + { + $this->_itemIndex=TPropertyValue::ensureInteger($value); + } + + /** + * @return mixed data associated with the item + * @since 3.1.0 + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + * @since 3.1.0 + */ + public function setData($value) + { + $this->_data=$value; + } + + /** + * This property is deprecated since v3.1.0. + * @return mixed data associated with the item + * @deprecated deprecated since v3.1.0. Use {@link getData} instead. + */ + public function getDataItem() + { + return $this->getData(); + } + + /** + * This property is deprecated since v3.1.0. + * @param mixed data to be associated with the item + * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. + */ + public function setDataItem($value) + { + return $this->setData($value); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + +/** + * TDataListItemCollection class. + * + * TDataListItemCollection represents a collection of data list items. + * + * @author Qiang Xue + * @version $Id: TDataList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataListItemCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only TControl descendants. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TControl descendant. + */ + public function insertAt($index,$item) + { + if($item instanceof TControl) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('datalistitemcollection_datalistitem_required'); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataListItemRenderer.php b/gui/baculum/framework/Web/UI/WebControls/TDataListItemRenderer.php new file mode 100644 index 0000000000..df63c1f247 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataListItemRenderer.php @@ -0,0 +1,172 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataListItemRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataList'); +Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); + +/** + * TDataListItemRenderer class + * + * TDataListItemRenderer can be used as a convenient base class to + * define an item renderer class specific for {@link TDataList}. + * + * TDataListItemRenderer extends {@link TItemDataRenderer} and implements + * the bubbling scheme for the OnCommand event of data list items. + * + * TDataListItemRenderer also implements the {@link IStyleable} interface, + * which allows TDataList to apply CSS styles to the renders. + * + * @author Qiang Xue + * @version $Id: TDataListItemRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +class TDataListItemRenderer extends TItemDataRenderer implements IStyleable +{ + /** + * Creates a style object to be used by the control. + * This method may be overriden by controls to provide customized style. + * @return TStyle + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * @return boolean whether the control has defined any style information + */ + public function getHasStyle() + { + return $this->getViewState('Style',null)!==null; + } + + /** + * @return TStyle the object representing the css style of the control + */ + public function getStyle() + { + if($style=$this->getViewState('Style',null)) + return $style; + else + { + $style=$this->createStyle(); + $this->setViewState('Style',$style,null); + return $style; + } + } + + /** + * Removes all style data. + */ + public function clearStyle() + { + $this->clearViewState('Style'); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TDataListCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } + + /** + * Returns the tag name used for this control. + * By default, the tag name is 'span'. + * You can override this method to provide customized tag names. + * If the tag name is empty, the opening and closing tag will NOT be rendered. + * @return string tag name of the control to be rendered + */ + protected function getTagName() + { + return 'span'; + } + + /** + * Adds attribute name-value pairs to renderer. + * By default, this method renders the style string. + * The method can be overriden to provide customized attribute rendering. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + if($style=$this->getViewState('Style',null)) + $style->addAttributesToRender($writer); + } + + /** + * Renders the control. + * This method overrides the parent implementation by replacing it with + * the following sequence: + * - {@link renderBeginTag} + * - {@link renderContents} + * - {@link renderEndTag} + * If the {@link getTagName TagName} is empty, only {@link renderContents} is invoked. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if($this->getTagName()!=='') + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + else + $this->renderContents($writer); + } + + /** + * Renders the openning tag for the control (including attributes) + * This method is invoked when {@link getTagName TagName} is not empty. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + $this->addAttributesToRender($writer); + $writer->renderBeginTag($this->getTagName()); + } + + /** + * Renders the body content enclosed between the control tag. + * By default, child controls and text strings will be rendered. + * You can override this method to provide customized content rendering. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + parent::renderChildren($writer); + } + + /** + * Renders the closing tag for the control + * This method is invoked when {@link getTagName TagName} is not empty. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderEndTag($writer) + { + $writer->renderEndTag(); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataRenderer.php b/gui/baculum/framework/Web/UI/WebControls/TDataRenderer.php new file mode 100644 index 0000000000..8bde015224 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataRenderer.php @@ -0,0 +1,52 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ + +/** + * TDataRenderer class + * + * TDataRenderer is the convenient base class for template-based renderer controls. + * It extends {@link TTemplateControl} and implements the methods required + * by the {@link IDataRenderer} interface. + * + * The following property is provided by TDataRenderer: + * - {@link getData Data}: data associated with this renderer. + + * @author Qiang Xue + * @version $Id: TDataRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ +abstract class TDataRenderer extends TTemplateControl implements IDataRenderer +{ + /** + * @var mixed data associated with this renderer + */ + private $_data; + + /** + * @return mixed data associated with the item + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + */ + public function setData($value) + { + $this->_data=$value; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataSourceControl.php b/gui/baculum/framework/Web/UI/WebControls/TDataSourceControl.php new file mode 100644 index 0000000000..93037c03d3 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataSourceControl.php @@ -0,0 +1,118 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataSourceControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * IDataSource class + * + * @author Qiang Xue + * @version $Id: TDataSourceControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +interface IDataSource +{ + public function getView($viewName); + public function getViewNames(); + public function onDataSourceChanged($param); +} + +/** + * TDataSourceControl class + * + * @author Qiang Xue + * @version $Id: TDataSourceControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataSourceControl extends TControl implements IDataSource +{ + public function getView($viewName) + { + return null; + } + + public function getViewNames() + { + return array(); + } + + public function onDataSourceChanged($param) + { + $this->raiseEvent('OnDataSourceChanged',$this,$param); + } + + public function focus() + { + throw new TNotSupportedException('datasourcecontrol_focus_unsupported'); + } + + public function getEnableTheming() + { + return false; + } + + public function setEnableTheming($value) + { + throw new TNotSupportedException('datasourcecontrol_enabletheming_unsupported'); + } + + public function getSkinID() + { + return ''; + } + + public function setSkinID($value) + { + throw new TNotSupportedException('datasourcecontrol_skinid_unsupported'); + } + + public function getVisible($checkParents=true) + { + return false; + } + + public function setVisible($value) + { + throw new TNotSupportedException('datasourcecontrol_visible_unsupported'); + } +} + +/** + * TDataSourceControl class + * + * @author Qiang Xue + * @version $Id: TDataSourceControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TReadOnlyDataSource extends TDataSourceControl +{ + private $_dataSource; + private $_dataMember; + + public function __construct($dataSource,$dataMember) + { + if(!is_array($dataSource) && !($dataSource instanceof IDataSource) && !($dataSource instanceof Traversable)) + throw new TInvalidDataTypeException('readonlydatasource_datasource_invalid'); + $this->_dataSource=$dataSource; + $this->_dataMember=$dataMember; + } + + public function getView($viewName) + { + if($this->_dataSource instanceof IDataSource) + return $this->_dataSource->getView($viewName); + else + return new TReadOnlyDataSourceView($this,$this->_dataMember,$this->_dataSource); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataSourceView.php b/gui/baculum/framework/Web/UI/WebControls/TDataSourceView.php new file mode 100644 index 0000000000..1e1ad7a9a6 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataSourceView.php @@ -0,0 +1,206 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataSourceView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TDataSourceSelectParameters class + * + * @author Qiang Xue + * @version $Id: TDataSourceView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataSourceSelectParameters extends TComponent +{ + private $_retrieveTotalRowCount=false; + private $_startRowIndex=0; + private $_totalRowCount=0; + private $_maximumRows=0; + + public function getStartRowIndex() + { + return $this->_startRowIndex; + } + + public function setStartRowIndex($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_startRowIndex=$value; + } + + public function getMaximumRows() + { + return $this->_maximumRows; + } + + public function setMaximumRows($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_maximumRows=$value; + } + + public function getRetrieveTotalRowCount() + { + return $this->_retrieveTotalRowCount; + } + + public function setRetrieveTotalRowCount($value) + { + $this->_retrieveTotalRowCount=TPropertyValue::ensureBoolean($value); + } + + public function getTotalRowCount() + { + return $this->_totalRowCount; + } + + public function setTotalRowCount($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=0; + $this->_totalRowCount=$value; + } +} + +/** + * TDataSourceView class + * + * @author Qiang Xue + * @version $Id: TDataSourceView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TDataSourceView extends TComponent +{ + private $_owner; + private $_name; + + public function __construct(IDataSource $owner,$viewName) + { + $this->_owner=$owner; + $this->_name=$viewName; + } + + /** + * Performs DB selection based on specified parameters. + * @param ??? + * @return Traversable + */ + abstract public function select($parameters); + + /** + * Inserts a DB record. + * @param array|TMap + * @return integer affected rows + */ + public function insertAt($values) + { + throw new TNotSupportedException('datasourceview_insert_unsupported'); + } + + /** + * Updates DB record(s) with the specified keys and new values + * @param array|TMap keys for specifying the records to be updated + * @param array|TMap new values + * @return integer affected rows + */ + public function update($keys,$values) + { + throw new TNotSupportedException('datasourceview_update_unsupported'); + } + + /** + * Deletes DB row(s) with the specified keys. + * @param array|TMap keys for specifying the rows to be deleted + * @return integer affected rows + */ + public function delete($keys) + { + throw new TNotSupportedException('datasourceview_delete_unsupported'); + } + + public function getCanDelete() + { + return false; + } + + public function getCanInsert() + { + return false; + } + + public function getCanPage() + { + return false; + } + + public function getCanGetRowCount() + { + return false; + } + + public function getCanSort() + { + return false; + } + + public function getCanUpdate() + { + return false; + } + + public function getName() + { + return $this->_name; + } + + public function getDataSource() + { + return $this->_owner; + } + + public function onDataSourceViewChanged($param) + { + $this->raiseEvent('OnDataSourceViewChanged',$this,$param); + } +} + +/** + * TReadOnlyDataSourceView class + * + * @author Qiang Xue + * @version $Id: TDataSourceView.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TReadOnlyDataSourceView extends TDataSourceView +{ + private $_dataSource=null; + + public function __construct(IDataSource $owner,$viewName,$dataSource) + { + parent::__construct($owner,$viewName); + if($dataSource===null || is_array($dataSource)) + $this->_dataSource=new TMap($dataSource); + else if($dataSource instanceof Traversable) + $this->_dataSource=$dataSource; + else + throw new TInvalidDataTypeException('readonlydatasourceview_datasource_invalid'); + } + + public function select($parameters) + { + return $this->_dataSource; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDataTypeValidator.php b/gui/baculum/framework/Web/UI/WebControls/TDataTypeValidator.php new file mode 100644 index 0000000000..b9ddbf6cf6 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDataTypeValidator.php @@ -0,0 +1,141 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDataTypeValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TDataTypeValidator class + * + * TDataTypeValidator verifies if the input data is of the type specified + * by {@link setDataType DataType}. + * The following data types are supported: + * - Integer A 32-bit signed integer data type. + * - Float A double-precision floating point number data type. + * - Date A date data type. + * - String A string data type. + * For Date type, the property {@link setDateFormat DateFormat} + * will be used to determine how to parse the date string. If it is not + * provided, the string will be assumed to be in GNU datetime format. + * + * @author Wei Zhuo + * @version $Id: TDataTypeValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDataTypeValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TDataTypeValidator'; + } + + /** + * @return TValidationDataType the data type that the values being compared are converted to before the comparison is made. Defaults to TValidationDataType::String. + */ + public function getDataType() + { + return $this->getViewState('DataType','String'); + } + + /** + * Sets the data type that the values being compared are converted to before the comparison is made. + * @param TValidationDataType the data type + */ + public function setDataType($value) + { + $this->setViewState('DataType',TPropertyValue::ensureEnum($value,'TValidationDataType'),TValidationDataType::String); + } + + /** + * Sets the date format for a date validation + * @param string the date format value + */ + public function setDateFormat($value) + { + $this->setViewState('DateFormat', $value, ''); + } + + /** + * @return string the date validation date format if any + */ + public function getDateFormat() + { + return $this->getViewState('DateFormat', ''); + } + + + /** + * Determine if the given value is of a particular type using RegExp. + * @param string value to check + * @return boolean true if value fits the type expression. + */ + protected function evaluateDataTypeCheck($value) + { + if($value=='') + return true; + + switch($this->getDataType()) + { + case TValidationDataType::Integer: + return preg_match('/^[-+]?[0-9]+$/',trim($value)); + case TValidationDataType::Float: + return preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value)); + case TValidationDataType::Date: + $dateFormat = $this->getDateFormat(); + if(strlen($dateFormat)) + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$dateFormat); + return $formatter->isValidDate($value); + } + else + return strtotime($value) > 0; + } + return true; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['DataType']=$this->getDataType(); + if(($dateFormat=$this->getDateFormat())!=='') + $options['DateFormat']=$dateFormat; + return $options; + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input data is of valid type. + * The validation always succeeds if ControlToValidate is not specified + * or the input data is empty. + * @return boolean whether the validation succeeds + */ + public function evaluateIsValid() + { + if(($value=$this->getValidationValue($this->getValidationTarget()))==='') + return true; + + return $this->evaluateDataTypeCheck($value); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TDatePicker.php b/gui/baculum/framework/Web/UI/WebControls/TDatePicker.php new file mode 100644 index 0000000000..0e8d7bda88 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDatePicker.php @@ -0,0 +1,991 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDatePicker.php 3318 2013-09-04 14:04:51Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTextBox class + */ +Prado::using('System.Web.UI.WebControls.TTextBox'); + +/** + * + * TDatePicker class. + * + * TDatePicker displays a text box for date input purpose. + * When the text box receives focus, a calendar will pop up and users can + * pick up from it a date that will be automatically entered into the text box. + * The format of the date string displayed in the text box is determined by + * the DateFormat property. Valid formats are the combination of the + * following tokens, + * + * + * Character Format Pattern (en-US) + * ----------------------------------------- + * d day digit + * dd padded day digit e.g. 01, 02 + * M month digit + * MM padded month digit + * MMMM localized month name, e.g. March, April + * yy 2 digit year + * yyyy 4 digit year + * ----------------------------------------- + * + * + * TDatePicker has four Mode to show the date picker popup. + * + * # Basic -- Only shows a text input, focusing on the input shows the + * date picker. This way you can access the popup using only + * the keyboard. Note that because of this, TAB-bing through + * this control will automatically select the current date if + * no previous date was selected. If you close the popup (eg. + * pressing the ESC key) you'll need to un-focus and re-focus + * the control again for the popup to reappear. + * # Clickable -- Only shows a text input, clicking on the input shows the + * date picker. This mode solves the two small problems of the + * Basic mode. It was first introduced in Prado 3.2. + * # Button -- Shows a button next to the text input, clicking on the + * button shows the date, button text can be by the + * ButtonText property + * # ImageButton -- Shows an image next to the text input, clicking on + * the image shows the date picker, image source can be + * change through the ButtonImageUrl property. + * + * The CssClass property can be used to override the css class name + * for the date picker panel. CalendarStyle property sets the packages + * styles available. E.g. default. + * + * The InputMode property can be set to "TextBox" or "DropDownList" with + * default as "TextBox". + * In DropDownList mode, in addition to the popup date picker, three + * drop down list (day, month and year) are presented to select the date . + * + * The PositionMode property can be set to "Top" or "Bottom" with default + * as "Bottom". It specifies the position of the calendar popup, relative to the + * input field. + * + * @author Wei Zhuo + * @author Carl G. Mathisen + * @version $Id: TDatePicker.php 3318 2013-09-04 14:04:51Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDatePicker extends TTextBox +{ + /** + * Script path relative to the TClientScriptManager::SCRIPT_PATH + */ + const SCRIPT_PATH = 'prado/datepicker'; + + /** + * @var TDatePickerClientScript validator client-script options. + */ + private $_clientScript; + /** + * AutoPostBack is not supported. + */ + public function setAutoPostBack($value) + { + throw new TNotSupportedException('tdatepicker_autopostback_unsupported', + get_class($this)); + } + + /** + * @return string the format of the date string + */ + public function getDateFormat() + { + return $this->getViewState('DateFormat','dd-MM-yyyy'); + } + + /** + * Sets the format of the date string. + * @param string the format of the date string + */ + public function setDateFormat($value) + { + $this->setViewState('DateFormat',$value,'dd-MM-yyyy'); + } + + /** + * @return boolean whether the calendar window should pop up when the control receives focus + */ + public function getShowCalendar() + { + return $this->getViewState('ShowCalendar',true); + } + + /** + * Sets whether to pop up the calendar window when the control receives focus + * @param boolean whether to show the calendar window + */ + public function setShowCalendar($value) + { + $this->setViewState('ShowCalendar',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Gets the current culture. + * @return string current culture, e.g. en_AU. + */ + public function getCulture() + { + return $this->getViewState('Culture', ''); + } + + /** + * Sets the culture/language for the date picker. + * @param string a culture string, e.g. en_AU. + */ + public function setCulture($value) + { + $this->setViewState('Culture', $value, ''); + } + + /** + * @param TDatePickerInputMode input method of date values + */ + public function setInputMode($value) + { + $this->setViewState('InputMode', TPropertyValue::ensureEnum($value, 'TDatePickerInputMode'), TDatePickerInputMode::TextBox); + } + + /** + * @return TDatePickerInputMode input method of date values. Defaults to TDatePickerInputMode::TextBox. + */ + public function getInputMode() + { + return $this->getViewState('InputMode', TDatePickerInputMode::TextBox); + } + + /** + * @param TDatePickerMode calendar UI mode + */ + public function setMode($value) + { + $this->setViewState('Mode', TPropertyValue::ensureEnum($value, 'TDatePickerMode'), TDatePickerMode::Basic); + } + + /** + * @return TDatePickerMode current calendar UI mode. + */ + public function getMode() + { + return $this->getViewState('Mode', TDatePickerMode::Basic); + } + /** + * @param string the image url for "Image" UI mode. + */ + public function setButtonImageUrl($value) + { + $this->setViewState('ImageUrl', $value, ''); + } + + /** + * @return string the image url for "Image" UI mode. + */ + public function getButtonImageUrl() + { + return $this->getViewState('ImageUrl', ''); + } + + /** + * @param string set the calendar style + */ + public function setCalendarStyle($value) + { + $this->setViewState('CalendarStyle', $value, 'default'); + } + + /** + * @return string current calendar style + */ + public function getCalendarStyle() + { + return $this->getViewState('CalendarStyle', 'default'); + } + + /** + * Set the first day of week, with 0 as Sunday, 1 as Monday, etc. + * @param integer 0 for Sunday, 1 for Monday, 2 for Tuesday, etc. + */ + public function setFirstDayOfWeek($value) + { + $this->setViewState('FirstDayOfWeek', TPropertyValue::ensureInteger($value), 1); + } + + /** + * @return integer first day of the week + */ + public function getFirstDayOfWeek() + { + return $this->getViewState('FirstDayOfWeek', 1); + } + + /** + * @return string text for the date picker button. Default is "...". + */ + public function getButtonText() + { + return $this->getViewState('ButtonText', '...'); + } + + /** + * @param string text for the date picker button + */ + public function setButtonText($value) + { + $this->setViewState('ButtonText', $value, '...'); + } + + /** + * @param integer date picker starting year, default is 2000. + */ + public function setFromYear($value) + { + $this->setViewState('FromYear', TPropertyValue::ensureInteger($value), intval(@date('Y'))-5); + } + + /** + * @return integer date picker starting year, default is -5 years + */ + public function getFromYear() + { + return $this->getViewState('FromYear', intval(@date('Y'))-5); + } + + /** + * @param integer date picker ending year, default +10 years + */ + public function setUpToYear($value) + { + $this->setViewState('UpToYear', TPropertyValue::ensureInteger($value), intval(@date('Y'))+10); + } + + /** + * @return integer date picker ending year, default +10 years + */ + public function getUpToYear() + { + return $this->getViewState('UpToYear', intval(@date('Y'))+10); + } + + /** + * @param TDatePickerPositionMode calendar UI position + */ + public function setPositionMode($value) + { + $this->setViewState('PositionMode', TPropertyValue::ensureEnum($value, 'TDatePickerPositionMode'), TDatePickerPositionMode::Bottom); + } + + /** + * @return TDatePickerPositionMode current calendar UI position. + */ + public function getPositionMode() + { + return $this->getViewState('PositionMode', TDatePickerPositionMode::Bottom); + } + + /** + * @return integer current selected date from the date picker as timestamp, NULL if timestamp is not set previously. + */ + public function getTimeStamp() + { + if(trim($this->getText())==='') + return null; + else + return $this->getTimeStampFromText(); + } + + /** + * Sets the date for the date picker using timestamp. + * @param float time stamp for the date picker + */ + public function setTimeStamp($value) + { + if($value===null || (is_string($value) && trim($value)==='')) + $this->setText(''); + else + { + $date = TPropertyValue::ensureFloat($value); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$this->getDateFormat()); + $this->setText($formatter->format($date)); + } + } + + /** + * Returns the timestamp selected by the user. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getTimeStamp()}. + * @return integer the timestamp of the TDatePicker control. + * @see getTimeStamp + * @since 3.1.2 + */ + public function getData() + { + return $this->getTimeStamp(); + } + + /** + * Sets the timestamp represented by this control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setTimeStamp()}. + * @param integer the timestamp of the TDatePicker control. + * @see setTimeStamp + * @since 3.1.2 + */ + public function setData($value) + { + $this->setTimeStamp($value); + } + + /** + * @return string the date string. + */ + public function getDate() + { + return $this->getText(); + } + + /** + * @param string date string + */ + public function setDate($value) + { + $this->setText($value); + } + + /** + * Gets the TDatePickerClientScript to set the TDatePicker event handlers. + * + * The date picker on the client-side supports the following events. + * # OnDateChanged -- raised when the date is changed. + * + * You can attach custom javascript code to each of these events + * + * @return TDatePickerClientScript javascript validator event options. + */ + public function getClientSide() + { + if($this->_clientScript===null) + $this->_clientScript = $this->createClientScript(); + return $this->_clientScript; + } + + /** + * @return TDatePickerClientScript javascript validator event options. + */ + protected function createClientScript() + { + return new TDatePickerClientScript; + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return integer the interger timestamp if valid, otherwise the original text. + */ + public function getValidationPropertyValue() + { + if(($text = $this->getText()) === '') + return ''; + $date = $this->getTimeStamp(); + return $date == null ? $text : $date; + } + + /** + * Publish the date picker Css asset files. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getInputMode() === TDatePickerInputMode::DropDownList) + { + $page = $this->getPage(); + $uniqueID = $this->getUniqueID(); + $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'day'); + $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'month'); + $page->registerPostDataLoader($uniqueID.TControl::ID_SEPARATOR.'year'); + } + $this->publishCalendarStyle(); + $this->registerCalendarClientScriptPre(); + } + + /** + * Renders body content. + * This method overrides parent implementation by adding + * additional date picker button if Mode is Button or ImageButton. + * @param THtmlWriter writer + */ + public function render($writer) + { + if($this->getInputMode() == TDatePickerInputMode::TextBox) + { + parent::render($writer); + $this->renderDatePickerButtons($writer); + } + else + { + $this->renderDropDownListCalendar($writer); + if($this->hasDayPattern()) + { + $this->renderClientControlScript($writer); + $this->renderDatePickerButtons($writer); + } + } + } + + /** + * Renders the date picker popup buttons. + */ + protected function renderDatePickerButtons($writer) + { + if($this->getShowCalendar()) + { + switch ($this->getMode()) + { + case TDatePickerMode::Button: + $this->renderButtonDatePicker($writer); + break; + case TDatePickerMode::ImageButton : + $this->renderImageButtonDatePicker($writer); + break; + } + } + } + + /** + * Loads user input data. Override parent implementation, when InputMode + * is DropDownList call getDateFromPostData to get date data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + if($this->getInputMode() == TDatePickerInputMode::TextBox) + return parent::loadPostData($key, $values); + $value = $this->getDateFromPostData($key, $values); + if(!$this->getReadOnly() && $this->getText()!==$value) + { + $this->setText($value); + return true; + } + else + return false; + } + + /** + * Loads date from drop down list data. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return array the date selected + */ + protected function getDateFromPostData($key, $values) + { + $date = @getdate(); + + if(isset($values[$key.'$day'])) + $day = intval($values[$key.'$day']); + else + $day = $date['mday']; + + if(isset($values[$key.'$month'])) + $month = intval($values[$key.'$month']) + 1; + else + $month = $date['mon']; + + if(isset($values[$key.'$year'])) + $year = intval($values[$key.'$year']); + else + $year = $date['year']; + + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getTimeStamp(0, 0, 0, $month, $day, $year); + //$date = @mktime(0, 0, 0, $month, $day, $year); + + $pattern = $this->getDateFormat(); + $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', $pattern); + return $formatter->format($date); + } + + /** + * Get javascript date picker options. + * @return array date picker client-side options + */ + protected function getDatePickerOptions() + { + $options['ID'] = $this->getClientID(); + $options['InputMode'] = $this->getInputMode(); + $options['Format'] = $this->getDateFormat(); + $options['FirstDayOfWeek'] = $this->getFirstDayOfWeek(); + if(($cssClass=$this->getCssClass())!=='') + $options['ClassName'] = $cssClass; + $options['CalendarStyle'] = $this->getCalendarStyle(); + $options['FromYear'] = $this->getFromYear(); + $options['UpToYear'] = $this->getUpToYear(); + switch($this->getMode()) + { + case TDatePickerMode::Basic: + break; + case TDatePickerMode::Clickable: + $options['TriggerEvent'] = "click"; + break; + default: + $options['Trigger'] = $this->getDatePickerButtonID(); + break; + } + $options['PositionMode'] = $this->getPositionMode(); + + $options = array_merge($options, $this->getCulturalOptions()); + if($this->_clientScript!==null) + $options = array_merge($options, + $this->_clientScript->getOptions()->toArray()); + return $options; + } + + /** + * Get javascript localization options, e.g. month and weekday names. + * @return array localization options. + */ + protected function getCulturalOptions() + { + if($this->getCurrentCulture() == 'en') + return array(); + + $date = $this->getLocalizedCalendarInfo(); + $options['MonthNames'] = $date->getMonthNames(); + $options['AbbreviatedMonthNames'] = $date->getAbbreviatedMonthNames(); + $options['ShortWeekDayNames'] = $date->getAbbreviatedDayNames(); + + return $options; + } + + /** + * @return string the current culture, falls back to application if culture is not set. + */ + protected function getCurrentCulture() + { + $app = $this->getApplication()->getGlobalization(false); + return $this->getCulture() == '' ? + ($app ? $app->getCulture() : 'en') : $this->getCulture(); + } + + /** + * @return DateTimeFormatInfo date time format information for the current culture. + */ + protected function getLocalizedCalendarInfo() + { + //expensive operations + $culture = $this->getCurrentCulture(); + Prado::using('System.I18N.core.DateTimeFormatInfo'); + $info = Prado::createComponent('System.I18N.core.CultureInfo', $culture); + return $info->getDateTimeFormat(); + } + + /** + * Renders the drop down list date picker. + */ + protected function renderDropDownListCalendar($writer) + { + if($this->getMode() == TDatePickerMode::Basic) + $this->setMode(TDatePickerMode::ImageButton); + parent::addAttributesToRender($writer); + $writer->removeAttribute('name'); + $writer->removeAttribute('type'); + $writer->addAttribute('id', $this->getClientID()); + + if(strlen($class = $this->getCssClass()) > 0) + $writer->addAttribute('class', $class); + $writer->renderBeginTag('span'); + + $s = Prado::createComponent('System.Util.TDateTimeStamp'); + $date = $s->getDate($this->getTimeStampFromText()); + //$date = @getdate($this->getTimeStampFromText()); + + $this->renderCalendarSelections($writer, $date); + + //render a hidden input field + $writer->addAttribute('name', $this->getUniqueID()); + $writer->addAttribute('type', 'hidden'); + $writer->addAttribute('value', $this->getText()); + $writer->renderBeginTag('input'); + + $writer->renderEndTag(); + $writer->renderEndTag(); + } + + protected function hasDayPattern() + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + return ($formatter->getDayPattern()!==null); + } + + /** + * Renders the calendar drop down list depending on the DateFormat pattern. + * @param THtmlWriter the Html writer to render the drop down lists. + * @param array the current selected date + */ + protected function renderCalendarSelections($writer, $date) + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + + foreach($formatter->getDayMonthYearOrdering() as $type) + { + if($type == 'day') + $this->renderCalendarDayOptions($writer,$date['mday']); + elseif($type == 'month') + $this->renderCalendarMonthOptions($writer,$date['mon']); + elseif($type == 'year') + $this->renderCalendarYearOptions($writer,$date['year']); + } + } + + /** + * Gets the date from the text input using TSimpleDateFormatter + * @return integer current selected date timestamp + */ + protected function getTimeStampFromText() + { + $pattern = $this->getDateFormat(); + $pattern = str_replace(array('MMMM', 'MMM'), array('MM','MM'), $pattern); + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter',$pattern); + return $formatter->parse($this->getText()); + } + + /** + * Renders a drop down lists. + * @param THtmlWriter the writer used for the rendering purpose + * @param array list of selection options + * @param mixed selected key. + */ + private function renderDropDownListOptions($writer,$options,$selected=null) + { + foreach($options as $k => $v) + { + $writer->addAttribute('value', $k); + if($k == $selected) + $writer->addAttribute('selected', 'selected'); + $writer->renderBeginTag('option'); + $writer->write($v); + $writer->renderEndTag(); + } + } + + /** + * Renders the day drop down list options. + * @param THtmlWriter the writer used for the rendering purpose + * @param mixed selected day. + */ + protected function renderCalendarDayOptions($writer, $selected=null) + { + $days = $this->getDropDownDayOptions(); + $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'day'); + $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'day'); + $writer->addAttribute('class', 'datepicker_day_options'); + if($this->getReadOnly() || !$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag('select'); + $this->renderDropDownListOptions($writer, $days, $selected); + $writer->renderEndTag(); + } + + /** + * @return array list of day options for a drop down list. + */ + protected function getDropDownDayOptions() + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + $days = array(); + $requiresPadding = $formatter->getDayPattern() === 'dd'; + for($i=1;$i<=31;$i++) + { + $days[$i] = $requiresPadding ? str_pad($i, 2, '0', STR_PAD_LEFT) : $i; + } + return $days; + } + + /** + * Renders the month drop down list options. + * @param THtmlWriter the writer used for the rendering purpose + * @param mixed selected month. + */ + protected function renderCalendarMonthOptions($writer, $selected=null) + { + $info = $this->getLocalizedCalendarInfo(); + $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'month'); + $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'month'); + $writer->addAttribute('class', 'datepicker_month_options'); + if($this->getReadOnly() || !$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag('select'); + $this->renderDropDownListOptions($writer, + $this->getLocalizedMonthNames($info), $selected-1); + $writer->renderEndTag(); + } + + /** + * Returns the localized month names that depends on the month format pattern. + * "MMMM" will return the month names, "MM" or "MMM" return abbr. month names + * and "M" return month digits. + * @param DateTimeFormatInfo localized date format information. + * @return array localized month names. + */ + protected function getLocalizedMonthNames($info) + { + $formatter = Prado::createComponent('System.Util.TSimpleDateFormatter', + $this->getDateFormat()); + switch($formatter->getMonthPattern()) + { + case 'MMM': return $info->getAbbreviatedMonthNames(); + case 'MM': + $array = array(); + for($i=1;$i<=12;$i++) + $array[$i-1] = $i < 10 ? '0'.$i : $i; + return $array; + case 'M': + $array = array(); for($i=1;$i<=12;$i++) $array[$i-1] = $i; + return $array; + default : return $info->getMonthNames(); + } + } + + /** + * Renders the year drop down list options. + * @param THtmlWriter the writer used for the rendering purpose + * @param mixed selected year. + */ + protected function renderCalendarYearOptions($writer, $selected=null) + { + $years = array(); + for($i = $this->getFromYear(); $i <= $this->getUpToYear(); $i++) + $years[$i] = $i; + $writer->addAttribute('id', $this->getClientID().TControl::CLIENT_ID_SEPARATOR.'year'); + $writer->addAttribute('name', $this->getUniqueID().TControl::ID_SEPARATOR.'year'); + $writer->addAttribute('class', 'datepicker_year_options'); + if($this->getReadOnly() || !$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag('select'); + $this->renderDropDownListOptions($writer, $years, $selected); + $writer->renderEndTag(); + } + + /** + * Gets the ID for the date picker trigger button. + * @return string unique button ID + */ + protected function getDatePickerButtonID() + { + return $this->getClientID().'button'; + } + + /** + * Adds an additional button such that when clicked it shows the date picker. + * @return THtmlWriter writer + */ + protected function renderButtonDatePicker($writer) + { + $writer->addAttribute('id', $this->getDatePickerButtonID()); + $writer->addAttribute('type', 'button'); + $writer->addAttribute('class', $this->getCssClass().' TDatePickerButton'); + $writer->addAttribute('value',$this->getButtonText()); + if(!$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->renderBeginTag("input"); + $writer->renderEndTag(); + } + + /** + * Adds an additional image button such that when clicked it shows the date picker. + * @return THtmlWriter writer + */ + protected function renderImageButtonDatePicker($writer) + { + $url = $this->getButtonImageUrl(); + $url = empty($url) ? $this->getAssetUrl('calendar.png') : $url; + $writer->addAttribute('id', $this->getDatePickerButtonID()); + $writer->addAttribute('src', $url); + $writer->addAttribute('alt', ' '); + $writer->addAttribute('class', $this->getCssClass().' TDatePickerImageButton'); + if(!$this->getEnabled(true)) + $writer->addAttribute('disabled', 'disabled'); + $writer->addAttribute('type', 'image'); + $writer->addAttribute('onclick', 'return false;'); + $writer->renderBeginTag('input'); + $writer->renderEndTag(); + } + + /** + * @param string date picker asset file in the self::SCRIPT_PATH directory. + * @return string date picker asset url. + */ + protected function getAssetUrl($file='') + { + $base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl(); + return $base.'/'.self::SCRIPT_PATH.'/'.$file; + } + + /** + * Publish the calendar style Css asset file. + * @return string Css file url. + */ + protected function publishCalendarStyle() + { + $url = $this->getAssetUrl($this->getCalendarStyle().'.css'); + $cs = $this->getPage()->getClientScript(); + if(!$cs->isStyleSheetFileRegistered($url)) + $cs->registerStyleSheetFile($url, $url); + return $url; + } + + /** + * Add the client id to the input textbox, and register the client scripts. + * @param THtmlWriter writer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + } + + /** + * Registers the javascript code to initialize the date picker. + */ + protected function registerCalendarClientScriptPre() + { + if($this->getShowCalendar()) + { + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript("datepicker"); + } + } + + protected function renderClientControlScript($writer) + { + if($this->getShowCalendar()) + { + $cs = $this->getPage()->getClientScript(); + if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) + { + $spacer = $this->getAssetUrl('spacer.gif'); + $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; + $cs->registerEndScript('TDatePicker.spacer', $code); + } + + $options = TJavaScript::encode($this->getDatePickerOptions()); + $code = "new Prado.WebUI.TDatePicker($options);"; + $cs->registerEndScript("prado:".$this->getClientID(), $code); + } + } +} + +/** + * TDatePickerClientScript class. + * + * Client-side date picker event {@link setOnDateChanged OnDateChanged} + * can be modified through the {@link TDatePicker::getClientSide ClientSide} + * property of a date picker. + * + * The OnDateChanged event is raise when the date picker's date + * is changed. + * The formatted date according to {@link TDatePicker::getDateFormat DateFormat} is sent + * as parameter to this event + * + * @author Wei Zhuo + * @version $Id: TDatePicker.php 3318 2013-09-04 14:04:51Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerClientScript extends TClientSideOptions +{ + /** + * Javascript code to execute when the date picker's date is changed. + * @param string javascript code + */ + public function setOnDateChanged($javascript) + { + $this->setFunction('OnDateChanged', $javascript); + } + + /** + * @return string javascript code to execute when the date picker's date is changed. + */ + public function getOnDateChanged() + { + return $this->getOption('OnDateChanged'); + } +} + + +/** + * TDatePickerInputMode class. + * TDatePickerInputMode defines the enumerable type for the possible datepicker input methods. + * + * The following enumerable values are defined: + * - TextBox: text boxes are used to input date values + * - DropDownList: dropdown lists are used to pick up date values + * + * @author Qiang Xue + * @version $Id: TDatePicker.php 3318 2013-09-04 14:04:51Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerInputMode extends TEnumerable +{ + const TextBox='TextBox'; + const DropDownList='DropDownList'; +} + +/** + * TDatePickerMode class. + * TDatePickerMode defines the enumerable type for the possible UI mode + * that a {@link TDatePicker} control can take. + * + * The following enumerable values are defined: + * - Basic: Only shows a text input, focusing on the input shows the date picker + * - Clickable: Only shows a text input, clicking on the input shows the date picker (since 3.2) + * - Button: Shows a button next to the text input, clicking on the button shows the date, button text can be by the + * - ImageButton: Shows an image next to the text input, clicking on the image shows the date picker, + * + * @author Qiang Xue + * @version $Id: TDatePicker.php 3318 2013-09-04 14:04:51Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerMode extends TEnumerable +{ + const Basic='Basic'; + const Clickable='Clickable'; + const Button='Button'; + const ImageButton='ImageButton'; +} + +/** + * TDatePickerPositionMode class. + * TDatePickerPositionMode defines the positions available for the calendar popup, relative to the corresponding input. + * + * The following enumerable values are defined: + * - Top: the date picker is placed above the input field + * - Bottom: the date picker is placed below the input field + * + * @author Carl G. Mathisen + * @package System.Web.UI.WebControls + * @since 3.1.4 + */ +class TDatePickerPositionMode extends TEnumerable +{ + const Top='Top'; + const Bottom='Bottom'; +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TDropDownList.php b/gui/baculum/framework/Web/UI/WebControls/TDropDownList.php new file mode 100644 index 0000000000..8dda857b5b --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDropDownList.php @@ -0,0 +1,154 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDropDownList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TListControl class + */ +Prado::using('System.Web.UI.WebControls.TListControl'); + +/** + * TDropDownList class + * + * TDropDownList displays a dropdown list on a Web page. + * It inherits all properties and events from {@link TListControl}. + * + * Since v3.0.3, TDropDownList starts to support optgroup. To specify an option group for + * a list item, set a Group attribute with it, + * + * $listitem->Attributes->Group="Group Name"; + * // or in template + * + * + * Since v3.1.1, TDropDownList starts to support prompt text. That is, a prompt item can be + * displayed as the first list item by specifying either {@link setPromptText PromptText} or + * {@link setPromptValue PromptValue}, or both. Choosing the prompt item will unselect the TDropDownList. + * + * When a prompt item is set, its index in the list is set to -1. So, the {@link getSelectedIndex SelectedIndex} + * property is not affected by a prompt item: the items list will still be zero-based. + * + * The {@link clearSelection clearSelection} method will select the prompt item if existing, otherway the first + * available item in the dropdown list will be selected. + * + * @author Qiang Xue + * @version $Id: TDropDownList.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TDropDownList extends TListControl implements IPostBackDataHandler, IValidatable +{ + private $_dataChanged=false; + private $_isValid=true; + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('name',$this->getUniqueID()); + parent::addAttributesToRender($writer); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TDropDownList'; + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + if(!$this->getEnabled(true)) + return false; + $this->ensureDataBound(); + $selection=isset($values[$key])?$values[$key]:null; + if($selection!==null) + { + $index=$this->getItems()->findIndexByValue($selection,false); + if($this->getSelectedIndex()!==$index) + { + $this->setSelectedIndex($index); + return $this->_dataChanged=true; + } + } + return false; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getSelectedIndex SelectedIndex} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + if($this->getAutoPostBack() && $this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onSelectedIndexChanged(null); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * @throws TNotSupportedException if this method is invoked + */ + public function setSelectedIndices($indices) + { + throw new TNotSupportedException('dropdownlist_selectedindices_unsupported'); + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getSelectedValue(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TDropDownListColumn.php b/gui/baculum/framework/Web/UI/WebControls/TDropDownListColumn.php new file mode 100644 index 0000000000..40e07aef1e --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TDropDownListColumn.php @@ -0,0 +1,321 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TDropDownListColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); +Prado::using('System.Web.UI.WebControls.TDropDownList'); + +/** + * TDropDownListColumn class + * + * TDropDownListColumn represents a column that is bound to a field in a data source. + * The cells in the column will be displayed using the data indexed by + * {@link setDataTextField DataTextField}. You can customize the display by + * setting {@link setDataTextFormatString DataTextFormatString}. + * + * If {@link setReadOnly ReadOnly} is false, TDropDownListColumn will display cells in edit mode + * with dropdown lists. Otherwise, a static text is displayed. + * The currently selected dropndown list item is specified by the data indexed with + * {@link setDataValueField DataValueField}. + * + * There are two approaches to specify the list items available for selection. + * The first approach uses template syntax as follows, + * + * + * + * + * + * + * + * The second approach specifies a data source to be bound to the dropdown lists + * by setting {@link setListDataSource ListDataSource}. Like generic list controls, + * you may also want to specify which data fields are used for item values and texts + * by setting {@link setListValueField ListValueField} and + * {@link setListTextField ListTextField}, respectively. + * Furthermore, the item texts may be formatted by using {@link setListTextFormatString ListTextFormatString}. + * Note, if you specify {@link setListDataSource ListDataSource}, do it before + * calling the datagrid's dataBind(). + * + * The dropdown list control in the TDropDownListColumn can be accessed by one of + * the following two methods: + * + * $datagridItem->DropDownListColumnID->DropDownList + * $datagridItem->DropDownListColumnID->Controls[0] + * + * The second method is possible because the dropdown list control created within the + * datagrid cell is the first child. + * + * @author Qiang Xue + * @version $Id: TDropDownListColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDropDownListColumn extends TDataGridColumn +{ + private $_stateLoaded=false; + private $_dataBound=false; + private $_listControl=null; + + public function __construct() + { + $this->_listControl=new TDropDownList; + } + + /** + * Loads items from viewstate. + * This method overrides the parent implementation by loading list items + * @param mixed state values + */ + public function loadState($state) + { + parent::loadState($state); + $this->_stateLoaded=true; + if(!$this->_dataBound) + $this->_listControl->getItems()->loadState($this->getViewState('Items',null)); + } + + /** + * Saves items into viewstate. + * This method overrides the parent implementation by saving list items + */ + public function saveState() + { + $this->setViewState('Items',$this->_listControl->getItems()->saveState(),null); + return parent::saveState(); + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TListItem} objects into the {@link getItems Items} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + // Do not add items from template if items are loaded from viewstate + if(!$this->_stateLoaded && ($object instanceof TListItem)) + { + $object->setSelected(false); + $index=$this->_listControl->getItems()->add($object); + } + } + + /** + * @return string the field of the data source that provides the text content of the column. + */ + public function getDataTextField() + { + return $this->getViewState('DataTextField',''); + } + + /** + * Sets the field of the data source that provides the text content of the column. + * If this is not set, the data specified via {@link getDataValueField DataValueField} + * will be displayed in the column. + * @param string the field of the data source that provides the text content of the column. + */ + public function setDataTextField($value) + { + $this->setViewState('DataTextField',$value,''); + } + + /** + * @return string the formatting string used to control how the bound data will be displayed. + */ + public function getDataTextFormatString() + { + return $this->getViewState('DataTextFormatString',''); + } + + /** + * @param string the formatting string used to control how the bound data will be displayed. + */ + public function setDataTextFormatString($value) + { + $this->setViewState('DataTextFormatString',$value,''); + } + + /** + * @return string the field of the data source that provides the key selecting an item in dropdown list. + */ + public function getDataValueField() + { + return $this->getViewState('DataValueField',''); + } + + /** + * Sets the field of the data source that provides the key selecting an item in dropdown list. + * If this is not present, the data specified via {@link getDataTextField DataTextField} (without + * applying the formatting string) will be used for selection, instead. + * @param string the field of the data source that provides the key selecting an item in dropdown list. + */ + public function setDataValueField($value) + { + $this->setViewState('DataValueField',$value,''); + } + + /** + * @return boolean whether the items in the column can be edited. Defaults to false. + */ + public function getReadOnly() + { + return $this->getViewState('ReadOnly',false); + } + + /** + * @param boolean whether the items in the column can be edited + */ + public function setReadOnly($value) + { + $this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return Traversable data source to be bound to the dropdown list boxes. + */ + public function getListDataSource() + { + return $this->_listControl->getDataSource(); + } + + /** + * @param Traversable|array|string data source to be bound to the dropdown list boxes. + */ + public function setListDataSource($value) + { + $this->_listControl->setDataSource($value); + } + + /** + * @return string the data field used to populate the values of the dropdown list items. Defaults to empty. + */ + public function getListValueField() + { + return $this->getViewState('ListValueField',''); + } + + /** + * @param string the data field used to populate the values of the dropdown list items + */ + public function setListValueField($value) + { + $this->setViewState('ListValueField',$value,''); + } + + /** + * @return string the data field used to populate the texts of the dropdown list items. Defaults to empty. + */ + public function getListTextField() + { + return $this->getViewState('ListTextField',''); + } + + /** + * @param string the data field used to populate the texts of the dropdown list items + */ + public function setListTextField($value) + { + $this->setViewState('ListTextField',$value,''); + } + + /** + * @return string the formatting string used to control how the list item texts will be displayed. + */ + public function getListTextFormatString() + { + return $this->getViewState('ListTextFormatString',''); + } + + /** + * @param string the formatting string used to control how the list item texts will be displayed. + */ + public function setListTextFormatString($value) + { + $this->setViewState('ListTextFormatString',$value,''); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates a textbox for item in edit mode and the column is not read-only. + * Otherwise it displays a static text. + * The caption of the button and the static text are retrieved + * from the datasource. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if(!$this->_dataBound && $this->_listControl->getDataSource()!==null) + { + $this->_listControl->setDataTextField($this->getListTextField()); + $this->_listControl->setDataValueField($this->getListValueField()); + $this->_listControl->setDataTextFormatString($this->getListTextFormatString()); + $this->_listControl->dataBind(); + $this->_dataBound=true; + } + switch($itemType) + { + case TListItemType::EditItem: + if(!$this->getReadOnly()) + { + $listControl=clone $this->_listControl; + $cell->getControls()->add($listControl); + $cell->registerObject('DropDownList',$listControl); + $control=$listControl; + } + else + $control=$cell; + $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + break; + case TListItemType::Item: + case TListItemType::AlternatingItem: + case TListItemType::SelectedItem: + if($this->getDataTextField()!=='' || $this->getDataValueField()!=='') + $cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + break; + default: + parent::initializeCell($cell,$columnIndex,$itemType); + break; + } + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $data=$item->getData(); + if(($valueField=$this->getDataValueField())!=='') + $value=$this->getDataFieldValue($data,$valueField); + else + $value=''; + if(($textField=$this->getDataTextField())!=='') + { + $text=$this->getDataFieldValue($data,$textField); + if($valueField==='') + $value=$text; + $formatString=$this->getDataTextFormatString(); + $text=$this->formatDataValue($formatString,$text); + } + else + $text=$value; + if($sender instanceof TTableCell) + $sender->setText($text); + else if($sender instanceof TDropDownList) + $sender->setSelectedValue($value); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TEditCommandColumn.php b/gui/baculum/framework/Web/UI/WebControls/TEditCommandColumn.php new file mode 100644 index 0000000000..2f69c001fe --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TEditCommandColumn.php @@ -0,0 +1,265 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TEditCommandColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); + +/** + * TEditCommandColumn class + * + * TEditCommandColumn contains the Edit command buttons for editing data items in each row. + * + * TEditCommandColumn will create an edit button if a cell is not in edit mode. + * Otherwise an update button and a cancel button will be created within the cell. + * The button captions are specified using {@link setEditText EditText}, + * {@link setUpdateText UpdateText}, and {@link setCancelText CancelText}. + * + * The buttons in the column can be set to display as hyperlinks, push or image buttons + * by setting the {@link setButtonType ButtonType} property. + * + * When an edit button is clicked, the datagrid will generate an + * {@link onEditCommand OnEditCommand} event. When an update/cancel button + * is clicked, the datagrid will generate an + * {@link onUpdateCommand OnUpdateCommand} or an {@link onCancelCommand OnCancelCommand} + * You can write these event handlers to change the state of specific datagrid item. + * + * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup} + * properties affect the corresponding properties of the edit and update buttons. + * The cancel button does not cause validation by default. + * + * The command buttons in the column can be accessed by one of the following methods: + * + * $datagridItem->ButtonColumnID->EditButton (or UpdateButton, CancelButton) + * $datagridItem->ButtonColumnID->Controls[0] + * + * The second method is possible because the button control created within the + * datagrid cell is the first child. + * + * @author Qiang Xue + * @version $Id: TEditCommandColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TEditCommandColumn extends TDataGridColumn +{ + /** + * @return TButtonColumnType the type of command button. Defaults to TButtonColumnType::LinkButton. + */ + public function getButtonType() + { + return $this->getViewState('ButtonType',TButtonColumnType::LinkButton); + } + + /** + * @param TButtonColumnType the type of command button. + */ + public function setButtonType($value) + { + $this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TButtonColumnType'),TButtonColumnType::LinkButton); + } + + /** + * @return string the caption of the edit button. Defaults to 'Edit'. + */ + public function getEditText() + { + return $this->getViewState('EditText','Edit'); + } + + /** + * @param string the caption of the edit button + */ + public function setEditText($value) + { + $this->setViewState('EditText',$value,'Edit'); + } + + /** + * @return string the URL of the image file for edit image buttons + */ + public function getEditImageUrl() + { + return $this->getViewState('EditImageUrl',''); + } + + /** + * @param string the URL of the image file for edit image buttons + */ + public function setEditImageUrl($value) + { + $this->setViewState('EditImageUrl',$value,''); + } + + /** + * @return string the caption of the update button. Defaults to 'Update'. + */ + public function getUpdateText() + { + return $this->getViewState('UpdateText','Update'); + } + + /** + * @param string the caption of the update button + */ + public function setUpdateText($value) + { + $this->setViewState('UpdateText',$value,'Update'); + } + + /** + * @return string the URL of the image file for update image buttons + */ + public function getUpdateImageUrl() + { + return $this->getViewState('UpdateImageUrl',''); + } + + /** + * @param string the URL of the image file for update image buttons + */ + public function setUpdateImageUrl($value) + { + $this->setViewState('UpdateImageUrl',$value,''); + } + + /** + * @return string the caption of the cancel button. Defaults to 'Cancel'. + */ + public function getCancelText() + { + return $this->getViewState('CancelText','Cancel'); + } + + /** + * @param string the caption of the cancel button + */ + public function setCancelText($value) + { + $this->setViewState('CancelText',$value,'Cancel'); + } + + /** + * @return string the URL of the image file for cancel image buttons + */ + public function getCancelImageUrl() + { + return $this->getViewState('CancelImageUrl',''); + } + + /** + * @param string the URL of the image file for cancel image buttons + */ + public function setCancelImageUrl($value) + { + $this->setViewState('CancelImageUrl',$value,''); + } + + /** + * @return boolean whether postback event trigger by edit or update button will cause input validation, default is true + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by edit or update button will cause input validation + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the edit or update button causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the edit or update button causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates an update and a cancel button for cell in edit mode. + * Otherwise it creates an edit button. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem) + { + $button=$this->createButton('Edit',$this->getEditText(),false,''); + $cell->getControls()->add($button); + $cell->registerObject('EditButton',$button); + } + else if($itemType===TListItemType::EditItem) + { + $controls=$cell->getControls(); + $button=$this->createButton('Update',$this->getUpdateText(),$this->getCausesValidation(),$this->getValidationGroup()); + $controls->add($button); + $cell->registerObject('UpdateButton',$button); + $controls->add(' '); + $button=$this->createButton('Cancel',$this->getCancelText(),false,''); + $controls->add($button); + $cell->registerObject('CancelButton',$button); + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + /** + * Creates a button and initializes its properties. + * The button type is determined by {@link getButtonType ButtonType}. + * @param string command name associated with the button + * @param string button caption + * @param boolean whether the button should cause validation + * @param string the validation group that the button belongs to + * @return mixed the newly created button. + */ + protected function createButton($commandName,$text,$causesValidation,$validationGroup) + { + if($this->getButtonType()===TButtonColumnType::LinkButton) + $button=Prado::createComponent('System.Web.UI.WebControls.TLinkButton'); + else if($this->getButtonType()===TButtonColumnType::PushButton) + $button=Prado::createComponent('System.Web.UI.WebControls.TButton'); + else // image buttons + { + $button=Prado::createComponent('System.Web.UI.WebControls.TImageButton'); + if(strcasecmp($commandName,'Update')===0) + $url=$this->getUpdateImageUrl(); + else if(strcasecmp($commandName,'Cancel')===0) + $url=$this->getCancelImageUrl(); + else + $url=$this->getEditImageUrl(); + $button->setImageUrl($url); + } + $button->setText($text); + $button->setCommandName($commandName); + $button->setCausesValidation($causesValidation); + $button->setValidationGroup($validationGroup); + return $button; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TEmailAddressValidator.php b/gui/baculum/framework/Web/UI/WebControls/TEmailAddressValidator.php new file mode 100644 index 0000000000..dffe912e0a --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TEmailAddressValidator.php @@ -0,0 +1,100 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TEmailAddressValidator.php 3283 2013-03-24 10:19:08Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TRegularExpressionValidator class + */ +Prado::using('System.Web.UI.WebControls.TRegularExpressionValidator'); + +/** + * TEmailAddressValidator class + * + * TEmailAddressValidator validates whether the value of an associated + * input component is a valid email address. If {@link getCheckMXRecord CheckMXRecord} + * is true, it will check MX record for the email adress, provided + * checkdnsrr() is available in the installed PHP. + * + * @author Qiang Xue + * @version $Id: TEmailAddressValidator.php 3283 2013-03-24 10:19:08Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TEmailAddressValidator extends TRegularExpressionValidator +{ + /** + * Regular expression used to validate the email address + * @see http://www.regular-expressions.info/email.html + */ + const EMAIL_REGEXP='[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?'; + + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TEmailAddressValidator'; + } + + /** + * @return string the regular expression that determines the pattern used to validate a field. + */ + public function getRegularExpression() + { + $regex=parent::getRegularExpression(); + return $regex===''?self::EMAIL_REGEXP:$regex; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + public function evaluateIsValid() + { + $value=$this->getValidationValue($this->getValidationTarget()); + $valid=$valid=is_string($value) && strlen($value)<=254 && parent::evaluateIsValid(); + + if($valid && $this->getCheckMXRecord() && function_exists('checkdnsrr')) + { + if($value!=='') + { + if(($pos=strpos($value,'@'))!==false) + { + $domain=substr($value,$pos+1); + return $domain===''?false:checkdnsrr($domain,'MX'); + } + else + return false; + } + } + return $valid; + } + + /** + * @return boolean whether to check MX record for the email address being validated. Defaults to true. + */ + public function getCheckMXRecord() + { + return $this->getViewState('CheckMXRecord',false); + } + + /** + * @param boolean whether to check MX record for the email address being validated. + * Note, if {@link checkdnsrr} is not available, this check will not be performed. + */ + public function setCheckMXRecord($value) + { + $this->setViewState('CheckMXRecord',TPropertyValue::ensureBoolean($value),false); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TExpression.php b/gui/baculum/framework/Web/UI/WebControls/TExpression.php new file mode 100644 index 0000000000..3cb1aa8cf7 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TExpression.php @@ -0,0 +1,62 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TExpression.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TExpression class + * + * TExpression evaluates a PHP expression and renders the result. + * The expression is evaluated during the rendering stage. The expression being + * evaluated can be set via the property {@link setExpression Expression}. + * The context of the expression evaluated is the TExpression object itself. + * + * Note, since TExpression allows evaluation of arbitrary PHP expression, + * make sure {@link setExpression Expression} does not come directly from user input. + * + * @author Qiang Xue + * @version $Id: TExpression.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TExpression extends TControl +{ + /** + * @var string PHP expression to be evaluated + */ + private $_e=''; + + /** + * @return string the expression to be evaluated + */ + public function getExpression() + { + return $this->_e; + } + + /** + * @param string the expression to be evaluated + */ + public function setExpression($value) + { + $this->_e=$value; + } + + /** + * Renders the evaluation result of the expression. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if($this->_e!=='') + $writer->write($this->evaluateExpression($this->_e)); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TFileUpload.php b/gui/baculum/framework/Web/UI/WebControls/TFileUpload.php new file mode 100644 index 0000000000..c6dc595af1 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TFileUpload.php @@ -0,0 +1,281 @@ +, Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TFileUpload.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TFileUpload class + * + * TFileUpload displays a file upload field on a page. Upon postback, + * the text entered into the field will be treated as the name of the file + * that will be uploaded to the server. The property {@link getHasFile HasFile} + * indicates whether the file upload is successful. If successful, the file + * may be obtained by calling {@link saveAs} to save it at a specified place. + * You can use {@link getFileName FileName}, {@link getFileType FileType}, + * {@link getFileSize FileSize} to get the original client-side file name, + * the file mime type, and the file size information. If the upload is not + * successful, {@link getErrorCode ErrorCode} contains the error code + * describing the cause of failure. + * + * TFileUpload raises {@link onFileUpload OnFileUpload} event if a file is uploaded + * (whether it succeeds or not). + * + * @author Marcus Nyeholt , Qiang Xue + * @version $Id: TFileUpload.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TFileUpload extends TWebControl implements IPostBackDataHandler, IValidatable +{ + /** + * Maximum file size (in bytes) allowed to be uploaded, defaults to 1MB. + */ + const MAX_FILE_SIZE=1048576; + /** + * @var integer the size of the uploaded file (in bytes) + */ + private $_fileSize=0; + /** + * @var string The original name of the file on the client machine + */ + private $_fileName=''; + /** + * @var string the name of the temporary file storing the uploaded file + */ + private $_localName=''; + /** + * @var string the uploaded file mime type + */ + private $_fileType=''; + /** + * @var integer error code of the current file upload + */ + protected $_errorCode=UPLOAD_ERR_NO_FILE; + private $_dataChanged=false; + private $_isValid=true; + + /** + * @return string tag name of the file upload control + */ + protected function getTagName() + { + return 'input'; + } + + /** + * Sets name attribute to the unique ID of the control. + * This method overrides the parent implementation with additional file update control specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $this->getPage()->ensureRenderInForm($this); + parent::addAttributesToRender($writer); + $writer->addAttribute('type','file'); + $writer->addAttribute('name',$this->getUniqueID()); + $isEnabled=$this->getEnabled(true); + if(!$isEnabled && $this->getEnabled()) // in this case parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + } + + /** + * Sets Enctype of the form on the page. + * This method overrides the parent implementation and is invoked before render. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if(($form=$this->getPage()->getForm())!==null) + $form->setEnctype('multipart/form-data'); + $this->getPage()->getClientScript()->registerHiddenField('MAX_FILE_SIZE',$this->getMaxFileSize()); + if($this->getEnabled(true)) + $this->getPage()->registerRequiresPostData($this); + } + + /** + * @return integer the maximum file size, defaults to 1MB (1048576 bytes). + * @see setMaxFileSize + */ + public function getMaxFileSize() + { + return $this->getViewState('MaxFileSize',self::MAX_FILE_SIZE); + } + + /** + * Sets the maximum size that a file can be uploaded. + * Note, this is an advisory value to the browser. Sets this property with + * a reasonably large size to save users the trouble of waiting + * for a big file being transferred only to find that it was too big + * and the transfer failed. + * @param int the maximum upload size allowed for a file. + */ + public function setMaxFileSize($size) + { + $this->setViewState('MaxFileSize',TPropertyValue::ensureInteger($size),self::MAX_FILE_SIZE); + } + + /** + * @return string the original full path name of the file on the client machine + */ + public function getFileName() + { + return $this->_fileName; + } + + /** + * @return integer the actual size of the uploaded file in bytes + */ + public function getFileSize() + { + return $this->_fileSize; + } + + /** + * @return string the MIME-type of the uploaded file (such as "image/gif"). + * This mime type is not checked on the server side and do not take its value for granted. + */ + public function getFileType() + { + return $this->_fileType; + } + + /** + * @return string the local name of the file (where it is after being uploaded). + * Note, PHP will delete this file automatically after finishing this round of request. + */ + public function getLocalName() + { + return $this->_localName; + } + + /** + * Returns an error code describing the status of this file uploading. + * @return integer the error code + * @see http://www.php.net/manual/en/features.file-upload.errors.php + */ + public function getErrorCode() + { + return $this->_errorCode; + } + + /** + * @return boolean whether the file is uploaded successfully + */ + public function getHasFile() + { + return $this->_errorCode===UPLOAD_ERR_OK; + } + + /** + * Saves the uploaded file. + * @param string the file name used to save the uploaded file + * @param boolean whether to delete the temporary file after saving. + * If true, you will not be able to save the uploaded file again. + * @return boolean true if the file saving is successful + */ + public function saveAs($fileName,$deleteTempFile=true) + { + if($this->_errorCode===UPLOAD_ERR_OK) + { + if($deleteTempFile) + return move_uploaded_file($this->_localName,$fileName); + else if(is_uploaded_file($this->_localName)) + return file_put_contents($fileName,file_get_contents($this->_localName))!==false; + else + return false; + } + else + return false; + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the control has been changed + */ + public function loadPostData($key,$values) + { + if(isset($_FILES[$key])) + { + $this->_fileName=$_FILES[$key]['name']; + $this->_fileSize=$_FILES[$key]['size']; + $this->_fileType=$_FILES[$key]['type']; + $this->_errorCode=$_FILES[$key]['error']; + $this->_localName=$_FILES[$key]['tmp_name']; + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Raises postdata changed event. + * This method calls {@link onFileUpload} method. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onFileUpload(null); + } + + /** + * This method is invoked when a file is uploaded during a postback. + * The method raises OnFileUpload event to fire up the event handler. + * If you override this method, be sure to call the parent implementation + * so that the event delegates can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onFileUpload($param) + { + $this->raiseEvent('OnFileUpload',$this,$param); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Returns the original file name as the property value to be validated. + * This method is required by IValidatable property. + * @return mixed the property value to be validated + */ + public function getValidationPropertyValue() + { + return $this->getFileName(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } + +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TFlushOutput.php b/gui/baculum/framework/Web/UI/WebControls/TFlushOutput.php new file mode 100644 index 0000000000..dba35c790d --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TFlushOutput.php @@ -0,0 +1,85 @@ + + * @link http://www.pradosoft.com/ + * @license http://www.pradosoft.com/license/ + * @version $Id: TFlushOutput.php $ + * @package System.Web.UI.WebControls + */ + +/** + * TFlushOutput class. + * + * TFlushOutput enables forced flushing of the current output buffer + * at (a) certain point(s) in the page, after rendering of all previous + * controls has been completed. + * + * To use TFlushOutput, simply place it in a template where you want + * the have the output buffered between the start of the page or the + * last TFlushOutput to be sent to the client immediately + * + * + * + * + * You can specify whether you want to keep buffering of the output + * (if it was enabled) till the next occourence of a + * or the end of the page rendering, or stop buffering, by using the + * {@link setContinueBuffering ContinueBuffering}. + * + * @author Berczi Gabor + * @version $Id: TFlushOutput.php $ + * @package System.Web.UI.WebControls + * @since 3.1 + */ +class TFlushOutput extends TControl +{ + /** + * @var boolean whether to continue buffering of output + */ + private $_continueBuffering=true; + + + /** + * Constructor. + */ + public function __construct() + { + parent::__construct(); + $this->EnableViewState = false; + } + + /** + * @return Tells whether buffering of output can continue after this point + */ + public function getContinueBuffering() + { + return $this->_continueBuffering; + } + + /** + * @param boolean sets whether buffering of output can continue after this point + */ + public function setContinueBuffering($value) + { + $this->_continueBuffering = TPropertyValue::ensureBoolean($value); + } + + /** + * Flushes the output of all completely rendered controls to the client. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { +//$writer->write(''); + // ajax responses can't be parsed by the client side before loaded and returned completely, + // so don't bother with flushing output somewhere mid-page if refreshing in a callback + if (!$this->Page->IsCallback) + { + $this->Page->flushWriter(); +// $this->Application->flushOutput($this->ContinueBuffering); + } + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TFont.php b/gui/baculum/framework/Web/UI/WebControls/TFont.php new file mode 100644 index 0000000000..12da993b5d --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TFont.php @@ -0,0 +1,318 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TFont.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TFont class + * + * TFont encapsulates the CSS style fields related with font settings. + * + * @author Qiang Xue + * @version $Id: TFont.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TFont extends TComponent +{ + /** + * Bits indicating the font states. + */ + const IS_BOLD=0x01; + const IS_ITALIC=0x02; + const IS_OVERLINE=0x04; + const IS_STRIKEOUT=0x08; + const IS_UNDERLINE=0x10; + + /** + * Bits indicating whether particular font states are changed. + */ + const IS_SET_BOLD=0x01000; + const IS_SET_ITALIC=0x02000; + const IS_SET_OVERLINE=0x04000; + const IS_SET_STRIKEOUT=0x08000; + const IS_SET_UNDERLINE=0x10000; + const IS_SET_SIZE=0x20000; + const IS_SET_NAME=0x40000; + + /** + * @var integer bits representing various states + */ + private $_flags=0; + /** + * @var string font name + */ + private $_name=''; + /** + * @var string font size + */ + private $_size=''; + + /** + * @return boolean whether the font is in bold face. Defaults to false. + */ + public function getBold() + { + return ($this->_flags & self::IS_BOLD)!==0; + } + + /** + * @param boolean whether the font is in bold face + */ + public function setBold($value) + { + $this->_flags |= self::IS_SET_BOLD; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_BOLD; + else + $this->_flags &= ~self::IS_BOLD; + } + + /** + * @return boolean whether the font is in italic face. Defaults to false. + */ + public function getItalic() + { + return ($this->_flags & self::IS_ITALIC)!==0; + } + + /** + * @param boolean whether the font is italic + */ + public function setItalic($value) + { + $this->_flags |= self::IS_SET_ITALIC; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_ITALIC; + else + $this->_flags &= ~self::IS_ITALIC; + } + + /** + * @return boolean whether the font is overlined. Defaults to false. + */ + public function getOverline() + { + return ($this->_flags & self::IS_OVERLINE)!==0; + } + + /** + * @param boolean whether the font is overlined + */ + public function setOverline($value) + { + $this->_flags |= self::IS_SET_OVERLINE; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_OVERLINE; + else + $this->_flags &= ~self::IS_OVERLINE; + } + + /** + * @return string the font size + */ + public function getSize() + { + return $this->_size; + } + + /** + * @param string the font size + */ + public function setSize($value) + { + $this->_flags |= self::IS_SET_SIZE; + $this->_size=$value; + } + + /** + * @return boolean whether the font is strikeout. Defaults to false. + */ + public function getStrikeout() + { + return ($this->_flags & self::IS_STRIKEOUT)!==0; + } + + /** + * @param boolean whether the font is strikeout + */ + public function setStrikeout($value) + { + $this->_flags |= self::IS_SET_STRIKEOUT; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_STRIKEOUT; + else + $this->_flags &= ~self::IS_STRIKEOUT; + } + + /** + * @return boolean whether the font is underlined. Defaults to false. + */ + public function getUnderline() + { + return ($this->_flags & self::IS_UNDERLINE)!==0; + } + + /** + * @param boolean whether the font is underlined + */ + public function setUnderline($value) + { + $this->_flags |= self::IS_SET_UNDERLINE; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_UNDERLINE; + else + $this->_flags &= ~self::IS_UNDERLINE; + } + + /** + * @return string the font name (family) + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string the font name (family) + */ + public function setName($value) + { + $this->_flags |= self::IS_SET_NAME; + $this->_name=$value; + } + + /** + * @return boolean whether the font is empty + */ + public function getIsEmpty() + { + return !$this->_flags; + } + + /** + * Clears up the font. + */ + public function reset() + { + $this->_flags=0; + $this->_name=''; + $this->_size=''; + } + + /** + * Merges the font with a new one. + * If a font field is not set in the font, it will be overwritten with + * the new one. + * @param TFont the new font + */ + public function mergeWith($font) + { + if($font===null || $font->_flags===0) + return; + if(!($this->_flags & self::IS_SET_BOLD) && ($font->_flags & self::IS_SET_BOLD)) + $this->setBold($font->getBold()); + if(!($this->_flags & self::IS_SET_ITALIC) && ($font->_flags & self::IS_SET_ITALIC)) + $this->setItalic($font->getItalic()); + if(!($this->_flags & self::IS_SET_OVERLINE) && ($font->_flags & self::IS_SET_OVERLINE)) + $this->setOverline($font->getOverline()); + if(!($this->_flags & self::IS_SET_STRIKEOUT) && ($font->_flags & self::IS_SET_STRIKEOUT)) + $this->setStrikeout($font->getStrikeout()); + if(!($this->_flags & self::IS_SET_UNDERLINE) && ($font->_flags & self::IS_SET_UNDERLINE)) + $this->setUnderline($font->getUnderline()); + if(!($this->_flags & self::IS_SET_SIZE) && ($font->_flags & self::IS_SET_SIZE)) + $this->setSize($font->getSize()); + if(!($this->_flags & self::IS_SET_NAME) && ($font->_flags & self::IS_SET_NAME)) + $this->setName($font->getName()); + } + + /** + * Copies the fields in a new font to this font. + * If a font field is set in the new font, the corresponding field + * in this font will be overwritten. + * @param TFont the new font + */ + public function copyFrom($font) + { + if($font===null || $font->_flags===0) + return; + if($font->_flags & self::IS_SET_BOLD) + $this->setBold($font->getBold()); + if($font->_flags & self::IS_SET_ITALIC) + $this->setItalic($font->getItalic()); + if($font->_flags & self::IS_SET_OVERLINE) + $this->setOverline($font->getOverline()); + if($font->_flags & self::IS_SET_STRIKEOUT) + $this->setStrikeout($font->getStrikeout()); + if($font->_flags & self::IS_SET_UNDERLINE) + $this->setUnderline($font->getUnderline()); + if($font->_flags & self::IS_SET_SIZE) + $this->setSize($font->getSize()); + if($font->_flags & self::IS_SET_NAME) + $this->setName($font->getName()); + } + + /** + * @return string the font in a css style string representation. + */ + public function toString() + { + if($this->_flags===0) + return ''; + $str=''; + if($this->_flags & self::IS_SET_BOLD) + $str.='font-weight:'.(($this->_flags & self::IS_BOLD)?'bold;':'normal;'); + if($this->_flags & self::IS_SET_ITALIC) + $str.='font-style:'.(($this->_flags & self::IS_ITALIC)?'italic;':'normal;'); + $textDec=''; + if($this->_flags & self::IS_UNDERLINE) + $textDec.='underline'; + if($this->_flags & self::IS_OVERLINE) + $textDec.=' overline'; + if($this->_flags & self::IS_STRIKEOUT) + $textDec.=' line-through'; + $textDec=ltrim($textDec); + if($textDec!=='') + $str.='text-decoration:'.$textDec.';'; + if($this->_size!=='') + $str.='font-size:'.$this->_size.';'; + if($this->_name!=='') + $str.='font-family:'.$this->_name.';'; + return $str; + } + + /** + * Adds attributes related to CSS styles to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if($this->_flags===0) + return; + if($this->_flags & self::IS_SET_BOLD) + $writer->addStyleAttribute('font-weight',(($this->_flags & self::IS_BOLD)?'bold':'normal')); + if($this->_flags & self::IS_SET_ITALIC) + $writer->addStyleAttribute('font-style',(($this->_flags & self::IS_ITALIC)?'italic':'normal')); + $textDec=''; + if($this->_flags & self::IS_UNDERLINE) + $textDec.='underline'; + if($this->_flags & self::IS_OVERLINE) + $textDec.=' overline'; + if($this->_flags & self::IS_STRIKEOUT) + $textDec.=' line-through'; + $textDec=ltrim($textDec); + if($textDec!=='') + $writer->addStyleAttribute('text-decoration',$textDec); + if($this->_size!=='') + $writer->addStyleAttribute('font-size',$this->_size); + if($this->_name!=='') + $writer->addStyleAttribute('font-family',$this->_name); + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THead.php b/gui/baculum/framework/Web/UI/WebControls/THead.php new file mode 100644 index 0000000000..52ce30134f --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THead.php @@ -0,0 +1,376 @@ + and Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THead.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + */ + +/** + * THead class + * + * THead displays a head element on a page. It displays the content + * enclosed in its body and the page title set by the + * {@link setTitle Title} property. In addition, stylesheets and JavaScripts registered via + * {@link TClientScriptManager::registerStyleSheet}, {@link TClientScriptManager::registerStyleSheetFile} + * {@link TClientScriptManager::registerHeadJavaScript}, and + * {@link TClientScriptManager::registerHeadJavaScriptFile} will also be displayed + * in the head. + * THead also manages and displays meta tags through its {@link getMetaTags MetaTags} + * property. You can add a meta object to the collection in code dynamically, + * or add it in template using the following syntax, + * + * + * + * + * + * + * + * Note, {@link TPage} has a property {@link TPage::getHead Head} that refers to + * the THead control currently on the page. A page can have at most one THead + * control. Although not required, it is recommended to place a THead on your page. + * Without a THead on the page, stylesheets and javascripts in the current page + * theme will not be rendered. + * + * @author Marcus Nyeholt and Qiang Xue + * @version $Id: THead.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0 + */ +class THead extends TControl +{ + /** + * @var TList list of meta name tags to be loaded by {@link THead} + */ + private $_metaTags=null; + + /** + * Registers the head control with the current page. + * This method is invoked when the control enters 'Init' stage. + * The method raises 'Init' event. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onInit($param) + { + parent::onInit($param); + $this->getPage()->setHead($this); + } + + /** + * Processes an object that is created during parsing template. + * This method adds TMetaTag components into the {@link getMetaTags MetaTags} + * collection of the head control. + * @param string|TComponent text string or component parsed and instantiated in template + * @see createdOnTemplate + */ + public function addParsedObject($object) + { + if($object instanceof TMetaTag) + $this->getMetaTags()->add($object); + else + parent::addParsedObject($object); + } + + /** + * @return string the page title. + */ + public function getTitle() + { + return $this->getViewState('Title',''); + } + + /** + * Sets the page title. + * This title will be rendered only if the {@link TPage::getTitle Title} property + * of the page is empty. + * @param string the page title. + */ + public function setTitle($value) + { + $this->setViewState('Title',$value,''); + } + + /** + * @return string base URL of the page. This URL is rendered as the 'href' attribute of tag. Defaults to ''. + */ + public function getBaseUrl() + { + return $this->getViewState('BaseUrl',''); + } + + /** + * @param string base URL of the page. This URL is rendered as the 'href' attribute of tag. + */ + public function setBaseUrl($url) + { + $this->setViewState('BaseUrl',$url,''); + } + + /** + * @return string the URL for the shortcut icon of the page. Defaults to ''. + */ + public function getShortcutIcon() + { + return $this->getViewState('ShortcutIcon',''); + } + + /** + * @param string the URL for the shortcut icon of the page. + */ + public function setShortcutIcon($url) + { + $this->setViewState('ShortcutIcon',$url,''); + } + + /** + * @return TMetaTagCollection meta tag collection + */ + public function getMetaTags() + { + if(($metaTags=$this->getViewState('MetaTags',null))===null) + { + $metaTags=new TMetaTagCollection; + $this->setViewState('MetaTags',$metaTags,null); + } + return $metaTags; + } + + /** + * Renders the head control. + * @param THtmlWriter the writer for rendering purpose. + */ + public function render($writer) + { + $page=$this->getPage(); + $title=$this->getTitle(); + $writer->write("\n".THttpUtility::htmlEncode($title)."\n"); + if(($baseUrl=$this->getBaseUrl())!=='') + $writer->write('\n"); + if(($icon=$this->getShortcutIcon())!=='') + $writer->write('\n"); + + if(($metaTags=$this->getMetaTags())!==null) + { + foreach($metaTags as $metaTag) + { + $metaTag->render($writer); + $writer->writeLine(); + } + } + $cs=$page->getClientScript(); + $cs->renderStyleSheetFiles($writer); + $cs->renderStyleSheets($writer); + if($page->getClientSupportsJavaScript()) + { + $cs->renderHeadScriptFiles($writer); + $cs->renderHeadScripts($writer); + } + parent::render($writer); + $writer->write("\n"); + } +} + +/** + * TMetaTag class. + * + * TMetaTag represents a meta tag appearing in a page head section. + * You can set its {@link setID ID}, {@link setHttpEquiv HttpEquiv}, + * {@link setName Name}, {@link setContent Content}, {@link setScheme Scheme} + * properties, which correspond to id, http-equiv, name, content, and scheme + * attributes for a meta tag, respectively. + * + * @author Qiang Xue + * @version $Id: THead.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TMetaTag extends TComponent +{ + /** + * @var string id of the meta tag + */ + private $_id=''; + /** + * @var string http-equiv attribute of the meta tag + */ + private $_httpEquiv=''; + /** + * @var string name attribute of the meta tag + */ + private $_name=''; + /** + * @var string content attribute of the meta tag + */ + private $_content=''; + /** + * @var string scheme attribute of the meta tag + */ + private $_scheme=''; + + /** + * @return string id of the meta tag + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string id of the meta tag + */ + public function setID($value) + { + $this->_id=$value; + } + + /** + * @return string http-equiv attribute of the meta tag + */ + public function getHttpEquiv() + { + return $this->_httpEquiv; + } + + /** + * @param string http-equiv attribute of the meta tag + */ + public function setHttpEquiv($value) + { + $this->_httpEquiv=$value; + } + + /** + * @return string name attribute of the meta tag + */ + public function getName() + { + return $this->_name; + } + + /** + * @param string name attribute of the meta tag + */ + public function setName($value) + { + $this->_name=$value; + } + + /** + * @return string content attribute of the meta tag + */ + public function getContent() + { + return $this->_content; + } + + /** + * @param string content attribute of the meta tag + */ + public function setContent($value) + { + $this->_content=$value; + } + + /** + * @return string scheme attribute of the meta tag + */ + public function getScheme() + { + return $this->_scheme; + } + + /** + * @param string scheme attribute of the meta tag + */ + public function setScheme($value) + { + $this->_scheme=$value; + } + + /** + * Renders the meta tag. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + if($this->_id!=='') + $writer->addAttribute('id',$this->_id); + if($this->_name!=='') + $writer->addAttribute('name',$this->_name); + if($this->_httpEquiv!=='') + $writer->addAttribute('http-equiv',$this->_httpEquiv); + if($this->_scheme!=='') + $writer->addAttribute('scheme',$this->_scheme); + $writer->addAttribute('content',$this->_content); + $writer->renderBeginTag('meta'); + $writer->renderEndTag(); + } +} + + +/** + * TMetaTagCollection class + * + * TMetaTagCollection represents a collection of meta tags + * contained in a {@link THead} control. + * + * @author Qiang Xue + * @version $Id: THead.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TMetaTagCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing type + * check on the item being added. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TMetaTag} + */ + public function insertAt($index,$item) + { + if($item instanceof TMetaTag) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('metatagcollection_metatag_invalid'); + } + + /** + * Finds the lowest cardinal index of the meta tag whose id is the one being looked for. + * @param string the ID of the meta tag to be looked for + * @return integer the index of the meta tag found, -1 if not found. + */ + public function findIndexByID($id) + { + $index=0; + foreach($this as $item) + { + if($item->getID()===$id) + return $index; + $index++; + } + return -1; + } + + /** + * Finds the item whose value is the one being looked for. + * @param string the id of the meta tag to be looked for + * @return TMetaTag the meta tag found, null if not found. + */ + public function findMetaTagByID($id) + { + if(($index=$this->findIndexByID($id))>=0) + return $this->itemAt($index); + else + return null; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/THeader1.php b/gui/baculum/framework/Web/UI/WebControls/THeader1.php new file mode 100644 index 0000000000..db516679df --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THeader1.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THeader1.php 2590 2008-12-10 11:34:24Z carlgmathisen $ + * @package System.Web.UI.WebControls + */ + +/** + * THeader1 class + * + * This is a simple class to enable your application to have headers but then have your + * theme be able to redefine the TagName + * This is also useful for the {@link TWebControlDecorator} (used by themes). + * + * @author Brad Anderson + * @version $Id: THeader1.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Web.UI.WebControls + * @since 3.2a + */ + +class THeader1 extends THtmlElement { + + /** + * @return string tag name + */ + public function getDefaultTagName() + { + return 'h1'; + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THeader2.php b/gui/baculum/framework/Web/UI/WebControls/THeader2.php new file mode 100644 index 0000000000..be1d76ae56 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THeader2.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THeader2.php 2590 2008-12-10 11:34:24Z carlgmathisen $ + * @package System.Web.UI.WebControls + */ + +/** + * THeader2 class + * + * This is a simple class to enable your application to have headers but then have your + * theme be able to redefine the TagName + * This is also useful for the {@link TWebControlDecorator} (used by themes). + * + * @author Brad Anderson + * @version $Id: THeader2.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Web.UI.WebControls + * @since 3.2a + */ + +class THeader2 extends THtmlElement { + + /** + * @return string tag name + */ + public function getDefaultTagName() + { + return 'h2'; + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THeader3.php b/gui/baculum/framework/Web/UI/WebControls/THeader3.php new file mode 100644 index 0000000000..ff96a190b8 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THeader3.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THeader3.php 2590 2008-12-10 11:34:24Z carlgmathisen $ + * @package System.Web.UI.WebControls + */ + +/** + * THeader3 class + * + * This is a simple class to enable your application to have headers but then have your + * theme be able to redefine the TagName + * This is also useful for the {@link TWebControlDecorator} (used by themes). + * + * @author Brad Anderson + * @version $Id: THeader3.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Web.UI.WebControls + * @since 3.2a + */ + +class THeader3 extends THtmlElement { + + /** + * @return string tag name + */ + public function getDefaultTagName() + { + return 'h3'; + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THeader4.php b/gui/baculum/framework/Web/UI/WebControls/THeader4.php new file mode 100644 index 0000000000..1374c70049 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THeader4.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THeader4.php 2590 2008-12-10 11:34:24Z carlgmathisen $ + * @package System.Web.UI.WebControls + */ + +/** + * THeader4 class + * + * This is a simple class to enable your application to have headers but then have your + * theme be able to redefine the TagName + * This is also useful for the {@link TWebControlDecorator} (used by themes). + * + * @author Brad Anderson + * @version $Id: THeader4.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Web.UI.WebControls + * @since 3.2 + */ + +class THeader4 extends THtmlElement { + + /** + * @return string tag name + */ + public function getDefaultTagName() + { + return 'h4'; + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THeader5.php b/gui/baculum/framework/Web/UI/WebControls/THeader5.php new file mode 100644 index 0000000000..911e17edbb --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THeader5.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THeader5.php 2590 2008-12-10 11:34:24Z carlgmathisen $ + * @package System.Web.UI.WebControls + */ + +/** + * THeader5 class + * + * This is a simple class to enable your application to have headers but then have your + * theme be able to redefine the TagName + * This is also useful for the {@link TWebControlDecorator} (used by themes). + * + * @author Brad Anderson + * @version $Id: THeader5.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Web.UI.WebControls + * @since 3.2 + */ + +class THeader5 extends THtmlElement { + + /** + * @return string tag name + */ + public function getDefaultTagName() + { + return 'h5'; + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THeader6.php b/gui/baculum/framework/Web/UI/WebControls/THeader6.php new file mode 100644 index 0000000000..a2891c3f6e --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THeader6.php @@ -0,0 +1,36 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THeader6.php 2590 2008-12-10 11:34:24Z carlgmathisen $ + * @package System.Web.UI.WebControls + */ + +/** + * THeader6 class + * + * This is a simple class to enable your application to have headers but then have your + * theme be able to redefine the TagName + * This is also useful for the {@link TWebControlDecorator} (used by themes). + * + * @author Brad Anderson + * @version $Id: THeader6.php 2541 2008-10-21 15:05:13Z javalizard $ + * @package System.Web.UI.WebControls + * @since 3.2 + */ + +class THeader6 extends THtmlElement { + + /** + * @return string tag name + */ + public function getDefaultTagName() + { + return 'h6'; + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THiddenField.php b/gui/baculum/framework/Web/UI/WebControls/THiddenField.php new file mode 100644 index 0000000000..041acdb245 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THiddenField.php @@ -0,0 +1,224 @@ + + * @link http://www.xisc.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version $Id: THiddenField.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * THiddenField class + * + * THiddenField displays a hidden input field on a Web page. + * The value of the input field can be accessed via {@link getValue Value} property. + * If upon postback the value is changed, a {@link onValueChanged OnValueChanged} + * event will be raised. + * + * @author Qiang Xue + * @version $Id: THiddenField.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THiddenField extends TControl implements IPostBackDataHandler, IValidatable, IDataRenderer +{ + private $_dataChanged=false; + private $_isValid=true; + + /** + * @return string tag name of the hidden field. + */ + protected function getTagName() + { + return 'input'; + } + + /** + * Sets focus to this control. + * This method overrides the parent implementation by forbidding setting focus to this control. + */ + public function focus() + { + throw new TNotSupportedException('hiddenfield_focus_unsupported'); + } + + /** + * Renders the control. + * This method overrides the parent implementation by rendering + * the hidden field input element. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + $uniqueID=$this->getUniqueID(); + $this->getPage()->ensureRenderInForm($this); + $writer->addAttribute('type','hidden'); + if($uniqueID!=='') + $writer->addAttribute('name',$uniqueID); + if($this->getID()!=='') + $writer->addAttribute('id',$this->getClientID()); + if(($value=$this->getValue())!=='') + $writer->addAttribute('value',$value); + + if($this->getHasAttributes()) + { + foreach($this->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + + $writer->renderBeginTag('input'); + $writer->renderEndTag(); + } + + /** + * Loads hidden field data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $value=$values[$key]; + if($value===$this->getValue()) + return false; + else + { + $this->setValue($value); + return $this->_dataChanged=true; + } + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getValue(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } + + /** + * Raises postdata changed event. + * This method calls {@link onValueChanged} method. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onValueChanged(null); + } + + /** + * This method is invoked when the value of the {@link getValue Value} property changes between posts to the server. + * The method raises 'OnValueChanged' event to fire up the event delegates. + * If you override this method, be sure to call the parent implementation + * so that the attached event handlers can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onValueChanged($param) + { + $this->raiseEvent('OnValueChanged',$this,$param); + } + + /** + * @return string the value of the THiddenField + */ + public function getValue() + { + return $this->getViewState('Value',''); + } + + /** + * Sets the value of the THiddenField + * @param string the value to be set + */ + public function setValue($value) + { + $this->setViewState('Value',$value,''); + } + + /** + * Returns the value of the hidden field. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string value of the hidden field + * @see getValue + * @since 3.1.0 + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Sets the value of the hidden field. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string value of the hidden field + * @see setValue + * @since 3.1.0 + */ + public function setData($value) + { + $this->setValue($value); + } + + + /** + * @return boolean whether theming is enabled for this control. Defaults to false. + */ + public function getEnableTheming() + { + return false; + } + + /** + * @param boolean whether theming is enabled for this control. + * @throws TNotSupportedException This method is always thrown when calling this method. + */ + public function setEnableTheming($value) + { + throw new TNotSupportedException('hiddenfield_theming_unsupported'); + } + + /** + * @param string Skin ID + * @throws TNotSupportedException This method is always thrown when calling this method. + */ + public function setSkinID($value) + { + throw new TNotSupportedException('hiddenfield_skinid_unsupported'); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/THtmlArea.php b/gui/baculum/framework/Web/UI/WebControls/THtmlArea.php new file mode 100644 index 0000000000..9d9ff3fab4 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THtmlArea.php @@ -0,0 +1,535 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THtmlArea.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTextBox class + */ +Prado::using('System.Web.UI.WebControls.TTextBox'); + +/** + * THtmlArea class + * + * THtmlArea wraps the visual editting functionalities provided by the + * TinyMCE project {@link http://tinymce.moxiecode.com/}. + * + * THtmlArea displays a WYSIWYG text area on the Web page for user input + * in the HTML format. The text displayed in the THtmlArea component is + * specified or determined by using the Text property. + * + * To enable the visual editting on the client side, set the property + * EnableVisualEdit to true (which is default value). + * To set the size of the editor when the visual editting is enabled, + * set the Width and Height properties instead of + * Columns and Rows because the latter has no meaning + * under the situation. + * + * The default editor gives only the basic tool bar. To change or add + * additional tool bars, use the {@link setOptions Options} property to add additional + * editor options with each options on a new line. + * See http://tinymce.moxiecode.com/tinymce/docs/index.html + * for a list of options. The options can be change/added as shown in the + * following example. + * + * + * + * plugins : "contextmenu,paste" + * language : "zh_cn" + * + * + * + * + * Compatibility + * The client-side visual editting capability is supported by + * Internet Explorer 5.0+ for Windows and Gecko-based browser. + * If the browser does not support the visual editting, + * a traditional textarea will be displayed. + * + * Browser support + * + * + * Windows XP MacOS X 10.4 + * ---------------------------------------------------- + * MSIE 6 OK + * MSIE 5.5 SP2 OK + * MSIE 5.0 OK + * Mozilla 1.7.x OK OK + * Firefox 1.0.x OK OK + * Firefox 1.5b2 OK OK + * Safari 2.0 (412) OK(1) + * Opera 9 Preview 1 OK(1) OK(1) + * ---------------------------------------------------- + * * (1) - Partialy working + * ---------------------------------------------------- + * + * + * @author Wei Zhuo + * @version $Id: THtmlArea.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THtmlArea extends TTextBox +{ + /** + * @var array list of locale => language file pairs. + */ + private static $_langs = array( + 'ar' => 'ar', + 'az' => 'az', + 'be' => 'be', + 'bg' => 'bg', + 'bn' => 'bn', + 'br' => 'br', + 'bs' => 'bs', + 'ca' => 'ca', + 'ch' => 'ch', + 'cn' => 'cn', + 'cs' => 'cs', + 'cy' => 'cy', + 'da' => 'da', + 'de' => 'de', + 'dv' => 'dv', + 'el' => 'el', + 'en' => 'en', + 'eo' => 'eo', + 'es' => 'es', + 'et' => 'et', + 'eu' => 'eu', + 'fa' => 'fa', + 'fi' => 'fi', + 'fr' => 'fr', + 'gl' => 'gl', + 'gu' => 'gu', + 'he' => 'he', + 'hi' => 'hi', + 'hr' => 'hr', + 'hu' => 'hu', + 'hy' => 'hy', + 'ia' => 'ia', + 'id' => 'id', + 'is' => 'is', + 'it' => 'it', + 'ja' => 'ja', + 'ka' => 'ka', + 'kl' => 'kl', + 'km' => 'km', + 'ko' => 'ko', + 'lb' => 'lb', + 'lt' => 'lt', + 'lv' => 'lv', + 'mk' => 'mk', + 'ml' => 'ml', + 'mn' => 'mn', + 'ms' => 'ms', + 'my' => 'my', + 'nb' => 'nb', + 'nl' => 'nl', + 'nn' => 'nn', + 'no' => 'no', + 'pl' => 'pl', + 'ps' => 'ps', + 'pt' => 'pt', + 'ro' => 'ro', + 'ru' => 'ru', + 'sc' => 'sc', + 'se' => 'se', + 'si' => 'si', + 'sk' => 'sk', + 'sl' => 'sl', + 'sq' => 'sq', + 'sr' => 'sr', + 'sv' => 'sv', + 'ta' => 'ta', + 'te' => 'te', + 'th' => 'th', + 'tn' => 'tn', + 'tr' => 'tr', + 'tt' => 'tt', + 'tw' => 'tw', + 'uk' => 'vi', + 'ur' => 'vi', + 'vi' => 'vi', + 'zh_CN' => 'zh-cn', + 'zh_TW' => 'zh-tw', + 'zh' => 'zh', + 'zu' => 'zu', + ); + + /** + * @var array list of default plugins to load, override using getAvailablePlugins(); + */ + private static $_plugins = array( + 'advhr', + 'advimage', + 'advlink', + 'advlist', + 'autolink', + 'autoresize', + 'autosave', + 'bbcode', + 'contextmenu', + 'directionality', + 'emotions', + 'example', + 'fullpage', + 'fullscreen', + 'iespell', + 'inlinepopups', + 'insertdatetime', + 'layer', + 'legacyoutput', + 'lists', + 'media', + 'nonbreaking', + 'noneditable', + 'pagebreak', + 'paste', + 'preview', + 'print', + 'save', + 'searchreplace', + 'spellchecker', + 'style', + 'tabfocus', + 'table', + 'template', + 'visualchars', + 'wordc', + 'wordcount', + 'xhtmlxtras' + ); + + /** + * @var array default themes to load + */ + private static $_themes = array( + 'simple', + 'advanced' + ); + + /** + * Constructor. + * Sets default width and height. + */ + public function __construct() + { + $this->setWidth('470px'); + $this->setHeight('250px'); + } + + /** + * Overrides the parent implementation. + * TextMode for THtmlArea control is always 'MultiLine' + * @return string the behavior mode of the THtmlArea component. + */ + public function getTextMode() + { + return 'MultiLine'; + } + + /** + * Overrides the parent implementation. + * TextMode for THtmlArea is always 'MultiLine' and cannot be changed to others. + * @param string the text mode + */ + public function setTextMode($value) + { + throw new TInvalidOperationException("htmlarea_textmode_readonly"); + } + + /** + * @return boolean whether change of the content should cause postback. Return false if EnableVisualEdit is true. + */ + public function getAutoPostBack() + { + return $this->getEnableVisualEdit() ? false : parent::getAutoPostBack(); + } + + /** + * @return boolean whether to show WYSIWYG text editor. Defaults to true. + */ + public function getEnableVisualEdit() + { + return $this->getViewState('EnableVisualEdit',true); + } + + /** + * Sets whether to show WYSIWYG text editor. + * @param boolean whether to show WYSIWYG text editor + */ + public function setEnableVisualEdit($value) + { + $this->setViewState('EnableVisualEdit',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Gets the current culture. + * @return string current culture, e.g. en_AU. + */ + public function getCulture() + { + return $this->getViewState('Culture', ''); + } + + /** + * Sets the culture/language for the html area + * @param string a culture string, e.g. en_AU. + */ + public function setCulture($value) + { + $this->setViewState('Culture', $value, ''); + } + + /** + * Gets the list of options for the WYSIWYG (TinyMCE) editor + * @see http://tinymce.moxiecode.com/tinymce/docs/index.html + * @return string options + */ + public function getOptions() + { + return $this->getViewState('Options', ''); + } + + /** + * Sets the list of options for the WYSIWYG (TinyMCE) editor + * @see http://tinymce.moxiecode.com/tinymce/docs/index.html + * @param string options + */ + public function setOptions($value) + { + $this->setViewState('Options', $value, ''); + } + + /** + * @param string path to custom plugins to be copied. + */ + public function setCustomPluginPath($value) + { + $this->setViewState('CustomPluginPath', $value); + } + + /** + * @return string path to custom plugins to be copied. + */ + public function getCustomPluginPath() + { + return $this->getViewState('CustomPluginPath'); + } + + /** + * @return boolean enable compression of the javascript files, default is true. + */ + public function getEnableCompression() + { + return $this->getViewState('EnableCompression', true); + } + + /** + * @param boolean enable compression of the javascript files, default is true. + */ + public function setEnableCompression($value) + { + $this->setViewState('EnableCompression', TPropertyValue::ensureBoolean($value)); + } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation by registering + * additional javacript code. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + if($this->getEnableVisualEdit() && $this->getEnabled(true)) + { + $writer->addAttribute('id',$this->getClientID()); + $this->registerEditorClientScript($writer); + } + + parent::addAttributesToRender($writer); + } + + /** + * Returns a list of plugins to be loaded. + * Override this method to customize. + * @return array list of plugins to be loaded + */ + public function getAvailablePlugins() + { + return self::$_plugins; + } + + /** + * @return array list of available themese + */ + public function getAvailableThemes() + { + return self::$_themes; + } + + protected function getCompressionOptions() + { + return array( + 'plugins' => implode(',', $this->getAvailablePlugins()), + 'themes' => implode(',', $this->getAvailableThemes()), + 'languages' => $this->getLanguageSuffix($this->getCulture()), + 'disk_cache' => true, + 'debug' => false + ); + } + + protected function loadJavascriptLibrary() + { + $scripts = $this->getPage()->getClientScript(); + $scripts->registerPradoScript('htmlarea'); + $scripts->registerScriptFile('prado:THtmlArea', $this->getScriptUrl()); + } + + /** + * Registers the editor javascript file and code to initialize the editor. + */ + protected function registerEditorClientScript($writer) + { + $this->loadJavascriptLibrary(); + $scripts = $this->getPage()->getClientScript(); + $options = array( + 'EditorOptions' => $this->getEditorOptions() + ); + if($this->getEnableCompression()) + $options['CompressionOptions'] = $this->getCompressionOptions(); + + $options = TJavaScript::encode($options,true,true); + $script = "new Prado.WebUI.THtmlArea($options)"; + $scripts->registerEndScript('prado:THtmlArea'.$this->ClientID,$script); + } + + /** + * @return string editor script URL. + */ + protected function getScriptUrl() + { + if($this->getEnableCompression()) + return $this->getScriptDeploymentPath().'/tiny_mce/tiny_mce_gzip.js'; + else + return $this->getScriptDeploymentPath().'/tiny_mce/tiny_mce.js'; + } + + /** + * Gets the editor script base URL by publishing the tarred source via TTarAssetManager. + * @return string URL base path to the published editor script + */ + protected function getScriptDeploymentPath() + { + $tarfile = Prado::getPathOfNamespace('System.3rdParty.TinyMCE.tiny_mce', '.tar'); + $md5sum = Prado::getPathOfNamespace('System.3rdParty.TinyMCE.tiny_mce', '.md5'); + if($tarfile===null || $md5sum===null) + throw new TConfigurationException('htmlarea_tarfile_invalid'); + $url = $this->getApplication()->getAssetManager()->publishTarFile($tarfile, $md5sum); + $this->copyCustomPlugins($url); + return $url; + } + + protected function copyCustomPlugins($url) + { + if($plugins = $this->getCustomPluginPath()) + { + $assets = $this->getApplication()->getAssetManager(); + $path = is_dir($plugins) ? $plugins : Prado::getPathOfNameSpace($plugins); + $dest = $assets->getBasePath().'/'.basename($url).'/tiny_mce/plugins/'; + if(!is_dir($dest) || $this->getApplication()->getMode()!==TApplicationMode::Performance) + $assets->copyDirectory($path, $dest); + } + } + + /** + * Default editor options gives basic tool bar only. + * @return array editor initialization options. + */ + protected function getEditorOptions() + { + $options['mode'] = 'exact'; + $options['elements'] = $this->getClientID(); + $options['language'] = $this->getLanguageSuffix($this->getCulture()); + $options['theme'] = 'advanced'; + + //make it basic advanced to fit into 1 line of buttons. + //$options['theme_advanced_buttons1'] = 'bold,italic,underline,strikethrough,separator,justifyleft,justifycenter,justifyright, justifyfull,separator,bullist,numlist,separator,undo,redo,separator,link,unlink,separator,charmap,separator,code,help'; + //$options['theme_advanced_buttons2'] = ' '; + $options['theme_advanced_buttons1'] = 'formatselect,fontselect,fontsizeselect,separator,bold,italic,underline,strikethrough,sub,sup'; + $options['theme_advanced_buttons2'] = 'justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,outdent,indent,separator,forecolor,backcolor,separator,hr,link,unlink,image,charmap,separator,removeformat,code,help'; + $options['theme_advanced_buttons3'] = ''; + + $options['theme_advanced_toolbar_location'] = 'top'; + $options['theme_advanced_toolbar_align'] = 'left'; + $options['theme_advanced_path_location'] = 'bottom'; + $options['extended_valid_elements'] = 'a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]'; + + $options = array_merge($options, $this->parseEditorOptions($this->getOptions())); + return $options; + } + + /** + * Parse additional options set in the Options property. + * @return array additional custom options + */ + protected function parseEditorOptions($string) + { + $options = array(); + $substrings = preg_split('/,\s*\n|\n/', trim($string)); + foreach($substrings as $bits) + { + $option = explode(":",$bits,2); + + if(count($option) == 2) + { + $value=trim(trim($option[1]),"'\""); + if (($s=strtolower($value))==='false') + $value=false; + elseif ($s==='true') + $value=true; + $options[trim($option[0])] = $value; + } + } + return $options; + } + + /** + * @return string localized editor interface language extension. + */ + protected function getLanguageSuffix($culture) + { + $app = $this->getApplication()->getGlobalization(); + if(empty($culture) && ($app!==null)) + $culture = $app->getCulture(); + $variants = array(); + if($app!==null) + $variants = $app->getCultureVariants($culture); + + foreach($variants as $variant) + { + if(isset(self::$_langs[$variant])) + return self::$_langs[$variant]; + } + + return 'en'; + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.THtmlArea'; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/THtmlArea4.php b/gui/baculum/framework/Web/UI/WebControls/THtmlArea4.php new file mode 100644 index 0000000000..f98c2ddf6f --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THtmlArea4.php @@ -0,0 +1,474 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THtmlArea4.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTextBox class + */ +Prado::using('System.Web.UI.WebControls.TTextBox'); + +/** + * THtmlArea4 class + * + * THtmlArea4 wraps the visual editing functionalities provided by the + * version 4 of TinyMCE project {@link http://tinymce.com/}. It has been + * developed as a plug'n'play substitute for {@link THtmlArea}, that is + * based on the previous iteration (version 3) of the same project. + * Please note that both components can't be used together in the same page. + * + * THtmlArea displays a WYSIWYG text area on the Web page for user input + * in the HTML format. The text displayed in the THtmlArea component is + * specified or determined by using the Text property. + * + * To enable the visual editting on the client side, set the property + * EnableVisualEdit to true (which is default value). + * To set the size of the editor when the visual editting is enabled, + * set the Width and Height properties instead of + * Columns and Rows because the latter has no meaning + * under the situation. + * + * The default editor gives only the basic tool bar. To change or add + * additional tool bars, use the {@link setOptions Options} property to add additional + * editor options with each options on a new line. + * See http://www.tinymce.com/wiki.php/Configuration + * for a list of options. The options can be change/added as shown in the + * following example. + * + * + * + * language : "de" + * plugins: [ advlist anchor autolink autoresize autosave bbcode charmap code contextmenu directionality emoticons fullpage fullscreen hr image importcss insertdatetime layer legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor visualblocks visualchars wordcount ] + * toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | print preview media", + * statusbar: false + * + * + * + * + * @author Wei Zhuo + * @version $Id: THtmlArea.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THtmlArea4 extends TTextBox +{ + /** + * @var array list of locale => language file pairs. + */ + private static $_langs = array( + 'ar' => 'ar', + 'bg_BG' => 'bg_BG', + 'bs' => 'bs', + 'ca' => 'ca', + 'cs' => 'cs', + 'cy' => 'cy', + 'da' => 'da', + 'de' => 'de', + 'de_AT' => 'de_AT', + 'el' => 'el', + 'es' => 'es', + 'et' => 'et', + 'eu' => 'eu', + 'fa' => 'fa', + 'fi' => 'fi', + 'fo' => 'fo', + 'fr_FR' => 'fr_FR', + 'gl' => 'gl', + 'he_IL' => 'he_IL', + 'hr' => 'hr', + 'hu_HU' => 'hu_HU', + 'id' => 'id', + 'it' => 'it', + 'ja' => 'ja', + 'ka_GE' => 'ka_GE', + 'ko_KR' => 'ko_KR', + 'lb' => 'lb', + 'lt' => 'lt', + 'lv' => 'lv', + 'nb_NO' => 'nb_NO', + 'nl' => 'nl', + 'pl' => 'pl', + 'pt_BR' => 'pt_BR', + 'pt_PT' => 'pt_PT', + 'ro' => 'ro', + 'ru' => 'ru', + 'si_LK' => 'si_LK', + 'sk' => 'sk', + 'sl_SI' => 'sl_SI', + 'sr' => 'sr', + 'sv_SE' => 'sv_SE', + 'ta' => 'ta', + 'ta_IN' => 'ta_IN', + 'th_TH' => 'th_TH', + 'tr_TR' => 'tr_TR', + 'ug' => 'ug', + 'uk' => 'uk', + 'uk_UA' => 'uk_UA', + 'vi' => 'vi', + 'vi_VN' => 'vi_VN', + 'zh_CN' => 'zh_CN', + 'zh_TW' => 'zh_TW', + ); + + /** + * @var array list of default plugins to load, override using getAvailablePlugins(); + */ + private static $_plugins = array( + 'advlist', + 'anchor', + 'autolink', + 'autoresize', + 'autosave', + 'bbcode', + 'charmap', + 'code', + 'contextmenu', + 'directionality', + 'emoticons', + 'fullpage', + 'fullscreen', + 'hr', + 'image', + 'importcss', + 'insertdatetime', + 'layer', + 'legacyoutput', + 'link', + 'lists', + 'media', + 'nonbreaking', + 'noneditable', + 'pagebreak', + 'paste', + 'preview', + 'print', + 'save', + 'searchreplace', + 'spellchecker', + 'tabfocus', + 'table', + 'template', + 'textcolor', + 'visualblocks', + 'visualchars', + 'wordcount', + ); + + /** + * @var array default themes to load + */ + private static $_themes = array( + 'modern', + ); + + /** + * Constructor. + * Sets default width and height. + */ + public function __construct() + { + $this->setWidth('600px'); + $this->setHeight('250px'); + } + + /** + * Overrides the parent implementation. + * TextMode for THtmlArea control is always 'MultiLine' + * @return string the behavior mode of the THtmlArea component. + */ + public function getTextMode() + { + return 'MultiLine'; + } + + /** + * Overrides the parent implementation. + * TextMode for THtmlArea is always 'MultiLine' and cannot be changed to others. + * @param string the text mode + */ + public function setTextMode($value) + { + throw new TInvalidOperationException("htmlarea_textmode_readonly"); + } + + /** + * @return boolean whether change of the content should cause postback. Return false if EnableVisualEdit is true. + */ + public function getAutoPostBack() + { + return $this->getEnableVisualEdit() ? false : parent::getAutoPostBack(); + } + + /** + * @return boolean whether to show WYSIWYG text editor. Defaults to true. + */ + public function getEnableVisualEdit() + { + return $this->getViewState('EnableVisualEdit',true); + } + + /** + * Sets whether to show WYSIWYG text editor. + * @param boolean whether to show WYSIWYG text editor + */ + public function setEnableVisualEdit($value) + { + $this->setViewState('EnableVisualEdit',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Gets the current culture. + * @return string current culture, e.g. de_AT. + */ + public function getCulture() + { + return $this->getViewState('Culture', ''); + } + + /** + * Sets the culture/language for the html area + * @param string a culture string, e.g. de_AT. + */ + public function setCulture($value) + { + $this->setViewState('Culture', $value, ''); + } + + /** + * Gets the list of options for the WYSIWYG (TinyMCE) editor + * @see http://www.tinymce.com/wiki.php/Configuration + * @return string options + */ + public function getOptions() + { + return $this->getViewState('Options', ''); + } + + /** + * Sets the list of options for the WYSIWYG (TinyMCE) editor + * @see http://www.tinymce.com/wiki.php/Configuration + * @param string options + */ + public function setOptions($value) + { + $this->setViewState('Options', $value, ''); + } + + /** + * @param string path to custom plugins to be copied. + */ + public function setCustomPluginPath($value) + { + $this->setViewState('CustomPluginPath', $value); + } + + /** + * @return string path to custom plugins to be copied. + */ + public function getCustomPluginPath() + { + return $this->getViewState('CustomPluginPath'); + } + + /** + * @return boolean enable compression of the javascript files, default is true. + * @deprecated since 3.2.3: tinyMCE 4 doesn't support this anymore + */ + public function getEnableCompression() + { + return $this->getViewState('EnableCompression', true); + } + + /** + * @param boolean enable compression of the javascript files, default is true. + * @deprecated since 3.2.3: tinyMCE 4 doesn't support this anymore + */ + public function setEnableCompression($value) + { + $this->setViewState('EnableCompression', TPropertyValue::ensureBoolean($value)); + } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation by registering + * additional javacript code. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + if($this->getEnableVisualEdit() && $this->getEnabled(true)) + { + $writer->addAttribute('id',$this->getClientID()); + $this->registerEditorClientScript($writer); + } + + parent::addAttributesToRender($writer); + } + + /** + * Returns a list of plugins to be loaded. + * Override this method to customize. + * @return array list of plugins to be loaded + */ + public function getAvailablePlugins() + { + return self::$_plugins; + } + + /** + * @return array list of available themese + */ + public function getAvailableThemes() + { + return self::$_themes; + } + + /** + * @deprecated since 3.2.3. tinyMCE4 doesn's use this anymore + */ + protected function getCompressionOptions() + { + return array(); + } + + protected function loadJavascriptLibrary() + { + $scripts = $this->getPage()->getClientScript(); + $scripts->registerPradoScript('htmlarea4'); + $scripts->registerScriptFile('prado:THtmlArea4', $this->getScriptUrl()); + } + + /** + * Registers the editor javascript file and code to initialize the editor. + */ + protected function registerEditorClientScript($writer) + { + $this->loadJavascriptLibrary(); + $scripts = $this->getPage()->getClientScript(); + $options = array( + 'EditorOptions' => $this->getEditorOptions() + ); + + $options = TJavaScript::encode($options,true,true); + $script = "new Prado.WebUI.THtmlArea4($options)"; + $scripts->registerEndScript('prado:THtmlArea4'.$this->ClientID,$script); + } + + /** + * @return string editor script URL. + */ + protected function getScriptUrl() + { + return $this->getScriptDeploymentPath().'/tinymce.min.js'; + } + + /** + * Gets the editor script base URL by publishing the tarred source via TTarAssetManager. + * @return string URL base path to the published editor script + */ + protected function getScriptDeploymentPath() + { + $basedir = Prado::getPathOfNamespace('System.Web.Javascripts.source.tinymce-405'); + $url = $this->getApplication()->getAssetManager()->publishFilePath($basedir); + $this->copyCustomPlugins($url); + return $url; + } + + protected function copyCustomPlugins($url) + { + if($plugins = $this->getCustomPluginPath()) + { + $assets = $this->getApplication()->getAssetManager(); + $path = is_dir($plugins) ? $plugins : Prado::getPathOfNameSpace($plugins); + $dest = $assets->getBasePath().'/'.basename($url).'/plugins/'; + if(!is_dir($dest) || $this->getApplication()->getMode()!==TApplicationMode::Performance) + $assets->copyDirectory($path, $dest); + } + } + + /** + * Default editor options gives basic tool bar only. + * @return array editor initialization options. + */ + protected function getEditorOptions() + { + $options['mode'] = 'exact'; + $options['elements'] = $this->getClientID(); + $options['language'] = $this->getLanguageSuffix($this->getCulture()); + //$options['theme'] = 'modern'; //default + // mimic previous (tinyMCE3) sizing behaviour + $options['width'] = $this->getWidth(); + $options['height'] = $this->getHeight(); + $options['resize'] = 'both'; + $options['menubar'] = false; + + $options['extended_valid_elements'] = 'a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]'; + + $options = array_merge($options, $this->parseEditorOptions($this->getOptions())); + return $options; + } + + /** + * Parse additional options set in the Options property. + * @return array additional custom options + */ + protected function parseEditorOptions($string) + { + $options = array(); + $substrings = preg_split('/,\s*\n|\n/', trim($string)); + foreach($substrings as $bits) + { + $option = explode(":",$bits,2); + + if(count($option) == 2) + { + $value=trim(trim($option[1]),"'\""); + if (($s=strtolower($value))==='false') + $value=false; + elseif ($s==='true') + $value=true; + $options[trim($option[0])] = $value; + } + } + return $options; + } + + /** + * @return string localized editor interface language extension. + */ + protected function getLanguageSuffix($culture) + { + $app = $this->getApplication()->getGlobalization(); + if(empty($culture) && ($app!==null)) + $culture = $app->getCulture(); + $variants = array(); + if($app!==null) + $variants = $app->getCultureVariants($culture); + + foreach($variants as $variant) + { + if(isset(self::$_langs[$variant])) + return self::$_langs[$variant]; + } + + return 'en'; + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.THtmlArea4'; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/THtmlElement.php b/gui/baculum/framework/Web/UI/WebControls/THtmlElement.php new file mode 100644 index 0000000000..5646a6ff3f --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THtmlElement.php @@ -0,0 +1,68 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THtmlElement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TWebControl'); + +/** + * THtmlElement class. + * + * THtmlElement represents a generic HTML element whose tag name is specified + * via {@link setTagName TagName} property. Because THtmlElement extends from + * {@link TWebControl}, it enjoys all its functionalities. + * + * To change the default tag your subclass should override {@link getDefaultTagName} + * + * @author Qiang Xue + * @author Brad Anderson + * @version $Id: THtmlElement.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ +class THtmlElement extends TWebControl +{ + /** + * @var the tag of this element + */ + private $_tagName=null; + + /** + * @return string the tag name of this control. Defaults to 'span'. + */ + public function getTagName() + { + return ($this->_tagName !== null) ? $this->_tagName : ($this->_tagName = $this->getDefaultTagName()); + } + + /** + * @param string the tag name of this control. + */ + public function setTagName($value) + { + $this->_tagName=TPropertyValue::ensureString($value); + } + + /** + * This is the default tag when no other is specified + * @return string the default tag + */ + public function getDefaultTagName() { + return 'span'; + } + + /** + * This tells you if this TagName has deviated from the original + * @return boolean true if TagName has deviated from the default. + */ + public function getIsMutated() { + return $this->_tagName !== null && $this->_tagName != $this->getDefaultTagName(); + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/THyperLink.php b/gui/baculum/framework/Web/UI/WebControls/THyperLink.php new file mode 100644 index 0000000000..56aa5391ef --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THyperLink.php @@ -0,0 +1,252 @@ + + * @link http://www.xisc.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version $Id: THyperLink.php 3286 2013-04-18 06:09:19Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * THyperLink class + * + * THyperLink displays a hyperlink on a page. The hyperlink URL is specified + * via the {@link setNavigateUrl NavigateUrl} property, and link text is via + * the {@link setText Text} property. It is also possible to display an image + * by setting the {@link setImageUrl ImageUrl} property. In this case, + * the alignment of the image displayed is set by the + * {@link setImageAlign ImageAlign} property and {@link getText Text} is + * displayed as the alternate text of the image. + * + * The link target is specified via the {@link setTarget Target} property. + * If both {@link getImageUrl ImageUrl} and {@link getText Text} are empty, + * the content enclosed within the control tag will be rendered. + * + * @author Qiang Xue + * @version $Id: THyperLink.php 3286 2013-04-18 06:09:19Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THyperLink extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name of the hyperlink + */ + protected function getTagName() + { + return 'a'; + } + + /** + * Adds attributes related to a hyperlink element to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $isEnabled=$this->getEnabled(true); + if($this->getEnabled() && !$isEnabled) + $writer->addAttribute('disabled','disabled'); + parent::addAttributesToRender($writer); + if(($url=$this->getNavigateUrl())!=='' && $isEnabled) + $writer->addAttribute('href',$url); + if(($target=$this->getTarget())!=='') + $writer->addAttribute('target',$target); + } + + /** + * Renders the body content of the hyperlink. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + if(($imageUrl=$this->getImageUrl())==='') + { + if(($text=$this->getText())!=='') + $writer->write(THttpUtility::htmlEncode($text)); + else if($this->getHasControls()) + parent::renderContents($writer); + else + $writer->write(THttpUtility::htmlEncode($this->getNavigateUrl())); + } + else + { + $this->createImage($imageUrl)->renderControl($writer); + } + } + + /** + * Gets the TImage for rendering the ImageUrl property. This is not for + * creating dynamic images. + * @param string image url. + * @return TImage image control for rendering. + */ + protected function createImage($imageUrl) + { + $image=Prado::createComponent('System.Web.UI.WebControls.TImage'); + $image->setImageUrl($imageUrl); + if(($width=$this->getImageWidth())!=='') + $image->setWidth($width); + if(($height=$this->getImageHeight())!=='') + $image->setHeight($height); + if(($toolTip=$this->getToolTip())!=='') + $image->setToolTip($toolTip); + if(($text=$this->getText())!=='') + $image->setAlternateText($text); + if(($align=$this->getImageAlign())!=='') + $image->setImageAlign($align); + $image->setBorderWidth('0'); + return $image; + } + + /** + * @return string the text caption of the THyperLink + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text caption of the THyperLink. + * @param string the text caption to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * @return string the alignment of the image with respective to other elements on the page, defaults to empty. + */ + public function getImageAlign() + { + return $this->getViewState('ImageAlign',''); + } + + /** + * Sets the alignment of the image with respective to other elements on the page. + * Possible values include: absbottom, absmiddle, baseline, bottom, left, + * middle, right, texttop, and top. If an empty string is passed in, + * imagealign attribute will not be rendered. + * @param string the alignment of the image + */ + public function setImageAlign($value) + { + $this->setViewState('ImageAlign',$value,''); + } + + /** + * @return string height of the image in the THyperLink + */ + public function getImageHeight() + { + return $this->getViewState('ImageHeight',''); + } + + /** + * Sets the height of the image in the THyperLink + * @param string height of the image in the THyperLink + */ + public function setImageHeight($value) + { + $this->setViewSTate('ImageHeight',$value,''); + } + + /** + * @return string the location of the image file for the THyperLink + */ + public function getImageUrl() + { + return $this->getViewState('ImageUrl',''); + } + + /** + * Sets the location of image file of the THyperLink. + * @param string the image file location + */ + public function setImageUrl($value) + { + $this->setViewState('ImageUrl',$value,''); + } + + /** + * @return string width of the image in the THyperLink + */ + public function getImageWidth() + { + return $this->getViewState('ImageWidth',''); + } + + /** + * Sets the width of the image in the THyperLink + * @param string width of the image + */ + public function setImageWidth($value) + { + $this->setViewState('ImageWidth',$value,''); + } + + /** + * @return string the URL to link to when the THyperLink component is clicked. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * Sets the URL to link to when the THyperLink component is clicked. + * @param string the URL + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',$value,''); + } + + /** + * Returns the URL to link to when the THyperLink component is clicked. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text caption + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the URL to link to when the THyperLink component is clicked. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text caption to be set + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string the target window or frame to display the Web page content linked to when the THyperLink component is clicked. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * Sets the target window or frame to display the Web page content linked to when the THyperLink component is clicked. + * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. + */ + public function setTarget($value) + { + $this->setViewState('Target',$value,''); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/THyperLinkColumn.php b/gui/baculum/framework/Web/UI/WebControls/THyperLinkColumn.php new file mode 100644 index 0000000000..171accadaa --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/THyperLinkColumn.php @@ -0,0 +1,273 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: THyperLinkColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); +/** + * THyperLink class file + */ +Prado::using('System.Web.UI.WebControls.THyperLink'); + +/** + * THyperLinkColumn class + * + * THyperLinkColumn contains a hyperlink for each item in the column. + * You can set the text and the url of the hyperlink by {@link setText Text} + * and {@link setNavigateUrl NavigateUrl} properties, respectively. + * You can also bind the text and url to specific data field in datasource + * by setting {@link setDataTextField DataTextField} and + * {@link setDataNavigateUrlField DataNavigateUrlField}. + * Both can be formatted before rendering according to the + * {@link setDataTextFormatString DataTextFormatString} and + * and {@link setDataNavigateUrlFormatString DataNavigateUrlFormatString} + * properties, respectively. If both {@link setText Text} and {@link setDataTextField DataTextField} + * are present, the latter takes precedence. + * The same rule applies to {@link setNavigateUrl NavigateUrl} and + * {@link setDataNavigateUrlField DataNavigateUrlField} properties. + * + * The hyperlinks in the column can be accessed by one of the following two methods: + * + * $datagridItem->HyperLinkColumnID->HyperLink + * $datagridItem->HyperLinkColumnID->Controls[0] + * + * The second method is possible because the hyperlink control created within the + * datagrid cell is the first child. + * + * @author Qiang Xue + * @version $Id: THyperLinkColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THyperLinkColumn extends TDataGridColumn +{ + /** + * @return string the text caption of the hyperlink + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text caption of the hyperlink. + * @param string the text caption to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * @return string the field name from the data source to bind to the hyperlink caption + */ + public function getDataTextField() + { + return $this->getViewState('DataTextField',''); + } + + /** + * @param string the field name from the data source to bind to the hyperlink caption + */ + public function setDataTextField($value) + { + $this->setViewState('DataTextField',$value,''); + } + + /** + * @return string the formatting string used to control how the hyperlink caption will be displayed. + */ + public function getDataTextFormatString() + { + return $this->getViewState('DataTextFormatString',''); + } + + /** + * @param string the formatting string used to control how the hyperlink caption will be displayed. + */ + public function setDataTextFormatString($value) + { + $this->setViewState('DataTextFormatString',$value,''); + } + + /** + * @return string height of the image in the THyperLink + */ + public function getImageHeight() + { + return $this->getViewState('ImageHeight',''); + } + + /** + * @param string height of the image in the THyperLink + */ + public function setImageHeight($value) + { + $this->setViewState('ImageHeight',$value,''); + } + + /** + * @return string url of the image in the THyperLink + */ + public function getImageUrl() + { + return $this->getViewState('ImageUrl',''); + } + + /** + * @param string url of the image in the THyperLink + */ + public function setImageUrl($value) + { + $this->setViewState('ImageUrl',$value,''); + } + + /** + * @return string width of the image in the THyperLink + */ + public function getImageWidth() + { + return $this->getViewState('ImageWidth',''); + } + + /** + * @param string width of the image in the THyperLink + */ + public function setImageWidth($value) + { + $this->setViewState('ImageWidth',$value,''); + } + + /** + * @return string the URL to link to when the hyperlink is clicked. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * Sets the URL to link to when the hyperlink is clicked. + * @param string the URL + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',$value,''); + } + + /** + * @return string the field name from the data source to bind to the navigate url of hyperlink + */ + public function getDataNavigateUrlField() + { + return $this->getViewState('DataNavigateUrlField',''); + } + + /** + * @param string the field name from the data source to bind to the navigate url of hyperlink + */ + public function setDataNavigateUrlField($value) + { + $this->setViewState('DataNavigateUrlField',$value,''); + } + + /** + * @return string the formatting string used to control how the navigate url of hyperlink will be displayed. + */ + public function getDataNavigateUrlFormatString() + { + return $this->getViewState('DataNavigateUrlFormatString',''); + } + + /** + * @param string the formatting string used to control how the navigate url of hyperlink will be displayed. + */ + public function setDataNavigateUrlFormatString($value) + { + $this->setViewState('DataNavigateUrlFormatString',$value,''); + } + + /** + * @return string the target window or frame to display the Web page content linked to when the hyperlink is clicked. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * Sets the target window or frame to display the Web page content linked to when the hyperlink is clicked. + * @param string the target window, valid values include '_blank', '_parent', '_self', '_top' and empty string. + */ + public function setTarget($value) + { + $this->setViewState('Target',$value,''); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It creates a hyperlink within the cell. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) + { + $link=new THyperLink; + if(($url = $this->getImageUrl())!=='') + { + $link->setImageUrl($url); + if(($width=$this->getImageWidth())!=='') + $link->setImageWidth($width); + if(($height=$this->getImageHeight())!=='') + $link->setImageHeight($height); + } + $link->setText($this->getText()); + $link->setNavigateUrl($this->getNavigateUrl()); + $link->setTarget($this->getTarget()); + if($this->getDataTextField()!=='' || $this->getDataNavigateUrlField()!=='') + $link->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + $cell->getControls()->add($link); + $cell->registerObject('HyperLink',$link); + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $data=$item->getData(); + if(($field=$this->getDataTextField())!=='') + { + $value=$this->getDataFieldValue($data,$field); + $text=$this->formatDataValue($this->getDataTextFormatString(),$value); + $sender->setText($text); + } + if(($field=$this->getDataNavigateUrlField())!=='') + { + $value=$this->getDataFieldValue($data,$field); + $url=$this->formatDataValue($this->getDataNavigateUrlFormatString(),$value); + $sender->setNavigateUrl($url); + } + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TImage.php b/gui/baculum/framework/Web/UI/WebControls/TImage.php new file mode 100644 index 0000000000..04a6d1162c --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TImage.php @@ -0,0 +1,157 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TImage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TImage class + * + * TImage displays an image on a page. The image is specified via the + * {@link setImageUrl ImageUrl} property which takes a relative or absolute + * URL to the image file. The alignment of the image displayed is set by + * the {@link setImageAlign ImageAlign} property. To set alternative texts + * or long description of the image, use {@link setAlternateText AlternateText} + * or {@link setDescriptionUrl DescriptionUrl} property, respectively. + * + * @author Qiang Xue + * @version $Id: TImage.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImage extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name of image control + */ + protected function getTagName() + { + return 'img'; + } + + /** + * Adds attributes related to an HTML image element to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('src',$this->getImageUrl()); + $writer->addAttribute('alt',$this->getAlternateText()); + if(($desc=$this->getDescriptionUrl())!=='') + $writer->addAttribute('longdesc',$desc); + if(($align=$this->getImageAlign())!=='') + $writer->addAttribute('align',$align); + parent::addAttributesToRender($writer); + } + + /** + * Renders the body content of the image. + * Nothing to be rendered within image tags. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + } + + /** + * @return string the alternative text displayed in the TImage component when the image is unavailable. + */ + public function getAlternateText() + { + return $this->getViewState('AlternateText',''); + } + + /** + * Sets the alternative text to be displayed in the TImage when the image is unavailable. + * @param string the alternative text + */ + public function setAlternateText($value) + { + $this->setViewState('AlternateText',$value,''); + } + + /** + * @return string the alignment of the image with respective to other elements on the page, defaults to empty. + */ + public function getImageAlign() + { + return $this->getViewState('ImageAlign',''); + } + + /** + * Sets the alignment of the image with respective to other elements on the page. + * Possible values include: absbottom, absmiddle, baseline, bottom, left, + * middle, right, texttop, and top. If an empty string is passed in, + * imagealign attribute will not be rendered. + * @param string the alignment of the image + */ + public function setImageAlign($value) + { + $this->setViewState('ImageAlign',$value,''); + } + + /** + * @return string the URL of the image file + */ + public function getImageUrl() + { + return $this->getViewState('ImageUrl',''); + } + + /** + * @param string the URL of the image file + */ + public function setImageUrl($value) + { + $this->setViewState('ImageUrl',$value,''); + } + + /** + * Returns the URL of the image file. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getImageUrl()}. + * @return string the URL of the image file. + * @see getImageUrl + * @since 3.1.0 + */ + public function getData() + { + return $this->getImageUrl(); + } + + /** + * Sets the URL of the image. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setImageUrl()}. + * @param string the URL of the image file. + * @see setImageUrl + * @since 3.1.0 + */ + public function setData($value) + { + $this->setImageUrl($value); + } + + /** + * @return string the URL to long description + */ + public function getDescriptionUrl() + { + return $this->getViewState('DescriptionUrl',''); + } + + /** + * @param string the URL to the long description of the image. + */ + public function setDescriptionUrl($value) + { + $this->setViewState('DescriptionUrl',$value,''); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TImageButton.php b/gui/baculum/framework/Web/UI/WebControls/TImageButton.php new file mode 100644 index 0000000000..0ee52a9441 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TImageButton.php @@ -0,0 +1,441 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TImageButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TImage class file + */ +Prado::using('System.Web.UI.WebControls.TImage'); + +/** + * TImageButton class + * + * TImageButton creates an image button on the page. It is used to submit data to a page. + * You can create either a submit button or a command button. + * + * A command button has a command name (specified by + * the {@link setCommandName CommandName} property) and and a command parameter + * (specified by {@link setCommandParameter CommandParameter} property) + * associated with the button. This allows you to create multiple TLinkButton + * components on a Web page and programmatically determine which one is clicked + * with what parameter. You can provide an event handler for + * {@link onCommand OnCommand} event to programmatically control the actions performed + * when the command button is clicked. In the event handler, you can determine + * the {@link setCommandName CommandName} property value and + * the {@link setCommandParameter CommandParameter} property value + * through the {@link TCommandParameter::getName Name} and + * {@link TCommandParameter::getParameter Parameter} properties of the event + * parameter which is of type {@link TCommandEventParameter}. + * + * A submit button does not have a command name associated with the button + * and clicking on it simply posts the Web page back to the server. + * By default, a TImageButton control is a submit button. + * You can provide an event handler for the {@link onClick OnClick} event + * to programmatically control the actions performed when the submit button is clicked. + * The coordinates of the clicking point can be obtained from the {@link onClick OnClick} + * event parameter, which is of type {@link TImageClickEventParameter}. + * + * Clicking on button can trigger form validation, if + * {@link setCausesValidation CausesValidation} is true. + * And the validation may be restricted within a certain group of validator + * controls by setting {@link setValidationGroup ValidationGroup} property. + * If validation is successful, the data will be post back to the same page. + * + * TImageButton displays the {@link setText Text} property as the hint text to the displayed image. + * + * @author Qiang Xue + * @version $Id: TImageButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageButton extends TImage implements IPostBackDataHandler, IPostBackEventHandler, IButtonControl +{ + /** + * @var integer x coordinate that the image is being clicked at + */ + private $_x=0; + /** + * @var integer y coordinate that the image is being clicked at + */ + private $_y=0; + private $_dataChanged=false; + + /** + * @return string tag name of the button + */ + protected function getTagName() + { + return 'input'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional button specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + $writer->addAttribute('type','image'); + if(($uniqueID=$this->getUniqueID())!=='') + $writer->addAttribute('name',$uniqueID); + if($this->getEnabled(true)) + { + if($this->getEnableClientScript() && $this->needPostBackScript()) + $this->renderClientControlScript($writer); + } + else if($this->getEnabled()) // in this case, parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + parent::addAttributesToRender($writer); + } + + /** + * Renders the client-script code. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $cs = $this->getPage()->getClientScript(); + $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TImageButton'; + } + + /** + * @return boolean whether to perform validation if the button is clicked + */ + protected function canCauseValidation() + { + if($this->getCausesValidation()) + { + $group=$this->getValidationGroup(); + return $this->getPage()->getValidators($group)->getCount()>0; + } + else + return false; + } + + /** + * @param boolean set by a panel to register this button as the default button for the panel. + */ + public function setIsDefaultButton($value) + { + $this->setViewState('IsDefaultButton', TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean true if this button is registered as a default button for a panel. + */ + public function getIsDefaultButton() + { + return $this->getViewState('IsDefaultButton', false); + } + + /** + * @return boolean whether the button needs javascript to do postback + */ + protected function needPostBackScript() + { + return $this->canCauseValidation() || $this->getIsDefaultButton(); + } + + /** + * Returns postback specifications for the button. + * This method is used by framework and control developers. + * @return array parameters about how the button defines its postback behavior. + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['EventTarget'] = $this->getUniqueID(); + + return $options; + } + + /** + * This method checks if the TImageButton is clicked and loads the coordinates of the clicking position. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $uid=$this->getUniqueID(); + if(isset($values["{$uid}_x"]) && isset($values["{$uid}_y"])) + { + $this->_x=intval($values["{$uid}_x"]); + $this->_y=intval($values["{$uid}_y"]); + if($this->getPage()->getPostBackEventTarget()===null) + $this->getPage()->setPostBackEventTarget($this); + $this->_dataChanged=true; + } + return false; + } + + /** + * A dummy implementation for the IPostBackDataHandler interface. + */ + public function raisePostDataChangedEvent() + { + // no post data to handle + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnClick' event to fire up the event handlers. + * If you override this method, be sure to call the parent implementation + * so that the event handler can be invoked. + * @param TImageClickEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param) + { + $this->raiseEvent('OnClick',$this,$param); + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnCommand' event to fire up the event handlers. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TCommandEventParameter event parameter to be passed to the event handlers + */ + public function onCommand($param) + { + $this->raiseEvent('OnCommand',$this,$param); + $this->raiseBubbleEvent($this,$param); + } + + /** + * Raises the postback event. + * This method is required by {@link IPostBackEventHandler} interface. + * If {@link getCausesValidation CausesValidation} is true, it will + * invoke the page's {@link TPage::validate validate} method first. + * It will raise {@link onClick OnClick} and {@link onCommand OnCommand} events. + * This method is mainly used by framework and control developers. + * @param TEventParameter the event parameter + */ + public function raisePostBackEvent($param) + { + if($this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onClick(new TImageClickEventParameter($this->_x,$this->_y)); + $this->onCommand(new TCommandEventParameter($this->getCommandName(),$this->getCommandParameter())); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * @return boolean whether postback event trigger by this button will cause input validation, default is true + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this button will cause input validation + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the command name associated with the {@link onCommand OnCommand} event. + */ + public function getCommandName() + { + return $this->getViewState('CommandName',''); + } + + /** + * @param string the command name associated with the {@link onCommand OnCommand} event. + */ + public function setCommandName($value) + { + $this->setViewState('CommandName',$value,''); + } + + /** + * @return string the parameter associated with the {@link onCommand OnCommand} event + */ + public function getCommandParameter() + { + return $this->getViewState('CommandParameter',''); + } + + /** + * @param string the parameter associated with the {@link onCommand OnCommand} event. + */ + public function setCommandParameter($value) + { + $this->setViewState('CommandParameter',$value,''); + } + + /** + * @return string the group of validators which the button causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the button causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return string caption of the button + */ + public function getText() + { + return $this->getAlternateText(); + } + + /** + * @param string caption of the button + */ + public function setText($value) + { + $this->setAlternateText($value); + } + + /** + * Registers the image button to receive postback data during postback. + * This is necessary because an image button, when postback, does not have + * direct mapping between post data and the image button name. + * This method overrides the parent implementation and is invoked before render. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getPage()->registerRequiresPostData($this); + } + + /** + * Renders the body content enclosed between the control tag. + * This overrides the parent implementation with nothing to be rendered. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + } +} + +/** + * TImageClickEventParameter class + * + * TImageClickEventParameter encapsulates the parameter data for + * {@link TImageButton::onClick Click} event of {@link TImageButton} controls. + * + * @author Qiang Xue + * @version $Id: TImageButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageClickEventParameter extends TEventParameter +{ + /** + * the X coordinate of the clicking point + * @var integer + */ + private $_x=0; + /** + * the Y coordinate of the clicking point + * @var integer + */ + private $_y=0; + + /** + * Constructor. + * @param integer X coordinate of the clicking point + * @param integer Y coordinate of the clicking point + */ + public function __construct($x,$y) + { + $this->_x=$x; + $this->_y=$y; + } + + /** + * @return integer X coordinate of the clicking point, defaults to 0 + */ + public function getX() + { + return $this->_x; + } + + /** + * @param integer X coordinate of the clicking point + */ + public function setX($value) + { + $this->_x=TPropertyValue::ensureInteger($value); + } + + /** + * @return integer Y coordinate of the clicking point, defaults to 0 + */ + public function getY() + { + return $this->_y; + } + + /** + * @param integer Y coordinate of the clicking point + */ + public function setY($value) + { + $this->_y=TPropertyValue::ensureInteger($value); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TImageMap.php b/gui/baculum/framework/Web/UI/WebControls/TImageMap.php new file mode 100644 index 0000000000..340f5089dc --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TImageMap.php @@ -0,0 +1,837 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TImage class file + */ +Prado::using('System.Web.UI.WebControls.TImage'); + +/** + * TImageMap class + * + * TImageMap represents an image on a page. Hotspot regions can be defined + * within the image. Depending on the {@link setHotSpotMode HotSpotMode}, + * clicking on the hotspots may trigger a postback or navigate to a specified + * URL. The hotspots defined may be accessed via {@link getHotSpots HotSpots}. + * Each hotspot is described as a {@link THotSpot}, which can be a circle, + * rectangle, polygon, etc. To add hotspot in a template, use the following, + * + * + * + * + * + * + * + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageMap extends TImage implements IPostBackEventHandler +{ + const MAP_NAME_PREFIX='ImageMap'; + + /** + * Processes an object that is created during parsing template. + * This method adds {@link THotSpot} objects into the hotspot collection + * of the imagemap. + * @param string|TComponent text string or component parsed and instantiated in template + */ + public function addParsedObject($object) + { + if($object instanceof THotSpot) + $this->getHotSpots()->add($object); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional imagemap specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if($this->getHotSpots()->getCount()>0) + { + $writer->addAttribute('usemap','#'.self::MAP_NAME_PREFIX.$this->getClientID()); + $writer->addAttribute('id',$this->getUniqueID()); + } + if($this->getEnabled() && !$this->getEnabled(true)) + $writer->addAttribute('disabled','disabled'); + } + + /** + * Renders this imagemap. + * @param THtmlWriter + */ + public function render($writer) + { + parent::render($writer); + + $hotspots=$this->getHotSpots(); + + if($hotspots->getCount()>0) + { + $clientID=$this->getClientID(); + $cs=$this->getPage()->getClientScript(); + $writer->writeLine(); + $writer->addAttribute('name',self::MAP_NAME_PREFIX.$clientID); + $writer->renderBeginTag('map'); + $writer->writeLine(); + if(($mode=$this->getHotSpotMode())===THotSpotMode::NotSet) + $mode=THotSpotMode::Navigate; + $target=$this->getTarget(); + $i=0; + $options['EventTarget'] = $this->getUniqueID(); + $options['StopEvent'] = true; + $cs=$this->getPage()->getClientScript(); + foreach($hotspots as $hotspot) + { + if($hotspot->getHotSpotMode()===THotSpotMode::NotSet) + $hotspot->setHotSpotMode($mode); + if($target!=='' && $hotspot->getTarget()==='') + $hotspot->setTarget($target); + if($hotspot->getHotSpotMode()===THotSpotMode::PostBack) + { + $id=$clientID.'_'.$i; + $writer->addAttribute('id',$id); + $writer->addAttribute('href','#'.$id); //create unique no-op url references + $options['ID']=$id; + $options['EventParameter']="$i"; + $options['CausesValidation']=$hotspot->getCausesValidation(); + $options['ValidationGroup']=$hotspot->getValidationGroup(); + $cs->registerPostBackControl($this->getClientClassName(),$options); + } + $hotspot->render($writer); + $writer->writeLine(); + $i++; + } + $writer->renderEndTag(); + } + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TImageMap'; + } + + /** + * Raises the postback event. + * This method is required by {@link IPostBackEventHandler} interface. + * This method is mainly used by framework and control developers. + * @param TEventParameter the event parameter + */ + public function raisePostBackEvent($param) + { + $postBackValue=null; + if($param!=='') + { + $index=TPropertyValue::ensureInteger($param); + $hotspots=$this->getHotSpots(); + if($index>=0 && $index<$hotspots->getCount()) + { + $hotspot=$hotspots->itemAt($index); + if(($mode=$hotspot->getHotSpotMode())===THotSpotMode::NotSet) + $mode=$this->getHotSpotMode(); + if($mode===THotSpotMode::PostBack) + { + $postBackValue=$hotspot->getPostBackValue(); + if($hotspot->getCausesValidation()) + $this->getPage()->validate($hotspot->getValidationGroup()); + } + } + } + if($postBackValue!==null) + $this->onClick(new TImageMapEventParameter($postBackValue)); + } + + /** + * @return THotSpotMode the behavior of hotspot regions in this imagemap when they are clicked. Defaults to THotSpotMode::NotSet. + */ + public function getHotSpotMode() + { + return $this->getViewState('HotSpotMode',THotSpotMode::NotSet); + } + + /** + * Sets the behavior of hotspot regions in this imagemap when they are clicked. + * If an individual hotspot has a mode other than 'NotSet', the mode set in this + * imagemap will be ignored. By default, 'NotSet' is equivalent to 'Navigate'. + * @param THotSpotMode the behavior of hotspot regions in this imagemap when they are clicked. + */ + public function setHotSpotMode($value) + { + $this->setViewState('HotSpotMode',TPropertyValue::ensureEnum($value,'THotSpotMode'),THotSpotMode::NotSet); + } + + /** + * @return THotSpotCollection collection of hotspots defined in this imagemap. + */ + public function getHotSpots() + { + if(($hotspots=$this->getViewState('HotSpots',null))===null) + { + $hotspots=new THotSpotCollection; + $this->setViewState('HotSpots',$hotspots); + } + return $hotspots; + } + + /** + * @return string the target window or frame to display the new page when a hotspot region is clicked within the imagemap. Defaults to ''. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * @param string the target window or frame to display the new page when a hotspot region is clicked within the imagemap. + */ + public function setTarget($value) + { + $this->setViewState('Target',TPropertyValue::ensureString($value),''); + } + + /** + * Raises OnClick event. + * This method is invoked when a hotspot region is clicked within the imagemap. + * If you override this method, be sure to call the parent implementation + * so that the event handler can be invoked. + * @param TImageMapEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param) + { + $this->raiseEvent('OnClick',$this,$param); + } +} + +/** + * TImageMapEventParameter class. + * + * TImageMapEventParameter represents a postback event parameter + * when a hotspot is clicked and posts back in a {@link TImageMap}. + * To retrieve the post back value associated with the hotspot being clicked, + * access {@link getPostBackValue PostBackValue}. + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TImageMapEventParameter extends TEventParameter +{ + private $_postBackValue; + + /** + * Constructor. + * @param string post back value associated with the hotspot clicked + */ + public function __construct($postBackValue) + { + $this->_postBackValue=$postBackValue; + } + + /** + * @return string post back value associated with the hotspot clicked + */ + public function getPostBackValue() + { + return $this->_postBackValue; + } +} + +/** + * THotSpotCollection class. + * + * THotSpotCollection represents a collection of hotspots in an imagemap. + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class THotSpotCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only {@link THotSpot}. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a THotSpot. + */ + public function insertAt($index,$item) + { + if($item instanceof THotSpot) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('hotspotcollection_hotspot_required'); + } +} + + +/** + * THotSpot class. + * + * THotSpot implements the basic functionality common to all hot spot shapes. + * Derived classes include {@link TCircleHotSpot}, {@link TPolygonHotSpot} + * and {@link TRectangleHotSpot}. + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class THotSpot extends TComponent +{ + private $_viewState=array(); + + /** + * Returns a viewstate value. + * + * This function is very useful in defining getter functions for component properties + * that must be kept in viewstate. + * @param string the name of the viewstate value to be returned + * @param mixed the default value. If $key is not found in viewstate, $defaultValue will be returned + * @return mixed the viewstate value corresponding to $key + */ + protected function getViewState($key,$defaultValue=null) + { + return isset($this->_viewState[$key])?$this->_viewState[$key]:$defaultValue; + } + + /** + * Sets a viewstate value. + * + * This function is very useful in defining setter functions for control properties + * that must be kept in viewstate. + * Make sure that the viewstate value must be serializable and unserializable. + * @param string the name of the viewstate value + * @param mixed the viewstate value to be set + * @param mixed default value. If $value===$defaultValue, the item will be cleared from the viewstate. + */ + protected function setViewState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_viewState[$key]); + else + $this->_viewState[$key]=$value; + } + + /** + * @return string shape of the hotspot, can be 'circle', 'rect', 'poly', etc. + */ + abstract public function getShape(); + /** + * @return string coordinates defining the hotspot shape. + */ + abstract public function getCoordinates(); + + /** + * @return string the access key that allows you to quickly navigate to the HotSpot region. Defaults to ''. + */ + public function getAccessKey() + { + return $this->getViewState('AccessKey',''); + } + + /** + * @param string the access key that allows you to quickly navigate to the HotSpot region. + */ + public function setAccessKey($value) + { + $this->setViewState('AccessKey',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the alternate text to display for a HotSpot object. Defaults to ''. + */ + public function getAlternateText() + { + return $this->getViewState('AlternateText',''); + } + + /** + * @param string the alternate text to display for a HotSpot object. + */ + public function setAlternateText($value) + { + $this->setViewState('AlternateText',TPropertyValue::ensureString($value),''); + } + + /** + * @return THotSpotMode the behavior of a HotSpot object when it is clicked. Defaults to THotSpotMode::NotSet. + */ + public function getHotSpotMode() + { + return $this->getViewState('HotSpotMode',THotSpotMode::NotSet); + } + + /** + * @param THotSpotMode the behavior of a HotSpot object when it is clicked. + */ + public function setHotSpotMode($value) + { + $this->setViewState('HotSpotMode',TPropertyValue::ensureEnum($value,'THotSpotMode'),THotSpotMode::NotSet); + } + + /** + * @return string the URL to navigate to when a HotSpot object is clicked. Defaults to ''. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * @param string the URL to navigate to when a HotSpot object is clicked. + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return string a value that is post back when the HotSpot is clicked. Defaults to ''. + */ + public function getPostBackValue() + { + return $this->getViewState('PostBackValue',''); + } + + /** + * @param string a value that is post back when the HotSpot is clicked. + */ + public function setPostBackValue($value) + { + $this->setViewState('PostBackValue',TPropertyValue::ensureString($value),''); + } + + /** + * @return integer the tab index of the HotSpot region. Defaults to 0. + */ + public function getTabIndex() + { + return $this->getViewState('TabIndex',0); + } + + /** + * @param integer the tab index of the HotSpot region. + */ + public function setTabIndex($value) + { + $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return boolean whether postback event trigger by this hotspot will cause input validation, default is true + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this hotspot will cause input validation + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the hotspot causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the hotspot causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return string the target window or frame to display the new page when the HotSpot region + * is clicked. Defaults to ''. + */ + public function getTarget() + { + return $this->getViewState('Target',''); + } + + /** + * @param string the target window or frame to display the new page when the HotSpot region + * is clicked. + */ + public function setTarget($value) + { + $this->setViewState('Target',TPropertyValue::ensureString($value),''); + } + + /** + * @return boolean whether the hotspot has custom attributes + */ + public function getHasAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->getCount()>0; + else + return false; + } + + /** + * Returns the list of custom attributes. + * Custom attributes are name-value pairs that may be rendered + * as HTML tags' attributes. + * @return TAttributeCollection the list of custom attributes + */ + public function getAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes; + else + { + $attributes=new TAttributeCollection; + $this->setViewState('Attributes',$attributes,null); + return $attributes; + } + } + + /** + * @return boolean whether the named attribute exists + */ + public function hasAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->contains($name); + else + return false; + } + + /** + * @return string attribute value, null if attribute does not exist + */ + public function getAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->itemAt($name); + else + return null; + } + + /** + * Sets a custom hotspot attribute. + * @param string attribute name + * @param string value of the attribute + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + + /** + * Removes the named attribute. + * @param string the name of the attribute to be removed. + * @return string attribute value removed, null if attribute does not exist. + */ + public function removeAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->remove($name); + else + return null; + } + + /** + * Renders this hotspot. + * @param THtmlWriter + */ + public function render($writer) + { + $writer->addAttribute('shape',$this->getShape()); + $writer->addAttribute('coords',$this->getCoordinates()); + if(($mode=$this->getHotSpotMode())===THotSpotMode::NotSet) + $mode=THotSpotMode::Navigate; + if($mode===THotSpotMode::Navigate) + { + $writer->addAttribute('href',$this->getNavigateUrl()); + if(($target=$this->getTarget())!=='') + $writer->addAttribute('target',$target); + } + else if($mode===THotSpotMode::Inactive) + $writer->addAttribute('nohref','true'); + $text=$this->getAlternateText(); + $writer->addAttribute('title',$text); + $writer->addAttribute('alt',$text); + if(($accessKey=$this->getAccessKey())!=='') + $writer->addAttribute('accesskey',$accessKey); + if(($tabIndex=$this->getTabIndex())!==0) + $writer->addAttribute('tabindex',"$tabIndex"); + if($this->getHasAttributes()) + { + foreach($this->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + $writer->renderBeginTag('area'); + $writer->renderEndTag(); + } +} + +/** + * Class TCircleHotSpot. + * + * TCircleHotSpot defines a circular hot spot region in a {@link TImageMap} + * control. + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCircleHotSpot extends THotSpot +{ + /** + * @return string shape of this hotspot. + */ + public function getShape() + { + return 'circle'; + } + + /** + * @return string coordinates defining this hotspot shape + */ + public function getCoordinates() + { + return $this->getX().','.$this->getY().','.$this->getRadius(); + } + + /** + * @return integer radius of the circular HotSpot region. Defaults to 0. + */ + public function getRadius() + { + return $this->getViewState('Radius',0); + } + + /** + * @param integer radius of the circular HotSpot region. + */ + public function setRadius($value) + { + $this->setViewState('Radius',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the X coordinate of the center of the circular HotSpot region. Defaults to 0. + */ + public function getX() + { + return $this->getViewState('X',0); + } + + /** + * @param integer the X coordinate of the center of the circular HotSpot region. + */ + public function setX($value) + { + $this->setViewState('X',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the Y coordinate of the center of the circular HotSpot region. Defaults to 0. + */ + public function getY() + { + return $this->getViewState('Y',0); + } + + /** + * @param integer the Y coordinate of the center of the circular HotSpot region. + */ + public function setY($value) + { + $this->setViewState('Y',TPropertyValue::ensureInteger($value),0); + } +} + +/** + * Class TRectangleHotSpot. + * + * TRectangleHotSpot defines a rectangle hot spot region in a {@link + * TImageMap} control. + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRectangleHotSpot extends THotSpot +{ + /** + * @return string shape of this hotspot. + */ + public function getShape() + { + return 'rect'; + } + + /** + * @return string coordinates defining this hotspot shape + */ + public function getCoordinates() + { + return $this->getLeft().','.$this->getTop().','.$this->getRight().','.$this->getBottom(); + } + + /** + * @return integer the Y coordinate of the bottom side of the rectangle HotSpot region. Defaults to 0. + */ + public function getBottom() + { + return $this->getViewState('Bottom',0); + } + + /** + * @param integer the Y coordinate of the bottom side of the rectangle HotSpot region. + */ + public function setBottom($value) + { + $this->setViewState('Bottom',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the X coordinate of the right side of the rectangle HotSpot region. Defaults to 0. + */ + public function getLeft() + { + return $this->getViewState('Left',0); + } + + /** + * @param integer the X coordinate of the right side of the rectangle HotSpot region. + */ + public function setLeft($value) + { + $this->setViewState('Left',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the X coordinate of the right side of the rectangle HotSpot region. Defaults to 0. + */ + public function getRight() + { + return $this->getViewState('Right',0); + } + + /** + * @param integer the X coordinate of the right side of the rectangle HotSpot region. + */ + public function setRight($value) + { + $this->setViewState('Right',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the Y coordinate of the top side of the rectangle HotSpot region. Defaults to 0. + */ + public function getTop() + { + return $this->getViewState('Top',0); + } + + /** + * @param integer the Y coordinate of the top side of the rectangle HotSpot region. + */ + public function setTop($value) + { + $this->setViewState('Top',TPropertyValue::ensureInteger($value),0); + } +} + +/** + * Class TPolygonHotSpot. + * + * TPolygonHotSpot defines a polygon hot spot region in a {@link + * TImageMap} control. + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TPolygonHotSpot extends THotSpot +{ + /** + * @return string shape of this hotspot. + */ + public function getShape() + { + return 'poly'; + } + + /** + * @return string coordinates of the vertices defining the polygon. + * Coordinates are concatenated together with comma ','. Each pair + * represents (x,y) of a vertex. + */ + public function getCoordinates() + { + return $this->getViewState('Coordinates',''); + } + + /** + * @param string coordinates of the vertices defining the polygon. + * Coordinates are concatenated together with comma ','. Each pair + * represents (x,y) of a vertex. + */ + public function setCoordinates($value) + { + $this->setViewState('Coordinates',$value,''); + } +} + + +/** + * THotSpotMode class. + * THotSpotMode defines the enumerable type for the possible hot spot modes. + * + * The following enumerable values are defined: + * - NotSet: the mode is not specified + * - Navigate: clicking on the hotspot will redirect the browser to a different page + * - PostBack: clicking on the hotspot will cause a postback + * - Inactive: the hotspot is inactive (not clickable) + * + * @author Qiang Xue + * @version $Id: TImageMap.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class THotSpotMode extends TEnumerable +{ + const NotSet='NotSet'; + const Navigate='Navigate'; + const PostBack='PostBack'; + const Inactive='Inactive'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TInlineFrame.php b/gui/baculum/framework/Web/UI/WebControls/TInlineFrame.php new file mode 100644 index 0000000000..2c1371aa18 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TInlineFrame.php @@ -0,0 +1,276 @@ + + * @author Harry Pottash + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TInlineFrame.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TInlineFrame class + * + * TInlineFrame displays an inline frame (iframe) on a Web page. + * The location of the frame content is specified by {@link setFrameUrl FrameUrl}. + * The frame's alignment is specified by {@link setAlign Align}. + * The {@link setMarginWidth MarginWidth} and {@link setMarginHeight MarginHeight} + * properties define the number of pixels to use as the left/right margins and + * top/bottom margins, respectively, within the inline frame. + * The {@link setScrollBars ScrollBars} property specifies whether scrollbars are + * provided for the inline frame. And {@link setDescriptionUrl DescriptionUrl} + * gives the URI of a long description of the frame's contents. + * + * Original Prado v2 IFrame Author Information + * @author Jason Ragsdale + * @author Harry Pottash + * @version $Id: TInlineFrame.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TInlineFrame extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name of the iframe. + */ + protected function getTagName() + { + return 'iframe'; + } + + /** + * @return TInlineFrameAlign alignment of the iframe. Defaults to TInlineFrameAlign::NotSet. + */ + public function getAlign() + { + return $this->getViewState('Align',TInlineFrameAlign::NotSet); + } + + /** + * @param TInlineFrameAlign alignment of the iframe. + */ + public function setAlign($value) + { + $this->setViewState('Align',TPropertyValue::ensureEnum($value,'TInlineFrameAlign'),TInlineFrameAlign::NotSet); + } + + /** + * @return string the URL to long description + */ + public function getDescriptionUrl() + { + return $this->getViewState('DescriptionUrl',''); + } + + /** + * @param string the URL to the long description of the image. + */ + public function setDescriptionUrl($value) + { + $this->setViewState('DescriptionUrl',$value,''); + } + + /** + * @return boolean whether there should be a visual separator between the frames. Defaults to true. + */ + public function getShowBorder() + { + return $this->getViewState('ShowBorder',true); + } + + /** + * @param boolean whether there should be a visual separator between the frames. + */ + public function setShowBorder($value) + { + $this->setViewState('ShowBorder',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string URL that this iframe will load content from. Defaults to ''. + */ + public function getFrameUrl() + { + return $this->getViewState('FrameUrl',''); + } + + /** + * @param string URL that this iframe will load content from. + */ + public function setFrameUrl($value) + { + $this->setViewState('FrameUrl',$value,''); + } + + /** + * Returns the URL that this iframe will load content from + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getFrameUrl()}. + * @return string the URL that this iframe will load content from + * @see getFrameUrl + * @since 3.1.0 + */ + public function getData() + { + return $this->getFrameUrl(); + } + + /** + * Sets the URL that this iframe will load content from. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setFrameUrl()}. + * @param string the URL that this iframe will load content from + * @see setFrameUrl + * @since 3.1.0 + */ + public function setData($value) + { + $this->setFrameUrl($value); + } + + /** + * @return TInlineFrameScrollBars the visibility and position of scroll bars in an iframe. Defaults to TInlineFrameScrollBars::Auto. + */ + public function getScrollBars() + { + return $this->getViewState('ScrollBars',TInlineFrameScrollBars::Auto); + } + + /** + * @param TInlineFrameScrollBars the visibility and position of scroll bars in an iframe. + */ + public function setScrollBars($value) + { + $this->setViewState('ScrollBars',TPropertyValue::ensureEnum($value,'TInlineFrameScrollBars'),TInlineFrameScrollBars::Auto); + } + + /** + * @return integer the amount of space, in pixels, that should be left between + * the frame's contents and the left and right margins. Defaults to -1, meaning not set. + */ + public function getMarginWidth() + { + return $this->getViewState('MarginWidth',-1); + } + + /** + * @param integer the amount of space, in pixels, that should be left between + * the frame's contents and the left and right margins. + */ + public function setMarginWidth($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->setViewState('MarginWidth',$value,-1); + } + + /** + * @return integer the amount of space, in pixels, that should be left between + * the frame's contents and the top and bottom margins. Defaults to -1, meaning not set. + */ + public function getMarginHeight() + { + return $this->getViewState('MarginHeight',-1); + } + + /** + * @param integer the amount of space, in pixels, that should be left between + * the frame's contents and the top and bottom margins. + */ + public function setMarginHeight($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->setViewState('MarginHeight',$value,-1); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional button specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + if($this->getID()!=='') + $writer->addAttribute('name',$this->getUniqueID()); + + if(($src=$this->getFrameUrl())!=='') + $writer->addAttribute('src',$src); + + if(($align=strtolower($this->getAlign()))!=='notset') + $writer->addAttribute('align',$align); + + $scrollBars=$this->getScrollBars(); + if($scrollBars===TInlineFrameScrollBars::None) + $writer->addAttribute('scrolling','no'); + else if($scrollBars===TInlineFrameScrollBars::Both) + $writer->addAttribute('scrolling','yes'); + + if (!$this->getShowBorder()) + $writer->addAttribute('frameborder','0'); + + if(($longdesc=$this->getDescriptionUrl())!=='') + $writer->addAttribute('longdesc',$longdesc); + + if(($marginheight=$this->getMarginHeight())!==-1) + $writer->addAttribute('marginheight',$marginheight); + + if(($marginwidth=$this->getMarginWidth())!==-1) + $writer->addAttribute('marginwidth',$marginwidth); + + parent::addAttributesToRender($writer); + } +} + +/** + * TInlineFrameAlign class. + * TInlineFrameAlign defines the enumerable type for the possible alignments + * that the content in a {@link TInlineFrame} could be. + * + * The following enumerable values are defined: + * - NotSet: the alignment is not specified. + * - Left: left aligned + * - Right: right aligned + * - Top: top aligned + * - Middle: middle aligned + * - Bottom: bottom aligned + * + * @author Qiang Xue + * @version $Id: TInlineFrame.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TInlineFrameAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Left='Left'; + const Right='Right'; + const Top='Top'; + const Middle='Middle'; + const Bottom='Bottom'; +} + +/** + * TInlineFrameScrollBars class. + * TInlineFrameScrollBars defines the enumerable type for the possible scroll bar mode + * that a {@link TInlineFrame} control could use. + * + * The following enumerable values are defined: + * - None: no scroll bars. + * - Auto: scroll bars automatically appeared when needed. + * - Both: show both horizontal and vertical scroll bars all the time. + * + * @author Qiang Xue + * @version $Id: TInlineFrame.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TInlineFrameScrollBars extends TEnumerable +{ + const None='None'; + const Auto='Auto'; + const Both='Both'; +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TItemDataRenderer.php b/gui/baculum/framework/Web/UI/WebControls/TItemDataRenderer.php new file mode 100644 index 0000000000..2809e046f2 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TItemDataRenderer.php @@ -0,0 +1,83 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TItemDataRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ + +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Web.UI.WebControls.TDataRenderer'); + +/** + * TItemDataRenderer class + * + * TItemDataRenderer is the convient base class for template-based item data renderers. + * It implements the {@link IItemDataRenderer} interface, and because + * TItemDataRenderer extends from {@link TTemplateControl}, derived child + * classes can have templates to define their presentational layout. + * + * The following properties are provided by TItemDataRenderer: + * - {@link getItemIndex ItemIndex}: zero-based index of this renderer in the item list collection. + * - {@link getItemType ItemType}: item type of this renderer, such as TListItemType::AlternatingItem + * - {@link getData Data}: data associated with this renderer + + * @author Qiang Xue + * @version $Id: TItemDataRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.2 + */ +abstract class TItemDataRenderer extends TDataRenderer implements IItemDataRenderer +{ + /** + * index of the data item in the Items collection of repeater + */ + private $_itemIndex; + /** + * type of the TRepeaterItem + * @var TListItemType + */ + private $_itemType; + + /** + * @return TListItemType item type + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type. + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. + * If the item is not in the collection (e.g. it is a header item), it returns -1. + * @return integer zero-based index of the item. + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value) + { + $this->_itemIndex=TPropertyValue::ensureInteger($value); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TJavascriptLogger.php b/gui/baculum/framework/Web/UI/WebControls/TJavascriptLogger.php new file mode 100644 index 0000000000..a2f1c1c29f --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TJavascriptLogger.php @@ -0,0 +1,93 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TJavascriptLogger.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TJavascriptLogger class. + * + * Provides logging for client-side javascript. Example: template code + * + * + * Client-side javascript code to log info, error, warn, debug + * Logger.warn('A warning'); + * Logger.info('something happend'); + * + * + * To see the logger and console, press ALT-D (or CTRL-D on OS X). + * More information on the logger can be found at + * http://web.archive.org/web/20060512041505/gleepglop.com/javascripts/logger/ + * + * @author Wei Zhuo + * @version $Id: TJavascriptLogger.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TJavascriptLogger extends TWebControl +{ + private static $_keyCodes = array( + '0'=>48, '1'=>49, '2'=>50, '3'=>51, '4'=>52, '5'=>53, '6'=>54, '7'=>55, '8'=>56, '9'=>57, + 'a'=>65, 'b'=>66, 'c'=>67, 'd'=>68, 'e'=>69, 'f'=>70, 'g'=>71, 'h'=>72, + 'i'=>73, 'j'=>74, 'k'=>75, 'l'=>76, 'm'=>77, 'n'=>78, 'o'=>79, 'p'=>80, + 'q'=>81, 'r'=>82, 's'=>83, 't'=>84, 'u'=>85, 'v'=>86, 'w'=>87, 'x'=>88, 'y'=>89, 'z'=>90); + + /** + * @return string tag name of the panel + */ + protected function getTagName() + { + return 'div'; + } + + /** + * @param string keyboard key for toggling the console, default is J. + */ + public function setToggleKey($value) + { + $this->setViewState('ToggleKey', $value, 'j'); + } + + /** + * @return string keyboard key for toggling the console. + */ + public function getToggleKey() + { + return $this->getViewState('ToggleKey', 'j'); + } + + /** + * Registers the required logger javascript. + * @param TEventParameter event parameter + */ + public function onPreRender($param) + { + $key = strtolower($this->getToggleKey()); + $code = isset(self::$_keyCodes[$key]) ? self::$_keyCodes[$key] : 74; + $js = "var logConsole; Event.OnLoad(function() { logConsole = new LogConsole($code)}); "; + $cs = $this->getPage()->getClientScript(); + $cs->registerBeginScript($this->getClientID(),$js); + $cs->registerPradoScript('logger'); + } + + /** + * Register the required javascript libraries and + * display some general usage information. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + $code = strtoupper($this->getToggleKey()); + $info = '(more info).'; + $link = 'toggle the javascript log console.'; + $usage = 'Press ALT-'.$code.' (Or CTRL-'.$code.' on OS X) to'; + $writer->write("{$usage} {$link} {$info}"); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TKeyboard.php b/gui/baculum/framework/Web/UI/WebControls/TKeyboard.php new file mode 100644 index 0000000000..1260c5affb --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TKeyboard.php @@ -0,0 +1,189 @@ + and Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TKeyboard.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ + +/** + * Class TKeyboard. + * + * TKeyboard displays a virtual keyboard that users can click on to enter input in + * an associated text box. It helps to reduce the keyboard recording hacking. + * + * To use TKeyboard, write a template like following: + * + * + * + * + * + * A TKeyboard control is associated with a {@link TTextBox} control by specifying {@link setForControl ForControl} + * to be the ID of that control. When the textbox is in focus, a virtual keyboard will pop up; and when + * the text box is losing focus, the keyboard will hide automatically. Set {@link setAutoHide AutoHide} to + * false to keep the keyboard showing all the time. + * + * The appearance of the keyboard can also be changed by specifying a customized CSS file via + * {@link setCssUrl CssUrl}. By default, the CSS class name for the keyboard is 'Keyboard'. This may + * also be changed by specifying {@link setKeyboardCssClass KeyboardCssClass}. + * + * @author Sergey Morkovkin and Qiang Xue + * @version $Id: TKeyboard.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TKeyboard extends TWebControl +{ + /** + * @return string the ID path of the {@link TTextBox} control + */ + public function getForControl() + { + return $this->getViewState('ForControl',''); + } + + /** + * Sets the ID path of the {@link TTextBox} control. + * The ID path is the dot-connected IDs of the controls reaching from + * the keyboard's naming container to the target control. + * @param string the ID path + */ + public function setForControl($value) + { + $this->setViewState('ForControl', TPropertyValue::ensureString($value)); + } + + /** + * @return boolean whether the keyboard should be hidden when the textbox is not in focus. Defaults to true. + */ + public function getAutoHide() + { + return $this->getViewState('AutoHide', true); + } + + /** + * @param boolean whether the keyboard should be hidden when the textbox is not in focus. + */ + public function setAutoHide($value) + { + $this->setViewState('AutoHide', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return string the CSS class name for the keyboard
      element. Defaults to 'Keyboard'. + */ + public function getKeyboardCssClass() + { + return $this->getViewState('KeyboardCssClass', 'Keyboard'); + } + + /** + * Sets a value indicating the CSS class name for the keyboard
      element. + * Note, if you change this property, make sure you also supply a customized CSS file + * by specifying {@link setCssUrl CssUrl} which uses the new CSS class name for styling. + * @param string the CSS class name for the keyboard
      element. + */ + public function setKeyboardCssClass($value) + { + $this->setViewState('KeyboardCssClass', $value, 'Keyboard'); + } + + /** + * @return string the URL for the CSS file to customize the appearance of the keyboard. + */ + public function getCssUrl() + { + return $this->getViewState('CssUrl', ''); + } + + /** + * @param string the URL for the CSS file to customize the appearance of the keyboard. + */ + public function setCssUrl($value) + { + $this->setViewState('CssUrl', $value, ''); + } + + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getPage()->getClientSupportsJavaScript()) + { + $this->registerStyleSheet(); + $this->registerClientScript(); + } + } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation with additional TKeyboard specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if($this->getPage()->getClientSupportsJavaScript()) + $writer->addAttribute('id',$this->getClientID()); + } + + /** + * Registers the CSS relevant to the TKeyboard. + * It will register the CSS file specified by {@link getCssUrl CssUrl}. + * If that is not set, it will use the default CSS. + */ + protected function registerStyleSheet() + { + if(($url=$this->getCssUrl())==='') + $url=$this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'keyboard.css'); + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + + /** + * Registers the relevant JavaScript. + */ + protected function registerClientScript() + { + $options=TJavaScript::encode($this->getClientOptions()); + $className=$this->getClientClassName(); + $cs=$this->getPage()->getClientScript(); + $cs->registerPradoScript('keyboard'); + $cs->registerEndScript('prado:'.$this->getClientID(), "new $className($options);"); + } + + /** + * @return string the Javascript class name for this control + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TKeyboard'; + } + + /** + * @return array the JavaScript options for this control + */ + protected function getClientOptions() + { + if(($forControl=$this->getForControl())==='') + throw new TConfigurationException('keyboard_forcontrol_required'); + if(($target=$this->findControl($forControl))===null) + throw new TConfigurationException('keyboard_forcontrol_invalid',$forControl); + + $options['ID'] = $this->getClientID(); + $options['ForControl'] = $target->getClientID(); + $options['AutoHide'] = $this->getAutoHide(); + $options['CssClass'] = $this->getKeyboardCssClass(); + + return $options; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TLabel.php b/gui/baculum/framework/Web/UI/WebControls/TLabel.php new file mode 100644 index 0000000000..4552b1a5e4 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TLabel.php @@ -0,0 +1,154 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLabel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TLabel class + * + * TLabel displays a piece of text on a Web page. + * Use {@link setText Text} property to set the text to be displayed. + * TLabel will render the contents enclosed within its component tag + * if {@link setText Text} is empty. + * To use TLabel as a form label, associate it with a control by setting the + * {@link setForControl ForControl} property. + * The associated control must be locatable within the label's naming container. + * If the associated control is not visible, the label will not be rendered, either. + * + * Note, {@link setText Text} will NOT be encoded for rendering. + * Make sure it does not contain dangerous characters that you want to avoid. + * + * @author Qiang Xue + * @version $Id: TLabel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TLabel extends TWebControl implements IDataRenderer +{ + private $_forControl=''; + + /** + * @return string tag name of the label, returns 'label' if there is an associated control, 'span' otherwise. + */ + protected function getTagName() + { + return ($this->getForControl()==='')?'span':'label'; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + * @throws TInvalidDataValueException if associated control cannot be found using the ID + */ + protected function addAttributesToRender($writer) + { + if($this->_forControl!=='') + $writer->addAttribute('for',$this->_forControl); + parent::addAttributesToRender($writer); + } + + /** + * Renders the label. + * It overrides the parent implementation by checking if an associated + * control is visible or not. If not, the label will not be rendered. + * @param THtmlWriter writer + */ + public function render($writer) + { + if(($aid=$this->getForControl())!=='') + { + if($control=$this->findControl($aid)) + { + if($control->getVisible(true)) + { + $this->_forControl=$control->getClientID(); + parent::render($writer); + } + } + else + throw new TInvalidDataValueException('label_associatedcontrol_invalid',$aid); + } + else + parent::render($writer); + } + + /** + * Renders the body content of the label. + * @param THtmlWriter the renderer + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='') + parent::renderContents($writer); + else + $writer->write($text); + } + + /** + * @return string the text value of the label + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string the text value of the label + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the text value of the label. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text value of the label + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the text value of the label. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text value of the label + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string the associated control ID + */ + public function getForControl() + { + return $this->getViewState('ForControl',''); + } + + /** + * Sets the ID of the control that the label is associated with. + * The control must be locatable via {@link TControl::findControl} using the ID. + * @param string the associated control ID + */ + public function setForControl($value) + { + $this->setViewState('ForControl',$value,''); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TLinkButton.php b/gui/baculum/framework/Web/UI/WebControls/TLinkButton.php new file mode 100644 index 0000000000..a9e6f4cffc --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TLinkButton.php @@ -0,0 +1,334 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLinkButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TLinkButton class + * + * TLinkButton creates a hyperlink style button on the page. + * TLinkButton has the same appearance as a hyperlink. However, it is mainly + * used to submit data to a page. Like {@link TButton}, you can create either + * a submit button or a command button. + * + * A command button has a command name (specified by + * the {@link setCommandName CommandName} property) and and a command parameter + * (specified by {@link setCommandParameter CommandParameter} property) + * associated with the button. This allows you to create multiple TLinkButton + * components on a Web page and programmatically determine which one is clicked + * with what parameter. You can provide an event handler for + * {@link onCommand OnCommand} event to programmatically control the actions performed + * when the command button is clicked. In the event handler, you can determine + * the {@link setCommandName CommandName} property value and + * the {@link setCommandParameter CommandParameter} property value + * through the {@link TCommandParameter::getName Name} and + * {@link TCommandParameter::getParameter Parameter} properties of the event + * parameter which is of type {@link TCommandEventParameter}. + * + * A submit button does not have a command name associated with the button + * and clicking on it simply posts the Web page back to the server. + * By default, a TLinkButton component is a submit button. + * You can provide an event handler for the {@link onClick OnClick} event + * to programmatically control the actions performed when the submit button is clicked. + * + * Clicking on button can trigger form validation, if + * {@link setCausesValidation CausesValidation} is true. + * And the validation may be restricted within a certain group of validator + * controls by setting {@link setValidationGroup ValidationGroup} property. + * If validation is successful, the data will be post back to the same page. + * + * TLinkButton will display the {@link setText Text} property value + * as the hyperlink text. If {@link setText Text} is empty, the body content + * of TLinkButton will be displayed. Therefore, you can use TLinkButton + * as an image button by enclosing an <img> tag as the body of TLinkButton. + * + * @author Qiang Xue + * @version $Id: TLinkButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TLinkButton extends TWebControl implements IPostBackEventHandler, IButtonControl, IDataRenderer +{ + /** + * @return string tag name of the button + */ + protected function getTagName() + { + return 'a'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attribute name-value pairs to renderer. + * This overrides the parent implementation with additional button specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + + $writer->addAttribute('id',$this->getClientID()); + + // We call parent implementation here because some attributes + // may be overwritten in the following + parent::addAttributesToRender($writer); + + if($this->getEnabled(true) && $this->getEnableClientScript()) + { + $this->renderLinkButtonHref($writer); + $this->renderClientControlScript($writer); + } + else if($this->getEnabled()) // in this case, parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + } + + /** + * Renders the client-script code. + * @param THtmlWriter renderer + */ + protected function renderClientControlScript($writer) + { + $cs = $this->getPage()->getClientScript(); + $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * @param boolean set by a panel to register this button as the default button for the panel. + */ + public function setIsDefaultButton($value) + { + $this->setViewState('IsDefaultButton', TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean true if this button is registered as a default button for a panel. + */ + public function getIsDefaultButton() + { + return $this->getViewState('IsDefaultButton', false); + } + + /** + * Renders the Href for link button. + * @param THtmlWriter renderer + */ + protected function renderLinkButtonHref($writer) + { + //create unique no-op url references + $nop = "javascript:;//".$this->getClientID(); + $writer->addAttribute('href', $nop); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TLinkButton'; + } + + /** + * Returns postback specifications for the button. + * This method is used by framework and control developers. + * @return array parameters about how the button defines its postback behavior. + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['StopEvent'] = true; + + return $options; + } + + /** + * Renders the body content enclosed between the control tag. + * If {@link getText Text} is not empty, it will be rendered. Otherwise, + * the body content enclosed in the control tag will be rendered. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='') + parent::renderContents($writer); + else + $writer->write($text); + } + + /** + * @return string the text caption of the button + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string the text caption to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the caption of the button. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string caption of the button. + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the caption of the button. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string caption of the button + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string the command name associated with the {@link onCommand OnCommand} event. + */ + public function getCommandName() + { + return $this->getViewState('CommandName',''); + } + + /** + * @param string the command name associated with the {@link onCommand OnCommand} event. + */ + public function setCommandName($value) + { + $this->setViewState('CommandName',$value,''); + } + + /** + * @return string the parameter associated with the {@link onCommand OnCommand} event + */ + public function getCommandParameter() + { + return $this->getViewState('CommandParameter',''); + } + + /** + * @param string the parameter associated with the {@link onCommand OnCommand} event. + */ + public function setCommandParameter($value) + { + $this->setViewState('CommandParameter',$value,''); + } + + /** + * @return boolean whether postback event trigger by this button will cause input validation + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * Sets the value indicating whether postback event trigger by this button will cause input validation. + * @param string the text caption to be set + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the group of validators which the button causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the button causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * Raises the postback event. + * This method is required by {@link IPostBackEventHandler} interface. + * If {@link getCausesValidation CausesValidation} is true, it will + * invoke the page's {@link TPage::validate validate} method first. + * It will raise {@link onClick OnClick} and {@link onCommand OnCommand} events. + * This method is mainly used by framework and control developers. + * @param TEventParameter the event parameter + */ + public function raisePostBackEvent($param) + { + if($this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onClick(null); + $this->onCommand(new TCommandEventParameter($this->getCommandName(),$this->getCommandParameter())); + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnClick' event to fire up the event handlers. + * If you override this method, be sure to call the parent implementation + * so that the event handler can be invoked. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onClick($param) + { + $this->raiseEvent('OnClick',$this,$param); + } + + /** + * This method is invoked when the button is clicked. + * The method raises 'OnCommand' event to fire up the event handlers. + * If you override this method, be sure to call the parent implementation + * so that the event handlers can be invoked. + * @param TCommandEventParameter event parameter to be passed to the event handlers + */ + public function onCommand($param) + { + $this->raiseEvent('OnCommand',$this,$param); + $this->raiseBubbleEvent($this,$param); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TListBox.php b/gui/baculum/framework/Web/UI/WebControls/TListBox.php new file mode 100644 index 0000000000..a616a2be09 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TListBox.php @@ -0,0 +1,262 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TListBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TListControl class + */ +Prado::using('System.Web.UI.WebControls.TListControl'); + +/** + * TListBox class + * + * TListBox displays a list box on a Web page that allows single or multiple selection. + * The list box allows multiple selections if {@link setSelectionMode SelectionMode} + * is TListSelectionMode::Multiple. It takes single selection only if Single. + * The property {@link setRows Rows} specifies how many rows of options are visible + * at a time. See {@link TListControl} for inherited properties. + * + * Since v3.0.3, TListBox starts to support optgroup. To specify an option group for + * a list item, set a Group attribute with it, + * + * $listitem->Attributes->Group="Group Name"; + * // or in template + * + * + * @author Qiang Xue + * @version $Id: TListBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TListBox extends TListControl implements IPostBackDataHandler, IValidatable +{ + private $_dataChanged=false; + private $_isValid=true; + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation with additional list box specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $rows=$this->getRows(); + $writer->addAttribute('size',"$rows"); + if($this->getSelectionMode()===TListSelectionMode::Multiple) + $writer->addAttribute('name',$this->getUniqueID().'[]'); + else + $writer->addAttribute('name',$this->getUniqueID()); + parent::addAttributesToRender($writer); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TListBox'; + } + + /** + * Registers the list control to load post data on postback. + * This method overrides the parent implementation. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getEnabled(true)) + $this->getPage()->registerRequiresPostData($this); + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + if(!$this->getEnabled(true)) + return false; + $this->ensureDataBound(); + $selections=isset($values[$key])?$values[$key]:null; + if($selections!==null) + { + $items=$this->getItems(); + if($this->getSelectionMode()===TListSelectionMode::Single) + { + $selection=is_array($selections)?$selections[0]:$selections; + $index=$items->findIndexByValue($selection,false); + if($this->getSelectedIndex()!==$index) + { + $this->setSelectedIndex($index); + return $this->_dataChanged=true; + } + else + return false; + } + if(!is_array($selections)) + $selections=array($selections); + $list=array(); + foreach($selections as $selection) + $list[]=$items->findIndexByValue($selection,false); + $list2=$this->getSelectedIndices(); + $n=count($list); + $flag=false; + if($n===count($list2)) + { + sort($list,SORT_NUMERIC); + for($i=0;$i<$n;++$i) + { + if($list[$i]!==$list2[$i]) + { + $flag=true; + break; + } + } + } + else + $flag=true; + if($flag) + { + $this->setSelectedIndices($list); + $this->_dataChanged=true; + } + return $flag; + } + else if($this->getSelectedIndex()!==-1) + { + $this->clearSelection(); + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getSelectedIndices SelectedIndices} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + if($this->getAutoPostBack() && $this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onSelectedIndexChanged(null); + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * @return boolean whether this control allows multiple selection + */ + protected function getIsMultiSelect() + { + return $this->getSelectionMode()===TListSelectionMode::Multiple; + } + + /** + * @return integer the number of rows to be displayed in the list control + */ + public function getRows() + { + return $this->getViewState('Rows', 4); + } + + /** + * @param integer the number of rows to be displayed in the list control + */ + public function setRows($value) + { + $value=TPropertyValue::ensureInteger($value); + if($value<=0) + $value=4; + $this->setViewState('Rows', $value, 4); + } + + /** + * @return TListSelectionMode the selection mode (Single, Multiple). Defaults to TListSelectionMode::Single. + */ + public function getSelectionMode() + { + return $this->getViewState('SelectionMode', TListSelectionMode::Single); + } + + /** + * @param TListSelectionMode the selection mode + */ + public function setSelectionMode($value) + { + $this->setViewState('SelectionMode',TPropertyValue::ensureEnum($value,'TListSelectionMode'),TListSelectionMode::Single); + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getSelectedValue(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } +} + + +/** + * TListSelectionMode class. + * TListSelectionMode defines the enumerable type for the possible selection modes of a {@link TListBox}. + * + * The following enumerable values are defined: + * - Single: single selection + * - Multiple: allow multiple selection + * + * @author Qiang Xue + * @version $Id: TListBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TListSelectionMode extends TEnumerable +{ + const Single='Single'; + const Multiple='Multiple'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TListControl.php b/gui/baculum/framework/Web/UI/WebControls/TListControl.php new file mode 100644 index 0000000000..f9fdd77fa3 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TListControl.php @@ -0,0 +1,925 @@ + + * @author Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TListControl.php 3288 2013-04-30 10:36:50Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes the supporting classes + */ +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Web.UI.WebControls.TListItem'); +Prado::using('System.Collections.TListItemCollection'); +Prado::using('System.Collections.TAttributeCollection'); +Prado::using('System.Util.TDataFieldAccessor'); + +/** + * TListControl class + * + * TListControl is a base class for list controls, such as {@link TListBox}, + * {@link TDropDownList}, {@link TCheckBoxList}, etc. + * It manages the items and their status in a list control. + * It also implements how the items can be populated from template and + * data source. + * + * The property {@link getItems} returns a list of the items in the control. + * To specify or determine which item is selected, use the + * {@link getSelectedIndex SelectedIndex} property that indicates the zero-based + * index of the selected item in the item list. You may also use + * {@link getSelectedItem SelectedItem} and {@link getSelectedValue SelectedValue} + * to get the selected item and its value. For multiple selection lists + * (such as {@link TCheckBoxList} and {@link TListBox}), property + * {@link getSelectedIndices SelectedIndices} is useful. + * + * TListControl implements {@link setAutoPostBack AutoPostBack} which allows + * a list control to postback the page if the selections of the list items are changed. + * The {@link setCausesValidation CausesValidation} and {@link setValidationGroup ValidationGroup} + * properties may be used to specify that validation be performed when auto postback occurs. + * + * There are three ways to populate the items in a list control: from template, + * using {@link setDataSource DataSource} and using {@link setDataSourceID DataSourceID}. + * The latter two are covered in {@link TDataBoundControl}. To specify items via + * template, using the following template syntax: + * + * + * + * + * + * + * + * + * When {@link setDataSource DataSource} or {@link setDataSourceID DataSourceID} + * is used to populate list items, the {@link setDataTextField DataTextField} and + * {@link setDataValueField DataValueField} properties are used to specify which + * columns of the data will be used to populate the text and value of the items. + * For example, if a data source is as follows, + * + * $dataSource=array( + * array('name'=>'John', 'age'=>31), + * array('name'=>'Cary', 'age'=>28), + * array('name'=>'Rose', 'age'=>35), + * ); + * + * setting {@link setDataTextField DataTextField} and {@link setDataValueField DataValueField} + * to 'name' and 'age' will make the first item's text be 'John', value be 31, + * the second item's text be 'Cary', value be 28, and so on. + * The {@link setDataTextFormatString DataTextFormatString} property may be further + * used to format how the item should be displayed. See {@link formatDataValue()} + * for an explanation of the format string. + * + * The {@link setPromptText PromptText} and {@link setPromptValue PromptValue} properties can + * be used to add a dummy list item that will be rendered first. + * + * @author Qiang Xue + * @version $Id: TListControl.php 3288 2013-04-30 10:36:50Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +abstract class TListControl extends TDataBoundControl implements IDataRenderer +{ + /** + * @var TListItemCollection item list + */ + private $_items=null; + /** + * @var boolean whether items are restored from viewstate + */ + private $_stateLoaded=false; + /** + * @var mixed the following selection variables are used + * to keep selections when Items are not available + */ + private $_cachedSelectedIndex=-1; + private $_cachedSelectedValue=null; + private $_cachedSelectedIndices=null; + private $_cachedSelectedValues=null; + + /** + * @return string tag name of the list control + */ + protected function getTagName() + { + return 'select'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + if($this->getIsMultiSelect()) + $writer->addAttribute('multiple','multiple'); + if($this->getEnabled(true)) + { + if($this->getAutoPostBack() + && $this->getEnableClientScript() + && $page->getClientSupportsJavaScript()) + { + $this->renderClientControlScript($writer); + } + } + else if($this->getEnabled()) + $writer->addAttribute('disabled','disabled'); + parent::addAttributesToRender($writer); + } + + /** + * Renders the javascript for list control. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $this->getPage()->getClientScript()->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * Derived classes may override this method and return customized js class names. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TListControl'; + } + + /** + * @return array postback options for JS postback code + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['EventTarget'] = $this->getUniqueID(); + return $options; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TListItem} objects into the {@link getItems Items} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + // Do not add items from template if items are loaded from viewstate + if(!$this->_stateLoaded && ($object instanceof TListItem)) + { + $index=$this->getItems()->add($object); + if(($this->_cachedSelectedValue!==null && $this->_cachedSelectedValue===$object->getValue()) || ($this->_cachedSelectedIndex===$index)) + { + $object->setSelected(true); + $this->_cachedSelectedValue=null; + $this->_cachedSelectedIndex=-1; + } + } + } + + /** + * Performs databinding to populate list items from data source. + * This method is invoked by dataBind(). + * You may override this function to provide your own way of data population. + * @param Traversable the data + */ + protected function performDataBinding($data) + { + $items=$this->getItems(); + if(!$this->getAppendDataBoundItems()) + $items->clear(); + $textField=$this->getDataTextField(); + if($textField==='') + $textField=0; + $valueField=$this->getDataValueField(); + if($valueField==='') + $valueField=1; + $textFormat=$this->getDataTextFormatString(); + $groupField=$this->getDataGroupField(); + foreach($data as $key=>$object) + { + $item=$items->createListItem(); + if(is_array($object) || is_object($object)) + { + $text=TDataFieldAccessor::getDataFieldValue($object,$textField); + $value=TDataFieldAccessor::getDataFieldValue($object,$valueField); + $item->setValue($value); + if($groupField!=='') + $item->setAttribute('Group',TDataFieldAccessor::getDataFieldValue($object,$groupField)); + } + else + { + $text=$object; + $item->setValue("$key"); + } + $item->setText($this->formatDataValue($textFormat,$text)); + } + // SelectedValue or SelectedIndex may be set before databinding + // so we make them be effective now + if($this->_cachedSelectedValue!==null) + { + $this->setSelectedValue($this->_cachedSelectedValue); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedIndex!==-1) + { + $this->setSelectedIndex($this->_cachedSelectedIndex); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedValues!==null) + { + $this->setSelectedValues($this->_cachedSelectedValues); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedIndices!==null) + { + $this->setSelectedIndices($this->_cachedSelectedIndices); + $this->resetCachedSelections(); + } + } + + private function resetCachedSelections() + { + $this->_cachedSelectedValue=null; + $this->_cachedSelectedIndex=-1; + $this->_cachedSelectedValues=null; + $this->_cachedSelectedIndices=null; + } + + /** + * Creates a collection object to hold list items. + * This method may be overriden to create a customized collection. + * @return TListItemCollection the collection object + */ + protected function createListItemCollection() + { + return new TListItemCollection; + } + + /** + * Saves items into viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if($this->_items) + $this->setViewState('Items',$this->_items->saveState(),null); + else + $this->clearViewState('Items'); + } + + /** + * Loads items from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + $this->_stateLoaded=true; + if(!$this->getIsDataBound()) + { + $this->_items=$this->createListItemCollection(); + $this->_items->loadState($this->getViewState('Items',null)); + } + $this->clearViewState('Items'); + } + + /** + * @return boolean whether this is a multiselect control. Defaults to false. + */ + protected function getIsMultiSelect() + { + return false; + } + + /** + * @return boolean whether performing databind should append items or clear the existing ones. Defaults to false. + */ + public function getAppendDataBoundItems() + { + return $this->getViewState('AppendDataBoundItems',false); + } + + /** + * @param boolean whether performing databind should append items or clear the existing ones. + */ + public function setAppendDataBoundItems($value) + { + $this->setViewState('AppendDataBoundItems',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user makes change to the list control and then tabs out of it. + * Defaults to false. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',false); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * makes change to the list control and then tabs out of it. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether postback event trigger by this list control will cause input validation, default is true. + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this list control will cause input validation. + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return string the field of the data source that provides the text content of the list items. + */ + public function getDataTextField() + { + return $this->getViewState('DataTextField',''); + } + + /** + * @param string the field of the data source that provides the text content of the list items. + */ + public function setDataTextField($value) + { + $this->setViewState('DataTextField',$value,''); + } + + /** + * @return string the formatting string used to control how data bound to the list control is displayed. + */ + public function getDataTextFormatString() + { + return $this->getViewState('DataTextFormatString',''); + } + + /** + * Sets data text format string. + * The format string is used in {@link TDataValueFormatter::format()} to format the Text property value + * of each item in the list control. + * @param string the formatting string used to control how data bound to the list control is displayed. + * @see TDataValueFormatter::format() + */ + public function setDataTextFormatString($value) + { + $this->setViewState('DataTextFormatString',$value,''); + } + + /** + * @return string the field of the data source that provides the value of each list item. + */ + public function getDataValueField() + { + return $this->getViewState('DataValueField',''); + } + + /** + * @param string the field of the data source that provides the value of each list item. + */ + public function setDataValueField($value) + { + $this->setViewState('DataValueField',$value,''); + } + + /** + * @return string the field of the data source that provides the label of the list item groups + */ + public function getDataGroupField() + { + return $this->getViewState('DataGroupField',''); + } + + /** + * @param string the field of the data source that provides the label of the list item groups + */ + public function setDataGroupField($value) + { + $this->setViewState('DataGroupField',$value,''); + } + + /** + * @return integer the number of items in the list control + */ + public function getItemCount() + { + return $this->_items?$this->_items->getCount():0; + } + + /** + * @return boolean whether the list control contains any items. + */ + public function getHasItems() + { + return ($this->_items && $this->_items->getCount()>0); + } + + /** + * @return TListItemCollection the item collection + */ + public function getItems() + { + if(!$this->_items) + $this->_items=$this->createListItemCollection(); + return $this->_items; + } + + /** + * @return integer the index (zero-based) of the item being selected, -1 if no item is selected. + */ + public function getSelectedIndex() + { + if($this->_items) + { + $n=$this->_items->getCount(); + for($i=0;$i<$n;++$i) + if($this->_items->itemAt($i)->getSelected()) + return $i; + } + return -1; + } + + /** + * @param integer the index (zero-based) of the item to be selected + */ + public function setSelectedIndex($index) + { + if(($index=TPropertyValue::ensureInteger($index))<0) + $index=-1; + if($this->_items) + { + $this->clearSelection(); + if($index>=0 && $index<$this->_items->getCount()) + $this->_items->itemAt($index)->setSelected(true); + } + $this->_cachedSelectedIndex=$index; + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedIndex($index); + } + + /** + * @return array list of index of items that are selected + */ + public function getSelectedIndices() + { + $selections=array(); + if($this->_items) + { + $n=$this->_items->getCount(); + for($i=0;$i<$n;++$i) + if($this->_items->itemAt($i)->getSelected()) + $selections[]=$i; + } + return $selections; + } + + /** + * @param array list of index of items to be selected + */ + public function setSelectedIndices($indices) + { + if($this->getIsMultiSelect()) + { + if($this->_items) + { + $this->clearSelection(); + $n=$this->_items->getCount(); + foreach($indices as $index) + { + if($index>=0 && $index<$n) + $this->_items->itemAt($index)->setSelected(true); + } + } + $this->_cachedSelectedIndices=$indices; + } + else + throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); + + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedIndices($indices); + } + + /** + * @return TListItem|null the selected item with the lowest cardinal index, null if no item is selected. + */ + public function getSelectedItem() + { + if(($index=$this->getSelectedIndex())>=0) + return $this->_items->itemAt($index); + else + return null; + } + + /** + * Returns the value of the selected item with the lowest cardinal index. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getSelectedValue()}. + * @return string the value of the selected item with the lowest cardinal index, empty if no selection. + * @see getSelectedValue + * @since 3.1.0 + */ + public function getData() + { + return $this->getSelectedValue(); + } + + /** + * Selects an item by the specified value. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setSelectedValue()}. + * @param string the value of the item to be selected. + * @see setSelectedValue + * @since 3.1.0 + */ + public function setData($value) + { + $this->setSelectedValue($value); + } + + /** + * @return string the value of the selected item with the lowest cardinal index, empty if no selection + */ + public function getSelectedValue() + { + $index=$this->getSelectedIndex(); + return $index>=0?$this->getItems()->itemAt($index)->getValue():''; + } + + /** + * Sets selection by item value. + * Existing selections will be cleared if the item value is found in the item collection. + * Note, if the value is null, existing selections will also be cleared. + * @param string the value of the item to be selected. + */ + public function setSelectedValue($value) + { + if($this->_items) + { + if($value===null) + $this->clearSelection(); + else if(($item=$this->_items->findItemByValue($value))!==null) + { + $this->clearSelection(); + $item->setSelected(true); + } + else + $this->clearSelection(); + } + $this->_cachedSelectedValue=$value; + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedValue($value); + } + + + /** + * @return array list of the selected item values (strings) + */ + public function getSelectedValues() + { + $values=array(); + if($this->_items) + { + foreach($this->_items as $item) + { + if($item->getSelected()) + $values[]=$item->getValue(); + } + } + return $values; + } + + /** + * @param array list of the selected item values + */ + public function setSelectedValues($values) + { + if($this->getIsMultiSelect()) + { + if($this->_items) + { + $this->clearSelection(); + $lookup=array(); + foreach($this->_items as $item) + $lookup[$item->getValue()]=$item; + foreach($values as $value) + { + if(isset($lookup["$value"])) + $lookup["$value"]->setSelected(true); + } + } + $this->_cachedSelectedValues=$values; + } + else + throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); + + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->setSelectedValues($values); + } + + /** + * @return string selected value + */ + public function getText() + { + return $this->getSelectedValue(); + } + + /** + * @param string value to be selected + */ + public function setText($value) + { + $this->setSelectedValue($value); + } + + /** + * Clears all existing selections. + */ + public function clearSelection() + { + if($this->_items) + { + foreach($this->_items as $item) + $item->setSelected(false); + } + + if($this->getAdapter() instanceof IListControlAdapter) + $this->getAdapter()->clearSelection(); + } + + /** + * @return string the group of validators which the list control causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the list control causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return string the prompt text which is to be displayed as the first list item. + * @since 3.1.1 + */ + public function getPromptText() + { + return $this->getViewState('PromptText',''); + } + + /** + * @param string the prompt text which is to be displayed as the first list item. + * @since 3.1.1 + */ + public function setPromptText($value) + { + $this->setViewState('PromptText',$value,''); + } + + /** + * @return string the prompt selection value. + * @see getPromptText + * @since 3.1.1 + */ + public function getPromptValue() + { + return $this->getViewState('PromptValue',''); + } + + /** + * @param string the prompt selection value. If empty, {@link getPromptText PromptText} will be used as the value. + * @see setPromptText + * @since 3.1.1 + */ + public function setPromptValue($value) + { + $this->setViewState('PromptValue',(string)$value,''); + } + + /** + * Raises OnSelectedIndexChanged event when selection is changed. + * This method is invoked when the list control has its selection changed + * by end-users. + * @param TEventParameter event parameter + */ + public function onSelectedIndexChanged($param) + { + $this->raiseEvent('OnSelectedIndexChanged',$this,$param); + $this->onTextChanged($param); + } + + /** + * Raises OnTextChanged event when selection is changed. + * This method is invoked when the list control has its selection changed + * by end-users. + * @param TEventParameter event parameter + */ + public function onTextChanged($param) + { + $this->raiseEvent('OnTextChanged',$this,$param); + } + + /** + * Renders the prompt text, if any. + * @param THtmlWriter writer + * @since 3.1.1 + */ + protected function renderPrompt($writer) + { + $text=$this->getPromptText(); + $value=$this->getPromptValue(); + if($value==='') + $value=$text; + if($value!=='') + { + $writer->addAttribute('value',$value); + $writer->renderBeginTag('option'); + $writer->write(THttpUtility::htmlEncode($text)); + $writer->renderEndTag(); + $writer->writeLine(); + } + } + + /** + * Renders body content of the list control. + * This method renders items contained in the list control as the body content. + * @param THtmlWriter writer + */ + public function renderContents($writer) + { + $this->renderPrompt($writer); + + if($this->_items) + { + $writer->writeLine(); + $previousGroup=null; + foreach($this->_items as $item) + { + if($item->getEnabled()) + { + if($item->getHasAttributes()) + { + $group=$item->getAttributes()->remove('Group'); + if($group!==$previousGroup) + { + if($previousGroup!==null) + { + $writer->renderEndTag(); + $writer->writeLine(); + $previousGroup=null; + } + if($group!==null) + { + $writer->addAttribute('label',$group); + $writer->renderBeginTag('optgroup'); + $writer->writeLine(); + $previousGroup=$group; + } + } + foreach($item->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + else if($previousGroup!==null) + { + $writer->renderEndTag(); + $writer->writeLine(); + $previousGroup=null; + } + if($item->getSelected()) + $writer->addAttribute('selected','selected'); + $writer->addAttribute('value',$item->getValue()); + $writer->renderBeginTag('option'); + $writer->write(THttpUtility::htmlEncode($item->getText())); + $writer->renderEndTag(); + $writer->writeLine(); + } + } + if($previousGroup!==null) + { + $writer->renderEndTag(); + $writer->writeLine(); + } + } + } + + /** + * Formats the text value according to a format string. + * If the format string is empty, the original value is converted into + * a string and returned. + * If the format string starts with '#', the string is treated as a PHP expression + * within which the token '{0}' is translated with the data value to be formated. + * Otherwise, the format string and the data value are passed + * as the first and second parameters in {@link sprintf}. + * @param string format string + * @param mixed the data to be formatted + * @return string the formatted result + */ + protected function formatDataValue($formatString,$value) + { + if($formatString==='') + return TPropertyValue::ensureString($value); + else if($formatString[0]==='#') + { + $expression=strtr(substr($formatString,1),array('{0}'=>'$value')); + try + { + if(eval("\$result=$expression;")===false) + throw new Exception(''); + return $result; + } + catch(Exception $e) + { + throw new TInvalidDataValueException('listcontrol_expression_invalid',get_class($this),$expression,$e->getMessage()); + } + } + else + return sprintf($formatString,$value); + } +} + +/** + * IListControlAdapter interface + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 25 04:53:43 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ +interface IListControlAdapter +{ + /** + * Selects an item based on zero-base index on the client side. + * @param integer the index (zero-based) of the item to be selected + */ + public function setSelectedIndex($index); + /** + * Selects a list of item based on zero-base indices on the client side. + * @param array list of index of items to be selected + */ + public function setSelectedIndices($indices); + + /** + * Sets selection by item value on the client side. + * @param string the value of the item to be selected. + */ + public function setSelectedValue($value); + + /** + * Sets selection by a list of item values on the client side. + * @param array list of the selected item values + */ + public function setSelectedValues($values); + + /** + * Clears all existing selections on the client side. + */ + public function clearSelection(); +} + + diff --git a/gui/baculum/framework/Web/UI/WebControls/TListControlValidator.php b/gui/baculum/framework/Web/UI/WebControls/TListControlValidator.php new file mode 100644 index 0000000000..7d9801ef3a --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TListControlValidator.php @@ -0,0 +1,225 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TListControlValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TListControlValidator class. + * + * TListControlValidator checks the number of selection and their values + * for a TListControl that allows multiple selection. + * + * You can specify the minimum or maximum (or both) number of selections + * required using the {@link setMinSelection MinSelection} and + * {@link setMaxSelection MaxSelection} properties, respectively. In addition, + * you can specify a comma separated list of required selected values via the + * {@link setRequiredSelections RequiredSelections} property. + * + * Examples + * - At least two selections + * + * + * + * + * + * + * + * + * + * - "value1" must be selected and at least 1 other + * + * + * + * + * + * + * + * + * + * + * @author Xiang Wei Zhuo + * @version $Id: TListControlValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TListControlValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TListControlValidator'; + } + + /** + * @return integer min number of selections. Defaults to -1, meaning not set. + */ + public function getMinSelection() + { + return $this->getViewState('MinSelection',-1); + } + + /** + * @param integer minimum number of selections. + */ + public function setMinSelection($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->setViewState('MinSelection',$value,-1); + } + + /** + * @return integer max number of selections. Defaults to -1, meaning not set. + */ + public function getMaxSelection() + { + return $this->getViewState('MaxSelection',-1); + } + + /** + * @param integer max number of selections. + */ + public function setMaxSelection($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + $value=-1; + $this->setViewState('MaxSelection',$value,-1); + } + + /** + * Get a comma separated list of required selected values. + * @return string comma separated list of required values. + */ + public function getRequiredSelections() + { + return $this->getViewState('RequiredSelections',''); + } + + /** + * Set the list of required values, using aa comma separated list. + * @param string comma separated list of required values. + */ + public function setRequiredSelections($value) + { + $this->setViewState('RequiredSelections',$value,''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input component changes its data + * from the InitialValue or the input component is not given. + * @return boolean whether the validation succeeds + */ + protected function evaluateIsValid() + { + $control=$this->getValidationTarget(); + + $exists = true; + $values = $this->getSelection($control); + $count = count($values); + $required = $this->getRequiredValues(); + + //if required, check the values + if(!empty($required)) + { + if($count < count($required) ) + return false; + foreach($required as $require) + $exists = $exists && in_array($require, $values); + } + + $min = $this->getMinSelection(); + $max = $this->getMaxSelection(); + + if($min !== -1 && $max !== -1) + return $exists && $count >= $min && $count <= $max; + else if($min === -1 && $max !== -1) + return $exists && $count <= $max; + else if($min !== -1 && $max === -1) + return $exists && $count >= $min; + else + return $exists; + } + + /** + * @param TListControl control to validate + * @return array number of selected values and its values. + */ + protected function getSelection($control) + { + $values = array(); + + //get the data + foreach($control->getItems() as $item) + { + if($item->getSelected()) + $values[] = $item->getValue(); + } + return $values; + } + + /** + * @return array list of required values. + */ + protected function getRequiredValues() + { + $required = array(); + $string = $this->getRequiredSelections(); + if(!empty($string)) + $required = preg_split('/,\s*/', $string); + return $required; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $control = $this->getValidationTarget(); + + if(!$control instanceof TListControl) + { + throw new TConfigurationException( + 'listcontrolvalidator_invalid_control', + $this->getID(),$this->getControlToValidate(), get_class($control)); + } + + $min = $this->getMinSelection(); + $max = $this->getMaxSelection(); + if($min !== -1) + $options['Min']= $min; + if($max !== -1) + $options['Max']= $max; + $required = $this->getRequiredSelections(); + if(strlen($required) > 0) + $options['Required']= $required; + $options['TotalItems'] = $control->getItemCount(); + + return $options; + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TListItem.php b/gui/baculum/framework/Web/UI/WebControls/TListItem.php new file mode 100644 index 0000000000..e9bfa17517 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TListItem.php @@ -0,0 +1,184 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TListItem.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TListItem class. + * + * TListItem represents an item in a list control. Each item has a {@link setText Text} + * property and a {@link setValue Value} property. If either one of them is not set, + * it will take the value of the other property. + * An item can be {@link setSelected Selected} or {@link setEnabled Enabled}, + * and it can have additional {@link getAttributes Attributes} which may be rendered + * if the list control supports so. + * + * @author Qiang Xue + * @version $Id: TListItem.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TListItem extends TComponent +{ + /** + * @var TMap list of custom attributes + */ + private $_attributes=null; + /** + * @var string text of the item + */ + private $_text; + /** + * @var string value of the item + */ + private $_value; + /** + * @var boolean whether the item is enabled + */ + private $_enabled; + /** + * @var boolean whether the item is selected + */ + private $_selected; + + /** + * Constructor. + * @param string text of the item + * @param string value of the item + * @param boolean whether the item is enabled + * @param boolean whether the item is selected + */ + public function __construct($text='',$value='',$enabled=true,$selected=false) + { + $this->setText($text); + $this->setValue($value); + $this->setEnabled($enabled); + $this->setSelected($selected); + } + + /** + * @return boolean whether the item is enabled + */ + public function getEnabled() + { + return $this->_enabled; + } + + /** + * @param boolean whether the item is enabled + */ + public function setEnabled($value) + { + $this->_enabled=TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean whether the item is selected + */ + public function getSelected() + { + return $this->_selected; + } + + /** + * @param boolean whether the item is selected + */ + public function setSelected($value) + { + $this->_selected=TPropertyValue::ensureBoolean($value); + } + + /** + * @return string text of the item + */ + public function getText() + { + return $this->_text===''?$this->_value:$this->_text; + } + + /** + * @param string text of the item + */ + public function setText($value) + { + $this->_text=TPropertyValue::ensureString($value); + } + + /** + * @return string value of the item + */ + public function getValue() + { + return $this->_value===''?$this->_text:$this->_value; + } + + /** + * @param string value of the item + */ + public function setValue($value) + { + $this->_value=TPropertyValue::ensureString($value); + } + + /** + * @return TAttributeCollection custom attributes + */ + public function getAttributes() + { + if(!$this->_attributes) + $this->_attributes=new TAttributeCollection; + return $this->_attributes; + } + + /** + * @return boolean whether the item has any custom attribute + */ + public function getHasAttributes() + { + return $this->_attributes && $this->_attributes->getCount()>0; + } + + /** + * @param string name of the attribute + * @return boolean whether the named attribute exists + */ + public function hasAttribute($name) + { + return $this->_attributes?$this->_attributes->contains($name):false; + } + + /** + * @return string the named attribute value, null if attribute does not exist + */ + public function getAttribute($name) + { + return $this->_attributes?$this->_attributes->itemAt($name):null; + } + + /** + * @param string attribute name + * @param string value of the attribute + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + + /** + * Removes the named attribute. + * @param string the name of the attribute to be removed. + * @return string attribute value removed, empty string if attribute does not exist. + */ + public function removeAttribute($name) + { + return $this->_attributes?$this->_attributes->remove($name):null; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TLiteral.php b/gui/baculum/framework/Web/UI/WebControls/TLiteral.php new file mode 100644 index 0000000000..da195f50a6 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TLiteral.php @@ -0,0 +1,112 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLiteral.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TLiteral class + * + * TLiteral displays a static text on the Web page. + * TLiteral is similar to the TLabel control, except that the TLiteral + * control does not have style properties (e.g. BackColor, Font, etc.) + * You can programmatically control the text displayed in the control by setting + * the {@link setText Text} property. The text displayed may be HTML-encoded + * if the {@link setEncode Encode} property is set true (defaults to false). + * + * TLiteral will render the contents enclosed within its component tag + * if {@link setText Text} is empty. + * + * Note, if {@link setEncode Encode} is false, make sure {@link setText Text} + * does not contain unwanted characters that may bring security vulnerabilities. + * + * @author Qiang Xue + * @version $Id: TLiteral.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TLiteral extends TControl implements IDataRenderer +{ + /** + * @return string the static text of the TLiteral + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the static text of the TLiteral + * @param string the text to be set + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the static text of the TLiteral. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the static text of the TLiteral + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the static text of the TLiteral. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the static text of the TLiteral + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return boolean whether the rendered text should be HTML-encoded. Defaults to false. + */ + public function getEncode() + { + return $this->getViewState('Encode',false); + } + + /** + * @param boolean whether the rendered text should be HTML-encoded. + */ + public function setEncode($value) + { + $this->setViewState('Encode',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Renders the literal control. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if(($text=$this->getText())!=='') + { + if($this->getEncode()) + $writer->write(THttpUtility::htmlEncode($text)); + else + $writer->write($text); + } + else + parent::render($writer); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TLiteralColumn.php b/gui/baculum/framework/Web/UI/WebControls/TLiteralColumn.php new file mode 100644 index 0000000000..f40f8f4e04 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TLiteralColumn.php @@ -0,0 +1,154 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TLiteralColumn.php 1397 2006-09-07 07:55:53Z wei $ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); + +/** + * TLiteralColumn class + * + * TLiteralColumn represents a static text column that is bound to a field in a data source. + * The cells in the column will be displayed with static texts using the data indexed by + * {@link setDataField DataField}. You can customize the display by + * setting {@link setDataFormatString DataFormatString}. + * + * If {@link setDataField DataField} is not specified, the cells will be filled + * with {@link setText Text}. + * + * If {@link setEncode Encode} is true, the static texts will be HTML-encoded. + * + * @author Qiang Xue + * @version $Id: TLiteralColumn.php 1397 2006-09-07 07:55:53Z wei $ + * @package System.Web.UI.WebControls + * @since 3.0.5 + */ +class TLiteralColumn extends TDataGridColumn +{ + /** + * @return string the field name from the data source to bind to the column + */ + public function getDataField() + { + return $this->getViewState('DataField',''); + } + + /** + * @param string the field name from the data source to bind to the column + */ + public function setDataField($value) + { + $this->setViewState('DataField',$value,''); + } + + /** + * @return string the formatting string used to control how the bound data will be displayed. + */ + public function getDataFormatString() + { + return $this->getViewState('DataFormatString',''); + } + + /** + * @param string the formatting string used to control how the bound data will be displayed. + */ + public function setDataFormatString($value) + { + $this->setViewState('DataFormatString',$value,''); + } + + /** + * @return string static text to be displayed in the column. Defaults to empty. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string static text to be displayed in the column. + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * @return boolean whether the rendered text should be HTML-encoded. Defaults to false. + */ + public function getEncode() + { + return $this->getViewState('Encode',false); + } + + /** + * @param boolean whether the rendered text should be HTML-encoded. + */ + public function setEncode($value) + { + $this->setViewState('Encode',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::EditItem || $itemType===TListItemType::SelectedItem) + { + if($this->getDataField()!=='') + $cell->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + else + { + if(($dataField=$this->getDataField())!=='') + $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + else + { + $text=$this->getText(); + if($this->getEncode()) + $text=THttpUtility::htmlEncode($text); + $cell->setText($text); + } + } + } + else + parent::initializeCell($cell,$columnIndex,$itemType); + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $data=$item->getData(); + $formatString=$this->getDataFormatString(); + if(($field=$this->getDataField())!=='') + $value=$this->formatDataValue($formatString,$this->getDataFieldValue($data,$field)); + else + $value=$this->formatDataValue($formatString,$data); + if($sender instanceof TTableCell) + { + if($this->getEncode()) + $value=THttpUtility::htmlEncode($value); + $sender->setText($value); + } + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TMarkdown.php b/gui/baculum/framework/Web/UI/WebControls/TMarkdown.php new file mode 100644 index 0000000000..1a2855ebf7 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TMarkdown.php @@ -0,0 +1,75 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TMarkdown.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TTextHighlighter and MarkdownParser classes + */ +Prado::using('System.Web.UI.WebControls.TTextHighlighter'); +Prado::using('System.3rdParty.Markdown.MarkdownParser'); + +/** + * TMarkdown class + * + * TMarkdown is a control that produces HTML from code with markdown syntax. + * + * Markdown is a text-to-HTML conversion tool for web writers. Markdown allows + * you to write using an easy-to-read, easy-to-write plain text format, then + * convert it to structurally valid XHTML (or HTML). + * Further documentation regarding Markdown can be found at + * http://daringfireball.net/projects/markdown/ + * + * To use TMarkdown, simply enclose the content to be rendered within + * the body of TMarkdown in a template. + * + * See http://www.pradosoft.com/demos/quickstart/?page=Markdown for + * details on the Markdown syntax usage. + * + * TMarkdown also performs syntax highlighting for code blocks whose language + * is recognized by {@link TTextHighlighter}. + * The language of a code block must be specified in the first line of the block + * and enclosed within a pair of square brackets (e.g. [php]). + * + * @author Wei Zhuo + * @version $Id: TMarkdown.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.1 + */ +class TMarkdown extends TTextHighlighter +{ + /** + * Processes a text string. + * This method is required by the parent class. + * @param string text string to be processed + * @return string the processed text result + */ + public function processText($text) + { + $renderer = new MarkdownParser; + $result = $renderer->parse($text); + return preg_replace_callback( + '/
      \[\s*(\w+)\s*\]\n+((.|\n)*?)\s*<\\/code><\\/pre>/im',
      +				array($this, 'highlightCode'), $result);
      +	}
      +
      +	/**
      +	 * Highlights source code using TTextHighlighter
      +	 * @param array matches of code blocks
      +	 * @return string highlighted code.
      +	 */
      +	protected function highlightCode($matches)
      +	{
      +		$text = html_entity_decode($matches[2],ENT_QUOTES,'UTF-8');
      +		$this->setLanguage($matches[1]);
      +		return parent::processText($text);
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TMultiView.php b/gui/baculum/framework/Web/UI/WebControls/TMultiView.php
      new file mode 100644
      index 0000000000..286f4e9517
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TMultiView.php
      @@ -0,0 +1,378 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TMultiView.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * TMultiView class
      + *
      + * TMultiView serves as a container for a group of {@link TView} controls.
      + * The view collection can be retrieved by {@link getViews Views}.
      + * Each view contains child controls. TMultiView determines which view and its
      + * child controls are visible. At any time, at most one view is visible (called
      + * active). To make a view active, set {@link setActiveView ActiveView} or
      + * {@link setActiveViewIndex ActiveViewIndex}.
      + *
      + * TMultiView also responds to specific command events raised from button controls
      + * contained in current active view. A command event with name 'NextView'
      + * will cause TMultiView to make the next available view active.
      + * Other command names recognized by TMultiView include
      + * - PreviousView : switch to previous view
      + * - SwitchViewID : switch to a view by its ID path
      + * - SwitchViewIndex : switch to a view by its index in the {@link getViews Views} collection.
      + *
      + * TMultiView raises {@link OnActiveViewChanged OnActiveViewChanged} event
      + * when its active view is changed during a postback.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TMultiView.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TMultiView extends TControl
      +{
      +	const CMD_NEXTVIEW='NextView';
      +	const CMD_PREVIOUSVIEW='PreviousView';
      +	const CMD_SWITCHVIEWID='SwitchViewID';
      +	const CMD_SWITCHVIEWINDEX='SwitchViewIndex';
      +	private $_cachedActiveViewIndex=-1;
      +	private $_ignoreBubbleEvents=false;
      +
      +	/**
      +	 * Processes an object that is created during parsing template.
      +	 * This method overrides the parent implementation by adding only {@link TView}
      +	 * controls as children.
      +	 * @param string|TComponent text string or component parsed and instantiated in template
      +	 * @see createdOnTemplate
      +	 * @throws TConfigurationException if controls other than {@link TView} is being added
      +	 */
      +	public function addParsedObject($object)
      +	{
      +		if($object instanceof TView)
      +			$this->getControls()->add($object);
      +		else if(!is_string($object))
      +			throw new TConfigurationException('multiview_view_required');
      +	}
      +
      +	/**
      +	 * Creates a control collection object that is to be used to hold child controls
      +	 * @return TViewCollection control collection
      +	 */
      +	protected function createControlCollection()
      +	{
      +		return new TViewCollection($this);
      +	}
      +
      +	/**
      +	 * @return integer the zero-based index of the current view in the view collection. -1 if no active view. Default is -1.
      +	 */
      +	public function getActiveViewIndex()
      +	{
      +		if($this->_cachedActiveViewIndex>-1)
      +			return $this->_cachedActiveViewIndex;
      +		else
      +			return $this->getControlState('ActiveViewIndex',-1);
      +	}
      +
      +	/**
      +	 * @param integer the zero-based index of the current view in the view collection. -1 if no active view.
      +	 * @throws TInvalidDataValueException if the view index is invalid
      +	 */
      +	public function setActiveViewIndex($value)
      +	{
      +		if(($index=TPropertyValue::ensureInteger($value))<0)
      +			$index=-1;
      +		$views=$this->getViews();
      +		$count=$views->getCount();
      +		if($count===0 && $this->getControlStage()_cachedActiveViewIndex=$index;
      +		else if($index<$count)
      +		{
      +			$this->setControlState('ActiveViewIndex',$index,-1);
      +			$this->_cachedActiveViewIndex=-1;
      +			if($index>=0)
      +				$this->activateView($views->itemAt($index),true);
      +		}
      +		else
      +			throw new TInvalidDataValueException('multiview_activeviewindex_invalid',$index);
      +	}
      +
      +	/**
      +	 * @return TView the currently active view, null if no active view
      +	 * @throws TInvalidDataValueException if the current active view index is invalid
      +	 */
      +	public function getActiveView()
      +	{
      +		$index=$this->getActiveViewIndex();
      +		$views=$this->getViews();
      +		if($index>=$views->getCount())
      +			throw new TInvalidDataValueException('multiview_activeviewindex_invalid',$index);
      +		if($index<0)
      +			return null;
      +		$view=$views->itemAt($index);
      +		if(!$view->getActive())
      +			$this->activateView($view,false);
      +		return $view;
      +	}
      +
      +	/**
      +	 * @param TView the view to be activated
      +	 * @throws TInvalidOperationException if the view is not in the view collection
      +	 */
      +	public function setActiveView($view)
      +	{
      +		if(($index=$this->getViews()->indexOf($view))>=0)
      +			$this->setActiveViewIndex($index);
      +		else
      +			throw new TInvalidOperationException('multiview_view_inexistent');
      +	}
      +
      +	/**
      +	 * Activates the specified view.
      +	 * If there is any view currently active, it will be deactivated.
      +	 * @param TView the view to be activated
      +	 * @param boolean whether to trigger OnActiveViewChanged event.
      +	 */
      +	protected function activateView($view,$triggerViewChangedEvent=true)
      +	{
      +		if($view->getActive())
      +			return;
      +		$triggerEvent=$triggerViewChangedEvent && ($this->getControlStage()>=TControl::CS_STATE_LOADED || ($this->getPage() && !$this->getPage()->getIsPostBack()));
      +		foreach($this->getViews() as $v)
      +		{
      +			if($v===$view)
      +			{
      +				$view->setActive(true);
      +				if($triggerEvent)
      +				{
      +					$view->onActivate(null);
      +					$this->onActiveViewChanged(null);
      +				}
      +			}
      +			else if($v->getActive())
      +			{
      +				$v->setActive(false);
      +				if($triggerEvent)
      +					$v->onDeactivate(null);
      +			}
      +		}
      +	}
      +
      +	/**
      +	 * @return TViewCollection the view collection
      +	 */
      +	public function getViews()
      +	{
      +		return $this->getControls();
      +	}
      +
      +	/**
      +	 * Makes the multiview ignore all bubbled events.
      +	 * This is method is used internally by framework and control
      +	 * developers.
      +	 */
      +	public function ignoreBubbleEvents()
      +	{
      +		$this->_ignoreBubbleEvents=true;
      +	}
      +
      +	/**
      +	 * Initializes the active view if any.
      +	 * This method overrides the parent implementation.
      +	 * @param TEventParameter event parameter
      +	 */
      +	public function onInit($param)
      +	{
      +		parent::onInit($param);
      +		if($this->_cachedActiveViewIndex>=0)
      +			$this->setActiveViewIndex($this->_cachedActiveViewIndex);
      +	}
      +
      +	/**
      +	 * Raises OnActiveViewChanged event.
      +	 * The event is raised when the currently active view is changed to a new one
      +	 * @param TEventParameter event parameter
      +	 */
      +	public function onActiveViewChanged($param)
      +	{
      +		$this->raiseEvent('OnActiveViewChanged',$this,$param);
      +	}
      +
      +	/**
      +	 * Processes the events bubbled from child controls.
      +	 * The method handles view-related command events.
      +	 * @param TControl sender of the event
      +	 * @param mixed event parameter
      +	 * @return boolean whether this event is handled
      +	 */
      +	public function bubbleEvent($sender,$param)
      +	{
      +		if(!$this->_ignoreBubbleEvents && ($param instanceof TCommandEventParameter))
      +		{
      +			switch($param->getCommandName())
      +			{
      +				case self::CMD_NEXTVIEW:
      +					if(($index=$this->getActiveViewIndex())<$this->getViews()->getCount()-1)
      +						$this->setActiveViewIndex($index+1);
      +					else
      +						$this->setActiveViewIndex(-1);
      +					return true;
      +				case self::CMD_PREVIOUSVIEW:
      +					if(($index=$this->getActiveViewIndex())>=0)
      +						$this->setActiveViewIndex($index-1);
      +					return true;
      +				case self::CMD_SWITCHVIEWID:
      +					$view=$this->findControl($viewID=$param->getCommandParameter());
      +					if($view!==null && $view->getParent()===$this)
      +					{
      +						$this->setActiveView($view);
      +						return true;
      +					}
      +					else
      +						throw new TInvalidDataValueException('multiview_viewid_invalid', $viewID);
      +				case self::CMD_SWITCHVIEWINDEX:
      +					$index=TPropertyValue::ensureInteger($param->getCommandParameter());
      +					$this->setActiveViewIndex($index);
      +					return true;
      +			}
      +		}
      +		return false;
      +	}
      +
      +	/**
      +	 * Loads state into the wizard.
      +	 * This method is invoked by the framework when the control state is being saved.
      +	 */
      +	public function loadState()
      +	{
      +		// a dummy call to ensure the view is activated
      +		$this->getActiveView();
      +	}
      +
      +	/**
      +	 * Renders the currently active view.
      +	 * @param THtmlWriter the writer for the rendering purpose.
      +	 */
      +	public function render($writer)
      +	{
      +		if(($view=$this->getActiveView())!==null)
      +			$view->renderControl($writer);
      +	}
      +}
      +
      +/**
      + * TViewCollection class.
      + * TViewCollection represents a collection that only takes {@link TView} instances
      + * as collection elements.
      + * @author Qiang Xue 
      + * @version $Id: TMultiView.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TViewCollection extends TControlCollection
      +{
      +	/**
      +	 * Inserts an item at the specified position.
      +	 * This overrides the parent implementation by ensuring only {@link TView}
      +	 * controls be added into the collection.
      +	 * @param integer the speicified position.
      +	 * @param mixed new item
      +	 * @throws TInvalidDataTypeException if the item to be inserted is neither a string nor a TControl.
      +	 */
      +	public function insertAt($index,$item)
      +	{
      +		if($item instanceof TView)
      +			parent::insertAt($index,$item);
      +		else
      +			throw new TInvalidDataTypeException('viewcollection_view_required');
      +	}
      +}
      +
      +/**
      + * TView class
      + *
      + * TView is a container for a group of controls. TView must be contained
      + * within a {@link TMultiView} control in which only one view can be active
      + * at one time.
      + *
      + * To activate a view, set {@link setActive Active} to true.
      + * When a view is activated, it raises {@link onActivate OnActivate} event;
      + * and when a view is deactivated, it raises {@link onDeactivate OnDeactivate}.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TMultiView.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TView extends TControl
      +{
      +	private $_active=false;
      +
      +	/**
      +	 * Raises OnActivate event.
      +	 * @param TEventParameter event parameter
      +	 */
      +	public function onActivate($param)
      +	{
      +		$this->raiseEvent('OnActivate',$this,$param);
      +	}
      +
      +	/**
      +	 * Raises OnDeactivate event.
      +	 * @param TEventParameter event parameter
      +	 */
      +	public function onDeactivate($param)
      +	{
      +		$this->raiseEvent('OnDeactivate',$this,$param);
      +	}
      +
      +	/**
      +	 * @return boolean whether this view is active. Defaults to false.
      +	 */
      +	public function getActive()
      +	{
      +		return $this->_active;
      +	}
      +
      +	/**
      +	 * @param boolean whether this view is active.
      +	 */
      +	public function setActive($value)
      +	{
      +		$value=TPropertyValue::ensureBoolean($value);
      +		$this->_active=$value;
      +		parent::setVisible($value);
      +	}
      +
      +	/**
      +	 * @param boolean whether the parents should also be checked if visible
      +	 * @return boolean whether this view is visible.
      +	 * The view is visible if it is active and its parent is visible.
      +	 */
      +	public function getVisible($checkParents=true)
      +	{
      +		if(($parent=$this->getParent())===null)
      +			return $this->getActive();
      +		else if($this->getActive())
      +			return $parent->getVisible($checkParents);
      +		else
      +			return false;
      +	}
      +
      +	/**
      +	 * @param boolean
      +	 * @throws TInvalidOperationException whenever this method is invoked.
      +	 */
      +	public function setVisible($value)
      +	{
      +		throw new TInvalidOperationException('view_visible_readonly');
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TOutputCache.php b/gui/baculum/framework/Web/UI/WebControls/TOutputCache.php
      new file mode 100644
      index 0000000000..89c0c8496b
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TOutputCache.php
      @@ -0,0 +1,621 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TOutputCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * TOutputCache class.
      + *
      + * TOutputCache enables caching a portion of a Web page, also known as
      + * partial caching. The content being cached can be either static or
      + * dynamic.
      + *
      + * To use TOutputCache, simply enclose the content to be cached
      + * within the TOutputCache component tag on a template, e.g.,
      + * 
      + * 
      + *   content to be cached
      + * 
      + * 
      + * where content to be cached can be static text and/or component tags.
      + *
      + * The validity of the cached content is determined based on two factors:
      + * the {@link setDuration Duration} and the cache dependency.
      + * The former specifies the number of seconds that the data can remain
      + * valid in cache (defaults to 60s), while the latter specifies conditions
      + * that the cached data depends on. If a dependency changes,
      + * (e.g. relevant data in DB are updated), the cached data will be invalidated.
      + *
      + * There are two ways to specify cache dependency. One may write event handlers
      + * to respond to the {@link onCheckDependency OnCheckDependency} event and set
      + * the event parameter's {@link TOutputCacheCheckDependencyEventParameter::getIsValid IsValid}
      + * property to indicate whether the cached data remains valid or not.
      + * One can also extend TOutputCache and override its {@link getCacheDependency}
      + * function. While the former is easier to use, the latter offers more extensibility.
      + *
      + * The content fetched from cache may be variated with respect to
      + * some parameters. It supports variation with respect to request parameters,
      + * which is specified by {@link setVaryByParam VaryByParam} property.
      + * If a specified request parameter is different, a different version of
      + * cached content is used. This is extremely useful if a page's content
      + * may be variated according to some GET parameters.
      + * The content being cached may also be variated with user sessions if
      + * {@link setVaryBySession VaryBySession} is set true.
      + * To variate the cached content by other factors, override {@link calculateCacheKey()} method.
      + *
      + * Output caches can be nested. An outer cache takes precedence over an
      + * inner cache. This means, if the content cached by the inner cache expires
      + * or is invalidated, while that by the outer cache not, the outer cached
      + * content will be used.
      + *
      + * Note, TOutputCache is effective only for non-postback page requests
      + * and when cache module is enabled.
      + *
      + * Do not attempt to address child controls of TOutputCache when the cached
      + * content is to be used. Use {@link getContentCached ContentCached} property
      + * to determine whether the content is cached or not.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TOutputCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.1
      + */
      +class TOutputCache extends TControl implements INamingContainer
      +{
      +	const CACHE_ID_PREFIX='prado:outputcache';
      +	private $_cacheModuleID='';
      +	private $_dataCached=false;
      +	private $_cacheAvailable=false;
      +	private $_cacheChecked=false;
      +	private $_cacheKey=null;
      +	private $_duration=60;
      +	private $_cache=null;
      +	private $_contents;
      +	private $_state;
      +	private $_actions=array();
      +	private $_varyByParam='';
      +	private $_keyPrefix='';
      +	private $_varyBySession=false;
      +	private $_cachePostBack=false;
      +	private $_cacheTime=0;
      +
      +	/**
      +	 * Returns a value indicating whether body contents are allowed for this control.
      +	 * This method overrides the parent implementation by checking if cached
      +	 * content is available or not. If yes, it returns false, otherwise true.
      +	 * @param boolean whether body contents are allowed for this control.
      +	 */
      +	public function getAllowChildControls()
      +	{
      +		$this->determineCacheability();
      +		return !$this->_dataCached;
      +	}
      +
      +	private function determineCacheability()
      +	{
      +		if(!$this->_cacheChecked)
      +		{
      +			$this->_cacheChecked=true;
      +			if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack()))
      +			{
      +				if($this->_cacheModuleID!=='')
      +				{
      +					$this->_cache=$this->getApplication()->getModule($this->_cacheModuleID);
      +					if(!($this->_cache instanceof ICache))
      +						throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID);
      +				}
      +				else
      +					$this->_cache=$this->getApplication()->getCache();
      +				if($this->_cache!==null)
      +				{
      +					$this->_cacheAvailable=true;
      +					$data=$this->_cache->get($this->getCacheKey());
      +					if(is_array($data))
      +					{
      +						$param=new TOutputCacheCheckDependencyEventParameter;
      +						$param->setCacheTime(isset($data[3])?$data[3]:0);
      +						$this->onCheckDependency($param);
      +						$this->_dataCached=$param->getIsValid();
      +					}
      +					else
      +						$this->_dataCached=false;
      +					if($this->_dataCached)
      +						list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data;
      +				}
      +			}
      +		}
      +	}
      +
      +	/**
      +	 * Performs the Init step for the control and all its child controls.
      +	 * This method overrides the parent implementation by setting up
      +	 * the stack of the output cache in the page.
      +	 * Only framework developers should use this method.
      +	 * @param TControl the naming container control
      +	 */
      +	protected function initRecursive($namingContainer=null)
      +	{
      +		if($this->_cacheAvailable && !$this->_dataCached)
      +		{
      +			$stack=$this->getPage()->getCachingStack();
      +			$stack->push($this);
      +			parent::initRecursive($namingContainer);
      +			$stack->pop();
      +		}
      +		else
      +			parent::initRecursive($namingContainer);
      +	}
      +
      +	/**
      +	 * Performs the Load step for the control and all its child controls.
      +	 * This method overrides the parent implementation by setting up
      +	 * the stack of the output cache in the page. If the data is restored
      +	 * from cache, it also recovers the actions associated with the cached data.
      +	 * Only framework developers should use this method.
      +	 * @param TControl the naming container control
      +	 */
      +	protected function loadRecursive()
      +	{
      +		if($this->_cacheAvailable && !$this->_dataCached)
      +		{
      +			$stack=$this->getPage()->getCachingStack();
      +			$stack->push($this);
      +			parent::loadRecursive();
      +			$stack->pop();
      +		}
      +		else
      +		{
      +			if($this->_dataCached)
      +				$this->performActions();
      +			parent::loadRecursive();
      +		}
      +	}
      +
      +	private function performActions()
      +	{
      +		$page=$this->getPage();
      +		$cs=$page->getClientScript();
      +		foreach($this->_actions as $action)
      +		{
      +			if($action[0]==='Page.ClientScript')
      +				call_user_func_array(array($cs,$action[1]),$action[2]);
      +			else if($action[0]==='Page')
      +				call_user_func_array(array($page,$action[1]),$action[2]);
      +			else
      +				call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]);
      +		}
      +	}
      +
      +	/**
      +	 * Performs the PreRender step for the control and all its child controls.
      +	 * This method overrides the parent implementation by setting up
      +	 * the stack of the output cache in the page.
      +	 * Only framework developers should use this method.
      +	 * @param TControl the naming container control
      +	 */
      +	protected function preRenderRecursive()
      +	{
      +		if($this->_cacheAvailable && !$this->_dataCached)
      +		{
      +			$stack=$this->getPage()->getCachingStack();
      +			$stack->push($this);
      +			parent::preRenderRecursive();
      +			$stack->pop();
      +		}
      +		else
      +			parent::preRenderRecursive();
      +	}
      +
      +	/**
      +	 * Loads state (viewstate and controlstate) into a control and its children.
      +	 * This method overrides the parent implementation by loading
      +	 * cached state if available.
      +	 * This method should only be used by framework developers.
      +	 * @param array the collection of the state
      +	 * @param boolean whether the viewstate should be loaded
      +	 */
      +	protected function loadStateRecursive(&$state,$needViewState=true)
      +	{
      +		$st=unserialize($state);
      +		parent::loadStateRecursive($st,$needViewState);
      +	}
      +
      +	/**
      +	 * Saves all control state (viewstate and controlstate) as a collection.
      +	 * This method overrides the parent implementation by saving state
      +	 * into cache if needed.
      +	 * This method should only be used by framework developers.
      +	 * @param boolean whether the viewstate should be saved
      +	 * @return array the collection of the control state (including its children's state).
      +	 */
      +	protected function &saveStateRecursive($needViewState=true)
      +	{
      +		if($this->_dataCached)
      +			return $this->_state;
      +		else
      +		{
      +			$st=parent::saveStateRecursive($needViewState);
      +			// serialization is needed to avoid undefined classes when loading state
      +			$this->_state=serialize($st);
      +			return $this->_state;
      +		}
      +	}
      +
      +	/**
      +	 * Registers an action associated with the content being cached.
      +	 * The registered action will be replayed if the content stored
      +	 * in the cache is served to end-users.
      +	 * @param string context of the action method. This is a property-path
      +	 * referring to the context object (e.g. Page, Page.ClientScript)
      +	 * @param string method name of the context object
      +	 * @param array list of parameters to be passed to the action method
      +	 */
      +	public function registerAction($context,$funcName,$funcParams)
      +	{
      +		$this->_actions[]=array($context,$funcName,$funcParams);
      +	}
      +
      +	public function getCacheKey()
      +	{
      +		if($this->_cacheKey===null)
      +			$this->_cacheKey=$this->calculateCacheKey();
      +		return $this->_cacheKey;
      +	}
      +
      +	/**
      +	 * Calculates the cache key.
      +	 * The key is calculated based on the unique ID of this control
      +	 * and the request parameters specified via {@link setVaryByParam VaryByParam}.
      +	 * If {@link getVaryBySession VaryBySession} is true, the session ID
      +	 * will also participate in the key calculation.
      +	 * This method may be overriden to support other variations in
      +	 * the calculated cache key.
      +	 * @return string cache key
      +	 */
      +	protected function calculateCacheKey()
      +	{
      +		$key=$this->getBaseCacheKey();
      +		if($this->_varyBySession)
      +			$key.=$this->getSession()->getSessionID();
      +		if($this->_varyByParam!=='')
      +		{
      +			$params=array();
      +			$request=$this->getRequest();
      +			foreach(explode(',',$this->_varyByParam) as $name)
      +			{
      +				$name=trim($name);
      +				$params[$name]=$request->itemAt($name);
      +			}
      +			$key.=serialize($params);
      +		}
      +		$param=new TOutputCacheCalculateKeyEventParameter;
      +		$this->onCalculateKey($param);
      +		$key.=$param->getCacheKey();
      +		return $key;
      +	}
      +
      +	/**
      +	 * @return string basic cache key without variations
      +	 */
      +	protected function getBaseCacheKey()
      +	{
      +		return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID();
      +	}
      +
      +	/**
      +	 * @return string the ID of the cache module. Defaults to '', meaning the primary cache module is used.
      +	 */
      +	public function getCacheModuleID()
      +	{
      +		return $this->_cacheModuleID;
      +	}
      +
      +	/**
      +	 * @param string the ID of the cache module. If empty, the primary cache module will be used.
      +	 */
      +	public function setCacheModuleID($value)
      +	{
      +		$this->_cacheModuleID=$value;
      +	}
      +
      +	/**
      +	 * Sets the prefix of the cache key.
      +	 * This method is used internally by {@link TTemplate}.
      +	 * @param string key prefix
      +	 */
      +	public function setCacheKeyPrefix($value)
      +	{
      +		$this->_keyPrefix=$value;
      +	}
      +
      +	/**
      +	 * @return integer the timestamp of the cached content. This is only valid if the content is being cached.
      +	 * @since 3.1.1
      +	 */
      +	public function getCacheTime()
      +	{
      +		return $this->_cacheTime;
      +	}
      +
      +	/**
      +	 * Returns the dependency of the data to be cached.
      +	 * The default implementation simply returns null, meaning no specific dependency.
      +	 * This method may be overriden to associate the data to be cached
      +	 * with additional dependencies.
      +	 * @return ICacheDependency
      +	 */
      +	protected function getCacheDependency()
      +	{
      +		return null;
      +	}
      +
      +	/**
      +	 * @return boolean whether content enclosed is cached or not
      +	 */
      +	public function getContentCached()
      +	{
      +		return $this->_dataCached;
      +	}
      +
      +	/**
      +	 * @return integer number of seconds that the data can remain in cache. Defaults to 60 seconds.
      +	 * Note, if cache dependency changes or cache space is limited,
      +	 * the data may be purged out of cache earlier.
      +	 */
      +	public function getDuration()
      +	{
      +		return $this->_duration;
      +	}
      +
      +	/**
      +	 * @param integer number of seconds that the data can remain in cache. If 0, it means data is not cached.
      +	 * @throws TInvalidDataValueException if the value is smaller than 0.
      +	 */
      +	public function setDuration($value)
      +	{
      +		if(($value=TPropertyValue::ensureInteger($value))<0)
      +			throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this));
      +		$this->_duration=$value;
      +	}
      +
      +	/**
      +	 * @return string a semicolon-separated list of strings used to vary the output cache. Defaults to ''.
      +	 */
      +	public function getVaryByParam()
      +	{
      +		return $this->_varyByParam;
      +	}
      +
      +	/**
      +	 * Sets the names of the request parameters that should be used in calculating the cache key.
      +	 * The names should be concatenated by semicolons.
      +	 * By setting this value, the output cache will use different cached data
      +	 * for each different set of request parameter values.
      +	 * @return string a semicolon-separated list of strings used to vary the output cache.
      +	 */
      +	public function setVaryByParam($value)
      +	{
      +		$this->_varyByParam=trim($value);
      +	}
      +
      +	/**
      +	 * @return boolean whether the content being cached should be differentiated according to user sessions. Defaults to false.
      +	 */
      +	public function getVaryBySession()
      +	{
      +		return $this->_varyBySession;
      +	}
      +
      +	/**
      +	 * @param boolean whether the content being cached should be differentiated according to user sessions.
      +	 */
      +	public function setVaryBySession($value)
      +	{
      +		$this->_varyBySession=TPropertyValue::ensureBoolean($value);
      +	}
      +
      +	/**
      +	 * @return boolean whether cached output will be used on postback requests. Defaults to false.
      +	 */
      +	public function getCachingPostBack()
      +	{
      +		return $this->_cachePostBack;
      +	}
      +
      +	/**
      +	 * Sets a value indicating whether cached output will be used on postback requests.
      +	 * By default, this is disabled. Be very cautious when enabling it.
      +	 * If the cached content including interactive user controls such as
      +	 * TTextBox, TDropDownList, your page may fail to render on postbacks.
      +	 * @param boolean whether cached output will be used on postback requests.
      +	 */
      +	public function setCachingPostBack($value)
      +	{
      +		$this->_cachePostBack=TPropertyValue::ensureBoolean($value);
      +	}
      +
      +	/**
      +	 * This event is raised when the output cache is checking cache dependency.
      +	 * An event handler may be written to check customized dependency conditions.
      +	 * The checking result should be saved by setting {@link TOutputCacheCheckDependencyEventParameter::setIsValid IsValid}
      +	 * property of the event parameter (which defaults to true).
      +	 * @param TOutputCacheCheckDependencyEventParameter event parameter
      +	 */
      +	public function onCheckDependency($param)
      +	{
      +		$this->raiseEvent('OnCheckDependency',$this,$param);
      +	}
      +
      +	/**
      +	 * This event is raised when the output cache is calculating cache key.
      +	 * By varying cache keys, one can obtain different versions of cached content.
      +	 * An event handler may be written to add variety of the key calculation.
      +	 * The value set in {@link TOutputCacheCalculateKeyEventParameter::setCacheKey CacheKey} of
      +	 * this event parameter will be appended to the default key calculation scheme.
      +	 * @param TOutputCacheCalculateKeyEventParameter event parameter
      +	 */
      +	public function onCalculateKey($param)
      +	{
      +		$this->raiseEvent('OnCalculateKey',$this,$param);
      +	}
      +
      +	/**
      +	 * Renders the output cache control.
      +	 * This method overrides the parent implementation by capturing the output
      +	 * from its child controls and saving it into cache, if output cache is needed.
      +	 * @param THtmlWriter
      +	 */
      +	public function render($writer)
      +	{
      +		if($this->_dataCached)
      +			$writer->write($this->_contents);
      +		else if($this->_cacheAvailable)
      +		{
      +			$textwriter = new TTextWriter();
      +			$multiwriter = new TOutputCacheTextWriterMulti(array($writer->getWriter(),$textwriter));
      +			$htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), $multiwriter);
      +			
      +			$stack=$this->getPage()->getCachingStack();
      +			$stack->push($this);
      +			parent::render($htmlWriter);
      +			$stack->pop();
      +
      +			$content=$textwriter->flush();
      +			$data=array($content,$this->_state,$this->_actions,time());
      +			$this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency());
      +		}
      +		else
      +			parent::render($writer);
      +	}
      +}
      +
      +/**
      + * TOutputCacheCheckDependencyEventParameter class
      + *
      + * TOutputCacheCheckDependencyEventParameter encapsulates the parameter data for
      + * OnCheckDependency event of {@link TOutputCache} control.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TOutputCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TOutputCacheCheckDependencyEventParameter extends TEventParameter
      +{
      +	private $_isValid=true;
      +	private $_cacheTime=0;
      +
      +	/**
      +	 * @return boolean whether the dependency remains valid. Defaults to true.
      +	 */
      +	public function getIsValid()
      +	{
      +		return $this->_isValid;
      +	}
      +
      +	/**
      +	 * @param boolean whether the dependency remains valid
      +	 */
      +	public function setIsValid($value)
      +	{
      +		$this->_isValid=TPropertyValue::ensureBoolean($value);
      +	}
      +
      +	/**
      +	 * @return integer the timestamp of the cached result. You may use this to help determine any dependency is changed.
      +	 * @since 3.1.1
      +	 */
      +	public function getCacheTime()
      +	{
      +		return $this->_cacheTime;
      +	}
      +
      +	/**
      +	 * @param integer the timestamp of the cached result. This is used internally.
      +	 * @since 3.1.1
      +	 */
      +	public function setCacheTime($value)
      +	{
      +		$this->_cacheTime=TPropertyValue::ensureInteger($value);
      +	}
      +}
      +
      +
      +/**
      + * TOutputCacheCalculateKeyEventParameter class
      + *
      + * TOutputCacheCalculateKeyEventParameter encapsulates the parameter data for
      + * OnCalculateKey event of {@link TOutputCache} control.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TOutputCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TOutputCacheCalculateKeyEventParameter extends TEventParameter
      +{
      +	/**
      +	 * @var string cache key to be appended to the default calculation scheme.
      +	 */
      +	private $_cacheKey='';
      +
      +	/**
      +	 * @return string cache key to be appended to the default calculation scheme.
      +	 */
      +	public function getCacheKey()
      +	{
      +		return $this->_cacheKey;
      +	}
      +
      +	/**
      +	 * @param string cache key to be appended to the default calculation scheme
      +	 */
      +	public function setCacheKey($value)
      +	{
      +		$this->_cacheKey=TPropertyValue::ensureString($value);
      +	}
      +}
      +
      +/**
      + * TOutputCacheTextWriterMulti class
      + *
      + * TOutputCacheTextWriterMulti is an internal class used by
      + * TOutputCache to write simultaneously to multiple writers.
      + *
      + * @author Gabor Berczi, DevWorx Hungary 
      + * @author Qiang Xue 
      + * @version $Id: TOutputCache.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.2
      + */
      +class TOutputCacheTextWriterMulti extends TTextWriter
      +{
      +	protected $_writers;
      +
      +	public function __construct(Array $writers)
      +	{
      +		//parent::__construct();
      +		$this->_writers = $writers;
      +	}
      +	
      +	public function write($s)
      +	{
      +		foreach($this->_writers as $writer)
      +			$writer->write($s);
      +	}
      +
      +	public function flush()
      +	{
      +		foreach($this->_writers as $writer)
      +			$s = $writer->flush();
      +		return $s;
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TPager.php b/gui/baculum/framework/Web/UI/WebControls/TPager.php
      new file mode 100644
      index 0000000000..6379fedd2d
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TPager.php
      @@ -0,0 +1,815 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TPager.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * TPager class.
      + *
      + * TPager creates a pager that provides UI for end-users to interactively
      + * specify which page of data to be rendered in a {@link TDataBoundControl}-derived control,
      + * such as {@link TDataList}, {@link TRepeater}, {@link TCheckBoxList}, etc.
      + * The target data-bound control is specified by {@link setControlToPaginate ControlToPaginate},
      + * which must be the ID path of the target control reaching from the pager's
      + * naming container. Note, the target control must have its {@link TDataBoundControl::setAllowPaging AllowPaging}
      + * set to true.
      + *
      + * TPager can display three different UIs, specified via {@link setMode Mode}:
      + * - NextPrev: a next page and a previous page button are rendered.
      + * - Numeric: a list of page index buttons are rendered.
      + * - List: a dropdown list of page indices are rendered.
      + *
      + * When the pager mode is either NextPrev or Numeric, the paging buttons may be displayed
      + * in three types by setting {@link setButtonType ButtonType}:
      + * - LinkButton: a hyperlink button
      + * - PushButton: a normal button
      + * - ImageButton: an image button (please set XXXPageImageUrl properties accordingly to specify the button images.)
      + *
      + * Since Prado 3.2.1, you can use the {@link setButtonCssClass ButtonCssClass} property to specify a css class
      + * that will be applied to each button created by the pager in NextPrev or Numeric mode.
      + * 
      + * TPager raises an {@link onPageIndexChanged OnPageIndexChanged} event when
      + * the end-user interacts with it and specifies a new page (e.g. clicking
      + * on a page button that leads to a new page.) The new page index may be obtained
      + * from the event parameter's property {@link TPagerPageChangedEventParameter::getNewPageIndex NewPageIndex}.
      + * Normally, in the event handler, one can set the {@link TDataBoundControl::getCurrentPageIndex CurrentPageIndex}
      + * to this new page index so that the new page of data is rendered.
      + *
      + * Multiple pagers can be associated with the same data-bound control.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPager.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.2
      + */
      +class TPager extends TWebControl implements INamingContainer
      +{
      +	/**
      +	 * Command name that TPager understands.
      +	 */
      +	const CMD_PAGE='Page';
      +	const CMD_PAGE_NEXT='Next';
      +	const CMD_PAGE_PREV='Previous';
      +	const CMD_PAGE_FIRST='First';
      +	const CMD_PAGE_LAST='Last';
      +
      +	private $_pageCount=0;
      +
      +	/**
      +	 * Restores the pager state.
      +	 * This method overrides the parent implementation and is invoked when
      +	 * the control is loading persistent state.
      +	 */
      +	public function loadState()
      +	{
      +		parent::loadState();
      +		if($this->getEnableViewState(true))
      +		{
      +			$this->getControls()->clear();
      +			$this->buildPager();
      +		}
      +	}
      +
      +	/**
      +	 * @return string the ID path of the control whose content would be paginated.
      +	 */
      +	public function getControlToPaginate()
      +	{
      +		return $this->getViewState('ControlToPaginate','');
      +	}
      +
      +	/**
      +	 * Sets the ID path of the control whose content would be paginated.
      +	 * The ID path is the dot-connected IDs of the controls reaching from
      +	 * the pager's naming container to the target control.
      +	 * @param string the ID path
      +	 */
      +	public function setControlToPaginate($value)
      +	{
      +		$this->setViewState('ControlToPaginate',$value,'');
      +	}
      +
      +	/**
      +	 * @return string the css class of the buttons.
      +	 * @since 3.2.1
      +	 */
      +	public function getButtonCssClass()
      +	{
      +		return $this->getViewState('ButtonCssClass','');
      +	}
      +
      +	/**
      +	 * @param Sets the css class of the buttons that will be rendered by this pager.
      +	 * @since 3.2.1
      +	 */
      +	public function setButtonCssClass($value)
      +	{
      +		$this->setViewState('ButtonCssClass',TPropertyValue::ensureString($value,''),'');
      +	}
      +
      +	/**
      +	 * @return TPagerMode pager mode. Defaults to TPagerMode::NextPrev.
      +	 */
      +	public function getMode()
      +	{
      +		return $this->getViewState('Mode',TPagerMode::NextPrev);
      +	}
      +
      +	/**
      +	 * @param TPagerMode pager mode.
      +	 */
      +	public function setMode($value)
      +	{
      +		$this->setViewState('Mode',TPropertyValue::ensureEnum($value,'TPagerMode'),TPagerMode::NextPrev);
      +	}
      +
      +	/**
      +	 * @return TPagerButtonType the type of command button for paging. Defaults to TPagerButtonType::LinkButton.
      +	 */
      +	public function getButtonType()
      +	{
      +		return $this->getViewState('ButtonType',TPagerButtonType::LinkButton);
      +	}
      +
      +	/**
      +	 * @param TPagerButtonType the type of command button for paging.
      +	 */
      +	public function setButtonType($value)
      +	{
      +		$this->setViewState('ButtonType',TPropertyValue::ensureEnum($value,'TPagerButtonType'),TPagerButtonType::LinkButton);
      +	}
      +
      +	/**
      +	 * @return string text for the next page button. Defaults to '>'.
      +	 */
      +	public function getNextPageText()
      +	{
      +		return $this->getViewState('NextPageText','>');
      +	}
      +
      +	/**
      +	 * @param string text for the next page button.
      +	 */
      +	public function setNextPageText($value)
      +	{
      +		$this->setViewState('NextPageText',$value,'>');
      +	}
      +
      +	/**
      +	 * @return string text for the previous page button. Defaults to '<'.
      +	 */
      +	public function getPrevPageText()
      +	{
      +		return $this->getViewState('PrevPageText','<');
      +	}
      +
      +	/**
      +	 * @param string text for the next page button.
      +	 */
      +	public function setPrevPageText($value)
      +	{
      +		$this->setViewState('PrevPageText',$value,'<');
      +	}
      +
      +	/**
      +	 * @return string text for the first page button. Defaults to '<<'.
      +	 */
      +	public function getFirstPageText()
      +	{
      +		return $this->getViewState('FirstPageText','<<');
      +	}
      +
      +	/**
      +	 * @param string text for the first page button. If empty, the first page button will not be rendered.
      +	 */
      +	public function setFirstPageText($value)
      +	{
      +		$this->setViewState('FirstPageText',$value,'<<');
      +	}
      +
      +	/**
      +	 * @return string text for the last page button. Defaults to '>>'.
      +	 */
      +	public function getLastPageText()
      +	{
      +		return $this->getViewState('LastPageText','>>');
      +	}
      +
      +	/**
      +	 * @param string text for the last page button. If empty, the last page button will not be rendered.
      +	 */
      +	public function setLastPageText($value)
      +	{
      +		$this->setViewState('LastPageText',$value,'>>');
      +	}
      +
      +	/**
      +	 * @return string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function getFirstPageImageUrl()
      +	{
      +		return $this->getViewState('FirstPageImageUrl','');
      +	}
      +
      +	/**
      +	 * @param string the image URL for the first page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function setFirstPageImageUrl($value)
      +	{
      +		$this->setViewState('FirstPageImageUrl',$value);
      +	}
      +
      +	/**
      +	 * @return string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function getLastPageImageUrl()
      +	{
      +		return $this->getViewState('LastPageImageUrl','');
      +	}
      +
      +	/**
      +	 * @param string the image URL for the last page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function setLastPageImageUrl($value)
      +	{
      +		$this->setViewState('LastPageImageUrl',$value);
      +	}
      +
      +	/**
      +	 * @return string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function getNextPageImageUrl()
      +	{
      +		return $this->getViewState('NextPageImageUrl','');
      +	}
      +
      +	/**
      +	 * @param string the image URL for the next page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function setNextPageImageUrl($value)
      +	{
      +		$this->setViewState('NextPageImageUrl',$value);
      +	}
      +
      +	/**
      +	 * @return string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function getPrevPageImageUrl()
      +	{
      +		return $this->getViewState('PrevPageImageUrl','');
      +	}
      +
      +	/**
      +	 * @param string the image URL for the previous page button. This is only used when {@link getButtonType ButtonType} is 'ImageButton'.
      +	 * @since 3.1.1
      +	 */
      +	public function setPrevPageImageUrl($value)
      +	{
      +		$this->setViewState('PrevPageImageUrl',$value);
      +	}
      +
      +	/**
      +	 * @return string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
      +	 * @see setNumericPageImageUrl
      +	 * @since 3.1.1
      +	 */
      +	public function getNumericPageImageUrl()
      +	{
      +		return $this->getViewState('NumericPageImageUrl','');
      +	}
      +
      +	/**
      +	 * Sets the image URL for the numeric page buttons.
      +	 * This is actually a template for generating a set of URLs corresponding to numeric button 1, 2, 3, .., etc.
      +	 * Use {0} as the placeholder for the numbers.
      +	 * For example, the image URL http://example.com/images/button{0}.gif
      +	 * will be replaced as http://example.com/images/button1.gif, http://example.com/images/button2.gif, etc.
      +	 * @param string the image URL for the numeric page buttons. This is only used when {@link getButtonType ButtonType} is 'ImageButton' and {@link getMode Mode} is 'Numeric'.
      +	 * @since 3.1.1
      +	 */
      +	public function setNumericPageImageUrl($value)
      +	{
      +		$this->setViewState('NumericPageImageUrl',$value);
      +	}
      +
      +	/**
      +	 * @return integer maximum number of pager buttons to be displayed. Defaults to 10.
      +	 */
      +	public function getPageButtonCount()
      +	{
      +		return $this->getViewState('PageButtonCount',10);
      +	}
      +
      +	/**
      +	 * @param integer maximum number of pager buttons to be displayed
      +	 * @throws TInvalidDataValueException if the value is less than 1.
      +	 */
      +	public function setPageButtonCount($value)
      +	{
      +		if(($value=TPropertyValue::ensureInteger($value))<1)
      +			throw new TInvalidDataValueException('pager_pagebuttoncount_invalid');
      +		$this->setViewState('PageButtonCount',$value,10);
      +	}
      +
      +	/**
      +	 * @return integer the zero-based index of the current page. Defaults to 0.
      +	 */
      +	public function getCurrentPageIndex()
      +	{
      +		return $this->getViewState('CurrentPageIndex',0);
      +	}
      +
      +	/**
      +	 * @param integer the zero-based index of the current page
      +	 * @throws TInvalidDataValueException if the value is less than 0
      +	 */
      +	protected function setCurrentPageIndex($value)
      +	{
      +		if(($value=TPropertyValue::ensureInteger($value))<0)
      +			throw new TInvalidDataValueException('pager_currentpageindex_invalid');
      +		$this->setViewState('CurrentPageIndex',$value,0);
      +	}
      +
      +	/**
      +	 * @return integer number of pages of data items available
      +	 */
      +	public function getPageCount()
      +	{
      +		return $this->getViewState('PageCount',0);
      +	}
      +
      +	/**
      +	 * @param integer number of pages of data items available
      +	 * @throws TInvalidDataValueException if the value is less than 0
      +	 */
      +	protected function setPageCount($value)
      +	{
      +		if(($value=TPropertyValue::ensureInteger($value))<0)
      +			throw new TInvalidDataValueException('pager_pagecount_invalid');
      +		$this->setViewState('PageCount',$value,0);
      +	}
      +
      +	/**
      +	 * @return boolean whether the current page is the first page Defaults to false.
      +	 */
      +	public function getIsFirstPage()
      +	{
      +		return $this->getCurrentPageIndex()===0;
      +	}
      +
      +	/**
      +	 * @return boolean whether the current page is the last page
      +	 */
      +	public function getIsLastPage()
      +	{
      +		return $this->getCurrentPageIndex()===$this->getPageCount()-1;
      +	}
      +
      +	/**
      +	 * Performs databinding to populate data items from data source.
      +	 * This method is invoked by {@link dataBind()}.
      +	 * You may override this function to provide your own way of data population.
      +	 * @param Traversable the bound data
      +	 */
      +	public function onPreRender($param)
      +	{
      +		parent::onPreRender($param);
      +
      +		$controlID=$this->getControlToPaginate();
      +		if(($targetControl=$this->getNamingContainer()->findControl($controlID))===null || !($targetControl instanceof TDataBoundControl))
      +			throw new TConfigurationException('pager_controltopaginate_invalid',$controlID);
      +
      +		if($targetControl->getAllowPaging())
      +		{
      +	 		$this->_pageCount=$targetControl->getPageCount();
      +			$this->getControls()->clear();
      +			$this->setPageCount($targetControl->getPageCount());
      +			$this->setCurrentPageIndex($targetControl->getCurrentPageIndex());
      +			$this->buildPager();
      +		}
      +		else
      +			$this->_pageCount=0;
      +	}
      +
      +	/**
      +	 * Renders the control.
      +	 * The method overrides the parent implementation by rendering
      +	 * the pager only when there are two or more pages.
      +	 * @param THtmlWriter the writer
      +	 */
      +	public function render($writer)
      +	{
      +		if($this->_pageCount>1)
      +			parent::render($writer);
      +	}
      +
      +	/**
      +	 * Builds the pager content based on the pager mode.
      +	 * Current implementation includes building 'NextPrev', 'Numeric' and 'DropDownList' pagers.
      +	 * Derived classes may override this method to provide additional pagers.
      +	 */
      +	protected function buildPager()
      +	{
      +		switch($this->getMode())
      +		{
      +			case TPagerMode::NextPrev:
      +				$this->buildNextPrevPager();
      +				break;
      +			case TPagerMode::Numeric:
      +				$this->buildNumericPager();
      +				break;
      +			case TPagerMode::DropDownList:
      +				$this->buildListPager();
      +				break;
      +		}
      +	}
      +
      +	/**
      +	 * Creates a pager button.
      +	 * Depending on the button type, a TLinkButton or a TButton may be created.
      +	 * If it is enabled (clickable), its command name and parameter will also be set.
      +	 * Derived classes may override this method to create additional types of buttons, such as TImageButton.
      +	 * @param string button type, either LinkButton or PushButton
      +	 * @param boolean whether the button should be enabled
      +	 * @param string caption of the button.
      +	 * @param string CommandName corresponding to the OnCommand event of the button.
      +	 * @param string CommandParameter corresponding to the OnCommand event of the button
      +	 * @return mixed the button instance
      +	 */
      +	protected function createPagerButton($buttonType,$enabled,$text,$commandName,$commandParameter)
      +	{
      +		if($buttonType===TPagerButtonType::LinkButton)
      +		{
      +			if($enabled)
      +				$button=new TLinkButton;
      +			else
      +			{
      +				$button=new TLabel;
      +				$button->setText($text);
      +				$button->setCssClass($this->getButtonCssClass());
      +				return $button;
      +			}
      +		}
      +		else
      +		{
      +			if($buttonType===TPagerButtonType::ImageButton)
      +			{
      +				$button=new TImageButton;
      +				$button->setImageUrl($this->getPageImageUrl($text,$commandName));
      +			}
      +			else
      +				$button=new TButton;
      +			if(!$enabled)
      +				$button->setEnabled(false);
      +		}
      +		$button->setText($text);
      +		$button->setCommandName($commandName);
      +		$button->setCommandParameter($commandParameter);
      +		$button->setCausesValidation(false);
      +		$button->setCssClass($this->getButtonCssClass());
      +		return $button;
      +	}
      +
      +	/**
      +	 * @param string the caption of the image button
      +	 * @param string the command name associated with the image button
      +	 * @since 3.1.1
      +	 */
      +	protected function getPageImageUrl($text,$commandName)
      +	{
      +		switch($commandName)
      +		{
      +			case self::CMD_PAGE:
      +				$url=$this->getNumericPageImageUrl();
      +				return str_replace('{0}',$text,$url);
      +			case self::CMD_PAGE_NEXT:
      +				return $this->getNextPageImageUrl();
      +			case self::CMD_PAGE_PREV:
      +				return $this->getPrevPageImageUrl();
      +			case self::CMD_PAGE_FIRST:
      +				return $this->getFirstPageImageUrl();
      +			case self::CMD_PAGE_LAST:
      +				return $this->getLastPageImageUrl();
      +			default:
      +				return '';
      +		}
      +	}
      +
      +	/**
      +	 * Builds a next-prev pager
      +	 */
      +	protected function buildNextPrevPager()
      +	{
      +		$buttonType=$this->getButtonType();
      +		$controls=$this->getControls();
      +		if($this->getIsFirstPage())
      +		{
      +			if(($text=$this->getFirstPageText())!=='')
      +			{
      +				$label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_FIRST,'');
      +				$controls->add($label);
      +				$controls->add("\n");
      +			}
      +			$label=$this->createPagerButton($buttonType,false,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
      +			$controls->add($label);
      +		}
      +		else
      +		{
      +			if(($text=$this->getFirstPageText())!=='')
      +			{
      +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,'');
      +				$controls->add($button);
      +				$controls->add("\n");
      +			}
      +			$button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE_PREV,'');
      +			$controls->add($button);
      +		}
      +		$controls->add("\n");
      +		if($this->getIsLastPage())
      +		{
      +			$label=$this->createPagerButton($buttonType,false,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
      +			$controls->add($label);
      +			if(($text=$this->getLastPageText())!=='')
      +			{
      +				$controls->add("\n");
      +				$label=$this->createPagerButton($buttonType,false,$text,self::CMD_PAGE_LAST,'');
      +				$controls->add($label);
      +			}
      +		}
      +		else
      +		{
      +			$button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE_NEXT,'');
      +			$controls->add($button);
      +			if(($text=$this->getLastPageText())!=='')
      +			{
      +				$controls->add("\n");
      +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,'');
      +				$controls->add($button);
      +			}
      +		}
      +	}
      +
      +	/**
      +	 * Builds a numeric pager
      +	 */
      +	protected function buildNumericPager()
      +	{
      +		$buttonType=$this->getButtonType();
      +		$controls=$this->getControls();
      +		$pageCount=$this->getPageCount();
      +		$pageIndex=$this->getCurrentPageIndex()+1;
      +		$maxButtonCount=$this->getPageButtonCount();
      +		$buttonCount=$maxButtonCount>$pageCount?$pageCount:$maxButtonCount;
      +		$startPageIndex=1;
      +		$endPageIndex=$buttonCount;
      +		if($pageIndex>$endPageIndex)
      +		{
      +			$startPageIndex=((int)(($pageIndex-1)/$maxButtonCount))*$maxButtonCount+1;
      +			if(($endPageIndex=$startPageIndex+$maxButtonCount-1)>$pageCount)
      +				$endPageIndex=$pageCount;
      +			if($endPageIndex-$startPageIndex+1<$maxButtonCount)
      +			{
      +				if(($startPageIndex=$endPageIndex-$maxButtonCount+1)<1)
      +					$startPageIndex=1;
      +			}
      +		}
      +
      +		if($startPageIndex>1)
      +		{
      +			if(($text=$this->getFirstPageText())!=='')
      +			{
      +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_FIRST,'');
      +				$controls->add($button);
      +				$controls->add("\n");
      +			}
      +			$prevPageIndex=$startPageIndex-1;
      +			$button=$this->createPagerButton($buttonType,true,$this->getPrevPageText(),self::CMD_PAGE,"$prevPageIndex");
      +			$controls->add($button);
      +			$controls->add("\n");
      +		}
      +
      +		for($i=$startPageIndex;$i<=$endPageIndex;++$i)
      +		{
      +			if($i===$pageIndex)
      +			{
      +				$label=$this->createPagerButton($buttonType,false,"$i",self::CMD_PAGE,'');
      +				$controls->add($label);
      +			}
      +			else
      +			{
      +				$button=$this->createPagerButton($buttonType,true,"$i",self::CMD_PAGE,"$i");
      +				$controls->add($button);
      +			}
      +			if($i<$endPageIndex)
      +				$controls->add("\n");
      +		}
      +
      +		if($pageCount>$endPageIndex)
      +		{
      +			$controls->add("\n");
      +			$nextPageIndex=$endPageIndex+1;
      +			$button=$this->createPagerButton($buttonType,true,$this->getNextPageText(),self::CMD_PAGE,"$nextPageIndex");
      +			$controls->add($button);
      +			if(($text=$this->getLastPageText())!=='')
      +			{
      +				$controls->add("\n");
      +				$button=$this->createPagerButton($buttonType,true,$text,self::CMD_PAGE_LAST,'');
      +				$controls->add($button);
      +			}
      +		}
      +	}
      +
      +	/**
      +	 * Builds a dropdown list pager
      +	 */
      +	protected function buildListPager()
      +	{
      +		$list=new TDropDownList;
      +		$this->getControls()->add($list);
      +		$list->setDataSource(range(1,$this->getPageCount()));
      +		$list->dataBind();
      +		$list->setSelectedIndex($this->getCurrentPageIndex());
      +		$list->setAutoPostBack(true);
      +		$list->attachEventHandler('OnSelectedIndexChanged',array($this,'listIndexChanged'));
      +	}
      +
      +	/**
      +	 * Event handler to the OnSelectedIndexChanged event of the dropdown list.
      +	 * This handler will raise {@link onPageIndexChanged OnPageIndexChanged} event.
      +	 * @param TDropDownList the dropdown list control raising the event
      +	 * @param TEventParameter event parameter
      +	 */
      +	public function listIndexChanged($sender,$param)
      +	{
      +		$pageIndex=$sender->getSelectedIndex();
      +		$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
      +	}
      +
      +	/**
      +	 * This event is raised when page index is changed due to a page button click.
      +	 * @param TPagerPageChangedEventParameter event parameter
      +	 */
      +	public function onPageIndexChanged($param)
      +	{
      +		$this->raiseEvent('OnPageIndexChanged',$this,$param);
      +	}
      +
      +	/**
      +	 * Processes a bubbled event.
      +	 * This method overrides parent's implementation by wrapping event parameter
      +	 * for OnCommand event with item information.
      +	 * @param TControl the sender of the event
      +	 * @param TEventParameter event parameter
      +	 * @return boolean whether the event bubbling should stop here.
      +	 */
      +	public function bubbleEvent($sender,$param)
      +	{
      +		if($param instanceof TCommandEventParameter)
      +		{
      +			$command=$param->getCommandName();
      +			if(strcasecmp($command,self::CMD_PAGE)===0)
      +			{
      +				$pageIndex=TPropertyValue::ensureInteger($param->getCommandParameter())-1;
      +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
      +				return true;
      +			}
      +			else if(strcasecmp($command,self::CMD_PAGE_NEXT)===0)
      +			{
      +				$pageIndex=$this->getCurrentPageIndex()+1;
      +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
      +				return true;
      +			}
      +			else if(strcasecmp($command,self::CMD_PAGE_PREV)===0)
      +			{
      +				$pageIndex=$this->getCurrentPageIndex()-1;
      +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$pageIndex));
      +				return true;
      +			}
      +			else if(strcasecmp($command,self::CMD_PAGE_FIRST)===0)
      +			{
      +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,0));
      +				return true;
      +			}
      +			else if(strcasecmp($command,self::CMD_PAGE_LAST)===0)
      +			{
      +				$this->onPageIndexChanged(new TPagerPageChangedEventParameter($sender,$this->getPageCount()-1));
      +				return true;
      +			}
      +			return false;
      +		}
      +		else
      +			return false;
      +	}
      +}
      +
      +/**
      + * TPagerPageChangedEventParameter class
      + *
      + * TPagerPageChangedEventParameter encapsulates the parameter data for
      + * {@link TPager::onPageIndexChanged PageIndexChanged} event of {@link TPager} controls.
      + *
      + * The {@link getCommandSource CommandSource} property refers to the control
      + * that originally raises the OnCommand event, while {@link getNewPageIndex NewPageIndex}
      + * returns the new page index carried with the page command.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPager.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.2
      + */
      +class TPagerPageChangedEventParameter extends TEventParameter
      +{
      +	/**
      +	 * @var integer new page index
      +	 */
      +	private $_newIndex;
      +	/**
      +	 * @var TControl original event sender
      +	 */
      +	private $_source=null;
      +
      +	/**
      +	 * Constructor.
      +	 * @param TControl the control originally raises the OnCommand event.
      +	 * @param integer new page index
      +	 */
      +	public function __construct($source,$newPageIndex)
      +	{
      +		$this->_source=$source;
      +		$this->_newIndex=$newPageIndex;
      +	}
      +
      +	/**
      +	 * @return TControl the control originally raises the OnCommand event.
      +	 */
      +	public function getCommandSource()
      +	{
      +		return $this->_source;
      +	}
      +
      +	/**
      +	 * @return integer new page index
      +	 */
      +	public function getNewPageIndex()
      +	{
      +		return $this->_newIndex;
      +	}
      +}
      +
      +
      +/**
      + * TPagerMode class.
      + * TPagerMode defines the enumerable type for the possible modes that a {@link TPager} control can take.
      + *
      + * The following enumerable values are defined:
      + * - NextPrev: pager buttons are displayed as next and previous pages
      + * - Numeric: pager buttons are displayed as numeric page numbers
      + * - DropDownList: a dropdown list is used to select pages
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPager.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.4
      + */
      +class TPagerMode extends TEnumerable
      +{
      +	const NextPrev='NextPrev';
      +	const Numeric='Numeric';
      +	const DropDownList='DropDownList';
      +}
      +
      +
      +/**
      + * TPagerButtonType class.
      + * TPagerButtonType defines the enumerable type for the possible types of pager buttons.
      + *
      + * The following enumerable values are defined:
      + * - LinkButton: link buttons
      + * - PushButton: form submit buttons
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPager.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.4
      + */
      +class TPagerButtonType extends TEnumerable
      +{
      +	const LinkButton='LinkButton';
      +	const PushButton='PushButton';
      +	const ImageButton='ImageButton';
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TPanel.php b/gui/baculum/framework/Web/UI/WebControls/TPanel.php
      new file mode 100644
      index 0000000000..cf20a86457
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TPanel.php
      @@ -0,0 +1,246 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TPanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * Includes TPanelStyle class file
      + */
      +Prado::using('System.Web.UI.WebControls.TPanelStyle');
      +
      +/**
      + * TPanel class
      + *
      + * TPanel represents a component that acts as a container for other component.
      + * It is especially useful when you want to generate components programmatically
      + * or hide/show a group of components.
      + *
      + * By default, TPanel displays a <div> element on a page.
      + * Children of TPanel are displayed as the body content of the element.
      + * The property {@link setWrap Wrap} can be used to set whether the body content
      + * should wrap or not. {@link setHorizontalAlign HorizontalAlign} governs how
      + * the content is aligned horizontally, and {@link getDirection Direction} indicates
      + * the content direction (left to right or right to left). You can set
      + * {@link setBackImageUrl BackImageUrl} to give a background image to the panel,
      + * and you can ste {@link setGroupingText GroupingText} so that the panel is
      + * displayed as a field set with a legend text. Finally, you can specify
      + * a default button to be fired when users press 'return' key within the panel
      + * by setting the {@link setDefaultButton DefaultButton} property.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TPanel extends TWebControl
      +{
      +	/**
      +	 * @var string ID path to the default button
      +	 */
      +	private $_defaultButton='';
      +
      +	/**
      +	 * @return string tag name of the panel
      +	 */
      +	protected function getTagName()
      +	{
      +		return 'div';
      +	}
      +
      +	/**
      +	 * Creates a style object to be used by the control.
      +	 * This method overrides the parent impementation by creating a TPanelStyle object.
      +	 * @return TPanelStyle the style used by TPanel.
      +	 */
      +	protected function createStyle()
      +	{
      +		return new TPanelStyle;
      +	}
      +
      +	/**
      +	 * Adds attributes to renderer.
      +	 * @param THtmlWriter the renderer
      +	 * @throws TInvalidDataValueException if default button is not right.
      +	 */
      +	protected function addAttributesToRender($writer)
      +	{
      +		parent::addAttributesToRender($writer);
      +		if(($butt=$this->getDefaultButton())!=='')
      +			$writer->addAttribute('id',$this->getClientID());
      +	}
      +
      +	/**
      +	 * @return boolean whether the content wraps within the panel. Defaults to true.
      +	 */
      +	public function getWrap()
      +	{
      +		return $this->getStyle()->getWrap();
      +	}
      +
      +	/**
      +	 * Sets the value indicating whether the content wraps within the panel.
      +	 * @param boolean whether the content wraps within the panel.
      +	 */
      +	public function setWrap($value)
      +	{
      +		$this->getStyle()->setWrap($value);
      +	}
      +
      +	/**
      +	 * @return string the horizontal alignment of the contents within the panel, defaults to 'NotSet'.
      +	 */
      +	public function getHorizontalAlign()
      +	{
      +		return $this->getStyle()->getHorizontalAlign();
      +	}
      +
      +	/**
      +	 * Sets the horizontal alignment of the contents within the panel.
      +     * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center'
      +	 * @param string the horizontal alignment
      +	 */
      +	public function setHorizontalAlign($value)
      +	{
      +		$this->getStyle()->setHorizontalAlign($value);
      +	}
      +
      +	/**
      +	 * @return string the URL of the background image for the panel component.
      +	 */
      +	public function getBackImageUrl()
      +	{
      +		return $this->getStyle()->getBackImageUrl();
      +	}
      +
      +	/**
      +	 * Sets the URL of the background image for the panel component.
      +	 * @param string the URL
      +	 */
      +	public function setBackImageUrl($value)
      +	{
      +		$this->getStyle()->setBackImageUrl($value);
      +	}
      +
      +	/**
      +	 * @return string alignment of the content in the panel. Defaults to 'NotSet'.
      +	 */
      +	public function getDirection()
      +	{
      +		return $this->getStyle()->getDirection();
      +	}
      +
      +	/**
      +	 * @param string alignment of the content in the panel.
      +	 * Valid values include 'NotSet', 'LeftToRight', 'RightToLeft'.
      +	 */
      +	public function setDirection($value)
      +	{
      +		$this->getStyle()->setDirection($value);
      +	}
      +
      +	/**
      +	 * @return string the ID path to the default button. Defaults to empty.
      +	 */
      +	public function getDefaultButton()
      +	{
      +		return $this->_defaultButton;
      +	}
      +
      +	/**
      +	 * Specifies the default button for the panel.
      +	 * The default button will be fired (clicked) whenever a user enters 'return'
      +	 * key within the panel.
      +	 * The button must be locatable via the function call {@link TControl::findControl findControl}.
      +	 * @param string the ID path to the default button.
      +	 */
      +	public function setDefaultButton($value)
      +	{
      +		$this->_defaultButton=$value;
      +	}
      +
      +	/**
      +	 * @return string the legend text when the panel is used as a fieldset. Defaults to empty.
      +	 */
      +	public function getGroupingText()
      +	{
      +		return $this->getViewState('GroupingText','');
      +	}
      +
      +	/**
      +	 * @param string the legend text. If this value is not empty, the panel will be rendered as a fieldset.
      +	 */
      +	public function setGroupingText($value)
      +	{
      +		$this->setViewState('GroupingText',$value,'');
      +	}
      +
      +	/**
      +	 * @return string the visibility and position of scroll bars in a panel control, defaults to None.
      +	 */
      +	public function getScrollBars()
      +	{
      +		return $this->getStyle()->getScrollBars();
      +	}
      +
      +	/**
      +	 * @param string the visibility and position of scroll bars in a panel control.
      +	 * Valid values include None, Auto, Both, Horizontal and Vertical.
      +	 */
      +	public function setScrollBars($value)
      +	{
      +		$this->getStyle()->setScrollBars($value);
      +	}
      +
      +	/**
      +	 * Renders the openning tag for the control (including attributes)
      +	 * @param THtmlWriter the writer used for the rendering purpose
      +	 */
      +	public function renderBeginTag($writer)
      +	{
      +		parent::renderBeginTag($writer);
      +		if(($text=$this->getGroupingText())!=='')
      +		{
      +			$writer->renderBeginTag('fieldset');
      +			$writer->renderBeginTag('legend');
      +			$writer->write($text);
      +			$writer->renderEndTag();
      +		}
      +	}
      +
      +	/**
      +	 * Renders the closing tag for the control
      +	 * @param THtmlWriter the writer used for the rendering purpose
      +	 */
      +	public function renderEndTag($writer)
      +	{
      +		if($this->getGroupingText()!=='')
      +			$writer->renderEndTag();
      +		parent::renderEndTag($writer);
      +	}
      +
      +	public function render($writer)
      +	{
      +		parent::render($writer);
      +
      +		if(($butt=$this->getDefaultButton())!=='')
      +		{
      +			$buttons = $this->findControlsByID($butt);
      +			if (count($buttons)>0)
      +				$button = reset($buttons);
      +			else
      +				$button = null;
      +			if($button===null)
      +				throw new TInvalidDataValueException('panel_defaultbutton_invalid',$butt);
      +			else
      +				$this->getPage()->getClientScript()->registerDefaultButton($this, $button);
      +		}
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TPanelStyle.php b/gui/baculum/framework/Web/UI/WebControls/TPanelStyle.php
      new file mode 100644
      index 0000000000..cb49b71540
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TPanelStyle.php
      @@ -0,0 +1,278 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TPanelStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * Includes TStyle class file
      + */
      +Prado::using('System.Web.UI.WebControls.TStyle');
      +
      +/**
      + * TPanelStyle class.
      + * TPanelStyle represents the CSS style specific for panel HTML tag.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPanelStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TPanelStyle extends TStyle
      +{
      +	/**
      +	 * @var string the URL of the background image for the panel component
      +	 */
      +	private $_backImageUrl=null;
      +	/**
      +	 * @var string alignment of the content in the panel.
      +	 */
      +	private $_direction=null;
      +	/**
      +	 * @var string horizontal alignment of the contents within the panel
      +	 */
      +	private $_horizontalAlign=null;
      +	/**
      +	 * @var string visibility and position of scroll bars
      +	 */
      +	private $_scrollBars=null;
      +	/**
      +	 * @var boolean whether the content wraps within the panel
      +	 */
      +	private $_wrap=null;
      +
      +	/**
      +	 * Adds attributes related to CSS styles to renderer.
      +	 * This method overrides the parent implementation.
      +	 * @param THtmlWriter the writer used for the rendering purpose
      +	 */
      +	public function addAttributesToRender($writer)
      +	{
      +		if(($url=trim($this->getBackImageUrl()))!=='')
      +			$this->setStyleField('background-image','url('.$url.')');
      +
      +		switch($this->getScrollBars())
      +		{
      +			case TScrollBars::Horizontal: $this->setStyleField('overflow-x','scroll'); break;
      +			case TScrollBars::Vertical: $this->setStyleField('overflow-y','scroll'); break;
      +			case TScrollBars::Both: $this->setStyleField('overflow','scroll'); break;
      +			case TScrollBars::Auto: $this->setStyleField('overflow','auto'); break;
      +		}
      +
      +		if(($align=$this->getHorizontalAlign())!==THorizontalAlign::NotSet)
      +			$this->setStyleField('text-align',strtolower($align));
      +
      +		if(!$this->getWrap())
      +			$this->setStyleField('white-space','nowrap');
      +
      +		if(($direction=$this->getDirection())!==TContentDirection::NotSet)
      +		{
      +			if($direction===TContentDirection::LeftToRight)
      +				$this->setStyleField('direction','ltr');
      +			else
      +				$this->setStyleField('direction','rtl');
      +		}
      +
      +		parent::addAttributesToRender($writer);
      +	}
      +
      +	/**
      +	 * @return string the URL of the background image for the panel component.
      +	 */
      +	public function getBackImageUrl()
      +	{
      +		return $this->_backImageUrl===null?'':$this->_backImageUrl;
      +	}
      +
      +	/**
      +	 * Sets the URL of the background image for the panel component.
      +	 * @param string the URL
      +	 */
      +	public function setBackImageUrl($value)
      +	{
      +		$this->_backImageUrl=$value;
      +	}
      +
      +	/**
      +	 * @return TContentDirection alignment of the content in the panel. Defaults to TContentDirection::NotSet.
      +	 */
      +	public function getDirection()
      +	{
      +		return $this->_direction===null?TContentDirection::NotSet:$this->_direction;
      +	}
      +
      +	/**
      +	 * @param TContentDirection alignment of the content in the panel.
      +	 */
      +	public function setDirection($value)
      +	{
      +		$this->_direction=TPropertyValue::ensureEnum($value,'TContentDirection');
      +	}
      +
      +	/**
      +	 * @return boolean whether the content wraps within the panel. Defaults to true.
      +	 */
      +	public function getWrap()
      +	{
      +		return $this->_wrap===null?true:$this->_wrap;
      +	}
      +
      +	/**
      +	 * Sets the value indicating whether the content wraps within the panel.
      +	 * @param boolean whether the content wraps within the panel.
      +	 */
      +	public function setWrap($value)
      +	{
      +		$this->_wrap=TPropertyValue::ensureBoolean($value);
      +	}
      +
      +	/**
      +	 * @return THorizontalAlign the horizontal alignment of the contents within the panel, defaults to THorizontalAlign::NotSet.
      +	 */
      +	public function getHorizontalAlign()
      +	{
      +		return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign;
      +	}
      +
      +	/**
      +	 * Sets the horizontal alignment of the contents within the panel.
      +	 * @param THorizontalAlign the horizontal alignment
      +	 */
      +	public function setHorizontalAlign($value)
      +	{
      +		$this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign');
      +	}
      +
      +	/**
      +	 * @return TScrollBars the visibility and position of scroll bars in a panel control, defaults to TScrollBars::None.
      +	 */
      +	public function getScrollBars()
      +	{
      +		return $this->_scrollBars===null?TScrollBars::None:$this->_scrollBars;
      +	}
      +
      +	/**
      +	 * @param TScrollBars the visibility and position of scroll bars in a panel control.
      +	 */
      +	public function setScrollBars($value)
      +	{
      +		$this->_scrollBars=TPropertyValue::ensureEnum($value,'TScrollBars');
      +	}
      +
      +	/**
      +	 * Sets the style attributes to default values.
      +	 * This method overrides the parent implementation by
      +	 * resetting additional TPanelStyle specific attributes.
      +	 */
      +	public function reset()
      +	{
      +		parent::reset();
      +		$this->_backImageUrl=null;
      +		$this->_direction=null;
      +		$this->_horizontalAlign=null;
      +		$this->_scrollBars=null;
      +		$this->_wrap=null;
      +	}
      +
      +	/**
      +	 * Copies the fields in a new style to this style.
      +	 * If a style field is set in the new style, the corresponding field
      +	 * in this style will be overwritten.
      +	 * @param TStyle the new style
      +	 */
      +	public function copyFrom($style)
      +	{
      +		parent::copyFrom($style);
      +		if($style instanceof TPanelStyle)
      +		{
      +			if($style->_backImageUrl!==null)
      +				$this->_backImageUrl=$style->_backImageUrl;
      +			if($style->_direction!==null)
      +				$this->_direction=$style->_direction;
      +			if($style->_horizontalAlign!==null)
      +				$this->_horizontalAlign=$style->_horizontalAlign;
      +			if($style->_scrollBars!==null)
      +				$this->_scrollBars=$style->_scrollBars;
      +			if($style->_wrap!==null)
      +				$this->_wrap=$style->_wrap;
      +		}
      +	}
      +
      +	/**
      +	 * Merges the style with a new one.
      +	 * If a style field is not set in this style, it will be overwritten by
      +	 * the new one.
      +	 * @param TStyle the new style
      +	 */
      +	public function mergeWith($style)
      +	{
      +		parent::mergeWith($style);
      +		if($style instanceof TPanelStyle)
      +		{
      +			if($this->_backImageUrl===null && $style->_backImageUrl!==null)
      +				$this->_backImageUrl=$style->_backImageUrl;
      +			if($this->_direction===null && $style->_direction!==null)
      +				$this->_direction=$style->_direction;
      +			if($this->_horizontalAlign===null && $style->_horizontalAlign!==null)
      +				$this->_horizontalAlign=$style->_horizontalAlign;
      +			if($this->_scrollBars===null && $style->_scrollBars!==null)
      +				$this->_scrollBars=$style->_scrollBars;
      +			if($this->_wrap===null && $style->_wrap!==null)
      +				$this->_wrap=$style->_wrap;
      +		}
      +	}
      +}
      +
      +/**
      + * TContentDirection class.
      + * TContentDirection defines the enumerable type for the possible directions that a panel can be at.
      + *
      + * The following enumerable values are defined:
      + * - NotSet: the direction is not specified
      + * - LeftToRight: content in a panel is left to right
      + * - RightToLeft: content in a panel is right to left
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPanelStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.4
      + */
      +class TContentDirection extends TEnumerable
      +{
      +	const NotSet='NotSet';
      +	const LeftToRight='LeftToRight';
      +	const RightToLeft='RightToLeft';
      +}
      +
      +/**
      + * TScrollBars class.
      + * TScrollBars defines the enumerable type for the possible scroll bar mode
      + * that a {@link TPanel} control could use.
      + *
      + * The following enumerable values are defined:
      + * - None: no scroll bars.
      + * - Auto: scroll bars automatically appeared when needed.
      + * - Both: show both horizontal and vertical scroll bars all the time.
      + * - Horizontal: horizontal scroll bar only
      + * - Vertical: vertical scroll bar only
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPanelStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.4
      + */
      +class TScrollBars extends TEnumerable
      +{
      +	const None='None';
      +	const Auto='Auto';
      +	const Both='Both';
      +	const Horizontal='Horizontal';
      +	const Vertical='Vertical';
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TPlaceHolder.php b/gui/baculum/framework/Web/UI/WebControls/TPlaceHolder.php
      new file mode 100644
      index 0000000000..020141cff0
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TPlaceHolder.php
      @@ -0,0 +1,28 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TPlaceHolder.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * TPlaceHolder class
      + *
      + * TPlaceHolder reserves a place on a template, where static texts or controls
      + * may be inserted. You may add or remove texts or child controls of TPlaceHolder
      + * by manipulating the {@link TControl::getControls Controls} property.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TPlaceHolder.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TPlaceHolder extends TControl
      +{
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TRadioButton.php b/gui/baculum/framework/Web/UI/WebControls/TRadioButton.php
      new file mode 100644
      index 0000000000..144704b419
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TRadioButton.php
      @@ -0,0 +1,320 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TRadioButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * Using TCheckBox parent class
      + */
      +Prado::using('System.Web.UI.WebControls.TCheckBox');
      +/**
      + * Using TRadioButtonList class
      + */
      +Prado::using('System.Web.UI.WebControls.TRadioButtonList');
      +
      +/**
      + * TRadioButton class
      + *
      + * TRadioButton displays a radio button on the page.
      + * You can specify the caption to display beside the radio buttonby setting
      + * the {@link setText Text} property.  The caption can appear either on the right
      + * or left of the radio button, which is determined by the {@link setTextAlign TextAlign}
      + * property.
      + *
      + * To determine whether the TRadioButton component is checked, test the {@link getChecked Checked}
      + * property. The {@link onCheckedChanged OnCheckedChanged} event is raised when
      + * the {@link getChecked Checked} state of the TRadioButton component changes
      + * between posts to the server. You can provide an event handler for
      + * the {@link onCheckedChanged OnCheckedChanged} event to  to programmatically
      + * control the actions performed when the state of the TRadioButton component changes
      + * between posts to the server.
      + *
      + * TRadioButton uses {@link setGroupName GroupName} to group together a set of radio buttons.
      + * Once the {@link setGroupName GroupName} is set, you can use the {@link getRadioButtonsInGroup}
      + * method to get an array of TRadioButtons having the same group name.
      + *
      + * If {@link setAutoPostBack AutoPostBack} is set true, changing the radio button state
      + * will cause postback action. And if {@link setCausesValidation CausesValidation}
      + * is true, validation will also be processed, which can be further restricted within
      + * a {@link setValidationGroup ValidationGroup}.
      + *
      + * Note, {@link setText Text} is rendered as is. Make sure it does not contain unwanted characters
      + * that may bring security vulnerabilities.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TRadioButton.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TRadioButton extends TCheckBox
      +{
      +	/**
      +	 * @param array list of radio buttons that are on the current page hierarchy
      +	 */
      +	private static $_activeButtons=array();
      +	/**
      +	 * @var integer number of radio buttons created
      +	 */
      +	private static $_buttonCount=0;
      +	/**
      +	 * @var integer global ID of this radiobutton
      +	 */
      +	private $_globalID;
      +	/**
      +	 * @var string previous UniqueID (used to calculate UniqueGroup)
      +	 */
      +	private $_previousUniqueID=null;
      +	/**
      +	 * @var string the name used to fetch radiobutton post data
      +	 */
      +	private $_uniqueGroupName=null;
      +
      +	/**
      +	 * Constructor.
      +	 * Registers the radiobutton in a global radiobutton collection.
      +	 * If overridden, the parent implementation must be invoked first.
      +	 */
      +	public function __construct()
      +	{
      +		parent::__construct();
      +		$this->_globalID = self::$_buttonCount++;
      +	}
      +
      +	/**
      +	 * Registers the radio button groupings. If overriding onInit method,
      +	 * ensure to call parent implemenation.
      +	 * @param TEventParameter event parameter to be passed to the event handlers
      +	 */
      +	public function onInit($param)
      +	{
      +		parent::onInit($param);
      +		self::$_activeButtons[$this->_globalID]=$this;
      +	}
      +
      +	/**
      +	 * Unregisters the radio button groupings. If overriding onInit method,
      +	 * ensure to call parent implemenation.
      +	 * @param TEventParameter event parameter to be passed to the event handlers
      +	 */
      +	public function onUnLoad($param)
      +	{
      +		unset(self::$_activeButtons[$this->_globalID]);
      +		parent::onUnLoad($param);
      +	}
      +
      +	/**
      +	 * Loads user input data.
      +	 * This method is primarly used by framework developers.
      +	 * @param string the key that can be used to retrieve data from the input data collection
      +	 * @param array the input data collection
      +	 * @return boolean whether the data of the control has been changed
      +	 */
      +	public function loadPostData($key,$values)
      +	{
      +		$uniqueGroupName=$this->getUniqueGroupName();
      +		$value=isset($values[$uniqueGroupName])?$values[$uniqueGroupName]:null;
      +		if($value!==null && $value===$this->getValueAttribute())
      +		{
      +			if(!$this->getChecked())
      +			{
      +				$this->setChecked(true);
      +				return true;
      +			}
      +			else
      +				return false;
      +		}
      +		else if($this->getChecked())
      +			$this->setChecked(false);
      +		return false;
      +	}
      +
      +	/**
      +	 * @return string the name of the group that the radio button belongs to. Defaults to empty.
      +	 */
      +	public function getGroupName()
      +	{
      +		return $this->getViewState('GroupName','');
      +	}
      +
      +	/**
      +	 * Sets the name of the group that the radio button belongs to.
      +	 * The group is unique among the control's naming container.
      +	 * @param string the group name
      +	 * @see setUniqueGroupName
      +	 */
      +	public function setGroupName($value)
      +	{
      +		$this->setViewState('GroupName',$value,'');
      +		$this->_uniqueGroupName=null;
      +	}
      +
      +	/**
      +	 * Add the group name as post data loader if group name is set.
      +	 */
      +	protected function addToPostDataLoader()
      +	{
      +		parent::addToPostDataLoader();
      +		$group = $this->getGroupName();
      +		if(!empty($group) || $this->getViewState('UniqueGroupName','') !== '')
      +			$this->getPage()->registerPostDataLoader($this->getUniqueGroupName());
      +	}
      +	/**
      +	 * @return string the name used to fetch radiobutton post data
      +	 */
      +	public function getUniqueGroupName()
      +	{
      +		if(($groupName=$this->getViewState('UniqueGroupName',''))!=='')
      +			return $groupName;
      +		else if(($uniqueID=$this->getUniqueID())!==$this->_previousUniqueID || $this->_uniqueGroupName===null)
      +		{
      +			$groupName=$this->getGroupName();
      +			$this->_previousUniqueID=$uniqueID;
      +			if($uniqueID!=='')
      +			{
      +				if(($pos=strrpos($uniqueID,TControl::ID_SEPARATOR))!==false)
      +				{
      +					if($groupName!=='')
      +						$groupName=substr($uniqueID,0,$pos+1).$groupName;
      +					else if($this->getNamingContainer() instanceof TRadioButtonList)
      +						$groupName=substr($uniqueID,0,$pos);
      +				}
      +				if($groupName==='')
      +					$groupName=$uniqueID;
      +			}
      +			$this->_uniqueGroupName=$groupName;
      +		}
      +		return $this->_uniqueGroupName;
      +	}
      +
      +	/**
      +	 * Sets the unique group name that the radio button belongs to.
      +	 * A unique group is a radiobutton group unique among the whole page hierarchy,
      +	 * while the {@link setGroupName GroupName} specifies a group that is unique
      +	 * among the control's naming container only.
      +	 * For example, each cell of a {@link TDataGrid} is a naming container.
      +	 * If you specify {@link setGroupName GroupName} for a radiobutton in a cell,
      +	 * it groups together radiobutton within a cell, but not the other, even though
      +	 * they have the same {@link setGroupName GroupName}.
      +	 * On the contratry, if {@link setUniqueGroupName UniqueGroupName} is used instead,
      +	 * it will group all appropriate radio buttons on the whole page hierarchy.
      +	 * Note, when both {@link setUniqueGroupName UniqueGroupName} and
      +	 * {@link setGroupName GroupName}, the former takes precedence.
      +	 * @param string the group name
      +	 * @see setGroupName
      +	 */
      +	public function setUniqueGroupName($value)
      +	{
      +		$this->setViewState('UniqueGroupName',$value,'');
      +	}
      +
      +	/**
      +	 * Gets an array of radiobuttons whose group name is the same as this radiobutton's.
      +	 * Note, only those radiobuttons that are on the current page hierarchy may be
      +	 * returned in the result.
      +	 * @return array list of TRadioButton with the same group
      +	 */
      +	public function getRadioButtonsInGroup()
      +	{
      +		$group = $this->getUniqueGroupName();
      +		$buttons = array();
      +		foreach(self::$_activeButtons as $control)
      +		{
      +			if($control->getUniqueGroupName() === $group)
      +				$buttons[] = $control;
      +		}
      +		return $buttons;
      +	}
      +
      +	/**
      +	 * @return string the value attribute to be rendered
      +	 */
      +	protected function getValueAttribute()
      +	{
      +		if(($value=parent::getValueAttribute())==='')
      +			return $this->getUniqueID();
      +		else
      +			return $value;
      +	}
      +
      +	/**
      +	 * @return boolean whether to render javascript.
      +	 */
      +	public function getEnableClientScript()
      +	{
      +		return $this->getViewState('EnableClientScript',true);
      +	}
      +
      +	/**
      +	 * @param boolean whether to render javascript.
      +	 */
      +	public function setEnableClientScript($value)
      +	{
      +		$this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true);
      +	}
      +
      +	/**
      +	 * Renders a radiobutton input element.
      +	 * @param THtmlWriter the writer for the rendering purpose
      +	 * @param string checkbox id
      +	 * @param string onclick js
      +	 */
      +	protected function renderInputTag($writer,$clientID,$onclick)
      +	{
      +		if($clientID!=='')
      +			$writer->addAttribute('id',$clientID);
      +		$writer->addAttribute('type','radio');
      +		$writer->addAttribute('name',$this->getUniqueGroupName());
      +		$writer->addAttribute('value',$this->getValueAttribute());
      +		if(!empty($onclick))
      +			$writer->addAttribute('onclick',$onclick);
      +		if($this->getChecked())
      +			$writer->addAttribute('checked','checked');
      +		if(!$this->getEnabled(true))
      +			$writer->addAttribute('disabled','disabled');
      +
      +		$page=$this->getPage();
      +		if($this->getEnabled(true)
      +			&& $this->getEnableClientScript()
      +			&& $this->getAutoPostBack()
      +			&& $page->getClientSupportsJavaScript())
      +		{
      +			$this->renderClientControlScript($writer);
      +		}
      +
      +		if(($accesskey=$this->getAccessKey())!=='')
      +			$writer->addAttribute('accesskey',$accesskey);
      +		if(($tabindex=$this->getTabIndex())>0)
      +			$writer->addAttribute('tabindex',"$tabindex");
      +		if($attributes=$this->getViewState('InputAttributes',null))
      +			$writer->addAttributes($attributes);
      +		$writer->renderBeginTag('input');
      +		$writer->renderEndTag();
      +	}
      +
      +	/**
      +	 * Renders the client-script code.
      +	 */
      +	protected function renderClientControlScript($writer)
      +	{
      +		$cs = $this->getPage()->getClientScript();
      +		$cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions());
      +	}
      +
      +	/**
      +	 * Gets the name of the javascript class responsible for performing postback for this control.
      +	 * This method overrides the parent implementation.
      +	 * @return string the javascript class name
      +	 */
      +	protected function getClientClassName()
      +	{
      +		return 'Prado.WebUI.TRadioButton';
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TRadioButtonList.php b/gui/baculum/framework/Web/UI/WebControls/TRadioButtonList.php
      new file mode 100644
      index 0000000000..d93caaa264
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TRadioButtonList.php
      @@ -0,0 +1,101 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TRadioButtonList.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * Includes TRadioButton class
      + */
      +Prado::using('System.Web.UI.WebControls.TRadioButton');
      +/**
      + * Includes TCheckBoxList class
      + */
      +Prado::using('System.Web.UI.WebControls.TCheckBoxList');
      +
      +/**
      + * TRadioButtonList class
      + *
      + * TRadioButtonList displays a list of radiobuttons on a Web page.
      + *
      + * TRadioButtonList inherits all properties and events of {@link TCheckBoxList}.
      + * Each TRadioButtonList displays one group of radiobuttons, i.e., at most
      + * one radiobutton can be selected at a time.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TRadioButtonList.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TRadioButtonList extends TCheckBoxList
      +{
      +	/**
      +	 * @return boolean whether this control supports multiple selection. Always false for radiobutton list.
      +	 */
      +	protected function getIsMultiSelect()
      +	{
      +		return false;
      +	}
      +
      +	/**
      +	 * Creates a control used for repetition (used as a template).
      +	 * @return TControl the control to be repeated
      +	 */
      +	protected function createRepeatedControl()
      +	{
      +		return new TRadioButton;
      +	}
      +
      +	/**
      +	 * Loads user input data.
      +	 * This method is primarly used by framework developers.
      +	 * @param string the key that can be used to retrieve data from the input data collection
      +	 * @param array the input data collection
      +	 * @return boolean whether the data of the control has been changed
      +	 */
      +	public function loadPostData($key,$values)
      +	{
      +		$value=isset($values[$key])?$values[$key]:'';
      +		$oldSelection=$this->getSelectedIndex();
      +		$this->ensureDataBound();
      +		foreach($this->getItems() as $index=>$item)
      +		{
      +			if($item->getEnabled() && $item->getValue()===$value)
      +			{
      +				if($index===$oldSelection)
      +					return false;
      +				else
      +				{
      +					$this->setSelectedIndex($index);
      +					return true;
      +				}
      +			}
      +		}
      +		return false;
      +	}
      +
      +	/**
      +	 * @throws TNotSupportedException if this method is invoked
      +	 */
      +	public function setSelectedIndices($indices)
      +	{
      +		throw new TNotSupportedException('radiobuttonlist_selectedindices_unsupported');
      +	}
      +
      +	/**
      +	 * Gets the name of the javascript class responsible for performing postback for this control.
      +	 * This method overrides the parent implementation.
      +	 * @return string the javascript class name
      +	 */
      +	protected function getClientClassName()
      +	{
      +		return 'Prado.WebUI.TRadioButtonList';
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TRangeValidator.php b/gui/baculum/framework/Web/UI/WebControls/TRangeValidator.php
      new file mode 100644
      index 0000000000..8b7550c85b
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TRangeValidator.php
      @@ -0,0 +1,360 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TRangeValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * Using TBaseValidator class
      + */
      +Prado::using('System.Web.UI.WebControls.TBaseValidator');
      +
      +/**
      + * TRangeValidator class
      + *
      + * TRangeValidator tests whether an input value is within a specified range.
      + *
      + * TRangeValidator uses three key properties to perform its validation.
      + * The {@link setMinValue MinValue} and {@link setMaxValue MaxValue}
      + * properties specify the minimum and maximum values of the valid range.
      + * The {@link setDataType DataType} property is used to specify the
      + * data type of the value and the minimum and maximum range values.
      + * These values are converted to this data type before the validation
      + * operation is performed. The following value types are supported:
      + * - Integer A 32-bit signed integer data type.
      + * - Float A double-precision floating point number data type.
      + * - Date A date data type. The date format can be specified by
      + *   setting {@link setDateFormat DateFormat} property, which must be recognizable
      + *   by {@link TSimpleDateFormatter}. If the property is not set,
      + *   the GNU date syntax is assumed.
      + * - String A string data type.
      + * - StringLength check for string length.
      + *
      + * If {@link setStrictComparison StrictComparison} is true, then the ranges
      + * are compared as strictly less than the max value and/or strictly greater than the min value.
      + *
      + * The TRangeValidator allows a special DataType "StringLength" that
      + * can be used to verify minimum and maximum string length. The
      + * {@link setCharset Charset} property can be used to force a particular
      + * charset for comparison. Otherwise, the application charset is used and is
      + * defaulted as UTF-8.
      + *
      + * @author Qiang Xue 
      + * @version $Id: TRangeValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TRangeValidator extends TBaseValidator
      +{
      +	/**
      +	 * Gets the name of the javascript class responsible for performing validation for this control.
      +	 * This method overrides the parent implementation.
      +	 * @return string the javascript class name
      +	 */
      +	protected function getClientClassName()
      +	{
      +		return 'Prado.WebUI.TRangeValidator';
      +	}
      +
      +	/**
      +	 * @return string the minimum value of the validation range.
      +	 */
      +	public function getMinValue()
      +	{
      +		return $this->getViewState('MinValue','');
      +	}
      +
      +	/**
      +	 * Sets the minimum value of the validation range.
      +	 * @param string the minimum value
      +	 */
      +	public function setMinValue($value)
      +	{
      +		$this->setViewState('MinValue',TPropertyValue::ensureString($value),'');
      +	}
      +
      +	/**
      +	 * @return string the maximum value of the validation range.
      +	 */
      +	public function getMaxValue()
      +	{
      +		return $this->getViewState('MaxValue','');
      +	}
      +
      +	/**
      +	 * Sets the maximum value of the validation range.
      +	 * @param string the maximum value
      +	 */
      +	public function setMaxValue($value)
      +	{
      +		$this->setViewState('MaxValue',TPropertyValue::ensureString($value),'');
      +	}
      +
      +	/**
      +	 * @param boolean true to perform strict comparison (i.e. strictly less than max and/or strictly greater than min).
      +	 */
      +	public function setStrictComparison($value)
      +	{
      +		$this->setViewState('StrictComparison', TPropertyValue::ensureBoolean($value),false);
      +	}
      +
      +	/**
      +	 * @return boolean true to perform strict comparison.
      +	 */
      +	public function getStrictComparison()
      +	{
      +		return $this->getViewState('StrictComparison', false);
      +	}
      +
      +	/**
      +	 * @return TRangeValidationDataType the data type that the values being compared are
      +	 * converted to before the comparison is made. Defaults to TRangeValidationDataType::String.
      +	 */
      +	public function getDataType()
      +	{
      +		return $this->getViewState('DataType',TRangeValidationDataType::String);
      +	}
      +
      +	/**
      +	 * Sets the data type that the values being compared are converted to before the comparison is made.
      +	 * @param TRangeValidationDataType the data type
      +	 */
      +	public function setDataType($value)
      +	{
      +		$this->setViewState('DataType',TPropertyValue::ensureEnum($value,'TRangeValidationDataType'),TRangeValidationDataType::String);
      +	}
      +
      +	/**
      +     * Sets the date format for a date validation
      +     * @param string the date format value
      +     */
      +	public function setDateFormat($value)
      +	{
      +		$this->setViewState('DateFormat', $value, '');
      +	}
      +
      +	/**
      +	 * @return string the date validation date format if any
      +	 */
      +	public function getDateFormat()
      +	{
      +		return $this->getViewState('DateFormat', '');
      +	}
      +
      +	/**
      +	 * @param string charset for string length comparison.
      +	 */
      +	public function setCharset($value)
      +	{
      +		$this->setViewState('Charset', $value, '');
      +	}
      +
      +	/**
      +	 * @return string charset for string length comparison.
      +	 */
      +	public function getCharset()
      +	{
      +		return $this->getViewState('Charset', '');
      +	}
      +
      +	/**
      +	 * This method overrides the parent's implementation.
      +	 * The validation succeeds if the input data is within the range.
      +	 * The validation always succeeds if the input data is empty.
      +	 * @return boolean whether the validation succeeds
      +	 */
      +	protected function evaluateIsValid()
      +	{
      +		$value=$this->getValidationValue($this->getValidationTarget());
      +		if($value==='')
      +			return true;
      +
      +		switch($this->getDataType())
      +		{
      +			case TRangeValidationDataType::Integer:
      +				return $this->isValidInteger($value);
      +			case TRangeValidationDataType::Float:
      +				return $this->isValidFloat($value);
      +			case TRangeValidationDataType::Date:
      +				return $this->isValidDate($value);
      +			case TRangeValidationDataType::StringLength:
      +				return $this->isValidStringLength($value);
      +			default:
      +				return $this->isValidString($value);
      +		}
      +	}
      +
      +	/**
      +	* Determine if the value is within the integer range.
      +	* @param string value to validate true
      +	* @return boolean true if within integer range.
      +	*/
      +	protected function isValidInteger($value)
      +	{
      +		$minValue=$this->getMinValue();
      +		$maxValue=$this->getMaxValue();
      +
      +		$valid=preg_match('/^[-+]?[0-9]+$/',trim($value));
      +		$value=intval($value);
      +		if($minValue!=='')
      +			$valid=$valid && $this->isGreaterThan($value, intval($minValue));
      +		if($maxValue!=='')
      +			$valid=$valid && $this->isLessThan($value,intval($maxValue));
      +		return $valid;
      +	}
      +
      +	protected function isLessThan($left,$right)
      +	{
      +		return $this->getStrictComparison() ? $left < $right : $left <= $right;
      +	}
      +
      +	protected function isGreaterThan($left, $right)
      +	{
      +		return $this->getStrictComparison() ? $left > $right : $left >= $right;
      +	}
      +
      +	/**
      +	 * Determine if the value is within the specified float range.
      +	 * @param string value to validate
      +	 * @return boolean true if within range.
      +	 */
      +	protected function isValidFloat($value)
      +	{
      +		$minValue=$this->getMinValue();
      +		$maxValue=$this->getMaxValue();
      +
      +		$valid=preg_match('/^[-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?$/',trim($value));
      +		$value=floatval($value);
      +		if($minValue!=='')
      +			$valid=$valid && $this->isGreaterThan($value,floatval($minValue));
      +		if($maxValue!=='')
      +			$valid=$valid && $this->isLessThan($value,floatval($maxValue));
      +		return $valid;
      +	}
      +
      +	/**
      +	 * Determine if the date is within the specified range.
      +	 * Uses pradoParseDate and strtotime to get the date from string.
      +	 * @param string date as string to validate
      +	 * @return boolean true if within range.
      +	 */
      +	protected function isValidDate($value)
      +	{
      +		$minValue=$this->getMinValue();
      +		$maxValue=$this->getMaxValue();
      +
      +		$valid=true;
      +
      +		$dateFormat = $this->getDateFormat();
      +		if($dateFormat!=='')
      +		{
      +			$formatter=Prado::createComponent('System.Util.TSimpleDateFormatter', $dateFormat);
      +			$value = $formatter->parse($value);
      +			if($minValue!=='')
      +				$valid=$valid && $this->isGreaterThan($value,$formatter->parse($minValue));
      +			if($maxValue!=='')
      +				$valid=$valid && $this->isLessThan($value,$formatter->parse($maxValue));
      +			return $valid;
      +		}
      +		else
      +		{
      +			$value=strtotime($value);
      +			if($minValue!=='')
      +				$valid=$valid && $this->isGreaterThan($value,strtotime($minValue));
      +			if($maxValue!=='')
      +				$valid=$valid && $this->isLessThan($value,strtotime($maxValue));
      +			return $valid;
      +		}
      +	}
      +
      +	/**
      +	 * Compare the string with a minimum and a maxiumum value.
      +	 * Uses strcmp for comparision.
      +	 * @param string value to compare with.
      +	 * @return boolean true if the string is within range.
      +	 */
      +	protected function isValidString($value)
      +	{
      +		$minValue=$this->getMinValue();
      +		$maxValue=$this->getMaxValue();
      +
      +		$valid=true;
      +		if($minValue!=='')
      +			$valid=$valid && $this->isGreaterThan(strcmp($value,$minValue),0);
      +		if($maxValue!=='')
      +			$valid=$valid && $this->isLessThan(strcmp($value,$maxValue),0);
      +		return $valid;
      +	}
      +
      +	/**
      +	 * @param string string for comparision
      +	 * @return boolean true if min and max string length are satisfied.
      +	 */
      +	protected function isValidStringLength($value)
      +	{
      +		$minValue=$this->getMinValue();
      +		$maxValue=$this->getMaxValue();
      +
      +		$valid=true;
      +		$charset = $this->getCharset();
      +		if($charset==='')
      +		{
      +			$app= $this->getApplication()->getGlobalization();
      +			$charset = $app ? $app->getCharset() : null;
      +			if(!$charset)
      +				$charset = 'UTF-8';
      +		}
      +
      +		$length = iconv_strlen($value, $charset);
      +		if($minValue!=='')
      +			$valid = $valid && $this->isGreaterThan($length,intval($minValue));
      +		if($maxValue!=='')
      +			$valid = $valid && $this->isLessThan($length,intval($maxValue));
      +		return $valid;
      +	}
      +
      +	/**
      +	 * Returns an array of javascript validator options.
      +	 * @return array javascript validator options.
      +	 */
      +	protected function getClientScriptOptions()
      +	{
      +		$options=parent::getClientScriptOptions();
      +		$options['MinValue']=$this->getMinValue();
      +		$options['MaxValue']=$this->getMaxValue();
      +		$options['DataType']=$this->getDataType();
      +		$options['StrictComparison']=$this->getStrictComparison();
      +		if(($dateFormat=$this->getDateFormat())!=='')
      +			$options['DateFormat']=$dateFormat;
      +		return $options;
      +	}
      +}
      +
      +
      +/**
      + * TRangeValidationDataType class.
      + * TRangeValidationDataType defines the enumerable type for the possible data types that
      + * a range validator can validate upon.
      + *
      + * The following enumerable values are defined:
      + * - Integer
      + * - Float
      + * - Date
      + * - String
      + * - StringLength
      + *
      + * @author Qiang Xue 
      + * @version $Id: TRangeValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0.4
      + */
      +class TRangeValidationDataType extends TValidationDataType
      +{
      +	const StringLength='StringLength';
      +}
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TRatingList.php b/gui/baculum/framework/Web/UI/WebControls/TRatingList.php
      new file mode 100644
      index 0000000000..f4d4adcf53
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TRatingList.php
      @@ -0,0 +1,359 @@
      +
      + * @link http://www.pradosoft.com/
      + * @copyright Copyright © 2005-2013 PradoSoft
      + * @license http://www.pradosoft.com/license/
      + * @version $Id: TRatingList.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + */
      +
      +/**
      + * Includes TRadioButtonList class
      + */
      +Prado::using('System.Web.UI.WebControls.TRadioButtonList');
      +
      +/**
      + * TRatingList class.
      + *
      + * This class is EXPERIMENTAL.
      + *
      + * @author Wei Zhuo 
      + * @author Bradley Booms 
      + * @version $Id: TRatingList.php 3245 2013-01-07 20:23:32Z ctrlaltca $
      + * @package System.Web.UI.WebControls
      + * @since 3.0
      + */
      +class TRatingList extends TRadioButtonList
      +{
      +	/**
      +	 * Script path relative to the TClientScriptManager::SCRIPT_PATH
      +	 */
      +	const SCRIPT_PATH='prado/ratings';
      +
      +	/**
      +	 * @var array list of published rating images.
      +	 */
      +	private $_ratingImages = array();
      +
      +	/**
      +	 * Sets the default repeat direction to horizontal.
      +	 */
      +	public function __construct()
      +	{
      +		parent::__construct();
      +		$this->setRepeatDirection(TRepeatDirection::Horizontal);
      +	}
      +
      +	/**
      +	 * @return boolean whether the items in the column can be edited. Defaults to false.
      +	 */
      +	public function getReadOnly()
      +	{
      +		return $this->getViewState('ReadOnly',false);
      +	}
      +
      +	/**
      +	 * @param boolean whether the items in the column can be edited
      +	 */
      +	public function setReadOnly($value)
      +	{
      +		$this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false);
      +	}
      +
      +	/**
      +	 * Wrapper for {@link setReadOnly ReadOnly} property.
      +	 * @return boolean whether the rating list can be edited. Defaults to true.
      +	 */
      +	public function getAllowInput()
      +	{
      +		return !$this->getReadOnly();
      +	}
      +
      +	/**
      +	 * Wrapper for {@link setReadOnly ReadOnly} property.
      +	 * @param boolean whether the rating list can be edited
      +	 */
      +	public function setAllowInput($value)
      +	{
      +		$this->setReadOnly(!TPropertyValue::ensureBoolean($value));
      +	}
      +
      +	/**
      +	 * Wrapper for {@link setReadOnly ReadOnly} property.
      +	 * @param boolean whether the rating list can be edited
      +	 */
      +	public function setEnabled($value)
      +	{
      +		$this->setReadOnly(!TPropertyValue::ensureBoolean($value));
      +	}
      +
      +	/**
      +	 * The repeat layout must be Table.
      +	 * @param string repeat layout type
      +	 * @throws TInvaliddataValueException when repeat layout is not Table.
      +	 */
      +	public function setRepeatLayout($value)
      +	{
      +		if($value!==TRepeatLayout::Table)
      +			throw new TInvalidDataValueException('ratinglist_table_layout_only');
      +		else
      +			parent::setRepeatLayout($value);
      +	}
      +
      +	/**
      +	 * @return float rating value.
      +	 */
      +	public function getRating()
      +	{
      +		$rating = $this->getViewState('Rating', null);
      +		if ($rating === null)
      +			return $this->getSelectedIndex()+1;
      +		else
      +			return $rating;
      +	}
      +
      +	/**
      +	 * @param float rating value, also sets the selected Index
      +	 */
      +	public function setRating($value)
      +	{
      +		$value = TPropertyValue::ensureFloat($value);
      +		$this->setViewState('Rating', $value, null);
      +		$index = $this->getRatingIndex($value);
      +		parent::setSelectedIndex($index);
      +	}
      +	
      +	public function setSelectedIndex($value)
      +	{
      +		$this->setRating($value+1);
      +		parent::setSelectedIndex($value);
      +	}
      +
      +	/**
      +	 * @param float rating value
      +	 * @return int rating as integer
      +	 */
      +	protected function getRatingIndex($rating)
      +	{
      +		$interval = $this->getHalfRatingInterval();
      +		$base = intval($rating)-1;
      +		$remainder = $rating-$base-1;
      +		return $remainder > $interval[1] ? $base+1 : $base;
      +	}
      +
      +	/**
      +	 * @param int change the rating selection index
      +	 */
      +	public function onSelectedIndexChanged($param)
      +	{
      +		$value = $this->getRating();
      +		$value = TPropertyValue::ensureInteger($value);
      +		$this->setRating($value);
      +		parent::onSelectedIndexChanged($param);
      +	}
      +
      +	/**
      +	 * @return string control or html element ID for displaying a caption.
      +	 */
      +	public function getCaptionID()
      +	{
      +		return $this->getViewState('CaptionID', '');
      +	}
      +
      +	/**
      +	 * @param string control or html element ID for displaying a caption.
      +	 */
      +	public function setCaptionID($value)
      +	{
      +		$this->setViewState('CaptionID', $value, '');
      +	}
      +
      +	protected function getCaptionControl()
      +	{
      +		if(($id=$this->getCaptionID())!=='')
      +		{
      +			if($control=$this->getParent()->findControl($id))
      +				return $control;
      +		}
      +		throw new TInvalidDataValueException(
      +			'ratinglist_invalid_caption_id',$id,$this->getID());
      +	}
      +
      +	/**
      +	 * @return string caption text. Default is "Rate It:".
      +	 */
      +	public function getCaption()
      +	{
      +		return $this->getCaptionControl()->getText();
      +	}
      +
      +	/**
      +	 * @return TRatingListStyle current rating style
      +	 */
      + 	public function setCaption($value)
      + 	{
      +		$this->getCaptionControl()->setText($value);
      + 	}
      +
      +	/**
      +	 * @param string set the rating style, default is "default"
      +	 */
      +	public function setRatingStyle($value)
      + 	{
      +	   $this->setViewState('RatingStyle', $value, 'default');
      + 	}
      +
      +	/**
      +	 * @return TRatingListStyle current rating style
      +	 */
      +	public function getRatingStyle()
      + 	{
      +	   return $this->getViewState('RatingStyle', 'default');
      + 	}
      + 
      + 	/**
      +	 * @return string rating style css class name.
      + 	 */
      +	protected function getRatingStyleCssClass()
      + 	{
      +		return 'TRatingList_'.$this->getRatingStyle();
      + 	}
      +
      +	/**
      +	 * Sets the interval such that those rating values within the interval
      +	 * will be considered as a half star rating.
      +	 * @param array rating display half value interval, default is array(0.3, 0.7);
      +	 */
      +	public function setHalfRatingInterval($value)
      + 	{
      +		$this->setViewState('HalfRating',
      +				TPropertyValue::ensureArray($value), array(0.3, 0.7));
      + 	}
      +
      +	/**
      +	 * @return array rating display half value interval, default is array(0.3, 0.7);
      +	 */
      +	public function getHalfRatingInterval()
      + 	{
      +		return $this->getViewState('HalfRating', array(0.3, 0.7));
      + 	}
      +
      +	/**
      +	 * @return array list of post back options.
      +	 */
      +	protected function getPostBackOptions()
      + 	{
      +		$options = parent::getPostBackOptions();
      +		$options['AutoPostBack'] = $this->getAutoPostBack();
      +		$options['ReadOnly'] = $this->getReadOnly();
      +		$options['Style'] = $this->getRatingStyleCssClass();
      +		$options['CaptionID'] = $this->getCaptionControlID();
      +		$options['SelectedIndex'] = $this->getSelectedIndex();
      +		$options['Rating'] = $this->getRating();
      +		$options['HalfRating'] = $this->getHalfRatingInterval();
      +		return $options;
      + 	}
      +
      + 	/**
      +	 * @return string find the client ID of the caption control.
      + 	 */
      +	protected function getCaptionControlID()
      + 	{
      +		if(($id=$this->getCaptionID())!=='')
      + 		{
      +			if($control=$this->getParent()->findControl($id))
      +			{
      +				if($control->getVisible(true))
      +					return $control->getClientID();
      +			}
      +			else
      +				return $id;
      + 		}
      +		return '';
      + 	}
      +
      +	/**
      +	 * Publish the the rating style css file and rating image files.
      +	 */
      +	public function onPreRender($param)
      + 	{
      +		parent::onPreRender($param);
      +		$this->publishStyle($this->getRatingStyle());
      +		$this->_ratingImages = $this->publishImages($this->getRatingStyle());
      + 		$this->registerClientScript();
      +	}
      +
      +	/**
      +	 * @param string rating style name
      +	 * @return string URL of the css style file
      +	 */
      +	protected function publishStyle($style)
      + 	{
      +		$cs = $this->getPage()->getClientScript();
      +		$url = $this->getAssetUrl($style.'.css');
      +		if(!$cs->isStyleSheetFileRegistered($url))
      +			$cs->registerStyleSheetFile($url, $url);
      +		return $url;
      + 	}
      +
      +	/**
      +	 * @param string rating style name
      +	 * @param string rating image file extension, default is '.gif'
      +	 * @return array URL of publish the rating images
      +	 */
      +	protected function publishImages($style, $fileExt='.gif')
      + 	{
      +		$types = array('blank', 'selected', 'half', 'combined');
      +		$files = array();
      +		foreach($types as $type)
      +			$files[$type] = $this->getAssetUrl("{$style}_{$type}{$fileExt}");
      +		return $files;
      + 	}
      +
      +	/**
      +	 * Registers the relevant JavaScript.
      +	 */
      +	protected function registerClientScript()
      +	{
      +		$cs=$this->getPage()->getClientScript();
      +		$cs->registerPradoScript('ratings');
      +	}
      +
      +	/**
      +	 * @param string asset file in the self::SCRIPT_PATH directory.
      +	 * @return string asset file url.
      +	 */
      +	protected function getAssetUrl($file='')
      + 	{
      +		$base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl();
      +		return $base.'/'.self::SCRIPT_PATH.'/'.$file;
      + 	}
      +
      +	/**
      +	 * Add rating style class name to the class attribute
      +	 * when {@link setReadOnly ReadOnly} property is true and when the
      +	 * {@link setCssClass CssClass} property is empty.
      +	 * @param THtmlWriter renderer
      +	 */
      +	public function render($writer)
      + 	{
      +		$writer->addAttribute('id',$this->getClientID());
      +		$this->getPage()->getClientScript()->registerPostBackControl(
      +			$this->getClientClassName(), $this->getPostBackOptions());
      +		parent::render($writer);
      + 	}
      +
      +	/**
      +	 * Gets the name of the javascript class responsible for performing postback for this control.
      +	 * This method overrides the parent implementation.
      +	 * @return string the javascript class name
      +	 */
      +	protected function getClientClassName()
      +	{
      +		return 'Prado.WebUI.TRatingList';
      +	}
      +}
      +
      diff --git a/gui/baculum/framework/Web/UI/WebControls/TReCaptcha.php b/gui/baculum/framework/Web/UI/WebControls/TReCaptcha.php
      new file mode 100644
      index 0000000000..31fcf1ca81
      --- /dev/null
      +++ b/gui/baculum/framework/Web/UI/WebControls/TReCaptcha.php
      @@ -0,0 +1,276 @@
      +
      + * @link http://www.devworx.hu/
      + * @copyright Copyright © 2011 DevWorx
      + * @license http://www.pradosoft.com/license/
      + * @package System.Web.UI.WebControls
      + */
      +
      +	Prado::using('System.3rdParty.ReCaptcha.recaptchalib');
      +
      +/**
      + * TReCaptcha class.
      + *
      + * TReCaptcha displays a reCAPTCHA (a token displayed as an image) that can be used
      + * to determine if the input is entered by a real user instead of some program. It can
      + * also prevent multiple submits of the same form either by accident, or on purpose (ie. spamming).
      + *
      + * The reCAPTCHA to solve (a string consisting of two separate words) displayed is automatically
      + * generated by the reCAPTCHA system at recaptcha.net. However, in order to use the services
      + * of the site you will need to register and get a public and a private API key pair, and 
      + * supply those to the reCAPTCHA control through setting the {@link setPrivateKey PrivateKey} 
      + * and {@link setPublicKey PublicKey} properties. 
      + *
      + * Currently the reCAPTCHA API supports only one reCAPTCHA field per page, so you MUST make sure that all 
      + * your input is protected and validated by a single reCAPTCHA control. Placing more than one reCAPTCHA
      + * control on the page will lead to unpredictable results, and the user will most likely unable to solve 
      + * any of them successfully.
      + *
      + * Upon postback, user input can be validated by calling {@link validate()}.
      + * The {@link TReCaptchaValidator} control can also be used to do validation, which provides
      + * server-side validation. Calling (@link validate()) will invalidate the token supplied, so all consecutive
      + * calls to the method - without solving a new captcha - will return false. Therefore if implementing a multi-stage
      + * input process, you must make sure that you call validate() only once, either at the end of the input process, or 
      + * you store the result till the end of the processing.
      + *
      + * The following template shows a typical use of TReCaptcha control:
      + * 
      + * 
      + * 
      + * 
      + *
      + * @author Bérczi Gábor 
      + * @package System.Web.UI.WebControls
      + * @since 3.2
      + */
      +class TReCaptcha extends TWebControl implements IValidatable
      +{
      +	private $_isValid=true;
      +
      +	const ChallengeFieldName = 'recaptcha_challenge_field';
      +	const ResponseFieldName = 'recaptcha_response_field';
      +
      +	public function getTagName()
      +	{
      +		return 'span';
      +	}
      +	
      +	/**
      +	 * Returns true if this control validated successfully. 
      +	 * Defaults to true.
      +	 * @return bool wether this control validated successfully.
      +	 */
      +	public function getIsValid()
      +	{
      +		return $this->_isValid;
      +	}
      +	/**
      +	 * @param bool wether this control is valid.
      +	 */
      +	public function setIsValid($value)
      +	{
      +		$this->_isValid=TPropertyValue::ensureBoolean($value);
      +	}
      +	
      +	public function getValidationPropertyValue()
      +	{
      +		return $this->Request[$this->getChallengeFieldName()];
      +	}
      +
      +	public function getPublicKey()
      +	{
      +		return $this->getViewState('PublicKey');
      +	}
      +
      +	public function setPublicKey($value)
      +	{
      +		return $this->setViewState('PublicKey', TPropertyValue::ensureString($value));
      +	}
      +
      +	public function getPrivateKey()
      +	{
      +		return $this->getViewState('PrivateKey');
      +	}
      +
      +	public function setPrivateKey($value)
      +	{
      +		return $this->setViewState('PrivateKey', TPropertyValue::ensureString($value));
      +	}
      +	
      +	public function getThemeName()
      +	{
      +		return $this->getViewState('ThemeName');
      +	}
      +
      +	public function setThemeName($value)
      +	{
      +		return $this->setViewState('ThemeName', TPropertyValue::ensureString($value));
      +	}
      +
      +	public function getCustomTranslations()
      +	{
      +		return TPropertyValue::ensureArray($this->getViewState('CustomTranslations'));
      +	}
      +
      +	public function setCustomTranslations($value)
      +	{
      +		return $this->setViewState('CustomTranslations', TPropertyValue::ensureArray($value));
      +	}
      +
      +	public function getLanguage()
      +	{
      +		return $this->getViewState('Language');
      +	}
      +
      +	public function setLanguage($value)
      +	{
      +		return $this->setViewState('Language', TPropertyValue::ensureString($value));
      +	}
      +
      +	public function getCallbackScript()
      +	{
      +		return $this->getViewState('CallbackScript');
      +	}
      +
      +	public function setCallbackScript($value)
      +	{
      +		return $this->setViewState('CallbackScript', TPropertyValue::ensureString($value));
      +	}
      +
      +	protected function getChallengeFieldName()
      +	{
      +		return /*$this->ClientID.'_'.*/self::ChallengeFieldName;
      +	}
      +	
      +	public function getResponseFieldName()
      +	{
      +		return /*$this->ClientID.'_'.*/self::ResponseFieldName;
      +	}
      +	
      +	public function getClientSideOptions()
      +	{
      +		$options = array();
      +		if ($theme = $this->getThemeName())
      +			$options['theme'] = $theme;
      +		if ($lang = $this->getLanguage())
      +			$options['lang'] = $lang;
      +		if ($trans = $this->getCustomTranslations())
      +			$options['custom_translations'] = $trans;
      +		return $options;
      +	}
      +
      +	public function validate()
      +	{
      +		if (!
      +		      (
      +			($challenge = @$_POST[$this->getChallengeFieldName()])
      +			and
      +			($response = @$_POST[$this->getResponseFieldName()])
      +		      )
      +                   )
      +		   return false;
      +
      +		$resp = recaptcha_check_answer(
      +			$this->getPrivateKey(),
      +			$_SERVER["REMOTE_ADDR"],
      +			$challenge,
      +			$response
      +		); 
      +		return ($resp->is_valid==1);
      +	}
      +
      +	/**
      +	 * Checks for API keys
      +	 * @param mixed event parameter
      +	 */
      +	public function onPreRender($param)
      +	{
      +		parent::onPreRender($param);
      +
      +		if("" == $this->getPublicKey())
      +			throw new TConfigurationException('recaptcha_publickey_unknown');
      +		if("" == $this->getPrivateKey())
      +			throw new TConfigurationException('recaptcha_privatekey_unknown');
      +
      +		// need to register captcha fields so they will be sent back also in callbacks 
      +		$page = $this->getPage();
      +		$page->registerRequiresPostData($this->getChallengeFieldName());
      +		$page->registerRequiresPostData($this->getResponseFieldName());
      +	}
      +
      +	protected function addAttributesToRender($writer)
      +	{
      +		parent::addAttributesToRender($writer);
      +		$writer->addAttribute('id',$this->getClientID());
      +	}
      +
      +	public function regenerateToken()
      +	{
      +		// if we're in a callback, then schedule re-rendering of the control 
      +		// if not, don't do anything, because a new challenge will be rendered anyway
      +		if ($this->Page->IsCallback)
      +			$this->Page->ClientScript->registerEndScript($this->getClientID().'::refresh', implode(' ', array(
      +				// work-around for "ReCaptchaState is undefined" bug 
      +				// (if there's no previous instance yet, regenerating the token is not needed anyway)
      +				'if (typeof ReCaptchaState != "undefined") '.
      +				'  Recaptcha.reload();',
      +			)));
      +	}
      +
      +	public function renderContents($writer)
      +	{
      +		$readyscript = 'Event.fire(document, '.TJavaScript::quoteString('captchaready:'.$this->getClientID()).')';
      +		$cs = $this->Page->ClientScript;
      +		$id = $this->getClientID();
      +		$divid = $id.'_1_recaptchadiv';
      +		$writer->write('
      '); + + if (!$this->Page->IsCallback) + { + $writer->write(TJavaScript::renderScriptBlock( + 'var RecaptchaOptions = '.TJavaScript::jsonEncode($this->getClientSideOptions()).';' + )); + + $html = recaptcha_get_html($this->getPublicKey()); + /* + reCAPTCHA currently does not support multiple validations per page + $html = str_replace( + array(self::ChallengeFieldName,self::ResponseFieldName), + array($this->getChallengeFieldName(),$this->getResponseFieldName()), + $html + ); + */ + $writer->write($html); + + $cs->registerEndScript('ReCaptcha::EventScript', 'Event.observe(document, "dom:loaded", function() { '.$readyscript.'; } );'); + } + else + { + $options = $this->getClientSideOptions(); + $options['callback'] = new TJavaScriptLiteral('function() { '.$readyscript.'; '.$this->getCallbackScript().'; }'); + $cs->registerScriptFile('ReCaptcha::AjaxScript', 'http://www.google.com/recaptcha/api/js/recaptcha_ajax.js'); + $cs->registerEndScript('ReCaptcha::CreateScript::'.$id, implode(' ', array( + 'if (!$('.TJavaScript::quoteString($this->getResponseFieldName()).'))', + '{', + 'Recaptcha.destroy();', + 'Recaptcha.create(', + TJavaScript::quoteString($this->getPublicKey()).', ', + TJavaScript::quoteString($divid).', ', + TJavaScript::encode($options), + ');', + '}', + ))); + } + + $writer->write('
      '); + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TReCaptchaValidator.php b/gui/baculum/framework/Web/UI/WebControls/TReCaptchaValidator.php new file mode 100644 index 0000000000..bba356b861 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TReCaptchaValidator.php @@ -0,0 +1,139 @@ + + * @link http://www.devworx.hu/ + * @copyright Copyright © 2011 DevWorx + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TBaseValidator'); +Prado::using('System.Web.UI.WebControls.TReCaptcha'); + +/** + * TReCaptchaValidator class + * + * TReCaptchaValidator validates user input against a reCAPTCHA represented by + * a {@link TReCaptcha} control. The input control fails validation if its value + * is not the same as the token displayed in reCAPTCHA. Note, if the user does + * not enter any thing, it is still considered as failing the validation. + * + * To use TReCaptchaValidator, specify the {@link setControlToValidate ControlToValidate} + * to be the ID path of the {@link TReCaptcha} control. + * + * @author Bérczi Gábor + * @package System.Web.UI.WebControls + * @since 3.2 + */ +class TReCaptchaValidator extends TBaseValidator +{ + protected $_isvalid = null; + + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TReCaptchaValidator'; + } + + public function getEnableClientScript() + { + return true; + } + + protected function getCaptchaControl() + { + $control = $this->getValidationTarget(); + if (!$control) + throw new Exception('No target control specified for TReCaptchaValidator'); + if (!($control instanceof TReCaptcha)) + throw new Exception('TReCaptchaValidator only works with TReCaptcha controls'); + return $control; + } + + public function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['ResponseFieldName'] = $this->getCaptchaControl()->getResponseFieldName(); + return $options; + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input control has the same value + * as the one displayed in the corresponding RECAPTCHA control. + * + * @return boolean whether the validation succeeds + */ + protected function evaluateIsValid() + { + // check validity only once (if trying to evaulate multiple times, all redundant checks would fail) + if (is_null($this->_isvalid)) + { + $control = $this->getCaptchaControl(); + $this->_isvalid = $control->validate(); + } + return ($this->_isvalid==true); + } + + public function onPreRender($param) + { + parent::onPreRender($param); + + $cs = $this->Page->getClientScript(); + $cs->registerPradoScript('validator'); + + // communicate validation status to the client side + $value = $this->_isvalid===false ? '0' : '1'; + $cs->registerHiddenField($this->getClientID().'_1',$value); + + // update validator display + if ($control = $this->getValidationTarget()) + { + $fn = 'captchaUpdateValidatorStatus_'.$this->getClientID(); + + // check if we need to request a new captcha too + if ($this->Page->IsCallback) + { + if ($control->getVisible(true)) + if (!is_null($this->_isvalid)) + { + // if the response has been tested and we reach the pre-render phase + // then we need to regenerate the token, because it won't test positive + // anymore, even if solves correctly + + $control->regenerateToken(); + } + } + + $cs->registerEndScript($this->getClientID().'::validate', implode(' ',array( + // this function will be used to update the validator + 'function '.$fn.'(valid)', + '{', + ' var v = $('.TJavaScript::quoteString($this->getClientID()).');', + ' $('.TJavaScript::quoteString($this->getClientID().'_1').').value = valid;', + ' Prado.Validation.validateControl('.TJavaScript::quoteString($control->ClientID).'); ', + '}', + '', + // update the validator to the result if we're in a callback + // (if we're in initial rendering or a postback then the result will be rendered directly to the page html anyway) + $this->Page->IsCallback ? $fn.'('.$value.');' : '', + '', + // wait for the captcha to be constructed + 'Event.observe(document,"captchaready:'.$control->getClientID().'",function() { ', + // install event handler that clears the validation error when user changes the captcha response field + 'Event.observe('.TJavaScript::quoteString($control->getResponseFieldName()).',"keyup",function() { ', + $fn.'("1");', + '});', + '});', + ))); + } + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TRegularExpressionValidator.php b/gui/baculum/framework/Web/UI/WebControls/TRegularExpressionValidator.php new file mode 100644 index 0000000000..890fb56ec6 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TRegularExpressionValidator.php @@ -0,0 +1,144 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TRegularExpressionValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TRegularExpressionValidator class + * + * TRegularExpressionValidator validates whether the value of an associated + * input component matches the pattern specified by a regular expression. + * + * You can specify the regular expression by setting the {@link setRegularExpression RegularExpression} + * property. Some commonly used regular expressions include: + *
      + * French Phone Number: (0( \d|\d ))?\d\d \d\d(\d \d| \d\d )\d\d
      + * French Postal Code: \d{5}
      + * German Phone Number: ((\(0\d\d\) |(\(0\d{3}\) )?\d )?\d\d \d\d \d\d|\(0\d{4}\) \d \d\d-\d\d?)
      + * German Postal Code: (D-)?\d{5}
      + * Email Address: \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
      + * Japanese Phone Number: (0\d{1,4}-|\(0\d{1,4}\) ?)?\d{1,4}-\d{4}
      + * Japanese Postal Code: \d{3}(-(\d{4}|\d{2}))?
      + * P.R.C. Phone Number: (\(\d{3}\)|\d{3}-)?\d{8}
      + * P.R.C. Postal Code: \d{6}
      + * P.R.C. Social Security Number: \d{18}|\d{15}
      + * U.S. Phone Number: ((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}
      + * U.S. ZIP Code: \d{5}(-\d{4})?
      + * U.S. Social Security Number: \d{3}-\d{2}-\d{4}
      + * 
      + * + * Note, the validation succeeds if the associated input control contains empty input. + * Use a {@link TRequiredFieldValidator} to ensure the input is not empty. + * + * @author Qiang Xue + * @version $Id: TRegularExpressionValidator.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRegularExpressionValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TRegularExpressionValidator'; + } + + /** + * @return string the regular expression that determines the pattern used to validate a field. + */ + public function getRegularExpression() + { + return $this->getViewState('RegularExpression',''); + } + + /** + * @param string the regular expression that determines the pattern used to validate a field. + */ + public function setRegularExpression($value) + { + $this->setViewState('RegularExpression',$value,''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input data matches the regular expression. + * The validation always succeeds if ControlToValidate is not specified + * or the regular expression is empty, or the input data is empty. + * @return boolean whether the validation succeeds + */ + public function evaluateIsValid() + { + if(($value=$this->getValidationValue($this->getValidationTarget()))==='') + return true; + if(($expression=addcslashes($this->getRegularExpression(),"/"))!=='') + { + $mods = $this->getPatternModifiers(); + return preg_match("/^$expression\$/{$mods}",$value); + } + else + return true; + } + + /** + * @param string pattern modifiers for server side validation, + * see http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php + */ + public function setPatternModifiers($value) + { + $this->setViewState('PatternModifiers', $value); + } + + /** + * @return string pattern modifiers, no modifiers by default. + */ + public function getPatternModifiers() + { + return $this->getViewState('PatternModifiers', ''); + } + + /** + * @param string pattern modifiers for clientside. + * (Only 'g','i' and 'm' are available.) + */ + public function setClientSidePatternModifiers($value) + { + $this->setViewState('ClientSidePatternModifiers', $value); + } + + /** + * @return string clientside pattern modifiers, no modifiers by default. + */ + public function getClientSidePatternModifiers() + { + return $this->getViewState('ClientSidePatternModifiers', ''); + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['ValidationExpression']=$this->getRegularExpression(); + $options['PatternModifiers']=$this->getClientSidePatternModifiers(); + return $options; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TRepeatInfo.php b/gui/baculum/framework/Web/UI/WebControls/TRepeatInfo.php new file mode 100644 index 0000000000..929d31e7fe --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TRepeatInfo.php @@ -0,0 +1,560 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TRepeatInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TTable'); + +/** + * IRepeatInfoUser interface. + * This interface must be implemented by classes who want to use {@link TRepeatInfo}. + * + * @author Qiang Xue + * @version $Id: TRepeatInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +interface IRepeatInfoUser +{ + /** + * @return boolean whether the repeat user contains footer + */ + public function getHasFooter(); + /** + * @return boolean whether the repeat user contains header + */ + public function getHasHeader(); + /** + * @return boolean whether the repeat user contains separators + */ + public function getHasSeparators(); + /** + * @return integer number of items to be rendered (excluding header, footer and separators) + */ + public function getItemCount(); + /** + * @param string item type (Header,Footer,Item,AlternatingItem,SelectedItem,EditItem,Separator,Pager) + * @param integer zero-based index of the current rendering item. + * @return TStyle CSS style used for rendering items (including header, footer and separators) + */ + public function generateItemStyle($itemType,$index); + /** + * Renders an item. + * @param THtmlWriter writer for the rendering purpose + * @param TRepeatInfo repeat information + * @param string item type + * @param integer zero-based index of the item being rendered + */ + public function renderItem($writer,$repeatInfo,$itemType,$index); +} + +/** + * TRepeatInfo class. + * TRepeatInfo represents repeat information for controls like {@link TCheckBoxList}. + * The layout of the repeated items is specified via {@link setRepeatLayout RepeatLayout}, + * which can be either Table (default), Flow or Raw. + * A table layout uses HTML table cells to organize the items while + * a flow layout uses line breaks to organize the items. + * The number of columns used to display the items is specified via + * {@link setRepeatColumns RepeatColumns} property, while the {@link setRepeatDirection RepeatDirection} + * governs the order of the items being rendered. + * + * Note, the Raw layout does not contain any formatting tags and thus ignores + * the column and repeat direction settings. + * + * @author Qiang Xue + * @version $Id: TRepeatInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeatInfo extends TComponent +{ + /** + * @var string caption of the table used to organize the repeated items + */ + private $_caption=''; + /** + * @var TTableCaptionAlign alignment of the caption of the table used to organize the repeated items + */ + private $_captionAlign=TTableCaptionAlign::NotSet; + /** + * @var integer number of columns that the items should be arranged in + */ + private $_repeatColumns=0; + /** + * @var TRepeatDirection direction of the repetition + */ + private $_repeatDirection=TRepeatDirection::Vertical; + /** + * @var TRepeatLayout layout of the repeated items + */ + private $_repeatLayout=TRepeatLayout::Table; + + /** + * @return string caption of the table layout + */ + public function getCaption() + { + return $this->_caption; + } + + /** + * @param string caption of the table layout + */ + public function setCaption($value) + { + $this->_caption=$value; + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->_captionAlign; + } + + /** + * @return TTableCaptionAlign alignment of the caption of the table layout. + */ + public function setCaptionAlign($value) + { + $this->_captionAlign=TPropertyValue::ensureEnum($value,'TTableCaptionAlign'); + } + + /** + * @return integer the number of columns that the repeated items should be displayed in. Defaults to 0, meaning not set. + */ + public function getRepeatColumns() + { + return $this->_repeatColumns; + } + + /** + * @param integer the number of columns that the repeated items should be displayed in. + */ + public function setRepeatColumns($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('repeatinfo_repeatcolumns_invalid'); + $this->_repeatColumns=$value; + } + + /** + * @return TRepeatDirection the direction of traversing the repeated items, defaults to TRepeatDirection::Vertical + */ + public function getRepeatDirection() + { + return $this->_repeatDirection; + } + + /** + * @param TRepeatDirection the direction of traversing the repeated items + */ + public function setRepeatDirection($value) + { + $this->_repeatDirection=TPropertyValue::ensureEnum($value,'TRepeatDirection'); + } + + /** + * @return TRepeatLayout how the repeated items should be displayed, using table or using line breaks. Defaults to TRepeatLayout::Table. + */ + public function getRepeatLayout() + { + return $this->_repeatLayout; + } + + /** + * @param TRepeatLayout how the repeated items should be displayed, using table or using line breaks. + */ + public function setRepeatLayout($value) + { + $this->_repeatLayout=TPropertyValue::ensureEnum($value,'TRepeatLayout'); + } + + /** + * Renders the repeated items. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + public function renderRepeater($writer, IRepeatInfoUser $user) + { + if($this->_repeatLayout===TRepeatLayout::Table) + { + $control=new TTable; + if($this->_caption!=='') + { + $control->setCaption($this->_caption); + $control->setCaptionAlign($this->_captionAlign); + } + } + else if($this->_repeatLayout===TRepeatLayout::Raw) + { + $this->renderRawContents($writer,$user); + return; + } + else + $control=new TWebControl; + $control->setID($user->getClientID()); + $control->copyBaseAttributes($user); + if($user->getHasStyle()) + $control->getStyle()->copyFrom($user->getStyle()); + $control->renderBeginTag($writer); + $writer->writeLine(); + + if($this->_repeatDirection===TRepeatDirection::Vertical) + $this->renderVerticalContents($writer,$user); + else + $this->renderHorizontalContents($writer,$user); + + $control->renderEndTag($writer); + } + + /** + * Renders contents in raw format. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + protected function renderRawContents($writer,$user) + { + if($user->getHasHeader()) + $user->renderItem($writer,$this,'Header',-1); + + // render items + $hasSeparators=$user->getHasSeparators(); + $itemCount=$user->getItemCount(); + for($i=0;$i<$itemCount;++$i) + { + $user->renderItem($writer,$this,'Item',$i); + if($hasSeparators && $i!=$itemCount-1) + $user->renderItem($writer,$this,'Separator',$i); + } + if($user->getHasFooter()) + $user->renderItem($writer,$this,'Footer',-1); + } + + /** + * Renders contents in horizontal repeat direction. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + protected function renderHorizontalContents($writer,$user) + { + $tableLayout=($this->_repeatLayout===TRepeatLayout::Table); + $hasSeparators=$user->getHasSeparators(); + $itemCount=$user->getItemCount(); + $columns=$this->_repeatColumns===0?$itemCount:$this->_repeatColumns; + $totalColumns=$hasSeparators?$columns+$columns:$columns; + $needBreak=$columns<$itemCount; + + if($user->getHasHeader()) + $this->renderHeader($writer,$user,$tableLayout,$totalColumns,$needBreak); + + // render items + if($tableLayout) + { + $writer->renderBeginTag('tbody'); + $column=0; + for($i=0;$i<$itemCount;++$i) + { + if($column==0) + $writer->renderBeginTag('tr'); + if(($style=$user->generateItemStyle('Item',$i))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Item',$i); + $writer->renderEndTag(); + $writer->writeLine(); + if($hasSeparators && $i!=$itemCount-1) + { + if(($style=$user->generateItemStyle('Separator',$i))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Separator',$i); + $writer->renderEndTag(); + $writer->writeLine(); + } + $column++; + if($i==$itemCount-1) + { + $restColumns=$columns-$column; + if($hasSeparators) + $restColumns=$restColumns?$restColumns+$restColumns+1:1; + for($j=0;$j<$restColumns;++$j) + $writer->write("\n"); + } + if($column==$columns || $i==$itemCount-1) + { + $writer->renderEndTag(); + $writer->writeLine(); + $column=0; + } + } + $writer->renderEndTag(); + } + else + { + $column=0; + for($i=0;$i<$itemCount;++$i) + { + $user->renderItem($writer,$this,'Item',$i); + if($hasSeparators && $i!=$itemCount-1) + $user->renderItem($writer,$this,'Separator',$i); + $column++; + if($column==$columns || $i==$itemCount-1) + { + if($needBreak) + $writer->writeBreak(); + $column=0; + } + $writer->writeLine(); + } + } + + if($user->getHasFooter()) + $this->renderFooter($writer,$user,$tableLayout,$totalColumns,$needBreak); + } + + /** + * Renders contents in veritcal repeat direction. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + */ + protected function renderVerticalContents($writer,$user) + { + $tableLayout=($this->_repeatLayout===TRepeatLayout::Table); + $hasSeparators=$user->getHasSeparators(); + $itemCount=$user->getItemCount(); + if($this->_repeatColumns<=1) + { + $rows=$itemCount; + $columns=1; + $lastColumns=1; + } + else + { + $columns=$this->_repeatColumns; + $rows=(int)(($itemCount+$columns-1)/$columns); + if($rows==0 && $itemCount>0) + $rows=1; + if(($lastColumns=$itemCount%$columns)==0) + $lastColumns=$columns; + } + $totalColumns=$hasSeparators?$columns+$columns:$columns; + + if($user->getHasHeader()) + $this->renderHeader($writer,$user,$tableLayout,$totalColumns,false); + + if($tableLayout) + { + $writer->renderBeginTag('tbody'); + $renderedItems=0; + for($row=0;$row<$rows;++$row) + { + $index=$row; + $writer->renderBeginTag('tr'); + for($col=0;$col<$columns;++$col) + { + if($renderedItems>=$itemCount) + break; + if($col>0) + { + $index+=$rows; + if($col-1>=$lastColumns) + $index--; + } + if($index>=$itemCount) + continue; + $renderedItems++; + if(($style=$user->generateItemStyle('Item',$index))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Item',$index); + $writer->renderEndTag(); + $writer->writeLine(); + if(!$hasSeparators) + continue; + if($renderedItems<$itemCount-1) + { + if($columns==1) + { + $writer->renderEndTag(); + $writer->renderBeginTag('tr'); + } + if(($style=$user->generateItemStyle('Separator',$index))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Separator',$index); + $writer->renderEndTag(); + $writer->writeLine(); + } + else if($columns>1) + $writer->write("\n"); + } + if($row==$rows-1) + { + $restColumns=$columns-$lastColumns; + if($hasSeparators) + $restColumns+=$restColumns; + for($col=0;$col<$restColumns;++$col) + $writer->write("\n"); + } + $writer->renderEndTag(); + $writer->writeLine(); + } + $writer->renderEndTag(); + } + else + { + $renderedItems=0; + for($row=0;$row<$rows;++$row) + { + $index=$row; + for($col=0;$col<$columns;++$col) + { + if($renderedItems>=$itemCount) + break; + if($col>0) + { + $index+=$rows; + if($col-1>=$lastColumns) + $index--; + } + if($index>=$itemCount) + continue; + $renderedItems++; + $user->renderItem($writer,$this,'Item',$index); + $writer->writeLine(); + if(!$hasSeparators) + continue; + if($renderedItems<$itemCount-1) + { + if($columns==1) + $writer->writeBreak(); + $user->renderItem($writer,$this,'Separator',$index); + } + $writer->writeLine(); + } + if($row<$rows-1 || $user->getHasFooter()) + $writer->writeBreak(); + } + } + + if($user->getHasFooter()) + $this->renderFooter($writer,$user,$tableLayout,$totalColumns,false); + + } + + /** + * Renders header. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + * @param boolean whether to render using table layout + * @param integer number of columns to be rendered + * @param boolean if a line break is needed at the end + */ + protected function renderHeader($writer,$user,$tableLayout,$columns,$needBreak) + { + if($tableLayout) + { + $writer->renderBeginTag('thead'); + $writer->renderBeginTag('tr'); + if($columns>1) + $writer->addAttribute('colspan',"$columns"); + $writer->addAttribute('scope','col'); + if(($style=$user->generateItemStyle('Header',-1))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('th'); + $user->renderItem($writer,$this,'Header',-1); + $writer->renderEndTag(); + $writer->renderEndTag(); + $writer->renderEndTag(); + } + else + { + $user->renderItem($writer,$this,'Header',-1); + if($needBreak) + $writer->writeBreak(); + } + $writer->writeLine(); + } + + /** + * Renders footer. + * @param THtmlWriter writer for the rendering purpose + * @param IRepeatInfoUser repeat information user + * @param boolean whether to render using table layout + * @param integer number of columns to be rendered + */ + protected function renderFooter($writer,$user,$tableLayout,$columns) + { + if($tableLayout) + { + $writer->renderBeginTag('tfoot'); + $writer->renderBeginTag('tr'); + if($columns>1) + $writer->addAttribute('colspan',"$columns"); + if(($style=$user->generateItemStyle('Footer',-1))!==null) + $style->addAttributesToRender($writer); + $writer->renderBeginTag('td'); + $user->renderItem($writer,$this,'Footer',-1); + $writer->renderEndTag(); + $writer->renderEndTag(); + $writer->renderEndTag(); + } + else + $user->renderItem($writer,$this,'Footer',-1); + $writer->writeLine(); + } +} + + +/** + * TRepeatDirection class. + * TRepeatDirection defines the enumerable type for the possible directions + * that repeated contents can repeat along + * + * The following enumerable values are defined: + * - Vertical + * - Horizontal + * + * @author Qiang Xue + * @version $Id: TRepeatInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TRepeatDirection extends TEnumerable +{ + const Vertical='Vertical'; + const Horizontal='Horizontal'; +} + +/** + * TRepeatLayout class. + * TRepeatLayout defines the enumerable type for the possible layouts + * that repeated contents can take. + * + * The following enumerable values are defined: + * - Table: the repeated contents are organized using an HTML table + * - Flow: the repeated contents are organized using HTML spans and breaks + * - Raw: the repeated contents are stacked together without any additional decorations + * + * @author Qiang Xue + * @version $Id: TRepeatInfo.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TRepeatLayout extends TEnumerable +{ + const Table='Table'; + const Flow='Flow'; + const Raw='Raw'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TRepeater.php b/gui/baculum/framework/Web/UI/WebControls/TRepeater.php new file mode 100644 index 0000000000..c32636fd8e --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TRepeater.php @@ -0,0 +1,1025 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TRepeater.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TDataBoundControl and TDataFieldAccessor cass + */ +Prado::using('System.Web.UI.WebControls.TDataBoundControl'); +Prado::using('System.Util.TDataFieldAccessor'); + +/** + * TRepeater class. + * + * TRepeater displays its content repeatedly based on the data fetched from + * {@link setDataSource DataSource}. + * The repeated contents in TRepeater are called items, which are controls and + * can be accessed through {@link getItems Items}. When {@link dataBind()} is invoked, + * TRepeater creates an item for each row of data and binds the data row to the item. + * Optionally, a repeater can have a header, a footer and/or separators between items. + * + * The layout of the repeated contents are specified by inline templates. + * Repeater items, header, footer, etc. are being instantiated with the corresponding + * templates when data is being bound to the repeater. + * + * Since v3.1.0, the layout can also be specified by renderers. A renderer is a control class + * that can be instantiated as repeater items, header, etc. A renderer can thus be viewed + * as an external template (in fact, it can also be non-templated controls). + * + * A renderer can be any control class. + * - If the class implements {@link IDataRenderer}, the Data + * property will be set as the data row during databinding. Many PRADO controls + * implement this interface, such as {@link TLabel}, {@link TTextBox}, etc. + * - If the class implements {@link IItemDataRenderer}, the ItemIndex property will be set + * as the zero-based index of the item in the repeater item collection, and + * the ItemType property as the item's type (such as TListItemType::Item). + * {@link TRepeaterItemRenderer} may be used as the convenient base class which + * already implements {@link IDataItemRenderer}. + * + * The following properties are used to specify different types of template and renderer + * for a repeater: + * - {@link setItemTemplate ItemTemplate}, {@link setItemRenderer ItemRenderer}: + * for each repeated row of data + * - {@link setAlternatingItemTemplate AlternatingItemTemplate}, {@link setAlternatingItemRenderer AlternatingItemRenderer}: + * for each alternating row of data. If not set, {@link setItemTemplate ItemTemplate} or {@link setItemRenderer ItemRenderer} + * will be used instead. + * - {@link setHeaderTemplate HeaderTemplate}, {@link setHeaderRenderer HeaderRenderer}: + * for the repeater header. + * - {@link setFooterTemplate FooterTemplate}, {@link setFooterRenderer FooterRenderer}: + * for the repeater footer. + * - {@link setSeparatorTemplate SeparatorTemplate}, {@link setSeparatorRenderer SeparatorRenderer}: + * for content to be displayed between items. + * - {@link setEmptyTemplate EmptyTemplate}, {@link setEmptyRenderer EmptyRenderer}: + * used when data bound to the repeater is empty. + * + * If a content type is defined with both a template and a renderer, the latter takes precedence. + * + * When {@link dataBind()} is being called, TRepeater undergoes the following lifecycles for each row of data: + * - create item based on templates or renderers + * - set the row of data to the item + * - raise {@link onItemCreated OnItemCreated}: + * - add the item as a child control + * - call dataBind() of the item + * - raise {@link onItemDataBound OnItemDataBound}: + * + * TRepeater raises an {@link onItemCommand OnItemCommand} whenever a button control + * within some repeater item raises a OnCommand event. Therefore, + * you can handle all sorts of OnCommand event in a central place by + * writing an event handler for {@link onItemCommand OnItemCommand}. + * + * When a page containing a repeater is post back, the repeater will restore automatically + * all its contents, including items, header, footer and separators. + * However, the data row associated with each item will not be recovered and become null. + * To access the data, use one of the following ways: + * - Use {@link getDataKeys DataKeys} to obtain the data key associated with + * the specified repeater item and use the key to fetch the corresponding data + * from some persistent storage such as DB. + * - Save the whole dataset in viewstate, which will restore the dataset automatically upon postback. + * Be aware though, if the size of your dataset is big, your page size will become big. Some + * complex data may also have serializing problem if saved in viewstate. + * + * @author Qiang Xue + * @version $Id: TRepeater.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeater extends TDataBoundControl implements INamingContainer +{ + /** + * Repeater item types + * @deprecated deprecated since version 3.0.4. Use TListItemType constants instead. + */ + const IT_HEADER='Header'; + const IT_FOOTER='Footer'; + const IT_ITEM='Item'; + const IT_SEPARATOR='Separator'; + const IT_ALTERNATINGITEM='AlternatingItem'; + + /** + * @var ITemplate template for repeater items + */ + private $_itemTemplate=null; + /** + * @var ITemplate template for each alternating item + */ + private $_alternatingItemTemplate=null; + /** + * @var ITemplate template for header + */ + private $_headerTemplate=null; + /** + * @var ITemplate template for footer + */ + private $_footerTemplate=null; + /** + * @var ITemplate template used for repeater when no data is bound + */ + private $_emptyTemplate=null; + /** + * @var ITemplate template for separator + */ + private $_separatorTemplate=null; + /** + * @var TRepeaterItemCollection list of repeater items + */ + private $_items=null; + /** + * @var TControl header item + */ + private $_header=null; + /** + * @var TControl footer item + */ + private $_footer=null; + + + /** + * @return string the class name for repeater items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getItemRenderer() + { + return $this->getViewState('ItemRenderer',''); + } + + /** + * Sets the item renderer class. + * + * If not empty, the class will be used to instantiate as repeater items. + * This property takes precedence over {@link getItemTemplate ItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setItemTemplate + * @since 3.1.0 + */ + public function setItemRenderer($value) + { + $this->setViewState('ItemRenderer',$value,''); + } + + /** + * @return string the class name for alternative repeater items. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getAlternatingItemRenderer() + { + return $this->getViewState('AlternatingItemRenderer',''); + } + + /** + * Sets the alternative item renderer class. + * + * If not empty, the class will be used to instantiate as alternative repeater items. + * This property takes precedence over {@link getAlternatingItemTemplate AlternatingItemTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setAlternatingItemTemplate + * @since 3.1.0 + */ + public function setAlternatingItemRenderer($value) + { + $this->setViewState('AlternatingItemRenderer',$value,''); + } + + /** + * @return string the class name for repeater item separators. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getSeparatorRenderer() + { + return $this->getViewState('SeparatorRenderer',''); + } + + /** + * Sets the repeater item separator renderer class. + * + * If not empty, the class will be used to instantiate as repeater item separators. + * This property takes precedence over {@link getSeparatorTemplate SeparatorTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setSeparatorTemplate + * @since 3.1.0 + */ + public function setSeparatorRenderer($value) + { + $this->setViewState('SeparatorRenderer',$value,''); + } + + /** + * @return string the class name for repeater header item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getHeaderRenderer() + { + return $this->getViewState('HeaderRenderer',''); + } + + /** + * Sets the repeater header renderer class. + * + * If not empty, the class will be used to instantiate as repeater header item. + * This property takes precedence over {@link getHeaderTemplate HeaderTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setHeaderTemplate + * @since 3.1.0 + */ + public function setHeaderRenderer($value) + { + $this->setViewState('HeaderRenderer',$value,''); + } + + /** + * @return string the class name for repeater footer item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getFooterRenderer() + { + return $this->getViewState('FooterRenderer',''); + } + + /** + * Sets the repeater footer renderer class. + * + * If not empty, the class will be used to instantiate as repeater footer item. + * This property takes precedence over {@link getFooterTemplate FooterTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setFooterTemplate + * @since 3.1.0 + */ + public function setFooterRenderer($value) + { + $this->setViewState('FooterRenderer',$value,''); + } + + /** + * @return string the class name for empty repeater item. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEmptyRenderer() + { + return $this->getViewState('EmptyRenderer',''); + } + + /** + * Sets the repeater empty renderer class. + * + * The empty renderer is created as the child of the repeater + * if data bound to the repeater is empty. + * This property takes precedence over {@link getEmptyTemplate EmptyTemplate}. + * + * @param string the renderer class name in namespace format. + * @see setEmptyTemplate + * @since 3.1.0 + */ + public function setEmptyRenderer($value) + { + $this->setViewState('EmptyRenderer',$value,''); + } + + /** + * @return ITemplate the template for repeater items + */ + public function getItemTemplate() + { + return $this->_itemTemplate; + } + + /** + * @param ITemplate the template for repeater items + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_itemTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','ItemTemplate'); + } + + /** + * @return ITemplate the alternative template string for the item + */ + public function getAlternatingItemTemplate() + { + return $this->_alternatingItemTemplate; + } + + /** + * @param ITemplate the alternative item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setAlternatingItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_alternatingItemTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','AlternatingItemTemplate'); + } + + /** + * @return ITemplate the header template + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate the header template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setHeaderTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_headerTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','HeaderTemplate'); + } + + /** + * @return ITemplate the footer template + */ + public function getFooterTemplate() + { + return $this->_footerTemplate; + } + + /** + * @param ITemplate the footer template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setFooterTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_footerTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','FooterTemplate'); + } + + /** + * @return ITemplate the template applied when no data is bound to the repeater + */ + public function getEmptyTemplate() + { + return $this->_emptyTemplate; + } + + /** + * @param ITemplate the template applied when no data is bound to the repeater + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEmptyTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_emptyTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','EmptyTemplate'); + } + + /** + * @return ITemplate the separator template + */ + public function getSeparatorTemplate() + { + return $this->_separatorTemplate; + } + + /** + * @param ITemplate the separator template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setSeparatorTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_separatorTemplate=$value; + else + throw new TInvalidDataTypeException('repeater_template_required','SeparatorTemplate'); + } + + /** + * @return TControl the header item + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return TControl the footer item + */ + public function getFooter() + { + return $this->_footer; + } + + /** + * @return TRepeaterItemCollection list of repeater item controls + */ + public function getItems() + { + if(!$this->_items) + $this->_items=new TRepeaterItemCollection; + return $this->_items; + } + + /** + * @return string the field of the data source that provides the keys of the list items. + */ + public function getDataKeyField() + { + return $this->getViewState('DataKeyField',''); + } + + /** + * @param string the field of the data source that provides the keys of the list items. + */ + public function setDataKeyField($value) + { + $this->setViewState('DataKeyField',$value,''); + } + + /** + * @return TList the keys used in the data listing control. + */ + public function getDataKeys() + { + if(($dataKeys=$this->getViewState('DataKeys',null))===null) + { + $dataKeys=new TList; + $this->setViewState('DataKeys',$dataKeys,null); + } + return $dataKeys; + } + + /** + * Creates a repeater item. + * This method invokes {@link createItem} to create a new repeater item. + * @param integer zero-based item index. + * @param TListItemType item type + * @return TControl the created item, null if item is not created + */ + private function createItemInternal($itemIndex,$itemType) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TRepeaterItemEventParameter($item); + $this->onItemCreated($param); + $this->getControls()->add($item); + return $item; + } + else + return null; + } + + /** + * Creates a repeater item and performs databinding. + * This method invokes {@link createItem} to create a new repeater item. + * @param integer zero-based item index. + * @param TListItemType item type + * @param mixed data to be associated with the item + * @return TControl the created item, null if item is not created + */ + private function createItemWithDataInternal($itemIndex,$itemType,$dataItem) + { + if(($item=$this->createItem($itemIndex,$itemType))!==null) + { + $param=new TRepeaterItemEventParameter($item); + if($item instanceof IDataRenderer) + $item->setData($dataItem); + $this->onItemCreated($param); + $this->getControls()->add($item); + $item->dataBind(); + $this->onItemDataBound($param); + return $item; + } + else + return null; + } + + /** + * Creates a repeater item instance based on the item type and index. + * @param integer zero-based item index + * @param TListItemType item type + * @return TControl created repeater item + */ + protected function createItem($itemIndex,$itemType) + { + $template=null; + $classPath=null; + switch($itemType) + { + case TListItemType::Item : + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + break; + case TListItemType::AlternatingItem : + if(($classPath=$this->getAlternatingItemRenderer())==='' && ($template=$this->_alternatingItemTemplate)===null) + { + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + } + break; + case TListItemType::Header : + $classPath=$this->getHeaderRenderer(); + $template=$this->_headerTemplate; + break; + case TListItemType::Footer : + $classPath=$this->getFooterRenderer(); + $template=$this->_footerTemplate; + break; + case TListItemType::Separator : + $classPath=$this->getSeparatorRenderer(); + $template=$this->_separatorTemplate; + break; + default: + throw new TInvalidDataValueException('repeater_itemtype_unknown',$itemType); + } + if($classPath!=='') + { + $item=Prado::createComponent($classPath); + if($item instanceof IItemDataRenderer) + { + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + } + } + else if($template!==null) + { + $item=new TRepeaterItem; + $item->setItemIndex($itemIndex); + $item->setItemType($itemType); + $template->instantiateIn($item); + } + else + $item=null; + + return $item; + } + + /** + * Creates empty repeater content. + */ + protected function createEmptyContent() + { + if(($classPath=$this->getEmptyRenderer())!=='') + $this->getControls()->add(Prado::createComponent($classPath)); + else if($this->_emptyTemplate!==null) + $this->_emptyTemplate->instantiateIn($this); + } + + /** + * Renders the repeater. + * This method overrides the parent implementation by rendering the body + * content as the whole presentation of the repeater. Outer tag is not rendered. + * @param THtmlWriter writer + */ + public function render($writer) + { + if($this->_items && $this->_items->getCount() || $this->_emptyTemplate!==null || $this->getEmptyRenderer()!=='') + $this->renderContents($writer); + } + + /** + * Saves item count in viewstate. + * This method is invoked right before control state is to be saved. + */ + public function saveState() + { + parent::saveState(); + if($this->_items) + $this->setViewState('ItemCount',$this->_items->getCount(),0); + else + $this->clearViewState('ItemCount'); + } + + /** + * Loads item count information from viewstate. + * This method is invoked right after control state is loaded. + */ + public function loadState() + { + parent::loadState(); + if(!$this->getIsDataBound()) + $this->restoreItemsFromViewState(); + $this->clearViewState('ItemCount'); + } + + /** + * Clears up all items in the repeater. + */ + public function reset() + { + $this->getControls()->clear(); + $this->getItems()->clear(); + $this->_header=null; + $this->_footer=null; + } + + /** + * Creates repeater items based on viewstate information. + */ + protected function restoreItemsFromViewState() + { + $this->reset(); + if(($itemCount=$this->getViewState('ItemCount',0))>0) + { + $items=$this->getItems(); + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + $this->_header=$this->createItemInternal(-1,TListItemType::Header); + for($i=0;$i<$itemCount;++$i) + { + if($hasSeparator && $i>0) + $this->createItemInternal($i-1,TListItemType::Separator); + $itemType=$i%2==0?TListItemType::Item : TListItemType::AlternatingItem; + $items->add($this->createItemInternal($i,$itemType,false,null)); + } + $this->_footer=$this->createItemInternal(-1,TListItemType::Footer); + } + else + $this->createEmptyContent(); + $this->clearChildState(); + } + + /** + * Performs databinding to populate repeater items from data source. + * This method is invoked by dataBind(). + * You may override this function to provide your own way of data population. + * @param Traversable the data + */ + protected function performDataBinding($data) + { + $this->reset(); + + $keys=$this->getDataKeys(); + $keys->clear(); + $keyField=$this->getDataKeyField(); + + $items=$this->getItems(); + $itemIndex=0; + $hasSeparator=$this->_separatorTemplate!==null || $this->getSeparatorRenderer()!==''; + foreach($data as $key=>$dataItem) + { + if($keyField!=='') + $keys->add($this->getDataFieldValue($dataItem,$keyField)); + else + $keys->add($key); + if($itemIndex===0) + $this->_header=$this->createItemWithDataInternal(-1,TListItemType::Header,null); + if($hasSeparator && $itemIndex>0) + $this->createItemWithDataInternal($itemIndex-1,TListItemType::Separator,null); + $itemType=$itemIndex%2==0?TListItemType::Item : TListItemType::AlternatingItem; + $items->add($this->createItemWithDataInternal($itemIndex,$itemType,$dataItem)); + $itemIndex++; + } + if($itemIndex>0) + $this->_footer=$this->createItemWithDataInternal(-1,TListItemType::Footer,null); + else + { + $this->createEmptyContent(); + $this->dataBindChildren(); + } + $this->setViewState('ItemCount',$itemIndex,0); + } + + /** + * This method overrides parent's implementation to handle + * {@link onItemCommand OnItemCommand} event which is bubbled from + * repeater items and their child controls. + * This method should only be used by control developers. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $this->onItemCommand($param); + return true; + } + else + return false; + } + + /** + * Raises OnItemCreated event. + * This method is invoked after a repeater item is created and instantiated with + * template, but before added to the page hierarchy. + * The repeater item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TRepeaterItemEventParameter event parameter + */ + public function onItemCreated($param) + { + $this->raiseEvent('OnItemCreated',$this,$param); + } + + /** + * Raises OnItemDataBound event. + * This method is invoked right after an item is data bound. + * The repeater item control responsible for the event + * can be determined from the event parameter. + * If you override this method, be sure to call parent's implementation + * so that event handlers have chance to respond to the event. + * @param TRepeaterItemEventParameter event parameter + */ + public function onItemDataBound($param) + { + $this->raiseEvent('OnItemDataBound',$this,$param); + } + + /** + * Raises OnItemCommand event. + * This method is invoked after a button control in + * a template raises OnCommand event. + * The repeater control responsible for the event + * can be determined from the event parameter. + * The event parameter also contains the information about + * the initial sender of the OnCommand event, command name + * and command parameter. + * You may override this method to provide customized event handling. + * Be sure to call parent's implementation so that + * event handlers have chance to respond to the event. + * @param TRepeaterCommandEventParameter event parameter + */ + public function onItemCommand($param) + { + $this->raiseEvent('OnItemCommand',$this,$param); + } + + /** + * Returns the value of the data at the specified field. + * If data is an array, TMap or TList, the value will be returned at the index + * of the specified field. If the data is a component with a property named + * as the field name, the property value will be returned. + * Otherwise, an exception will be raised. + * @param mixed data item + * @param mixed field name + * @return mixed data value at the specified field + * @throws TInvalidDataValueException if the data is invalid + */ + protected function getDataFieldValue($data,$field) + { + return TDataFieldAccessor::getDataFieldValue($data,$field); + } +} + +/** + * TRepeaterItemEventParameter class + * + * TRepeaterItemEventParameter encapsulates the parameter data for + * {@link TRepeater::onItemCreated ItemCreated} event of {@link TRepeater} controls. + * The {@link getItem Item} property indicates the repeater item related with the event. + * + * @author Qiang Xue + * @version $Id: TRepeater.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterItemEventParameter extends TEventParameter +{ + /** + * The repeater item control responsible for the event. + * @var TControl + */ + private $_item=null; + + /** + * Constructor. + * @param TControl repeater item related with the corresponding event + */ + public function __construct($item) + { + $this->_item=$item; + } + + /** + * @return TControl repeater item related with the corresponding event + */ + public function getItem() + { + return $this->_item; + } +} + +/** + * TRepeaterCommandEventParameter class + * + * TRepeaterCommandEventParameter encapsulates the parameter data for + * {@link TRepeater::onItemCommand ItemCommand} event of {@link TRepeater} controls. + * + * The {@link getItem Item} property indicates the repeater item related with the event. + * The {@link getCommandSource CommandSource} refers to the control that originally + * raises the Command event. + * + * @author Qiang Xue + * @version $Id: TRepeater.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterCommandEventParameter extends TCommandEventParameter +{ + /** + * @var TControl the repeater item control responsible for the event. + */ + private $_item=null; + /** + * @var TControl the control originally raises the OnCommand event. + */ + private $_source=null; + + /** + * Constructor. + * @param TControl repeater item responsible for the event + * @param TControl original event sender + * @param TCommandEventParameter original event parameter + */ + public function __construct($item,$source,TCommandEventParameter $param) + { + $this->_item=$item; + $this->_source=$source; + parent::__construct($param->getCommandName(),$param->getCommandParameter()); + } + + /** + * @return TControl the repeater item control responsible for the event. + */ + public function getItem() + { + return $this->_item; + } + + /** + * @return TControl the control originally raises the OnCommand event. + */ + public function getCommandSource() + { + return $this->_source; + } +} + +/** + * TRepeaterItem class + * + * A TRepeaterItem control represents an item in the {@link TRepeater} control, + * such as heading section, footer section, or a data item. + * The index and data value of the item can be accessed via {@link getItemIndex ItemIndex}> + * and {@link getDataItem DataItem} properties, respectively. The type of the item + * is given by {@link getItemType ItemType} property. + * + * @author Qiang Xue + * @version $Id: TRepeater.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterItem extends TControl implements INamingContainer, IItemDataRenderer +{ + /** + * index of the data item in the Items collection of repeater + */ + private $_itemIndex; + /** + * type of the TRepeaterItem + * @var TListItemType + */ + private $_itemType; + /** + * data associated with this item + * @var mixed + */ + private $_data; + + /** + * @return TListItemType item type + */ + public function getItemType() + { + return $this->_itemType; + } + + /** + * @param TListItemType item type. + */ + public function setItemType($value) + { + $this->_itemType=TPropertyValue::ensureEnum($value,'TListItemType'); + } + + /** + * Returns a value indicating the zero-based index of the item in the corresponding data control's item collection. + * If the item is not in the collection (e.g. it is a header item), it returns -1. + * @return integer zero-based index of the item. + */ + public function getItemIndex() + { + return $this->_itemIndex; + } + + /** + * Sets the zero-based index for the item. + * If the item is not in the item collection (e.g. it is a header item), -1 should be used. + * @param integer zero-based index of the item. + */ + public function setItemIndex($value) + { + $this->_itemIndex=TPropertyValue::ensureInteger($value); + } + + /** + * @return mixed data associated with the item + * @since 3.1.0 + */ + public function getData() + { + return $this->_data; + } + + /** + * @param mixed data to be associated with the item + * @since 3.1.0 + */ + public function setData($value) + { + $this->_data=$value; + } + + /** + * This property is deprecated since v3.1.0. + * @return mixed data associated with the item + * @deprecated deprecated since v3.1.0. Use {@link getData} instead. + */ + public function getDataItem() + { + return $this->getData(); + } + + /** + * This property is deprecated since v3.1.0. + * @param mixed data to be associated with the item + * @deprecated deprecated since version 3.1.0. Use {@link setData} instead. + */ + public function setDataItem($value) + { + return $this->setData($value); + } + + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TRepeaterCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + + +/** + * TRepeaterItemCollection class. + * + * TRepeaterItemCollection represents a collection of repeater items. + * + * @author Qiang Xue + * @version $Id: TRepeater.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRepeaterItemCollection extends TList +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by inserting only objects that are descendant of {@link TControl}. + * @param integer the speicified position. + * @param TControl new item + * @throws TInvalidDataTypeException if the item to be inserted is not a control. + */ + public function insertAt($index,$item) + { + if($item instanceof TControl) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('repeateritemcollection_item_invalid'); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TRepeaterItemRenderer.php b/gui/baculum/framework/Web/UI/WebControls/TRepeaterItemRenderer.php new file mode 100644 index 0000000000..0e39e76559 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TRepeaterItemRenderer.php @@ -0,0 +1,50 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TRepeaterItemRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TRepeater'); +Prado::using('System.Web.UI.WebControls.TItemDataRenderer'); + +/** + * TRepeaterItemRenderer class + * + * TRepeaterItemRenderer can be used as a convenient base class to + * define an item renderer class specific for {@link TRepeater}. + * + * TRepeaterItemRenderer extends {@link TItemDataRenderer} and implements + * the bubbling scheme for the OnCommand event of repeater items. + * + * @author Qiang Xue + * @version $Id: TRepeaterItemRenderer.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.0 + */ +class TRepeaterItemRenderer extends TItemDataRenderer +{ + /** + * This method overrides parent's implementation by wrapping event parameter + * for OnCommand event with item information. + * @param TControl the sender of the event + * @param TEventParameter event parameter + * @return boolean whether the event bubbling should stop here. + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $this->raiseBubbleEvent($this,new TRepeaterCommandEventParameter($this,$sender,$param)); + return true; + } + else + return false; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TRequiredFieldValidator.php b/gui/baculum/framework/Web/UI/WebControls/TRequiredFieldValidator.php new file mode 100644 index 0000000000..7a0bad8c89 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TRequiredFieldValidator.php @@ -0,0 +1,154 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TRequiredFieldValidator.php 3288 2013-04-30 10:36:50Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Using TBaseValidator class + */ +Prado::using('System.Web.UI.WebControls.TBaseValidator'); + +/** + * TRequiredFieldValidator class + * + * TRequiredFieldValidator makes the associated input control a required field. + * The input control fails validation if its value does not change from + * the {@link setInitialValue InitialValue} property upon losing focus. + * + * Validation will also succeed if input is of TListControl type and the number + * of selected values different from the initial value is greater than zero. + * + * If the input is of TListControl type and has a {@link TListControl::setPromptValue PromptValue} + * set, it will be automatically considered as the validator's {@link setInitialValue InitialValue}. + * + * @author Qiang Xue + * @version $Id: TRequiredFieldValidator.php 3288 2013-04-30 10:36:50Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TRequiredFieldValidator extends TBaseValidator +{ + /** + * Gets the name of the javascript class responsible for performing validation for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TRequiredFieldValidator'; + } + + /** + * @return string the initial value of the associated input control. Defaults to empty string + * unless the control has a prompt value set. + * If the associated input control does not change from this initial value + * upon postback, the validation fails. + */ + public function getInitialValue() + { + return $this->getViewState('InitialValue',$this->getControlPromptValue()); + } + + /** + * @return string the initial value of the associated input control. Defaults to empty string. + * If the associated input control does not change from this initial value + * upon postback, the validation fails. + */ + protected function getControlPromptValue() + { + $control = $this->getValidationTarget(); + if($control instanceof TListControl) + return $control->getPromptValue(); + return ''; + } + /** + * @param string the initial value of the associated input control. + * If the associated input control does not change from this initial value + * upon postback, the validation fails. + */ + public function setInitialValue($value) + { + $this->setViewState('InitialValue',TPropertyValue::ensureString($value),''); + } + + /** + * This method overrides the parent's implementation. + * The validation succeeds if the input component changes its data + * from the {@link getInitialValue InitialValue} or the input control is not given. + * + * Validation will also succeed if input is of TListControl type and the + * number of selected values different from the initial value is greater + * than zero. + * + * @return boolean whether the validation succeeds + */ + protected function evaluateIsValid() + { + $control = $this->getValidationTarget(); + if($control instanceof TListControl) + return $this->validateListControl($control); + else if($control instanceof TRadioButton && strlen($control->getGroupName()) > 0) + return $this->validateRadioButtonGroup($control); + else + return $this->validateStandardControl($control); + } + + private function validateListControl($control) + { + $initial = trim($this->getInitialValue()); + $count = 0; + foreach($control->getItems() as $item) + { + if($item->getSelected() && $item->getValue() != $initial) + $count++; + } + return $count > 0; + } + + private function validateRadioButtonGroup($control) + { + $initial = trim($this->getInitialValue()); + foreach($control->getRadioButtonsInGroup() as $radio) + { + if($radio->getChecked()) + { + if(strlen($value = $radio->getValue()) > 0) + return $value !== $initial; + else + return true; + } + } + return false; + } + + private function validateStandardControl($control) + { + $initial = trim($this->getInitialValue()); + $value=$this->getValidationValue($control); + return (is_bool($value) && $value) || trim($value)!==$initial; + } + + /** + * Returns an array of javascript validator options. + * @return array javascript validator options. + */ + protected function getClientScriptOptions() + { + $options = parent::getClientScriptOptions(); + $options['InitialValue']=$this->getInitialValue(); + $control = $this->getValidationTarget(); + if($control instanceof TListControl) + $options['TotalItems'] = $control->getItemCount(); + if($control instanceof TRadioButton && strlen($control->getGroupName()) > 0) + $options['GroupName'] = $control->getGroupName(); + return $options; + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TSafeHtml.php b/gui/baculum/framework/Web/UI/WebControls/TSafeHtml.php new file mode 100644 index 0000000000..775086b4b2 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TSafeHtml.php @@ -0,0 +1,85 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSafeHtml.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TSafeHtml class + * + * TSafeHtml is a control that strips down all potentially dangerous + * HTML content. It is mainly a wrapper of {@link http://pear.php.net/package/SafeHTML SafeHTML} + * project. According to the SafeHTML project, it tries to safeguard + * the following situations when the string is to be displayed to end-users, + * - Opening tag without its closing tag + * - closing tag without its opening tag + * - any of these tags: base, basefont, head, html, body, applet, object, + * iframe, frame, frameset, script, layer, ilayer, embed, bgsound, link, + * meta, style, title, blink, xml, etc. + * - any of these attributes: on*, data*, dynsrc + * - javascript:/vbscript:/about: etc. protocols + * - expression/behavior etc. in styles + * - any other active content. + * + * To use TSafeHtml, simply enclose the content to be secured within + * the body of TSafeHtml in a template. + * + * If the content is encoded in UTF-7, you'll need to enable the {@link setRepackUTF7 RepackUTF7} property + * to ensure the contents gets parsed correctly. + * + * @author Wei Zhuo + * @version $Id: TSafeHtml.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TSafeHtml extends TControl +{ + /** + * Sets whether to parse the contents as UTF-7. This property enables a routine + * that repacks the content as UTF-7 before parsing it. Defaults to false. + * @param boolean whether to parse the contents as UTF-7 + */ + public function setRepackUTF7($value) + { + $this->setViewState('RepackUTF7',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether to parse the contents as UTF-7. Defaults to false. + */ + public function getRepackUTF7() + { + return $this->getViewState('RepackUTF7',false); + } + + /** + * Renders body content. + * This method overrides parent implementation by removing + * malicious javascript code from the body content + * @param THtmlWriter writer + */ + public function render($writer) + { + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + parent::render($htmlWriter); + $writer->write($this->parseSafeHtml($htmlWriter->flush())); + } + + /** + * Use SafeHTML to remove malicous javascript from the HTML content. + * @param string HTML content + * @return string safer HTML content + */ + protected function parseSafeHtml($text) + { + $renderer = Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser'); + return $renderer->parse($text, $this->getRepackUTF7()); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TSlider.php b/gui/baculum/framework/Web/UI/WebControls/TSlider.php new file mode 100644 index 0000000000..dcf34bfc1c --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TSlider.php @@ -0,0 +1,574 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TSlider.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ + +/** + * TSlider class + * + * TSlider displays a slider for numeric input purpose. A slider consists of a 'track', + * which define the range of possible value, and a 'handle' which can slide on the track, to select + * a value in the range. The track can be either Horizontal or Vertical, depending of the {@link SetDirection Direction} + * property. By default, it's horizontal. + * + * The range boundaries are defined by {@link SetMinValue MinValue} and {@link SetMaxValue MaxValue} properties. + * The default range is from 0 to 100. + * The {@link SetStepSize StepSize} property can be used to define the step between 2 values inside the range. + * Notice that this step will be recomputed if there is more than 200 values between the range boundaries. + * You can also provide the allowed values by setting the {@link SetValues Values} array. + * + * A 'Progress Indicator' can be displayed within the track with the {@link SetProgressIndicator ProgressIndicator} property. + * + * The TSlider control can be easily customized using CssClasses. You can provide your own css file, using the + * {@link SetCssUrl CssUrl} property. + * The css class for TSlider can be set by the {@link setCssClass CssClass} property. Default value is "Slider HorizontalSlider" + * for an horizontal slider, and "Slider VerticalSlider" for a vertical one. + * + * If {@link SetAutoPostBack AutoPostBack} property is true, postback is sent as soon as the value changed. + * + * TSlider raises the {@link onValueChanged} event when the value of the slider has changed during postback. + * + * You can also attach ClientSide javascript events handler to the slider : + * - ClientSide.onSlide is called when the handle is slided on the track. You can get the current value in the value + * javascript variable. You can use this event to update on client side a label with the current value + * - ClientSide.onChange is called when the slider value has changed (at the end of a move). + * + * @author Christophe Boulain + * @version $Id: TSlider.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TSlider extends TWebControl implements IPostBackDataHandler, IDataRenderer +{ + const MAX_STEPS=200; + /** + * @var TSliderHandle handle component + */ + private $_handle; + /* + * @var boolean Wether the data has changed during postback + */ + private $_dataChanged=false; + /** + * @var TSliderClientScript Clients side javascripts + */ + private $_clientScript=null; + + /** + * @return TSliderDirection Direction of slider (Horizontal or Vertical). Defaults to Horizontal. + */ + public function getDirection() + { + return $this->getViewState('Direction', TSliderDirection::Horizontal); + } + + /** + * @param TSliderDirection Direction of slider (Horizontal or Vertical) + */ + public function setDirection($value) + { + $this->setViewState('Direction', TPropertyValue::ensureEnum($value,'TSliderDirection'),TSliderDirection::Horizontal); + } + + /** + * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to '' (a default CSS file will be applied in this case.) + */ + public function getCssUrl() + { + return $this->getViewState('CssUrl',''); + } + + /** + * @param string URL for the CSS file including all relevant CSS class definitions. + */ + public function setCssUrl($value) + { + $this->setViewState('CssUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return float Maximum value for the slider. Defaults to 100.0. + */ + public function getMaxValue() + { + return $this->getViewState('MaxValue',100.0); + } + + /** + * @param float Maximum value for slider + */ + public function setMaxValue($value) + { + $this->setViewState('MaxValue', TPropertyValue::ensureFloat($value),100.0); + } + + /** + * @return float Minimum value for slider. Defaults to 0.0. + */ + public function getMinValue() + { + return $this->getViewState('MinValue',0.0); + } + + /** + * @param float Minimum value for slider + */ + public function setMinValue($value) + { + $this->setViewState('MinValue', TPropertyValue::ensureFloat($value),0.0); + } + + /** + * @return float Step size. Defaults to 1.0. + */ + public function getStepSize() + { + return $this->getViewState('StepSize', 1.0); + } + + /** + * Sets the step size used to determine the places where the slider handle can stop at. + * An evenly distributed stop marks will be generated according to + * {@link getMinValue MinValue}, {@link getMaxValue MaxValue} and StepSize. + * To use uneven stop marks, set {@link setValues Values}. + * @param float Step size. + */ + public function setStepSize($value) + { + $this->setViewState('StepSize', $value, 1.0); + } + + /** + * @return boolean wether to display a progress indicator or not. Defaults to true. + */ + public function getProgressIndicator () + { + return $this->getViewState('ProgressIndicator', true); + } + + /** + * @param boolean wether to display a progress indicator or not. Defaults to true. + */ + public function setProgressIndicator ($value) + { + $this->setViewState('ProgressIndicator', TPropertyValue::ensureBoolean($value), true); + } + /** + * @return float current value of slider + */ + public function getValue() + { + return $this->getViewState('Value',0.0); + } + + /** + * @param float current value of slider + */ + public function setValue($value) + { + $this->setViewState('Value', TPropertyValue::ensureFloat($value),0.0); + } + + /** + * Returns the value of the TSlider control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getValue()}. + * @return string the value of the TSlider control. + * @see getValue + */ + public function getData() + { + return $this->getValue(); + } + + /** + * Sets the value of the TSlider control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setValue()}. + * @param string the value of the TSlider control. + * @see setValue + */ + public function setData($value) + { + $this->setValue($value); + } + + /** + * @return array list of allowed values the slider can take. Defaults to an empty array. + */ + public function getValues() + { + return $this->getViewState('Values', array()); + } + + /** + * Sets the possible values that the slider can take. + * If this is set, {@link setStepSize StepSize} will be ignored. The latter + * generates a set of evenly distributed candidate values. + * @param array list of allowed values the slider can take + */ + public function setValues($value) + { + $this->setViewState('Values', TPropertyValue::ensureArray($value), array()); + } + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user modifies the slider value. Defaults to false. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',false); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * modifies the slider value. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TSlider'; + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getValue Value} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + $this->onValueChanged(null); + } + + /** + * Raises OnValueChanged event. + * This method is invoked when the {@link getValue Value} + * property changes on postback. + * If you override this method, be sure to call the parent implementation to ensure + * the invocation of the attached event handlers. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onValueChanged($param) + { + $this->raiseEvent('OnValueChanged',$this,$param); + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $value=(float)$values[$this->getClientID().'_1']; + if($this->getValue()!==$value) + { + $this->setValue($value); + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Gets the TSliderClientScript to set the TSlider event handlers. + * + * The slider on the client-side supports the following events. + * # OnSliderMove -- raised when the slider is moved. + * # OnSliderChanged -- raised when the slider value is changed + * + * You can attach custom javascript code to each of these events + * + * @return TSliderClientScript javascript validator event options. + */ + public function getClientSide() + { + if($this->_clientScript===null) + $this->_clientScript = $this->createClientScript(); + return $this->_clientScript; + } + + /** + * @return TSliderClientScript javascript event options. + */ + protected function createClientScript() + { + return new TSliderClientScript; + } + + /** + * @return string the HTML tag name for slider. Defaults to div. + */ + public function getTagName () + { + return "div"; + } + + /** + * Add the specified css classes to the track + * @param THtmlWriter writer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $writer->addAttribute('id',$this->getClientID()); + if ($this->getCssClass()==='') + { + $class=($this->getDirection()==TSliderDirection::Horizontal)?'HorizontalSlider':'VerticalSlider'; + $writer->addAttribute('class', 'Slider '.$class); + } + + } + + /** + * Render the body content + */ + public function renderContents($writer) + { + // Render the 'Track' + $writer->addAttribute('class', 'Track'); + $writer->addAttribute('id', $this->getClientID().'_track'); + $writer->renderBeginTag('div'); + // Render the 'Progress Indicator' + if ($this->getProgressIndicator()) + { + $writer->addAttribute('class', 'Progress'); + $writer->addAttribute('id', $this->getClientID().'_progress'); + $writer->renderBeginTag('div'); + $writer->renderEndTag(); + } + // Render the 'Ruler' + /* + * Removing for now + $writer->addAttribute('class', 'RuleContainer'); + $writer->addAttribute('id', $this->getClientID()."_rule"); + $writer->renderBeginTag('div'); + for ($i=0;$i<=100;$i+=10) + { + $writer->addAttribute('class', 'RuleMark'); + $attr=($this->getDirection()===TSliderDirection::Horizontal)?"left":"top"; + $writer->addStyleAttribute($attr, $i.'%'); + $writer->renderBeginTag('div'); + $writer->renderEndTag(); + } + $writer->renderEndTag(); + */ + + $writer->renderEndTag(); + + // Render the 'Handle' + $writer->addAttribute('class', 'Handle'); + $writer->addAttribute('id', $this->getClientID().'_handle'); + $writer->renderBeginTag('div'); + $writer->renderEndTag(); + } + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender ($param) + { + parent::onPreRender($param); + $this->registerStyleSheet(); + $this->registerSliderClientScript(); + + } + + /** + * Registers the CSS relevant to the TSlider. + * It will register the CSS file specified by {@link getCssUrl CssUrl}. + * If that is not set, it will use the default CSS. + */ + protected function registerStyleSheet() + { + if(($url=$this->getCssUrl())==='') + { + $manager=$this->getApplication()->getAssetManager(); + // publish the assets + $url=$manager->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'TSlider'); + $url.='/TSlider.css'; + } + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + + /** + * Registers the javascript code to initialize the slider. + */ + protected function registerSliderClientScript() + { + $page=$this->getPage(); + $cs = $page->getClientScript(); + $cs->registerPradoScript("slider"); + $id=$this->getClientID(); + $cs->registerHiddenField($id.'_1',$this->getValue()); + $page->registerRequiresPostData($this); + $cs->registerPostBackControl($this->getClientClassName(),$this->getSliderOptions()); + } + + /** + * Get javascript sliderr options. + * @return array slider client-side options + */ + protected function getSliderOptions() + { + // PostBack Options : + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['AutoPostBack'] = $this->getAutoPostBack(); + + // Slider Control options + $minValue=$this->getMinValue(); + $maxValue=$this->getMaxValue(); + $options['axis'] = strtolower($this->getDirection()); + $options['maximum'] = $maxValue; + $options['minimum'] = $minValue; + $options['range'] = TJavascript::quoteJsLiteral('$R('.$minValue.",".$maxValue.")"); + $options['sliderValue'] = $this->getValue(); + $options['disabled'] = !$this->getEnabled(); + $values=$this->getValues(); + if (!empty($values)) + { + // Values are provided. Check if min/max are present in them + if (!in_array($minValue, $values)) $values[]=$minValue; + if (!in_array($maxValue, $values)) $values[]=$maxValue; + // Remove all values outsize the range [min..max] + foreach ($values as $idx=>$value) + { + if ($value < $minValue) unset ($values[$idx]); + if ($value > $maxValue) unset ($values[$idx]); + } + } + else + { + // Values are not provided, generate automatically using stepsize + $step=$this->getStepSize(); + // We want at most self::MAX_STEPS values, so, change the step if necessary + if (($maxValue-$minValue)/$step > self::MAX_STEPS) + { + $step=($maxValue-$minValue)/self::MAX_STEPS; + } + $values=array(); + for ($i=$minValue;$i<=$maxValue;$i+=$step) + $values[]=$i; + // Add max if it's not in the array because of step + if (!in_array($maxValue, $values)) $values[]=$maxValue; + } + $options['values'] = $values; + if($this->_clientScript!==null) + $options = array_merge($options,$this->_clientScript->getOptions()->toArray()); + return $options; + } +} + +/** + * TSliderClientScript class. + * + * Client-side slider events {@link setOnChange OnChange} and {@line setOnMove OnMove} + * can be modified through the {@link TSlider:: getClientSide ClientSide} + * property of a slider. + * + * The current value of the slider can be get in the 'value' js variable + * + * The OnMove event is raised when the slider moves + * The OnChange event is raised when the slider value is changed (or at the end of a move) + * + * @author Christophe Boulain + * @version $Id: TSlider.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TSliderClientScript extends TClientSideOptions +{ + /** + * Javascript code to execute when the slider value is changed. + * @param string javascript code + */ + public function setOnChange($javascript) + { + $code=TJavascript::quoteJsLiteral("function (value) { {$javascript} }"); + $this->setFunction('onChange', $code); + } + + /** + * @return string javascript code to execute when the slider value is changed. + */ + public function getOnChange() + { + return $this->getOption('onChange'); + } + + /* Javascript code to execute when the slider moves. + * @param string javascript code + */ + public function setOnSlide($javascript) + { + $code=TJavascript::quoteJsLiteral("function (value) { {$javascript} }"); + $this->setFunction('onSlide', $code); + } + + /** + * @return string javascript code to execute when the slider moves. + */ + public function getOnSlide() + { + return $this->getOption('onSlide'); + } +} + + +/** + * TSliderDirection class. + * + * TSliderDirection defines the enumerable type for the possible direction that can be used in a {@link TSlider} + * + * The following enumerable values are defined : + * - Horizontal : Horizontal slider + * - Vertical : Vertical slider + * + * @author Christophe Boulain + * @version $Id: TSlider.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TSliderDirection extends TEnumerable +{ + const Horizontal='Horizontal'; + const Vertical='Vertical'; +} + + diff --git a/gui/baculum/framework/Web/UI/WebControls/TStatements.php b/gui/baculum/framework/Web/UI/WebControls/TStatements.php new file mode 100644 index 0000000000..32f98296d2 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TStatements.php @@ -0,0 +1,63 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TStatements.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TStatements class + * + * TStatements executes one or several PHP statements and renders the display + * generated during the execution. The execution happens during the rendering stage. + * The PHP statements being executed can be set via the property + * {@link setStatements Statements}. The context of the statemenets executed + * is the TStatements object itself. + * + * Note, since TStatements allows execution of arbitrary PHP statements, + * make sure {@link setStatements Statements} does not come directly from user input. + * + * @author Qiang Xue + * @version $Id: TStatements.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TStatements extends TControl +{ + /** + * @var string PHP statements + */ + private $_s=''; + + /** + * @return string the statements to be executed + */ + public function getStatements() + { + return $this->_s; + } + + /** + * @param string the PHP statements to be executed + */ + public function setStatements($value) + { + $this->_s=$value; + } + + /** + * Renders the evaluation result of the statements. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + if($this->_s!=='') + $writer->write($this->evaluateStatements($this->_s)); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TStyle.php b/gui/baculum/framework/Web/UI/WebControls/TStyle.php new file mode 100644 index 0000000000..4ba37d53f5 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TStyle.php @@ -0,0 +1,893 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TFont definition + */ +Prado::using('System.Web.UI.WebControls.TFont'); + +/** + * TStyle class + * + * TStyle encapsulates the CSS style applied to a control. + * + * @author Qiang Xue + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TStyle extends TComponent +{ + /** + * @var array storage of CSS fields + */ + private $_fields=array(); + /** + * @var TFont font object + */ + private $_font=null; + /** + * @var string CSS class name + */ + private $_class=null; + /** + * @var string CSS style string (those not represented by specific fields of TStyle) + */ + private $_customStyle=null; + /** + * @var string display style + */ + private $_displayStyle='Fixed'; + + /** + * Constructor. + * @param TStyle style to copy from + */ + public function __construct($style=null) + { + if($style!==null) + $this->copyFrom($style); + } + + /** + * Need to clone the font object. + */ + public function __clone() + { + if($this->_font!==null) + $this->_font = clone($this->_font); + } + + /** + * @return string the background color of the control + */ + public function getBackColor() + { + return isset($this->_fields['background-color'])?$this->_fields['background-color']:''; + } + + /** + * @param string the background color of the control + */ + public function setBackColor($value) + { + if(trim($value)==='') + unset($this->_fields['background-color']); + else + $this->_fields['background-color']=$value; + } + + /** + * @return string the border color of the control + */ + public function getBorderColor() + { + return isset($this->_fields['border-color'])?$this->_fields['border-color']:''; + } + + /** + * @param string the border color of the control + */ + public function setBorderColor($value) + { + if(trim($value)==='') + unset($this->_fields['border-color']); + else + $this->_fields['border-color']=$value; + } + + /** + * @return string the border style of the control + */ + public function getBorderStyle() + { + return isset($this->_fields['border-style'])?$this->_fields['border-style']:''; + } + + /** + * Sets the border style of the control. + * @param string the border style of the control + */ + public function setBorderStyle($value) + { + if(trim($value)==='') + unset($this->_fields['border-style']); + else + $this->_fields['border-style']=$value; + } + + /** + * @return string the border width of the control + */ + public function getBorderWidth() + { + return isset($this->_fields['border-width'])?$this->_fields['border-width']:''; + } + + /** + * @param string the border width of the control + */ + public function setBorderWidth($value) + { + if(trim($value)==='') + unset($this->_fields['border-width']); + else + $this->_fields['border-width']=$value; + } + + /** + * @return string the CSS class of the control + */ + public function getCssClass() + { + return $this->_class===null?'':$this->_class; + } + + /** + * @return boolean true if CSS is set or empty. + */ + public function hasCssClass() + { + return ($this->_class!==null); + } + + /** + * @param string the name of the CSS class of the control + */ + public function setCssClass($value) + { + $this->_class=$value; + } + + /** + * @return TFont the font of the control + */ + public function getFont() + { + if($this->_font===null) + $this->_font=new TFont; + return $this->_font; + } + + /** + * @return boolean true if font is set. + */ + public function hasFont() + { + return $this->_font !== null; + } + + /** + * @param TDisplayStyle control display style, default is TDisplayStyle::Fixed + */ + public function setDisplayStyle($value) + { + $this->_displayStyle = TPropertyValue::ensureEnum($value, 'TDisplayStyle'); + switch($this->_displayStyle) + { + case TDisplayStyle::None: + $this->_fields['display'] = 'none'; + break; + case TDisplayStyle::Dynamic: + $this->_fields['display'] = ''; //remove the display property + break; + case TDisplayStyle::Fixed: + $this->_fields['visibility'] = 'visible'; + break; + case TDisplayStyle::Hidden: + $this->_fields['visibility'] = 'hidden'; + break; + } + } + + /** + * @return TDisplayStyle display style + */ + public function getDisplayStyle() + { + return $this->_displayStyle; + } + + /** + * @return string the foreground color of the control + */ + public function getForeColor() + { + return isset($this->_fields['color'])?$this->_fields['color']:''; + } + + /** + * @param string the foreground color of the control + */ + public function setForeColor($value) + { + if(trim($value)==='') + unset($this->_fields['color']); + else + $this->_fields['color']=$value; + } + + /** + * @return string the height of the control + */ + public function getHeight() + { + return isset($this->_fields['height'])?$this->_fields['height']:''; + } + + /** + * @param string the height of the control + */ + public function setHeight($value) + { + if(trim($value)==='') + unset($this->_fields['height']); + else + $this->_fields['height']=$value; + } + + /** + * @return string the custom style of the control + */ + public function getCustomStyle() + { + return $this->_customStyle===null?'':$this->_customStyle; + } + + /** + * Sets custom style fields from a string. + * Custom style fields will be overwritten by style fields explicitly defined. + * @param string the custom style of the control + */ + public function setCustomStyle($value) + { + $this->_customStyle=$value; + } + + /** + * @return string a single style field value set via {@link setStyleField}. Defaults to empty string. + */ + public function getStyleField($name) + { + return isset($this->_fields[$name])?$this->_fields[$name]:''; + } + + /** + * Sets a single style field value. + * Style fields set by this method will overwrite those set by {@link setCustomStyle}. + * @param string style field name + * @param string style field value + */ + public function setStyleField($name,$value) + { + $this->_fields[$name]=$value; + } + + /** + * Clears a single style field value; + * @param string style field name + */ + public function clearStyleField($name) + { + unset($this->_fields[$name]); + } + + /** + * @return boolean whether a style field has been defined by {@link setStyleField} + */ + public function hasStyleField($name) + { + return isset($this->_fields[$name]); + } + + /** + * @return string the width of the control + */ + public function getWidth() + { + return isset($this->_fields['width'])?$this->_fields['width']:''; + } + + /** + * @param string the width of the control + */ + public function setWidth($value) + { + $this->_fields['width']=$value; + } + + /** + * Resets the style to the original empty state. + */ + public function reset() + { + $this->_fields=array(); + $this->_font=null; + $this->_class=null; + $this->_customStyle=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + if($style instanceof TStyle) + { + $this->_fields=array_merge($this->_fields,$style->_fields); + if($style->_class!==null) + $this->_class=$style->_class; + if($style->_customStyle!==null) + $this->_customStyle=$style->_customStyle; + if($style->_font!==null) + $this->getFont()->copyFrom($style->_font); + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + if($style instanceof TStyle) + { + $this->_fields=array_merge($style->_fields,$this->_fields); + if($this->_class===null) + $this->_class=$style->_class; + if($this->_customStyle===null) + $this->_customStyle=$style->_customStyle; + if($style->_font!==null) + $this->getFont()->mergeWith($style->_font); + } + } + + /** + * Adds attributes related to CSS styles to renderer. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if($this->_customStyle!==null) + { + foreach(explode(';',$this->_customStyle) as $style) + { + $arr=explode(':',$style,2); + if(isset($arr[1]) && trim($arr[0])!=='') + $writer->addStyleAttribute(trim($arr[0]),trim($arr[1])); + } + } + $writer->addStyleAttributes($this->_fields); + if($this->_font!==null) + $this->_font->addAttributesToRender($writer); + if($this->_class!==null) + $writer->addAttribute('class',$this->_class); + } + + /** + * @return array list of style fields. + */ + public function getStyleFields() + { + return $this->_fields; + } +} + +/** + * TDisplayStyle defines the enumerable type for the possible styles + * that a web control can display. + * + * The following enumerable values are defined: + * - None: the control is not displayed and not included in the layout. + * - Dynamic: the control is displayed and included in the layout, the layout flow is dependent on the control (equivalent to display:'' in css). + * - Fixed: Similar to Dynamic with CSS "visibility" set "shown". + * - Hidden: the control is not displayed and is included in the layout. + * + * @author Wei Zhuo + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1 + */ +class TDisplayStyle extends TEnumerable +{ + const None='None'; + const Dynamic='Dynamic'; + const Fixed='Fixed'; + const Hidden='Hidden'; +} + +/** + * TTableStyle class. + * TTableStyle represents the CSS style specific for HTML table. + * + * @author Qiang Xue + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableStyle extends TStyle +{ + /** + * @var TVerticalAlign the URL of the background image for the table + */ + private $_backImageUrl=null; + /** + * @var THorizontalAlign horizontal alignment of the contents within the table + */ + private $_horizontalAlign=null; + /** + * @var integer cellpadding of the table + */ + private $_cellPadding=null; + /** + * @var integer cellspacing of the table + */ + private $_cellSpacing=null; + /** + * @var TTableGridLines grid line setting of the table + */ + private $_gridLines=null; + /** + * @var boolean whether the table border should be collapsed + */ + private $_borderCollapse=null; + + /** + * Sets the style attributes to default values. + * This method overrides the parent implementation by + * resetting additional TTableStyle specific attributes. + */ + public function reset() + { + $this->_backImageUrl=null; + $this->_horizontalAlign=null; + $this->_cellPadding=null; + $this->_cellSpacing=null; + $this->_gridLines=null; + $this->_borderCollapse=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TTableStyle) + { + if($style->_backImageUrl!==null) + $this->_backImageUrl=$style->_backImageUrl; + if($style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($style->_cellPadding!==null) + $this->_cellPadding=$style->_cellPadding; + if($style->_cellSpacing!==null) + $this->_cellSpacing=$style->_cellSpacing; + if($style->_gridLines!==null) + $this->_gridLines=$style->_gridLines; + if($style->_borderCollapse!==null) + $this->_borderCollapse=$style->_borderCollapse; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TTableStyle) + { + if($this->_backImageUrl===null && $style->_backImageUrl!==null) + $this->_backImageUrl=$style->_backImageUrl; + if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($this->_cellPadding===null && $style->_cellPadding!==null) + $this->_cellPadding=$style->_cellPadding; + if($this->_cellSpacing===null && $style->_cellSpacing!==null) + $this->_cellSpacing=$style->_cellSpacing; + if($this->_gridLines===null && $style->_gridLines!==null) + $this->_gridLines=$style->_gridLines; + if($this->_borderCollapse===null && $style->_borderCollapse!==null) + $this->_borderCollapse=$style->_borderCollapse; + } + } + + + /** + * Adds attributes related to CSS styles to renderer. + * This method overrides the parent implementation. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if(($url=trim($this->getBackImageUrl()))!=='') + $writer->addStyleAttribute('background-image','url('.$url.')'); + + if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) + $writer->addStyleAttribute('text-align',strtolower($horizontalAlign)); + + if(($cellPadding=$this->getCellPadding())>=0) + $writer->addAttribute('cellpadding',"$cellPadding"); + + if(($cellSpacing=$this->getCellSpacing())>=0) + $writer->addAttribute('cellspacing',"$cellSpacing"); + + if($this->getBorderCollapse()) + $writer->addStyleAttribute('border-collapse','collapse'); + + switch($this->getGridLines()) + { + case TTableGridLines::Horizontal : $writer->addAttribute('rules','rows'); break; + case TTableGridLines::Vertical : $writer->addAttribute('rules','cols'); break; + case TTableGridLines::Both : $writer->addAttribute('rules','all'); break; + } + + parent::addAttributesToRender($writer); + } + + /** + * @return string the URL of the background image for the table + */ + public function getBackImageUrl() + { + return $this->_backImageUrl===null?'':$this->_backImageUrl; + } + + /** + * Sets the URL of the background image for the table + * @param string the URL + */ + public function setBackImageUrl($value) + { + $this->_backImageUrl=$value; + } + + /** + * @return THorizontalAlign the horizontal alignment of the contents within the table, defaults to THorizontalAlign::NotSet. + */ + public function getHorizontalAlign() + { + return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; + } + + /** + * Sets the horizontal alignment of the contents within the table. + * @param THorizontalAlign the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); + } + + /** + * @return integer cellpadding of the table. Defaults to -1, meaning not set. + */ + public function getCellPadding() + { + return $this->_cellPadding===null?-1:$this->_cellPadding; + } + + /** + * @param integer cellpadding of the table. A value equal to -1 clears up the setting. + * @throws TInvalidDataValueException if the value is less than -1. + */ + public function setCellPadding($value) + { + if(($this->_cellPadding=TPropertyValue::ensureInteger($value))<-1) + throw new TInvalidDataValueException('tablestyle_cellpadding_invalid'); + } + + /** + * @return integer cellspacing of the table. Defaults to -1, meaning not set. + */ + public function getCellSpacing() + { + return $this->_cellSpacing===null?-1:$this->_cellSpacing; + } + + /** + * @param integer cellspacing of the table. A value equal to -1 clears up the setting. + * @throws TInvalidDataValueException if the value is less than -1. + */ + public function setCellSpacing($value) + { + if(($this->_cellSpacing=TPropertyValue::ensureInteger($value))<-1) + throw new TInvalidDataValueException('tablestyle_cellspacing_invalid'); + } + + /** + * @return TTableGridLines the grid line setting of the table. Defaults to TTableGridLines::None. + */ + public function getGridLines() + { + return $this->_gridLines===null?TTableGridLines::None:$this->_gridLines; + } + + /** + * Sets the grid line style of the table. + * @param TTableGridLines the grid line setting of the table + */ + public function setGridLines($value) + { + $this->_gridLines=TPropertyValue::ensureEnum($value,'TTableGridLines'); + } + + + /** + * @return boolean whether the table borders should be collapsed. Defaults to false. + */ + public function getBorderCollapse() + { + return $this->_borderCollapse===null?false:$this->_borderCollapse; + } + + /** + * @param boolean whether the table borders should be collapsed. + */ + public function setBorderCollapse($value) + { + $this->_borderCollapse=TPropertyValue::ensureBoolean($value); + } +} + +/** + * TTableItemStyle class. + * TTableItemStyle represents the CSS style specific for HTML table item. + * + * @author Qiang Xue + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableItemStyle extends TStyle +{ + /** + * @var THorizontalAlign horizontal alignment of the contents within the table item + */ + private $_horizontalAlign=null; + /** + * @var TVerticalAlign vertical alignment of the contents within the table item + */ + private $_verticalAlign=null; + /** + * @var boolean whether the content wraps within the table item + */ + private $_wrap=null; + + /** + * Sets the style attributes to default values. + * This method overrides the parent implementation by + * resetting additional TTableItemStyle specific attributes. + */ + public function reset() + { + parent::reset(); + $this->_verticalAlign=null; + $this->_horizontalAlign=null; + $this->_wrap=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TTableItemStyle) + { + if($this->_verticalAlign===null && $style->_verticalAlign!==null) + $this->_verticalAlign=$style->_verticalAlign; + if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($this->_wrap===null && $style->_wrap!==null) + $this->_wrap=$style->_wrap; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TTableItemStyle) + { + if($style->_verticalAlign!==null) + $this->_verticalAlign=$style->_verticalAlign; + if($style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($style->_wrap!==null) + $this->_wrap=$style->_wrap; + } + } + + /** + * Adds attributes related to CSS styles to renderer. + * This method overrides the parent implementation. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function addAttributesToRender($writer) + { + if(!$this->getWrap()) + $writer->addStyleAttribute('white-space','nowrap'); + + if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) + $writer->addAttribute('align',strtolower($horizontalAlign)); + + if(($verticalAlign=$this->getVerticalAlign())!==TVerticalAlign::NotSet) + $writer->addAttribute('valign',strtolower($verticalAlign)); + + parent::addAttributesToRender($writer); + } + + /** + * @return THorizontalAlign the horizontal alignment of the contents within the table item, defaults to THorizontalAlign::NotSet. + */ + public function getHorizontalAlign() + { + return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; + } + + /** + * Sets the horizontal alignment of the contents within the table item. + * @param THorizontalAlign the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); + } + + /** + * @return TVerticalAlign the vertical alignment of the contents within the table item, defaults to TVerticalAlign::NotSet. + */ + public function getVerticalAlign() + { + return $this->_verticalAlign===null?TVerticalAlign::NotSet:$this->_verticalAlign; + } + + /** + * Sets the vertical alignment of the contents within the table item. + * @param TVerticalAlign the horizontal alignment + */ + public function setVerticalAlign($value) + { + $this->_verticalAlign=TPropertyValue::ensureEnum($value,'TVerticalAlign'); + } + + /** + * @return boolean whether the content wraps within the table item. Defaults to true. + */ + public function getWrap() + { + return $this->_wrap===null?true:$this->_wrap; + } + + /** + * Sets the value indicating whether the content wraps within the table item. + * @param boolean whether the content wraps within the panel. + */ + public function setWrap($value) + { + $this->_wrap=TPropertyValue::ensureBoolean($value); + } +} + +/** + * THorizontalAlign class. + * THorizontalAlign defines the enumerable type for the possible horizontal alignments in a CSS style. + * + * The following enumerable values are defined: + * - NotSet: the alignment is not specified. + * - Left: left aligned + * - Right: right aligned + * - Center: center aligned + * - Justify: the begin and end are justified + * + * @author Qiang Xue + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class THorizontalAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Left='Left'; + const Right='Right'; + const Center='Center'; + const Justify='Justify'; +} + +/** + * TVerticalAlign class. + * TVerticalAlign defines the enumerable type for the possible vertical alignments in a CSS style. + * + * The following enumerable values are defined: + * - NotSet: the alignment is not specified. + * - Top: top aligned + * - Bottom: bottom aligned + * - Middle: middle aligned + * + * @author Qiang Xue + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TVerticalAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Top='Top'; + const Bottom='Bottom'; + const Middle='Middle'; +} + + +/** + * TTableGridLines class. + * TTableGridLines defines the enumerable type for the possible grid line types of an HTML table. + * + * The following enumerable values are defined: + * - None: no grid lines + * - Horizontal: horizontal grid lines only + * - Vertical: vertical grid lines only + * - Both: both horizontal and vertical grid lines are shown + * + * @author Qiang Xue + * @version $Id: TStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableGridLines extends TEnumerable +{ + const None='None'; + const Horizontal='Horizontal'; + const Vertical='Vertical'; + const Both='Both'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TStyleSheet.php b/gui/baculum/framework/Web/UI/WebControls/TStyleSheet.php new file mode 100644 index 0000000000..5faf52f0be --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TStyleSheet.php @@ -0,0 +1,90 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TStyleSheet.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TStyleSheet class. + * + * TStyleSheet represents the link to a stylesheet file and/or a piece of + * stylesheet code. To specify the link to a CSS file, set {@link setStyleSheetUrl StyleSheetUrl}. + * The child rendering result of TStyleSheet is treated as CSS code and + * is rendered within an appropriate style HTML element. + * Therefore, if the child content is not empty, you should place the TStyleSheet + * control in the head section of your page to conform to the HTML standard. + * If only CSS file URL is specified, you may place the control anywhere on your page + * and the style element will be rendered in the right position. + * + * @author Wei Zhuo + * @version : $ Tue Jul 4 04:38:16 EST 2006 $ + * @package System.Web.UI.WebControls + * @since 3.0.2 + */ +class TStyleSheet extends TControl +{ + /** + * @param string URL to the stylesheet file + */ + public function setStyleSheetUrl($value) + { + $this->setViewState('StyleSheetUrl', $value); + } + + /** + * @return string URL to the stylesheet file + */ + public function getStyleSheetUrl() + { + return $this->getViewState('StyleSheetUrl', ''); + } + + /** + * @return string media type of the CSS (such as 'print', 'screen', etc.). Defaults to empty, meaning the CSS applies to all media types. + */ + public function getMediaType() + { + return $this->getViewState('MediaType',''); + } + + /** + * @param string media type of the CSS (such as 'print', 'screen', etc.). If empty, it means the CSS applies to all media types. + */ + public function setMediaType($value) + { + $this->setViewState('MediaType',$value,''); + } + + /** + * Registers the stylesheet file and content to be rendered. + * This method overrides the parent implementation and is invoked right before rendering. + * @param mixed event parameter + */ + public function onPreRender($param) + { + if(($url=$this->getStyleSheetUrl())!=='') + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url,$this->getMediaType()); + } + + /** + * Renders the control. + * This method overrides the parent implementation and renders nothing. + * @param ITextWriter writer + */ + public function render($writer) + { + if($this->getHasControls()) + { + $writer->write("\n"); + } + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTabPanel.php b/gui/baculum/framework/Web/UI/WebControls/TTabPanel.php new file mode 100644 index 0000000000..0c6128f1d8 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTabPanel.php @@ -0,0 +1,732 @@ + and Qiang Xue + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTabPanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ + +/** + * Class TTabPanel. + * + * TTabPanel displays a tabbed panel. Users can click on the tab bar to switching among + * different tab views. Each tab view is an independent panel that can contain arbitrary content. + * + * If the {@link setAutoSwitch AutoSwitch} property is enabled, the user will be able to switch the active view + * to another one just hovering its corresponding tab caption. + * + * A TTabPanel control consists of one or several {@link TTabView} controls representing the possible + * tab views. At any time, only one tab view is visible (active), which is specified by any of + * the following properties: + * - {@link setActiveViewIndex ActiveViewIndex} - the zero-based integer index of the view in the view collection. + * - {@link setActiveViewID ActiveViewID} - the text ID of the visible view. + * - {@link setActiveView ActiveView} - the visible view instance. + * If both {@link setActiveViewIndex ActiveViewIndex} and {@link setActiveViewID ActiveViewID} + * are set, the latter takes precedence. + * + * TTabPanel uses CSS to specify the appearance of the tab bar and panel. By default, + * an embedded CSS file will be published which contains the default CSS for TTabPanel. + * You may also use your own CSS file by specifying the {@link setCssUrl CssUrl} property. + * The following properties specify the CSS classes used for elements in a TTabPanel: + * - {@link setCssClass CssClass} - the CSS class name for the outer-most div element (defaults to 'tab-panel'); + * - {@link setTabCssClass TabCssClass} - the CSS class name for nonactive tab div elements (defaults to 'tab-normal'); + * - {@link setActiveTabCssClass ActiveTabCssClass} - the CSS class name for the active tab div element (defaults to 'tab-active'); + * - {@link setViewCssClass ViewCssClass} - the CSS class for the div element enclosing view content (defaults to 'tab-view'); + * + * To use TTabPanel, write a template like following: + * + * + * + * content for view 1 + * + * + * content for view 2 + * + * + * content for view 3 + * + * + * + * + * @author Tomasz Wolny and Qiang Xue + * @version $Id: TTabPanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TTabPanel extends TWebControl implements IPostBackDataHandler +{ + private $_dataChanged=false; + + /** + * @return string tag name for the control + */ + protected function getTagName() + { + return 'div'; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TTabView} objects into the {@link getViews Views} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + if($object instanceof TTabView) + $this->getControls()->add($object); + } + + /** + * Returns the index of the active tab view. + * Note, this property may not return the correct index. + * To ensure the correctness, call {@link getActiveView()} first. + * @return integer the zero-based index of the active tab view. If -1, it means no active tab view. Default is 0 (the first view is active). + */ + public function getActiveViewIndex() + { + return $this->getViewState('ActiveViewIndex',0); + } + + /** + * @param integer the zero-based index of the current view in the view collection. -1 if no active view. + * @throws TInvalidDataValueException if the view index is invalid + */ + public function setActiveViewIndex($value) + { + $this->setViewState('ActiveViewIndex',TPropertyValue::ensureInteger($value),0); + } + + /** + * Returns the ID of the active tab view. + * Note, this property may not return the correct ID. + * To ensure the correctness, call {@link getActiveView()} first. + * @return string The ID of the active tab view. Defaults to '', meaning not set. + */ + public function getActiveViewID() + { + return $this->getViewState('ActiveViewID',''); + } + + /** + * @param string The ID of the active tab view. + */ + public function setActiveViewID($value) + { + $this->setViewState('ActiveViewID',$value,''); + } + + /** + * Returns the currently active view. + * This method will examin the ActiveViewID, ActiveViewIndex and Views collection to + * determine which view is currently active. It will update ActiveViewID and ActiveViewIndex accordingly. + * @return TTabView the currently active view, null if no active view + * @throws TInvalidDataValueException if the active view ID or index set previously is invalid + */ + public function getActiveView() + { + $activeView=null; + $views=$this->getViews(); + if(($id=$this->getActiveViewID())!=='') + { + if(($index=$views->findIndexByID($id))>=0) + $activeView=$views->itemAt($index); + else + throw new TInvalidDataValueException('tabpanel_activeviewid_invalid',$id); + } + else if(($index=$this->getActiveViewIndex())>=0) + { + if($index<$views->getCount()) + $activeView=$views->itemAt($index); + else + throw new TInvalidDataValueException('tabpanel_activeviewindex_invalid',$index); + } + else + { + foreach($views as $index=>$view) + { + if($view->getActive()) + { + $activeView=$view; + break; + } + } + } + if($activeView!==null) + $this->activateView($activeView); + return $activeView; + } + + /** + * @param TTabView the view to be activated + * @throws TInvalidOperationException if the view is not in the view collection + */ + public function setActiveView($view) + { + if($this->getViews()->indexOf($view)>=0) + $this->activateView($view); + else + throw new TInvalidOperationException('tabpanel_view_inexistent'); + } + + /** + * @return bool status of automatic tab switch on hover + */ + public function getAutoSwitch() + { + return TPropertyValue::ensureBoolean($this->getViewState('AutoSwitch')); + } + + /** + * @param bool whether to enable automatic tab switch on hover + */ + public function setAutoSwitch($value) + { + $this->setViewState('AutoSwitch',TPropertyValue::ensureBoolean($value)); + } + + + /** + * @return string URL for the CSS file including all relevant CSS class definitions. Defaults to ''. + */ + public function getCssUrl() + { + return $this->getViewState('CssUrl','default'); + } + + /** + * @param string URL for the CSS file including all relevant CSS class definitions. + */ + public function setCssUrl($value) + { + $this->setViewState('CssUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return string CSS class for the whole tab control div. Defaults to 'tab-panel'. + */ + public function getCssClass() + { + $cssClass=parent::getCssClass(); + return $cssClass===''?'tab-panel':$cssClass; + } + + /** + * @return string CSS class for the currently displayed view div. Defaults to 'tab-view'. + */ + public function getViewCssClass() + { + return $this->getViewStyle()->getCssClass(); + } + + /** + * @param string CSS class for the currently displayed view div. + */ + public function setViewCssClass($value) + { + $this->getViewStyle()->setCssClass($value); + } + + /** + * @return TStyle the style for all the view div + */ + public function getViewStyle() + { + if(($style=$this->getViewState('ViewStyle',null))===null) + { + $style=new TStyle; + $style->setCssClass('tab-view'); + $this->setViewState('ViewStyle',$style,null); + } + return $style; + } + + /** + * @return string CSS class for non-active tabs. Defaults to 'tab-normal'. + */ + public function getTabCssClass() + { + return $this->getTabStyle()->getCssClass(); + } + + /** + * @param string CSS class for non-active tabs. + */ + public function setTabCssClass($value) + { + $this->getTabStyle()->setCssClass($value); + } + + /** + * @return TStyle the style for all the inactive tab div + */ + public function getTabStyle() + { + if(($style=$this->getViewState('TabStyle',null))===null) + { + $style=new TStyle; + $style->setCssClass('tab-normal'); + $this->setViewState('TabStyle',$style,null); + } + return $style; + } + + /** + * @return string CSS class for the active tab. Defaults to 'tab-active'. + */ + public function getActiveTabCssClass() + { + return $this->getActiveTabStyle()->getCssClass(); + } + + /** + * @param string CSS class for the active tab. + */ + public function setActiveTabCssClass($value) + { + $this->getActiveTabStyle()->setCssClass($value); + } + + /** + * @return TStyle the style for the active tab div + */ + public function getActiveTabStyle() + { + if(($style=$this->getViewState('ActiveTabStyle',null))===null) + { + $style=new TStyle; + $style->setCssClass('tab-active'); + $this->setViewState('ActiveTabStyle',$style,null); + } + return $style; + } + + /** + * Activates the specified view. + * If there is any other view currently active, it will be deactivated. + * @param TTabView the view to be activated. If null, all views will be deactivated. + */ + protected function activateView($view) + { + $this->setActiveViewIndex(-1); + $this->setActiveViewID(''); + foreach($this->getViews() as $index=>$v) + { + if($view===$v) + { + $this->setActiveViewIndex($index); + $this->setActiveViewID($view->getID(false)); + $view->setActive(true); + } + else + $v->setActive(false); + } + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the control has been changed + */ + public function loadPostData($key,$values) + { + if(($index=$values[$this->getClientID().'_1'])!==null) + { + $index=(int)$index; + $currentIndex=$this->getActiveViewIndex(); + if($currentIndex!==$index) + { + $this->setActiveViewID(''); // clear up view ID + $this->setActiveViewIndex($index); + return $this->_dataChanged=true; + } + } + return false; + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getActiveViewIndex ActiveViewIndex} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + // do nothing + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $this->setCssClass($this->getCssClass()); + parent::addAttributesToRender($writer); + } + + /** + * Registers CSS and JS. + * This method is invoked right before the control rendering, if the control is visible. + * @param mixed event parameter + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getActiveView(); // determine the active view + $this->registerStyleSheet(); + + $page=$this->getPage(); + $page->registerRequiresPostData($this); + $page->registerRequiresPostData($this->getClientID()."_1"); + } + + /** + * Registers the CSS relevant to the TTabControl. + * It will register the CSS file specified by {@link getCssUrl CssUrl}. + * If that is not set, it will use the default CSS. + */ + protected function registerStyleSheet() + { + $url = $this->getCssUrl(); + + if($url === '') { + return; + } + + if($url === 'default') { + $url = $this->getApplication()->getAssetManager()->publishFilePath(dirname(__FILE__).DIRECTORY_SEPARATOR.'assets'.DIRECTORY_SEPARATOR.'tabpanel.css'); + } + + if($url !== '') { + $this->getPage()->getClientScript()->registerStyleSheetFile($url, $url); + } + } + + /** + * Registers the relevant JavaScript. + */ + protected function registerClientScript() + { + $id=$this->getClientID(); + $options=TJavaScript::encode($this->getClientOptions()); + $className=$this->getClientClassName(); + $cs=$this->getPage()->getClientScript(); + $cs->registerPradoScript('tabpanel'); + $code="new $className($options);"; + $cs->registerEndScript("prado:$id", $code); + // ensure an item is always active and visible + $index = $this->getActiveViewIndex(); + if(!$this->getViews()->itemAt($index)->Visible) + $index=0; + $cs->registerHiddenField($id.'_1', $index); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TTabPanel'; + } + + /** + * @return array the options for JavaScript + */ + protected function getClientOptions() + { + $options['ID'] = $this->getClientID(); + $options['ActiveCssClass'] = $this->getActiveTabCssClass(); + $options['NormalCssClass'] = $this->getTabCssClass(); + $viewIDs = array(); + $viewVis = array(); + foreach($this->getViews() as $view) + { + $viewIDs[] = $view->getClientID(); + $viewVis[] = $view->getVisible(); + } + $options['Views'] = $viewIDs; + $options['ViewsVis'] = $viewVis; + $options['AutoSwitch'] = $this->getAutoSwitch(); + + return $options; + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TTabViewCollection control collection + */ + protected function createControlCollection() + { + return new TTabViewCollection($this); + } + + /** + * @return TTabViewCollection list of {@link TTabView} controls + */ + public function getViews() + { + return $this->getControls(); + } + + public function render($writer) + { + $this->registerClientScript(); + parent::render($writer); + } + + /** + * Renders body contents of the tab control. + * @param THtmlWriter the writer used for the rendering purpose. + */ + public function renderContents($writer) + { + $views=$this->getViews(); + if($views->getCount()>0) + { + $writer->writeLine(); + // render tab bar + foreach($views as $view) + { + $view->renderTab($writer); + $writer->writeLine(); + } + // render tab views + foreach($views as $view) + { + $view->renderControl($writer); + $writer->writeLine(); + } + } + } +} + +/** + * TTabView class. + * + * TTabView represents a view in a {@link TTabPanel} control. + * + * The content in a TTabView can be specified by the {@link setText Text} property + * or its child controls. In template syntax, the latter means enclosing the content + * within the TTabView component element. If both are set, {@link getText Text} takes precedence. + * + * Each TTabView is associated with a tab in the tab bar of the TTabPanel control. + * The tab caption is specified by {@link setCaption Caption}. If {@link setNavigateUrl NavigateUrl} + * is set, the tab will contain a hyperlink pointing to the specified URL. In this case, + * clicking on the tab will redirect the browser to the specified URL. + * + * TTabView may be toggled between visible (active) and invisible (inactive) by + * setting the {@link setActive Active} property. + * + * @author Tomasz Wolny and Qiang Xue + * @version $Id: TTabPanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TTabView extends TWebControl +{ + private $_active=false; + + /** + * @return the tag name for the view element + */ + protected function getTagName() + { + return 'div'; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + if(!$this->getActive() && $this->getPage()->getClientSupportsJavaScript()) + $this->getStyle()->setStyleField('display','none'); + + $this->getStyle()->mergeWith($this->getParent()->getViewStyle()); + + parent::addAttributesToRender($writer); + + $writer->addAttribute('id',$this->getClientID()); + } + + /** + * @return string the caption displayed on this tab. Defaults to ''. + */ + public function getCaption() + { + return $this->getViewState('Caption',''); + } + + /** + * @param string the caption displayed on this tab + */ + public function setCaption($value) + { + $this->setViewState('Caption',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the URL of the target page. Defaults to ''. + */ + public function getNavigateUrl() + { + return $this->getViewState('NavigateUrl',''); + } + + /** + * Sets the URL of the target page. + * If not empty, clicking on this tab will redirect the browser to the specified URL. + * @param string the URL of the target page. + */ + public function setNavigateUrl($value) + { + $this->setViewState('NavigateUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the text content displayed on this view. Defaults to ''. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text content to be displayed on this view. + * If this is not empty, the child content of the view will be ignored. + * @param string the text content displayed on this view + */ + public function setText($value) + { + $this->setViewState('Text',TPropertyValue::ensureString($value),''); + } + + /** + * @return boolean whether this tab view is active. Defaults to false. + */ + public function getActive() + { + return $this->_active; + } + + /** + * @param boolean whether this tab view is active. + */ + public function setActive($value) + { + $this->_active=TPropertyValue::ensureBoolean($value); + } + + /** + * Renders body contents of the tab view. + * @param THtmlWriter the writer used for the rendering purpose. + */ + public function renderContents($writer) + { + if(($text=$this->getText())!=='') + $writer->write($text); + else if($this->getHasControls()) + parent::renderContents($writer); + } + + /** + * Renders the tab associated with the tab view. + * @param THtmlWriter the writer for rendering purpose. + */ + public function renderTab($writer) + { + if($this->getVisible(false) && $this->getPage()->getClientSupportsJavaScript()) + { + $writer->addAttribute('id',$this->getClientID().'_0'); + + $style=$this->getActive()?$this->getParent()->getActiveTabStyle():$this->getParent()->getTabStyle(); + $style->addAttributesToRender($writer); + + $writer->renderBeginTag($this->getTagName()); + + $this->renderTabContent($writer); + + $writer->renderEndTag(); + } + } + + /** + * Renders the content in the tab. + * By default, a hyperlink is displayed. + * @param THtmlWriter the HTML writer + */ + protected function renderTabContent($writer) + { + if(($url=$this->getNavigateUrl())==='') + $url='javascript://'; + if(($caption=$this->getCaption())==='') + $caption=' '; + $writer->write("{$caption}"); + } +} + +/** + * TTabViewCollection class. + * + * TTabViewCollection is used to maintain a list of views belong to a {@link TTabPanel}. + * + * @author Tomasz Wolny and Qiang Xue + * @version $Id: TTabPanel.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.1.1 + */ +class TTabViewCollection extends TControlCollection +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing sanity check on the type of new item. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a {@link TTabView} object. + */ + public function insertAt($index,$item) + { + if($item instanceof TTabView) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('tabviewcollection_tabview_required'); + } + + /** + * Finds the index of the tab view whose ID is the same as the one being looked for. + * @param string the explicit ID of the tab view to be looked for + * @return integer the index of the tab view found, -1 if not found. + */ + public function findIndexByID($id) + { + foreach($this as $index=>$view) + { + if($view->getID(false)===$id) + return $index; + } + return -1; + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TTable.php b/gui/baculum/framework/Web/UI/WebControls/TTable.php new file mode 100644 index 0000000000..5be68cbd98 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTable.php @@ -0,0 +1,410 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTable.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableRow class + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); + +/** + * TTable class + * + * TTable displays an HTML table on a Web page. + * + * A table may have {@link setCaption Caption}, whose alignment is specified + * via {@link setCaptionAlign CaptionAlign}. The table cellpadding and cellspacing + * are specified via {@link setCellPadding CellPadding} and {@link setCellSpacing CellSpacing} + * properties, respectively. The {@link setGridLines GridLines} specifies how + * the table should display its borders. The horizontal alignment of the table + * content can be specified via {@link setHorizontalAlign HorizontalAlign}, + * and {@link setBackImageUrl BackImageUrl} can assign a background image to the table. + * + * A TTable maintains a list of {@link TTableRow} controls in its + * {@link getRows Rows} property. Each {@link TTableRow} represents + * an HTML table row. + * + * To populate the table {@link getRows Rows}, you may either use control template + * or dynamically create {@link TTableRow} in code. + * In template, do as follows to create the table rows and cells, + * + * + * + * + * + * + * + * + * + * + * + * + * The above can also be accomplished in code as follows, + * + * $table=new TTable; + * $row=new TTableRow; + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $table->Rows->add($row); + * $row=new TTableRow; + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $cell=new TTableCell; $cell->Text="content"; $row->Cells->add($cell); + * $table->Rows->add($row); + * + * + * @author Qiang Xue + * @version $Id: TTable.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTable extends TWebControl +{ + /** + * @return string tag name for the table + */ + protected function getTagName() + { + return 'table'; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TTableRow} objects into the {@link getRows Rows} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + if($object instanceof TTableRow) + $this->getRows()->add($object); + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableStyle} to be used by the table. + * @return TTableStyle control style to be used + */ + protected function createStyle() + { + return new TTableStyle; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + $border=0; + if($this->getHasStyle()) + { + if($this->getGridLines()!==TTableGridLines::None) + { + if(($border=$this->getBorderWidth())==='') + $border=1; + else + $border=(int)$border; + } + } + $writer->addAttribute('border',"$border"); + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TTableRowCollection control collection + * @see getControls + */ + protected function createControlCollection() + { + return new TTableRowCollection($this); + } + + /** + * @return TTableRowCollection list of {@link TTableRow} controls + */ + public function getRows() + { + return $this->getControls(); + } + + /** + * @return string table caption + */ + public function getCaption() + { + return $this->getViewState('Caption',''); + } + + /** + * @param string table caption + */ + public function setCaption($value) + { + $this->setViewState('Caption',$value,''); + } + + /** + * @return TTableCaptionAlign table caption alignment. Defaults to TTableCaptionAlign::NotSet. + */ + public function getCaptionAlign() + { + return $this->getViewState('CaptionAlign',TTableCaptionAlign::NotSet); + } + + /** + * @param TTableCaptionAlign table caption alignment. + */ + public function setCaptionAlign($value) + { + $this->setViewState('CaptionAlign',TPropertyValue::ensureEnum($value,'TTableCaptionAlign'),TTableCaptionAlign::NotSet); + } + + /** + * @return integer the cellspacing for the table. Defaults to -1, meaning not set. + */ + public function getCellSpacing() + { + if($this->getHasStyle()) + return $this->getStyle()->getCellSpacing(); + else + return -1; + } + + /** + * @param integer the cellspacing for the table. Defaults to -1, meaning not set. + */ + public function setCellSpacing($value) + { + $this->getStyle()->setCellSpacing($value); + } + + /** + * @return integer the cellpadding for the table. Defaults to -1, meaning not set. + */ + public function getCellPadding() + { + if($this->getHasStyle()) + return $this->getStyle()->getCellPadding(); + else + return -1; + } + + /** + * @param integer the cellpadding for the table. Defaults to -1, meaning not set. + */ + public function setCellPadding($value) + { + $this->getStyle()->setCellPadding($value); + } + + /** + * @return THorizontalAlign the horizontal alignment of the table content. Defaults to THorizontalAlign::NotSet. + */ + public function getHorizontalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getHorizontalAlign(); + else + return THorizontalAlign::NotSet; + } + + /** + * @param THorizontalAlign the horizontal alignment of the table content. + */ + public function setHorizontalAlign($value) + { + $this->getStyle()->setHorizontalAlign($value); + } + + /** + * @return TTableGridLines the grid line setting of the table. Defaults to TTableGridLines::None. + */ + public function getGridLines() + { + if($this->getHasStyle()) + return $this->getStyle()->getGridLines(); + else + return TTableGridLines::None; + } + + /** + * @param TTableGridLines the grid line setting of the table + */ + public function setGridLines($value) + { + $this->getStyle()->setGridLines($value); + } + + /** + * @return string the URL of the background image for the table + */ + public function getBackImageUrl() + { + if($this->getHasStyle()) + return $this->getStyle()->getBackImageUrl(); + else + return ''; + } + + /** + * Sets the URL of the background image for the table + * @param string the URL + */ + public function setBackImageUrl($value) + { + $this->getStyle()->setBackImageUrl($value); + } + + /** + * Renders the openning tag for the table control which will render table caption if present. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + parent::renderBeginTag($writer); + if(($caption=$this->getCaption())!=='') + { + if(($align=$this->getCaptionAlign())!==TTableCaptionAlign::NotSet) + $writer->addAttribute('align',strtolower($align)); + $writer->renderBeginTag('caption'); + $writer->write($caption); + $writer->renderEndTag(); + } + } + + /** + * Renders body contents of the table. + * @param THtmlWriter the writer used for the rendering purpose. + */ + public function renderContents($writer) + { + if($this->getHasControls()) + { + $renderTableSection=false; + foreach($this->getControls() as $row) + { + if($row->getTableSection()!==TTableRowSection::Body) + { + $renderTableSection=true; + break; + } + } + if($renderTableSection) + { + $currentSection=TTableRowSection::Header; + $writer->writeLine(); + foreach($this->getControls() as $index=>$row) + { + if(($section=$row->getTableSection())===$currentSection) + { + if($index===0 && $currentSection===TTableRowSection::Header) + $writer->renderBeginTag('thead'); + } + else + { + if($currentSection===TTableRowSection::Header) + { + if($index>0) + $writer->renderEndTag(); + if($section===TTableRowSection::Body) + $writer->renderBeginTag('tbody'); + else + $writer->renderBeginTag('tfoot'); + $currentSection=$section; + } + else if($currentSection===TTableRowSection::Body) + { + $writer->renderEndTag(); + if($section===TTableRowSection::Footer) + $writer->renderBeginTag('tfoot'); + else + throw new TConfigurationException('table_tablesection_outoforder'); + $currentSection=$section; + } + else // Footer + throw new TConfigurationException('table_tablesection_outoforder'); + } + $row->renderControl($writer); + $writer->writeLine(); + } + $writer->renderEndTag(); + } + else + { + $writer->writeLine(); + foreach($this->getControls() as $row) + { + $row->renderControl($writer); + $writer->writeLine(); + } + } + } + } +} + + +/** + * TTableRowCollection class. + * + * TTableRowCollection is used to maintain a list of rows belong to a table. + * + * @author Qiang Xue + * @version $Id: TTable.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableRowCollection extends TControlCollection +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added table row. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TTableRow object. + */ + public function insertAt($index,$item) + { + if($item instanceof TTableRow) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('tablerowcollection_tablerow_required'); + } +} + + +/** + * TTableCaptionAlign class. + * TTableCaptionAlign defines the enumerable type for the possible alignments + * that a table caption can take. + * + * The following enumerable values are defined: + * - NotSet: alignment not specified + * - Top: top aligned + * - Bottom: bottom aligned + * - Left: left aligned + * - Right: right aligned + * + * @author Qiang Xue + * @version $Id: TTable.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableCaptionAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Top='Top'; + const Bottom='Bottom'; + const Left='Left'; + const Right='Right'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTableCell.php b/gui/baculum/framework/Web/UI/WebControls/TTableCell.php new file mode 100644 index 0000000000..96254e623e --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTableCell.php @@ -0,0 +1,222 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTableCell.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TTableCell class. + * + * TTableCell displays a table cell on a Web page. Content of the table cell + * is specified by the {@link setText Text} property. If {@link setText Text} + * is empty, the body contents enclosed by the table cell component tag are rendered. + * Note, {@link setText Text} is not HTML-encoded when displayed. So make sure + * it does not contain dangerous characters. + * + * The horizontal and vertical alignments of the contents in the cell + * are specified via {@link setHorizontalAlign HorizontalAlign} and + * {@link setVerticalAlign VerticalAlign} properties, respectively. + * + * The colspan and rowspan of the cell are specified via {@link setColumnSpan ColumnSpan} + * and {@link setRowSpan RowSpan} properties. And the {@link setWrap Wrap} property + * indicates whether the contents in the cell should be wrapped. + * + * @author Qiang Xue + * @version $Id: TTableCell.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableCell extends TWebControl implements IDataRenderer +{ + /** + * @return string tag name for the table cell + */ + protected function getTagName() + { + return 'td'; + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableItemStyle} to be used by the table cell. + * @return TStyle control style to be used + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * @return string the horizontal alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getHorizontalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getHorizontalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the horizontal alignment of the contents within the table item. + * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center' + * @param string the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->getStyle()->setHorizontalAlign($value); + } + + /** + * @return string the vertical alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getVerticalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getVerticalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the vertical alignment of the contents within the table item. + * Valid values include 'NotSet','Top','Bottom','Middle' + * @param string the horizontal alignment + */ + public function setVerticalAlign($value) + { + $this->getStyle()->setVerticalAlign($value); + } + + /** + * @return integer the columnspan for the table cell, 0 if not set. + */ + public function getColumnSpan() + { + return $this->getViewState('ColumnSpan', 0); + } + + /** + * Sets the columnspan for the table cell. + * @param integer the columnspan for the table cell, 0 if not set. + */ + public function setColumnSpan($value) + { + $this->setViewState('ColumnSpan', TPropertyValue::ensureInteger($value), 0); + } + + /** + * @return integer the rowspan for the table cell, 0 if not set. + */ + public function getRowSpan() + { + return $this->getViewState('RowSpan', 0); + } + + /** + * Sets the rowspan for the table cell. + * @param integer the rowspan for the table cell, 0 if not set. + */ + public function setRowSpan($value) + { + $this->setViewState('RowSpan', TPropertyValue::ensureInteger($value), 0); + } + + /** + * @return boolean whether the text content wraps within a table cell. Defaults to true. + */ + public function getWrap() + { + if($this->getHasStyle()) + return $this->getStyle()->getWrap(); + else + return true; + } + + /** + * Sets the value indicating whether the text content wraps within a table cell. + * @param boolean whether the text content wraps within a table cell. + */ + public function setWrap($value) + { + $this->getStyle()->setWrap($value); + } + + /** + * @return string the text content of the table cell. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text content of the table cell. + * If the text content is empty, body content (child controls) of the cell will be rendered. + * @param string the text content + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + } + + /** + * Returns the text content of the table cell. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text content of the table cell. + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the text content of the table cell. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text content of the table cell. + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if(($colspan=$this->getColumnSpan())>0) + $writer->addAttribute('colspan',"$colspan"); + if(($rowspan=$this->getRowSpan())>0) + $writer->addAttribute('rowspan',"$rowspan"); + } + + /** + * Renders body contents of the table cell. + * @param THtmlWriter the writer used for the rendering purpose. + */ + public function renderContents($writer) + { + if(($text=$this->getText())!=='') + $writer->write($text); + else if($this->getHasControls()) + parent::renderContents($writer); + else + $writer->write(' '); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTableFooterRow.php b/gui/baculum/framework/Web/UI/WebControls/TTableFooterRow.php new file mode 100644 index 0000000000..46d1a3beed --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTableFooterRow.php @@ -0,0 +1,47 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTableFooterRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableRow class. + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); + +/** + * TTableFooterRow class. + * + * TTableFooterRow displays a table footer row. + * + * @author Qiang Xue + * @version $Id: TTableFooterRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.1 + */ +class TTableFooterRow extends TTableRow +{ + /** + * @return string location of a row in a table. Always returns 'Footer'. + */ + public function getTableSection() + { + return 'Footer'; + } + + /** + * @param string location of a row in a table. + * @throws TInvalidOperationException if this method is invoked + */ + public function setTableSection($value) + { + throw new TInvalidOperationException('tablefooterrow_tablesection_readonly'); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTableHeaderCell.php b/gui/baculum/framework/Web/UI/WebControls/TTableHeaderCell.php new file mode 100644 index 0000000000..80036d4154 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTableHeaderCell.php @@ -0,0 +1,124 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTableHeaderCell.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableCell class + */ +Prado::using('System.Web.UI.WebControls.TTableCell'); + + +/** + * TTableHeaderCell class. + * + * TTableHeaderCell displays a table header cell on a Web page. + * + * @author Qiang Xue + * @version $Id: TTableHeaderCell.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableHeaderCell extends TTableCell +{ + /** + * @return string tag name for the table header cell + */ + protected function getTagName() + { + return 'th'; + } + + /** + * Adds attributes to renderer. + * @param THtmlWriter the renderer + */ + protected function addAttributesToRender($writer) + { + parent::addAttributesToRender($writer); + if(($scope=$this->getScope())!==TTableHeaderScope::NotSet) + $writer->addAttribute('scope',$scope===TTableHeaderScope::Row?'row':'col'); + if(($text=$this->getAbbreviatedText())!=='') + $writer->addAttribute('abbr',$text); + if(($text=$this->getCategoryText())!=='') + $writer->addAttribute('axis',$text); + } + + /** + * @return TTableHeaderScope the scope of the cells that the header cell applies to. Defaults to TTableHeaderScope::NotSet. + */ + public function getScope() + { + return $this->getViewState('Scope',TTableHeaderScope::NotSet); + } + + /** + * @param TTableHeaderScope the scope of the cells that the header cell applies to. + */ + public function setScope($value) + { + $this->setViewState('Scope',TPropertyValue::ensureEnum($value,'TTableHeaderScope'),TTableHeaderScope::NotSet); + } + + /** + * @return string the abbr attribute of the HTML th element + */ + public function getAbbreviatedText() + { + return $this->getViewState('AbbreviatedText',''); + } + + /** + * @param string the abbr attribute of the HTML th element + */ + public function setAbbreviatedText($value) + { + $this->setViewState('AbbreviatedText',$value,''); + } + + /** + * @return string the axis attribute of the HTML th element + */ + public function getCategoryText() + { + return $this->getViewState('CategoryText',''); + } + + /** + * @param string the axis attribute of the HTML th element + */ + public function setCategoryText($value) + { + $this->setViewState('CategoryText',$value,''); + } +} + + +/** + * TTableHeaderScope class. + * TTableHeaderScope defines the enumerable type for the possible table scopes that a table header is associated with. + * + * The following enumerable values are defined: + * - NotSet: the scope is not specified + * - Row: the scope is row-wise + * - Column: the scope is column-wise + * + * @author Qiang Xue + * @version $Id: TTableHeaderCell.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableHeaderScope extends TEnumerable +{ + const NotSet='NotSet'; + const Row='Row'; + const Column='Column'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTableHeaderRow.php b/gui/baculum/framework/Web/UI/WebControls/TTableHeaderRow.php new file mode 100644 index 0000000000..166a20e49e --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTableHeaderRow.php @@ -0,0 +1,47 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTableHeaderRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableRow class. + */ +Prado::using('System.Web.UI.WebControls.TTableRow'); + +/** + * TTableHeaderRow class. + * + * TTableHeaderRow displays a table header row. + * + * @author Qiang Xue + * @version $Id: TTableHeaderRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.1 + */ +class TTableHeaderRow extends TTableRow +{ + /** + * @return string location of a row in a table. Always returns 'Header'. + */ + public function getTableSection() + { + return 'Header'; + } + + /** + * @param string location of a row in a table. + * @throws TInvalidOperationException if this method is invoked + */ + public function setTableSection($value) + { + throw new TInvalidOperationException('tableheaderrow_tablesection_readonly'); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTableRow.php b/gui/baculum/framework/Web/UI/WebControls/TTableRow.php new file mode 100644 index 0000000000..a998e6fb94 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTableRow.php @@ -0,0 +1,208 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTableRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TTableCell class + */ +Prado::using('System.Web.UI.WebControls.TTableCell'); + +/** + * TTableRow class. + * + * TTableRow displays a table row. The table cells in the row can be accessed + * via {@link getCells Cells}. The horizontal and vertical alignments of the row + * are specified via {@link setHorizontalAlign HorizontalAlign} and + * {@link setVerticalAlign VerticalAlign} properties, respectively. + * + * @author Qiang Xue + * @version $Id: TTableRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableRow extends TWebControl +{ + /** + * @return string tag name for the table + */ + protected function getTagName() + { + return 'tr'; + } + + /** + * Adds object parsed from template to the control. + * This method adds only {@link TTableCell} objects into the {@link getCells Cells} collection. + * All other objects are ignored. + * @param mixed object parsed from template + */ + public function addParsedObject($object) + { + if($object instanceof TTableCell) + $this->getCells()->add($object); + } + + /** + * Creates a style object for the control. + * This method creates a {@link TTableItemStyle} to be used by the table row. + * @return TStyle control style to be used + */ + protected function createStyle() + { + return new TTableItemStyle; + } + + /** + * Creates a control collection object that is to be used to hold child controls + * @return TTableCellCollection control collection + * @see getControls + */ + protected function createControlCollection() + { + return new TTableCellCollection($this); + } + + /** + * @return TTableCellCollection list of {@link TTableCell} controls + */ + public function getCells() + { + return $this->getControls(); + } + + /** + * @return string the horizontal alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getHorizontalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getHorizontalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the horizontal alignment of the contents within the table item. + * Valid values include 'NotSet', 'Justify', 'Left', 'Right', 'Center' + * @param string the horizontal alignment + */ + public function setHorizontalAlign($value) + { + $this->getStyle()->setHorizontalAlign($value); + } + + /** + * @return string the vertical alignment of the contents within the table item, defaults to 'NotSet'. + */ + public function getVerticalAlign() + { + if($this->getHasStyle()) + return $this->getStyle()->getVerticalAlign(); + else + return 'NotSet'; + } + + /** + * Sets the vertical alignment of the contents within the table item. + * Valid values include 'NotSet','Top','Bottom','Middle' + * @param string the horizontal alignment + */ + public function setVerticalAlign($value) + { + $this->getStyle()->setVerticalAlign($value); + } + + /** + * @return TTableRowSection location of a row in a table. Defaults to TTableRowSection::Body. + */ + public function getTableSection() + { + return $this->getViewState('TableSection',TTableRowSection::Body); + } + + /** + * @param TTableRowSection location of a row in a table. + */ + public function setTableSection($value) + { + $this->setViewState('TableSection',TPropertyValue::ensureEnum($value,'TTableRowSection'),TTableRowSection::Body); + } + + /** + * Renders body contents of the table row + * @param THtmlWriter writer for the rendering purpose + */ + public function renderContents($writer) + { + if($this->getHasControls()) + { + $writer->writeLine(); + foreach($this->getControls() as $cell) + { + $cell->renderControl($writer); + $writer->writeLine(); + } + } + } +} + +/** + * TTableCellCollection class. + * + * TTableCellCollection is used to maintain a list of cells belong to a table row. + * + * @author Qiang Xue + * @version $Id: TTableRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTableCellCollection extends TControlCollection +{ + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added table cell. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TTableCell object. + */ + public function insertAt($index,$item) + { + if($item instanceof TTableCell) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('tablecellcollection_tablecell_required'); + } +} + + +/** + * TTableRowSection class. + * TTableRowSection defines the enumerable type for the possible table sections + * that a {@link TTableRow} can be within. + * + * The following enumerable values are defined: + * - Header: in table header + * - Body: in table body + * - Footer: in table footer + * + * @author Qiang Xue + * @version $Id: TTableRow.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTableRowSection extends TEnumerable +{ + const Header='Header'; + const Body='Body'; + const Footer='Footer'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTemplateColumn.php b/gui/baculum/framework/Web/UI/WebControls/TTemplateColumn.php new file mode 100644 index 0000000000..173476f2aa --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTemplateColumn.php @@ -0,0 +1,256 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTemplateColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TDataGridColumn class file + */ +Prado::using('System.Web.UI.WebControls.TDataGridColumn'); + +/** + * TTemplateColumn class + * + * TTemplateColumn customizes the layout of controls in the column with templates. + * In particular, you can specify {@link setItemTemplate ItemTemplate}, + * {@link setEditItemTemplate EditItemTemplate}, {@link setHeaderTemplate HeaderTemplate} + * and {@link setFooterTemplate FooterTemplate} to customize specific + * type of cells in the column. + * + * Since v3.1.0, TTemplateColumn has introduced two new properties {@link setItemRenderer ItemRenderer} + * and {@link setEditItemRenderer EditItemRenderer} which can be used to specify + * the layout of the datagrid cells in browsing and editing mode. + * A renderer refers to a control class that is to be instantiated as a control. + * For more details, see {@link TRepeater} and {@link TDataList}. + * + * When a renderer and a template are both defined for a type of item, the former + * takes precedence. + * + * @author Qiang Xue + * @version $Id: TTemplateColumn.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTemplateColumn extends TDataGridColumn +{ + /** + * Various item templates + * @var string + */ + private $_itemTemplate=null; + private $_editItemTemplate=null; + private $_headerTemplate=null; + private $_footerTemplate=null; + + /** + * @return string the class name for the item cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getItemRenderer() + { + return $this->getViewState('ItemRenderer',''); + } + + /** + * Sets the item cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the item cells of the column. + * + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the row of the data associated with the datagrid item that this cell resides in. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setItemRenderer($value) + { + $this->setViewState('ItemRenderer',$value,''); + } + + /** + * @return string the class name for the edit item cell renderer. Defaults to empty, meaning not set. + * @since 3.1.0 + */ + public function getEditItemRenderer() + { + return $this->getViewState('EditItemRenderer',''); + } + + /** + * Sets the edit item cell renderer class. + * + * If not empty, the class will be used to instantiate as a child control in the item cell that is in edit mode. + * + * If the class implements {@link IDataRenderer}, the Data property + * will be set as the row of the data associated with the datagrid item that this cell resides in. + * + * @param string the renderer class name in namespace format. + * @since 3.1.0 + */ + public function setEditItemRenderer($value) + { + $this->setViewState('EditItemRenderer',$value,''); + } + + /** + * @return ITemplate the edit item template + */ + public function getEditItemTemplate() + { + return $this->_editItemTemplate; + } + + /** + * @param ITemplate the edit item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setEditItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_editItemTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','EditItemTemplate'); + } + + /** + * @return ITemplate the item template + */ + public function getItemTemplate() + { + return $this->_itemTemplate; + } + + /** + * @param ITemplate the item template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setItemTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_itemTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','ItemTemplate'); + } + + /** + * @return ITemplate the header template + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate the header template. + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setHeaderTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_headerTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','HeaderTemplate'); + } + + /** + * @return ITemplate the footer template + */ + public function getFooterTemplate() + { + return $this->_footerTemplate; + } + + /** + * @param ITemplate the footer template + * @throws TInvalidDataTypeException if the input is not an {@link ITemplate} or not null. + */ + public function setFooterTemplate($value) + { + if($value instanceof ITemplate || $value===null) + $this->_footerTemplate=$value; + else + throw new TInvalidDataTypeException('templatecolumn_template_required','FooterTemplate'); + } + + /** + * Initializes the specified cell to its initial values. + * This method overrides the parent implementation. + * It initializes the cell based on different templates + * (ItemTemplate, EditItemTemplate, HeaderTemplate, FooterTemplate). + * @param TTableCell the cell to be initialized. + * @param integer the index to the Columns property that the cell resides in. + * @param string the type of cell (Header,Footer,Item,AlternatingItem,EditItem,SelectedItem) + */ + public function initializeCell($cell,$columnIndex,$itemType) + { + if($itemType===TListItemType::Item || $itemType===TListItemType::AlternatingItem || $itemType===TListItemType::SelectedItem || $itemType===TListItemType::EditItem) + { + if($itemType===TListItemType::EditItem) + { + if(($classPath=$this->getEditItemRenderer())==='' && ($template=$this->_editItemTemplate)===null) + { + $classPath=$this->getItemRenderer(); + $template=$this->_itemTemplate; + } + } + else + { + $template=$this->_itemTemplate; + $classPath=$this->getItemRenderer(); + } + if($classPath!=='') + { + $control=Prado::createComponent($classPath); + $cell->getControls()->add($control); + if($control instanceof IItemDataRenderer) + { + $control->setItemIndex($cell->getParent()->getItemIndex()); + $control->setItemType($itemType); + } + if($control instanceof IDataRenderer) + $control->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); + } + else if($template!==null) + $template->instantiateIn($cell); + else if($itemType!==TListItemType::EditItem) + $cell->setText(' '); + } + else if($itemType===TListItemType::Header) + { + if(($classPath=$this->getHeaderRenderer())!=='') + $this->initializeHeaderCell($cell,$columnIndex); + else if($this->_headerTemplate!==null) + $this->_headerTemplate->instantiateIn($cell); + else + $this->initializeHeaderCell($cell,$columnIndex); + } + else if($itemType===TListItemType::Footer) + { + if(($classPath=$this->getFooterRenderer())!=='') + $this->initializeFooterCell($cell,$columnIndex); + else if($this->_footerTemplate!==null) + $this->_footerTemplate->instantiateIn($cell); + else + $this->initializeFooterCell($cell,$columnIndex); + } + } + + /** + * Databinds a cell in the column. + * This method is invoked when datagrid performs databinding. + * It populates the content of the cell with the relevant data from data source. + */ + public function dataBindColumn($sender,$param) + { + $item=$sender->getNamingContainer(); + $sender->setData($item->getData()); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTextBox.php b/gui/baculum/framework/Web/UI/WebControls/TTextBox.php new file mode 100644 index 0000000000..969d296964 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTextBox.php @@ -0,0 +1,652 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TTextBox class + * + * TTextBox displays a text box on the Web page for user input. + * The text displayed in the TTextBox control is determined by the + * {@link setText Text} property. You can create a SingleLine, + * a MultiLine, or a Password text box by setting + * the {@link setTextMode TextMode} property. If the TTextBox control + * is a multiline text box, the number of rows it displays is determined + * by the {@link setRows Rows} property, and the {@link setWrap Wrap} property + * can be used to determine whether to wrap the text in the component. + * + * To specify the display width of the text box, in characters, set + * the {@link setColumns Columns} property. To prevent the text displayed + * in the component from being modified, set the {@link setReadOnly ReadOnly} + * property to true. If you want to limit the user input to a specified number + * of characters, set the {@link setMaxLength MaxLength} property. + * To use AutoComplete feature, set the {@link setAutoCompleteType AutoCompleteType} property. + * + * If {@link setAutoPostBack AutoPostBack} is set true, updating the text box + * and then changing the focus out of it will cause postback action. + * And if {@link setCausesValidation CausesValidation} is true, validation will + * also be processed, which can be further restricted within + * a {@link setValidationGroup ValidationGroup}. + * + * WARNING: Be careful if you want to display the text collected via TTextBox. + * Malicious cross-site script may be injected in. You may use {@link getSafeText SafeText} + * to prevent this problem. + * + * NOTE: If you set {@link setWrap Wrap} to false or use {@link setAutoCompleteType AutoCompleteType}, + * the generated HTML output for the textbox will not be XHTML-compatible. + * Currently, no alternatives are available. + * + * @author Qiang Xue + * @version $Id: TTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTextBox extends TWebControl implements IPostBackDataHandler, IValidatable, IDataRenderer +{ + /** + * Default number of rows (for MultiLine text box) + */ + const DEFAULT_ROWS=4; + /** + * Default number of columns (for MultiLine text box) + */ + const DEFAULT_COLUMNS=20; + /** + * @var mixed safe text parser + */ + private static $_safeTextParser=null; + /** + * @var string safe textbox content with javascript stripped off + */ + private $_safeText; + private $_dataChanged=false; + private $_isValid=true; + + /** + * @return string tag name of the textbox + */ + protected function getTagName() + { + return ($this->getTextMode()==='MultiLine')?'textarea':'input'; + } + + /** + * @return boolean whether to render javascript. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether to render javascript. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * Adds attribute name-value pairs to renderer. + * This method overrides the parent implementation with additional textbox specific attributes. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + $page=$this->getPage(); + $page->ensureRenderInForm($this); + if(($uid=$this->getUniqueID())!=='') + $writer->addAttribute('name',$uid); + if(($textMode=$this->getTextMode())===TTextBoxMode::MultiLine) + { + if(($rows=$this->getRows())<=0) + $rows=self::DEFAULT_ROWS; + if(($cols=$this->getColumns())<=0) + $cols=self::DEFAULT_COLUMNS; + $writer->addAttribute('rows',"$rows"); + $writer->addAttribute('cols',"$cols"); + if(!$this->getWrap()) + $writer->addAttribute('wrap','off'); + } + else + { + if($textMode===TTextBoxMode::SingleLine) + { + $writer->addAttribute('type','text'); + if(($text=$this->getText())!=='') + $writer->addAttribute('value',$text); + } + else + { + if($this->getPersistPassword() && ($text=$this->getText())!=='') + $writer->addAttribute('value',$text); + $writer->addAttribute('type','password'); + } + + if(($act=$this->getAutoCompleteType())!=='None') + { + if($act==='Disabled') + $writer->addAttribute('autocomplete','off'); + else if($act==='Search') + $writer->addAttribute('vcard_name','search'); + else if($act==='HomeCountryRegion') + $writer->addAttribute('vcard_name','HomeCountry'); + else if($act==='BusinessCountryRegion') + $writer->addAttribute('vcard_name','BusinessCountry'); + else + { + if(strpos($act,'Business')===0) + $act='Business'.'.'.substr($act,8); + else if(strpos($act,'Home')===0) + $act='Home'.'.'.substr($act,4); + $writer->addAttribute('vcard_name','vCard.'.$act); + } + } + + if(($cols=$this->getColumns())>0) + $writer->addAttribute('size',"$cols"); + if(($maxLength=$this->getMaxLength())>0) + $writer->addAttribute('maxlength',"$maxLength"); + } + if($this->getReadOnly()) + $writer->addAttribute('readonly','readonly'); + $isEnabled=$this->getEnabled(true); + if(!$isEnabled && $this->getEnabled()) // in this case parent will not render 'disabled' + $writer->addAttribute('disabled','disabled'); + if($isEnabled + && $this->getEnableClientScript() + && ( $this->getAutoPostBack() || $textMode===TTextBoxMode::SingleLine) + && $page->getClientSupportsJavaScript()) + { + $this->renderClientControlScript($writer); + } + parent::addAttributesToRender($writer); + } + + /** + * Renders the javascript for textbox. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $cs = $this->getPage()->getClientScript(); + $cs->registerPostBackControl($this->getClientClassName(),$this->getPostBackOptions()); + } + + /** + * Gets the name of the javascript class responsible for performing postback for this control. + * This method overrides the parent implementation. + * @return string the javascript class name + */ + protected function getClientClassName() + { + return 'Prado.WebUI.TTextBox'; + } + + /** + * Gets the post back options for this textbox. + * @return array + */ + protected function getPostBackOptions() + { + $options['ID'] = $this->getClientID(); + $options['EventTarget'] = $this->getUniqueID(); + $options['AutoPostBack'] = $this->getAutoPostBack(); + $options['CausesValidation'] = $this->getCausesValidation(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['TextMode'] = $this->getTextMode(); + return $options; + } + + /** + * Loads user input data. + * This method is primarly used by framework developers. + * @param string the key that can be used to retrieve data from the input data collection + * @param array the input data collection + * @return boolean whether the data of the component has been changed + */ + public function loadPostData($key,$values) + { + $value=$values[$key]; + if($this->getAutoTrim()) + $value=trim($value); + if(!$this->getReadOnly() && $this->getText()!==$value) + { + $this->setText($value); + return $this->_dataChanged=true; + } + else + return false; + } + + /** + * Returns a value indicating whether postback has caused the control data change. + * This method is required by the IPostBackDataHandler interface. + * @return boolean whether postback has caused the control data change. False if the page is not in postback mode. + */ + public function getDataChanged() + { + return $this->_dataChanged; + } + + /** + * Returns the value to be validated. + * This methid is required by IValidatable interface. + * @return mixed the value of the property to be validated. + */ + public function getValidationPropertyValue() + { + return $this->getText(); + } + + /** + * Returns true if this control validated successfully. + * Defaults to true. + * @return bool wether this control validated successfully. + */ + public function getIsValid() + { + return $this->_isValid; + } + /** + * @param bool wether this control is valid. + */ + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } + + /** + * Raises OnTextChanged event. + * This method is invoked when the value of the {@link getText Text} + * property changes on postback. + * If you override this method, be sure to call the parent implementation to ensure + * the invocation of the attached event handlers. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onTextChanged($param) + { + $this->raiseEvent('OnTextChanged',$this,$param); + } + + /** + * Raises postdata changed event. + * This method is required by {@link IPostBackDataHandler} interface. + * It is invoked by the framework when {@link getText Text} property + * is changed on postback. + * This method is primarly used by framework developers. + */ + public function raisePostDataChangedEvent() + { + if($this->getAutoPostBack() && $this->getCausesValidation()) + $this->getPage()->validate($this->getValidationGroup()); + $this->onTextChanged(null); + } + + /** + * Renders the body content of the textbox when it is in MultiLine text mode. + * @param THtmlWriter the writer for rendering + */ + public function renderContents($writer) + { + if($this->getTextMode()==='MultiLine') + $writer->write(THttpUtility::htmlEncode($this->getText())); + } + + /** + * Renders an additional line-break after the opening tag when it + * is in MultiLine text mode. + * @param THtmlWriter the writer used for the rendering purpose^M + */ + public function renderBeginTag($writer) + { + parent::renderBeginTag($writer); + if($this->getTextMode()==='MultiLine') + $writer->write("\n"); + } + + /** + * @return TTextBoxAutoCompleteType the AutoComplete type of the textbox + */ + public function getAutoCompleteType() + { + return $this->getViewState('AutoCompleteType',TTextBoxAutoCompleteType::None); + } + + /** + * @param TTextBoxAutoCompleteType the AutoComplete type of the textbox, default value is TTextBoxAutoCompleteType::None. + * @throws TInvalidDataValueException if the input parameter is not a valid AutoComplete type + */ + public function setAutoCompleteType($value) + { + $this->setViewState('AutoCompleteType',TPropertyValue::ensureEnum($value,'TTextBoxAutoCompleteType'),TTextBoxAutoCompleteType::None); + } + + /** + * @return boolean a value indicating whether an automatic postback to the server + * will occur whenever the user modifies the text in the TTextBox control and + * then tabs out of the component. Defaults to false. + */ + public function getAutoPostBack() + { + return $this->getViewState('AutoPostBack',false); + } + + /** + * Sets the value indicating if postback automatically. + * An automatic postback to the server will occur whenever the user + * modifies the text in the TTextBox control and then tabs out of the component. + * @param boolean the value indicating if postback automatically + */ + public function setAutoPostBack($value) + { + $this->setViewState('AutoPostBack',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean a value indicating whether the input text should be trimmed spaces. Defaults to false. + */ + public function getAutoTrim() + { + return $this->getViewState('AutoTrim',false); + } + + /** + * Sets the value indicating if the input text should be trimmed spaces + * @param boolean the value indicating if the input text should be trimmed spaces + */ + public function setAutoTrim($value) + { + $this->setViewState('AutoTrim',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether postback event trigger by this text box will cause input validation, default is true. + */ + public function getCausesValidation() + { + return $this->getViewState('CausesValidation',true); + } + + /** + * @param boolean whether postback event trigger by this text box will cause input validation. + */ + public function setCausesValidation($value) + { + $this->setViewState('CausesValidation',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return integer the display width of the text box in characters, default is 0 meaning not set. + */ + public function getColumns() + { + return $this->getViewState('Columns',0); + } + + /** + * Sets the display width of the text box in characters. + * @param integer the display width, set it 0 to clear the setting + */ + public function setColumns($value) + { + $this->setViewState('Columns',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return integer the maximum number of characters allowed in the text box, default is 0 meaning not set. + */ + public function getMaxLength() + { + return $this->getViewState('MaxLength',0); + } + + /** + * Sets the maximum number of characters allowed in the text box. + * @param integer the maximum length, set it 0 to clear the setting + */ + public function setMaxLength($value) + { + $this->setViewState('MaxLength',TPropertyValue::ensureInteger($value),0); + } + + /** + * @return boolean whether the textbox is read only, default is false. + */ + public function getReadOnly() + { + return $this->getViewState('ReadOnly',false); + } + + /** + * @param boolean whether the textbox is read only + */ + public function setReadOnly($value) + { + $this->setViewState('ReadOnly',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return integer the number of rows displayed in a multiline text box, default is 4 + */ + public function getRows() + { + return $this->getViewState('Rows',self::DEFAULT_ROWS); + } + + /** + * Sets the number of rows displayed in a multiline text box. + * @param integer the number of rows + */ + public function setRows($value) + { + $this->setViewState('Rows',TPropertyValue::ensureInteger($value),self::DEFAULT_ROWS); + } + + /** + * @return boolean whether password should be displayed in the textbox during postback. Defaults to false. This property only applies when TextMode='Password'. + */ + public function getPersistPassword() + { + return $this->getViewState('PersistPassword',false); + } + + /** + * @param boolean whether password should be displayed in the textbox during postback. This property only applies when TextMode='Password'. + */ + public function setPersistPassword($value) + { + $this->setViewState('PersistPassword',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return string the text content of the TTextBox control. + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * Sets the text content of the TTextBox control. + * @param string the text content + */ + public function setText($value) + { + $this->setViewState('Text',$value,''); + $this->_safeText = null; + } + + /** + * Returns the text content of the TTextBox control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link getText()}. + * @return string the text content of the TTextBox control. + * @see getText + * @since 3.1.0 + */ + public function getData() + { + return $this->getText(); + } + + /** + * Sets the text content of the TTextBox control. + * This method is required by {@link IDataRenderer}. + * It is the same as {@link setText()}. + * @param string the text content of the TTextBox control. + * @see setText + * @since 3.1.0 + */ + public function setData($value) + { + $this->setText($value); + } + + /** + * @return string safe text content with javascript stripped off + */ + public function getSafeText() + { + if($this->_safeText===null) + $this->_safeText=$this->getSafeTextParser()->parse($this->getText()); + return $this->_safeText; + } + + /** + * @return mixed safe text parser + */ + protected function getSafeTextParser() + { + if(!self::$_safeTextParser) + self::$_safeTextParser=Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser'); + return self::$_safeTextParser; + } + + /** + * @return TTextBoxMode the behavior mode of the TTextBox component. Defaults to TTextBoxMode::SingleLine. + */ + public function getTextMode() + { + return $this->getViewState('TextMode',TTextBoxMode::SingleLine); + } + + /** + * Sets the behavior mode of the TTextBox component. + * @param TTextBoxMode the text mode + * @throws TInvalidDataValueException if the input value is not a valid text mode. + */ + public function setTextMode($value) + { + $this->setViewState('TextMode',TPropertyValue::ensureEnum($value,'TTextBoxMode'),TTextBoxMode::SingleLine); + } + + /** + * @return string the group of validators which the text box causes validation upon postback + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group of validators which the text box causes validation upon postback + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + /** + * @return boolean whether the text content wraps within a multiline text box. Defaults to true. + */ + public function getWrap() + { + return $this->getViewState('Wrap',true); + } + + /** + * Sets the value indicating whether the text content wraps within a multiline text box. + * @param boolean whether the text content wraps within a multiline text box. + */ + public function setWrap($value) + { + $this->setViewState('Wrap',TPropertyValue::ensureBoolean($value),true); + } +} + +/** + * TTextBoxMode class. + * TTextBoxMode defines the enumerable type for the possible mode + * that a {@link TTextBox} control could be at. + * + * The following enumerable values are defined: + * - SingleLine: the textbox will be a regular single line input + * - MultiLine: the textbox will be a textarea allowing multiple line input + * - Password: the textbox will hide user input like a password input box + * + * @author Qiang Xue + * @version $Id: TTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTextBoxMode extends TEnumerable +{ + const SingleLine='SingleLine'; + const MultiLine='MultiLine'; + const Password='Password'; +} + +/** + * TTextBoxAutoCompleteType class. + * TTextBoxAutoCompleteType defines the possible AutoComplete type that is supported + * by a {@link TTextBox} control. + * + * @author Qiang Xue + * @version $Id: TTextBox.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TTextBoxAutoCompleteType extends TEnumerable +{ + const BusinessCity='BusinessCity'; + const BusinessCountryRegion='BusinessCountryRegion'; + const BusinessFax='BusinessFax'; + const BusinessPhone='BusinessPhone'; + const BusinessState='BusinessState'; + const BusinessStreetAddress='BusinessStreetAddress'; + const BusinessUrl='BusinessUrl'; + const BusinessZipCode='BusinessZipCode'; + const Cellular='Cellular'; + const Company='Company'; + const Department='Department'; + const Disabled='Disabled'; + const DisplayName='DisplayName'; + const Email='Email'; + const FirstName='FirstName'; + const Gender='Gender'; + const HomeCity='HomeCity'; + const HomeCountryRegion='HomeCountryRegion'; + const HomeFax='HomeFax'; + const Homepage='Homepage'; + const HomePhone='HomePhone'; + const HomeState='HomeState'; + const HomeStreetAddress='HomeStreetAddress'; + const HomeZipCode='HomeZipCode'; + const JobTitle='JobTitle'; + const LastName='LastName'; + const MiddleName='MiddleName'; + const None='None'; + const Notes='Notes'; + const Office='Office'; + const Pager='Pager'; + const Search='Search'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TTextHighlighter.php b/gui/baculum/framework/Web/UI/WebControls/TTextHighlighter.php new file mode 100644 index 0000000000..64878d4951 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTextHighlighter.php @@ -0,0 +1,213 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTextHighlighter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.3rdParty.TextHighlighter.Text.Highlighter',false); +Prado::using('System.3rdParty.TextHighlighter.Text.Highlighter.Renderer.Html',false); +Prado::using('System.Web.UI.WebControls.TTextProcessor'); + + +/** + * TTextHighlighter class. + * + * TTextHighlighter does syntax highlighting its body content, including + * static text and rendering results of child controls. + * You can set {@link setLanguage Language} to specify what kind of syntax + * the body content is. Currently, TTextHighlighter supports the following + * languages: ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, MYSQL, PERL, + * PHP, PYTHON, RUBY, SQL, XML and PRADO, where PRADO refers to PRADO template + * syntax. By setting {@link setShowLineNumbers ShowLineNumbers} + * to true, the highlighted result may be shown with line numbers. + * + * Note, TTextHighlighter requires {@link THead} to be placed on the page template + * because it needs to insert some CSS styles. + * + * @author Wei Zhuo + * @version $Id: TTextHighlighter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTextHighlighter extends TTextProcessor +{ + private static $_lineNumberStyle=array(TTextHighlighterLineNumberStyle::Li => HL_NUMBERS_LI, TTextHighlighterLineNumberStyle::Table => HL_NUMBERS_TABLE); + + /** + * @return string tag name of the panel + */ + protected function getTagName() + { + return 'div'; + } + + /** + * @return string language whose syntax is to be used for highlighting. Defaults to 'php'. + */ + public function getLanguage() + { + return $this->getViewState('Language', 'php'); + } + + /** + * @param string language (case-insensitive) whose syntax is to be used for highlighting. + * Valid values are those file names (without suffix) that are contained + * in '3rdParty/TextHighlighter/Text/Highlighter'. Currently, the following languages are supported: + * ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT, + * MYSQL, PERL, PHP, PRADO, PYTHON, RUBY, SQL, XML + * If a language is not supported, it will be displayed as plain text. + */ + public function setLanguage($value) + { + $this->setViewState('Language', $value, 'php'); + } + + /** + * @return boolean whether to show line numbers in the highlighted result. + */ + public function getShowLineNumbers() + { + return $this->getViewState('ShowLineNumbers', false); + } + + /** + * @param boolean whether to show line numbers in the highlighted result. + */ + public function setShowLineNumbers($value) + { + $this->setViewState('ShowLineNumbers', TPropertyValue::ensureBoolean($value), false); + } + + /** + * @return boolean true will show "Copy Code" link. Defaults to false. + */ + public function getEnableCopyCode() + { + return $this->getViewState('CopyCode', false); + } + + /** + * @param boolean true to show the "Copy Code" link. + */ + public function setEnableCopyCode($value) + { + $this->setViewState('CopyCode', TPropertyValue::ensureBoolean($value), false); + } + + /** + * @return TTextHighlighterLineNumberStyle style of row number, Table by default + */ + public function getLineNumberStyle() + { + return $this->getViewState('LineNumberStyle', TTextHighlighterLineNumberStyle::Table); + } + + /** + * @param TTextHighlighterLineNumberStyle style of row number + */ + public function setLineNumberStyle($value) + { + $this->setViewState('LineNumberStyle', TPropertyValue::ensureEnum($value,'TTextHighlighterLineNumberStyle')); + } + + /** + * @return integer tab size. Defaults to 4. + */ + public function getTabSize() + { + return $this->getViewState('TabSize', 4); + } + + /** + * @param integer tab size + */ + public function setTabSize($value) + { + $this->setViewState('TabSize', TPropertyValue::ensureInteger($value)); + } + + /** + * Registers css style for the highlighted result. + * This method overrides parent implementation. + * @param THtmlWriter writer + */ + public function onPreRender($writer) + { + parent::onPreRender($writer); + $this->registerStyleSheet(); + } + + /** + * Registers the stylesheet for presentation. + */ + protected function registerStyleSheet() + { + $cs=$this->getPage()->getClientScript(); + $cssFile=Prado::getPathOfNamespace('System.3rdParty.TextHighlighter.highlight','.css'); + $cssKey='prado:TTextHighlighter:'.$cssFile; + if(!$cs->isStyleSheetFileRegistered($cssKey)) + $cs->registerStyleSheetFile($cssKey, $this->publishFilePath($cssFile)); + } + + /** + * Processes a text string. + * This method is required by the parent class. + * @param string text string to be processed + * @return string the processed text result + */ + public function processText($text) + { + try + { + $highlighter=Text_Highlighter::factory($this->getLanguage()); + } + catch(Exception $e) + { + $highlighter=false; + } + if($highlighter===false) + return ('
      '.htmlentities(trim($text)).'
      '); + + $options["use_language"]=true; + $options["tabsize"] = $this->getTabSize(); + if ($this->getShowLineNumbers()) + $options["numbers"] = self::$_lineNumberStyle[$this->getLineNumberStyle()]; + $highlighter->setRenderer(new Text_Highlighter_Renderer_Html($options)); + return $highlighter->highlight(trim($text)); + } + + /** + * @return string header template with "Copy code" link. + */ + protected function getHeaderTemplate() + { + $id = $this->getClientID(); + return TJavaScript::renderScriptBlock("new Prado.WebUI.TTextHighlighter('{$id}');"); + } + + public function render($writer) + { + $this->getPage()->getClientScript()->registerPradoScript('prado'); + parent::render($writer); + } + + +} + +/** + * @author Wei Zhuo + * @version $Id: TTextHighlighter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTextHighlighterLineNumberStyle extends TEnumerable +{ + const Li='Li'; + const Table='Table'; +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TTextProcessor.php b/gui/baculum/framework/Web/UI/WebControls/TTextProcessor.php new file mode 100644 index 0000000000..e569e780fe --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TTextProcessor.php @@ -0,0 +1,86 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TTextProcessor.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TTextProcessor class. + * + * TTextProcessor is the base class for classes that process or transform + * text content into different forms. The text content to be processed + * is specified by {@link setText Text} property. If it is not set, the body + * content enclosed within the processor control will be processed and rendered. + * The body content includes static text strings and the rendering result + * of child controls. + * + * Note, all child classes must implement {@link processText} method. + * + * @author Qiang Xue + * @version $Id: TTextProcessor.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI + * @since 3.0.1 + */ +abstract class TTextProcessor extends TWebControl +{ + /** + * Processes a text string. + * This method must be implemented by child classes. + * @param string text string to be processed + * @return string the processed text result + */ + abstract public function processText($text); + + /** + * HTML-decodes static text. + * This method overrides parent implementation. + * @param mixed object to be added as body content + */ + public function addParsedObject($object) + { + if(is_string($object)) + $object=html_entity_decode($object,ENT_QUOTES,'UTF-8'); + parent::addParsedObject($object); + } + + /** + * @return string text to be processed + */ + public function getText() + { + return $this->getViewState('Text',''); + } + + /** + * @param string text to be processed + */ + public function setText($value) + { + $this->setViewState('Text',$value); + } + + /** + * Renders body content. + * This method overrides the parent implementation by replacing + * the body content with the processed text content. + * @param THtmlWriter writer + */ + public function renderContents($writer) + { + if(($text=$this->getText())==='' && $this->getHasControls()) + { + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + parent::renderContents($htmlWriter); + $text=$htmlWriter->flush(); + } + if($text!=='') + $writer->write($this->processText($text)); + } + +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TValidationSummary.php b/gui/baculum/framework/Web/UI/WebControls/TValidationSummary.php new file mode 100644 index 0000000000..00c96c90dd --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TValidationSummary.php @@ -0,0 +1,536 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TValidationSummary.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TValidationSummary class + * + * TValidationSummary displays a summary of validation errors inline on a Web page, + * in a message box, or both. By default, a validation summary will collect + * {@link TBaseValidator::getErrorMessage ErrorMessage} of all failed validators + * on the page. If {@link getValidationGroup ValidationGroup} is not + * empty, only those validators who belong to the group will show their error messages + * in the summary. + * + * The summary can be displayed as a list, as a bulleted list, or as a single + * paragraph based on the {@link setDisplayMode DisplayMode} property. + * The messages shown can be prefixed with {@link setHeaderText HeaderText}. + * + * The summary can be displayed on the Web page and in a message box by setting + * the {@link setShowSummary ShowSummary} and {@link setShowMessageBox ShowMessageBox} + * properties, respectively. Note, the latter is only effective when + * {@link setEnableClientScript EnableClientScript} is true. + * + * @author Qiang Xue + * @version $Id: TValidationSummary.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TValidationSummary extends TWebControl +{ + /** + * @var TClientSideValidationSummaryOptions validation client side options. + */ + private $_clientSide; + + /** + * Constructor. + * This method sets the foreground color to red. + */ + public function __construct() + { + parent::__construct(); + $this->setForeColor('red'); + } + + /** + * @return TValidationSummaryDisplayStyle the style of displaying the error messages. Defaults to TValidationSummaryDisplayStyle::Fixed. + */ + public function getDisplay() + { + return $this->getViewState('Display',TValidationSummaryDisplayStyle::Fixed); + } + + /** + * @param TValidationSummaryDisplayStyle the style of displaying the error messages + */ + public function setDisplay($value) + { + $this->setViewState('Display',TPropertyValue::ensureEnum($value,'TValidationSummaryDisplayStyle'),TValidationSummaryDisplayStyle::Fixed); + } + + /** + * @return string the header text displayed at the top of the summary + */ + public function getHeaderText() + { + return $this->getViewState('HeaderText',''); + } + + /** + * Sets the header text to be displayed at the top of the summary + * @param string the header text + */ + public function setHeaderText($value) + { + $this->setViewState('HeaderText',$value,''); + } + + /** + * @return TValidationSummaryDisplayMode the mode of displaying error messages. Defaults to TValidationSummaryDisplayMode::BulletList. + */ + public function getDisplayMode() + { + return $this->getViewState('DisplayMode',TValidationSummaryDisplayMode::BulletList); + } + + /** + * @param TValidationSummaryDisplayMode the mode of displaying error messages + */ + public function setDisplayMode($value) + { + $this->setViewState('DisplayMode',TPropertyValue::ensureEnum($value,'TValidationSummaryDisplayMode'),TValidationSummaryDisplayMode::BulletList); + } + + /** + * @return boolean whether the TValidationSummary component updates itself using client-side script. Defaults to true. + */ + public function getEnableClientScript() + { + return $this->getViewState('EnableClientScript',true); + } + + /** + * @param boolean whether the TValidationSummary component updates itself using client-side script. + */ + public function setEnableClientScript($value) + { + $this->setViewState('EnableClientScript',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the validation summary is displayed in a message box. Defaults to false. + */ + public function getShowMessageBox() + { + return $this->getViewState('ShowMessageBox',false); + } + + /** + * @param boolean whether the validation summary is displayed in a message box. + */ + public function setShowMessageBox($value) + { + $this->setViewState('ShowMessageBox',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether the validation summary is displayed inline. Defaults to true. + */ + public function getShowSummary() + { + return $this->getViewState('ShowSummary',true); + } + + /** + * @param boolean whether the validation summary is displayed inline. + */ + public function setShowSummary($value) + { + $this->setViewState('ShowSummary',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether scroll summary into viewport or not. Defaults to true. + */ + public function getScrollToSummary() + { + return $this->getViewState('ScrollToSummary',true); + } + + /** + * @param boolean whether scroll summary into viewport or not. + */ + public function setScrollToSummary($value) + { + $this->setViewState('ScrollToSummary',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return boolean whether the validation summary should be anchored. Defaults to false. + */ + public function getShowAnchor() + { + return $this->getViewState('ShowAnchor',false); + } + + /** + * @param boolean whether the validation summary should be anchored. + */ + public function setShowAnchor($value) + { + $this->setViewState('ShowAnchor',TPropertyValue::ensureBoolean($value),false); + } + + /** + * Gets the auto-update for this summary. + * @return boolean automatic client-side summary updates. Defaults to true. + */ + public function getAutoUpdate() + { + return $this->getViewState('AutoUpdate', true); + } + + /** + * Sets the summary to auto-update on the client-side + * @param boolean true for automatic summary updates. + */ + public function setAutoUpdate($value) + { + $this->setViewState('AutoUpdate', TPropertyValue::ensureBoolean($value), true); + } + + /** + * @return string the group which this validator belongs to + */ + public function getValidationGroup() + { + return $this->getViewState('ValidationGroup',''); + } + + /** + * @param string the group which this validator belongs to + */ + public function setValidationGroup($value) + { + $this->setViewState('ValidationGroup',$value,''); + } + + protected function addAttributesToRender($writer) + { + $display=$this->getDisplay(); + $visible=$this->getEnabled(true) && count($this->getErrorMessages()) > 0; + if(!$visible) + { + if($display===TValidationSummaryDisplayStyle::None || $display===TValidationSummaryDisplayStyle::Dynamic) + $writer->addStyleAttribute('display','none'); + else + $writer->addStyleAttribute('visibility','hidden'); + } + $writer->addAttribute('id',$this->getClientID()); + parent::addAttributesToRender($writer); + } + + /** + * Render the javascript for validation summary. + * @param array list of options for validation summary. + */ + protected function renderJsSummary() + { + if(!$this->getEnabled(true) || !$this->getEnableClientScript()) + return; + $cs = $this->getPage()->getClientScript(); + $cs->registerPradoScript('validator'); + + //need to register the validation manager is validation summary is alone. + $formID=$this->getPage()->getForm()->getClientID(); + $scriptKey = "TBaseValidator:$formID"; + if($this->getEnableClientScript() && !$cs->isEndScriptRegistered($scriptKey)) + { + $manager['FormID'] = $formID; + $options = TJavaScript::encode($manager); + $cs->registerPradoScript('validator'); + $cs->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});"); + } + + + $options=TJavaScript::encode($this->getClientScriptOptions()); + $script = "new Prado.WebUI.TValidationSummary({$options});"; + $cs->registerEndScript($this->getClientID(), $script); + } + + /** + * Get a list of options for the client-side javascript validation summary. + * @return array list of options for the summary + */ + protected function getClientScriptOptions() + { + $options['ID'] = $this->getClientID(); + $options['FormID'] = $this->getPage()->getForm()->getClientID(); + if($this->getShowMessageBox()) + $options['ShowMessageBox']=true; + if(!$this->getShowSummary()) + $options['ShowSummary']=false; + + $options['ScrollToSummary']=$this->getScrollToSummary(); + $options['HeaderText']=$this->getHeaderText(); + $options['DisplayMode']=$this->getDisplayMode(); + + $options['Refresh'] = $this->getAutoUpdate(); + $options['ValidationGroup'] = $this->getValidationGroup(); + $options['Display'] = $this->getDisplay(); + + if($this->_clientSide!==null) + $options = array_merge($options,$this->_clientSide->getOptions()->toArray()); + + return $options; + } + + /** + * @return TClientSideValidationSummaryOptions client-side validation summary + * event options. + */ + public function getClientSide() + { + if($this->_clientSide===null) + $this->_clientSide = $this->createClientScript(); + return $this->_clientSide; + } + + /** + * @return TClientSideValidationSummaryOptions javascript validation summary + * event options. + */ + protected function createClientScript() + { + return new TClientSideValidationSummaryOptions; + } + /** + * Get the list of validation error messages. + * @return array list of validator error messages. + */ + protected function getErrorMessages() + { + $validators=$this->getPage()->getValidators($this->getValidationGroup()); + $messages = array(); + foreach($validators as $validator) + { + if(!$validator->getIsValid() && ($msg=$validator->getErrorMessage())!=='') + //$messages[] = $validator->getAnchoredMessage($msg); + $messages[] = $msg; + } + return $messages; + } + + /** + * Overrides parent implementation by rendering TValidationSummary-specific presentation. + * @return string the rendering result + */ + public function renderContents($writer) + { + $this->renderJsSummary(); + if($this->getShowSummary()) + { +// $this->setStyle('display:block'); + switch($this->getDisplayMode()) + { + case TValidationSummaryDisplayMode::SimpleList: + $this->renderList($writer); + break; + case TValidationSummaryDisplayMode::SingleParagraph: + $this->renderSingleParagraph($writer); + break; + case TValidationSummaryDisplayMode::BulletList: + $this->renderBulletList($writer); + break; + case TValidationSummaryDisplayMode::HeaderOnly: + $this->renderHeaderOnly($writer); + break; + } + } + } + + /** + * Render the validation summary as a simple list. + * @param array list of messages + * @param string the header text + * @return string summary list + */ + protected function renderList($writer) + { + $header=$this->getHeaderText(); + $messages=$this->getErrorMessages(); + $content = ''; + if(strlen($header)) + $content.= $header."
      \n"; + foreach($messages as $message) + $content.="$message
      \n"; + $writer->write($content); + } + + /** + * Render the validation summary as a paragraph. + * @param array list of messages + * @param string the header text + * @return string summary paragraph + */ + protected function renderSingleParagraph($writer) + { + $header=$this->getHeaderText(); + $messages=$this->getErrorMessages(); + $content = $header; + foreach($messages as $message) + $content.= ' '.$message; + $writer->write($content); + } + + /** + * Render the validation summary as a bullet list. + * @param array list of messages + * @param string the header text + * @return string summary bullet list + */ + protected function renderBulletList($writer) + { + $header=$this->getHeaderText(); + $messages=$this->getErrorMessages(); + $content = $header; + if(count($messages)>0) + { + $content .= "
        \n"; + foreach($messages as $message) + $content.= '
      • '.$message."
      • \n"; + $content .= "
      \n"; + } + $writer->write($content); + } + + /** + * Render the validation summary header text only. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function renderHeaderOnly($writer) + { + $writer->write($this->getHeaderText()); + } +} + +/** + * TClientSideValidationSummaryOptions class. + * + * Client-side validation summary events such as {@link setOnHideSummary + * OnHideSummary} and {@link setOnShowSummary OnShowSummary} can be modified + * through the {@link TBaseValidator:: getClientSide ClientSide} property of a + * validation summary. + * + * The OnHideSummary event is raise when the validation summary + * requests to hide the messages. + * + * The OnShowSummary event is raised when the validation summary + * requests to show the messages. + * + * See the quickstart documentation for further details. + * + * @author Wei Zhuo + * @version $Id: TValidationSummary.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TClientSideValidationSummaryOptions extends TClientSideOptions +{ + /** + * @return string javascript code for client-side OnHideSummary event. + */ + public function getOnHideSummary() + { + return $this->getOption('OnHideSummary'); + } + + /** + * Client-side OnHideSummary validation summary event is raise when all the + * validators are valid. This will override the default client-side + * validation summary behaviour. + * @param string javascript code for client-side OnHideSummary event. + */ + public function setOnHideSummary($javascript) + { + $this->setFunction('OnHideSummary', $javascript); + } + + /** + * Client-side OnShowSummary event is raise when one or more validators are + * not valid. This will override the default client-side validation summary + * behaviour. + * @param string javascript code for client-side OnShowSummary event. + */ + public function setOnShowSummary($javascript) + { + $this->setFunction('OnShowSummary', $javascript); + } + + /** + * @return string javascript code for client-side OnShowSummary event. + */ + public function getOnShowSummary() + { + return $this->getOption('OnShowSummary'); + } + + /** + * Ensure the string is a valid javascript function. The code block + * is enclosed with "function(summary, validators){ }" block. + * @param string javascript code. + * @return string javascript function code. + */ + protected function ensureFunction($javascript) + { + return "function(summary, validators){ {$javascript} }"; + } +} + + +/** + * TValidationSummaryDisplayMode class. + * TValidationSummaryDisplayMode defines the enumerable type for the possible modes + * that a {@link TValidationSummary} can organize and display the collected error messages. + * + * The following enumerable values are defined: + * - SimpleList: the error messages are displayed as a list without any decorations. + * - SingleParagraph: the error messages are concatenated together into a paragraph. + * - BulletList: the error messages are displayed as a bulleted list. + * - HeaderOnly: only the HeaderText will be display. + * + * @author Qiang Xue + * @version $Id: TValidationSummary.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TValidationSummaryDisplayMode extends TEnumerable +{ + const SimpleList='SimpleList'; + const SingleParagraph='SingleParagraph'; + const BulletList='BulletList'; + const HeaderOnly='HeaderOnly'; +} + + +/** + * TValidationSummaryDisplay class. + * TValidationSummaryDisplay defines the enumerable type for the possible styles + * that a {@link TValidationSummary} can display the collected error messages. + * + * The following enumerable values are defined: + * - None: the error messages are not displayed + * - Dynamic: the error messages are dynamically added to display as the corresponding validators fail + * - Fixed: Similar to Dynamic except that the error messages physically occupy the page layout (even though they may not be visible) + * + * @author Qiang Xue + * @version $Id: TValidationSummary.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TValidationSummaryDisplayStyle extends TEnumerable +{ + const None='None'; + const Dynamic='Dynamic'; + const Fixed='Fixed'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TWebControl.php b/gui/baculum/framework/Web/UI/WebControls/TWebControl.php new file mode 100644 index 0000000000..0da4d88c67 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TWebControl.php @@ -0,0 +1,500 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TWebControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TStyle and TWebAdapter definition + */ +Prado::using('System.Web.UI.WebControls.TStyle'); +Prado::using('System.Web.UI.WebControls.TWebControlAdapter'); +Prado::using('System.Web.UI.WebControls.TWebControlDecorator'); + +/** + * TWebControl class + * + * TWebControl is the base class for controls that share a common set + * of UI-related properties and methods. TWebControl-derived controls + * are usually associated with HTML tags. They thus have tag name, attributes + * and body contents. You can override {@link getTagName} to specify the tag name, + * {@link addAttributesToRender} to specify the attributes to be rendered, + * and {@link renderContents} to customize the body content rendering. + * TWebControl encapsulates a set of properties related with CSS style fields, + * such as {@link getBackColor BackColor}, {@link getBorderWidth BorderWidth}, etc. + * + * Subclasses of TWebControl typically needs to override {@link addAttributesToRender} + * and {@link renderContents}. The former is used to render the attributes + * of the HTML tag associated with the control, while the latter is to render + * the body contents enclosed within the HTML tag. + * + * @author Qiang Xue + * @version $Id: TWebControl.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWebControl extends TControl implements IStyleable +{ + /** + * @var boolean ensures the inclusion the id in the tag rendering. + */ + private $_ensureid=false; + + /** + * @var TWebControlDecorator this render things before and after both the open and close tag + */ + protected $_decorator; + + + /** + * Subclasses can override getEnsureId or just set this property. eg. If your subclass + * control does work with javascript and your class wants to flag that it requires an id + * to operate properly. Once set to true, it stays that way. + * @param boolean pass true to enable enforcement of the tag attribute id. + */ + public function setEnsureId($value) + { + $this->_ensureid |= TPropertyValue::ensureBoolean($value); + } + + /** + * @return whether this web control must have an id + */ + public function getEnsureId() + { + return $this->_ensureid; + } + + /** + * @return TWebControlDecorator + */ + public function getDecorator($create=true) + { + if($create && !$this->_decorator) + $this->_decorator = Prado::createComponent('TWebControlDecorator', $this); + return $this->_decorator; + } + + /** + * Copies basic control attributes from another control. + * Properties including AccessKey, ToolTip, TabIndex, Enabled + * and Attributes are copied. + * @param TWebControl source control + */ + public function copyBaseAttributes(TWebControl $control) + { + $this->setAccessKey($control->getAccessKey()); + $this->setToolTip($control->getToolTip()); + $this->setTabIndex($control->getTabIndex()); + if(!$control->getEnabled()) + $this->setEnabled(false); + if($control->getHasAttributes()) + $this->getAttributes()->copyFrom($control->getAttributes()); + } + + /** + * @return string the access key of the control + */ + public function getAccessKey() + { + return $this->getViewState('AccessKey',''); + } + + /** + * Sets the access key of the control. + * Only one-character string can be set, or an exception will be raised. + * Pass in an empty string if you want to disable access key. + * @param string the access key to be set + * @throws TInvalidDataValueException if the access key is specified with more than one character + */ + public function setAccessKey($value) + { + if(strlen($value)>1) + throw new TInvalidDataValueException('webcontrol_accesskey_invalid',get_class($this),$value); + $this->setViewState('AccessKey',$value,''); + } + + /** + * @return string the background color of the control + */ + public function getBackColor() + { + if($style=$this->getViewState('Style',null)) + return $style->getBackColor(); + else + return ''; + } + + /** + * @param string the background color of the control + */ + public function setBackColor($value) + { + $this->getStyle()->setBackColor($value); + } + + /** + * @return string the border color of the control + */ + public function getBorderColor() + { + if($style=$this->getViewState('Style',null)) + return $style->getBorderColor(); + else + return ''; + } + + /** + * @param string the border color of the control + */ + public function setBorderColor($value) + { + $this->getStyle()->setBorderColor($value); + } + + /** + * @return string the border style of the control + */ + public function getBorderStyle() + { + if($style=$this->getViewState('Style',null)) + return $style->getBorderStyle(); + else + return ''; + } + + /** + * @param string the border style of the control + */ + public function setBorderStyle($value) + { + $this->getStyle()->setBorderStyle($value); + } + + /** + * @return string the border width of the control + */ + public function getBorderWidth() + { + if($style=$this->getViewState('Style',null)) + return $style->getBorderWidth(); + else + return ''; + } + + /** + * @param string the border width of the control + */ + public function setBorderWidth($value) + { + $this->getStyle()->setBorderWidth($value); + } + + /** + * @return TFont the font of the control + */ + public function getFont() + { + return $this->getStyle()->getFont(); + } + + /** + * @return string the foreground color of the control + */ + public function getForeColor() + { + if($style=$this->getViewState('Style',null)) + return $style->getForeColor(); + else + return ''; + } + + /** + * @param string the foreground color of the control + */ + public function setForeColor($value) + { + $this->getStyle()->setForeColor($value); + } + + /** + * @return string the height of the control + */ + public function getHeight() + { + if($style=$this->getViewState('Style',null)) + return $style->getHeight(); + else + return ''; + } + + /** + * @param TDisplayStyle display style of the control, default is TDisplayStyle::Fixed + */ + public function setDisplay($value) + { + $this->getStyle()->setDisplayStyle($value); + } + + /** + * @return TDisplayStyle display style of the control, default is TDisplayStyle::Fixed + */ + public function getDisplay() + { + return $this->getStyle()->getDisplayStyle(); + } + + /** + * @param string the css class of the control + */ + public function setCssClass($value) + { + $this->getStyle()->setCssClass($value); + } + + /** + * @return string the css class of the control + */ + public function getCssClass() + { + if($style=$this->getViewState('Style',null)) + return $style->getCssClass(); + else + return ''; + } + + /** + * @param string the height of the control + */ + public function setHeight($value) + { + $this->getStyle()->setHeight($value); + } + + /** + * @return boolean whether the control has defined any style information + */ + public function getHasStyle() + { + return $this->getViewState('Style',null)!==null; + } + + /** + * Creates a style object to be used by the control. + * This method may be overriden by controls to provide customized style. + * @return TStyle the default style created for TWebControl + */ + protected function createStyle() + { + return new TStyle; + } + + /** + * @return TStyle the object representing the css style of the control + */ + public function getStyle() + { + if($style=$this->getViewState('Style',null)) + return $style; + else + { + $style=$this->createStyle(); + $this->setViewState('Style',$style,null); + return $style; + } + } + + /** + * Sets the css style string of the control. + * The style string will be prefixed to the styles set via other control properties (e.g. Height, Width). + * @param string the css style string + * @throws TInvalidDataValueException if the parameter is not a string + */ + public function setStyle($value) + { + if(is_string($value)) + $this->getStyle()->setCustomStyle($value); + else + throw new TInvalidDataValueException('webcontrol_style_invalid',get_class($this)); + } + + /** + * Removes all style data. + */ + public function clearStyle() + { + $this->clearViewState('Style'); + } + + /** + * @return integer the tab index of the control + */ + public function getTabIndex() + { + return $this->getViewState('TabIndex',0); + } + + /** + * Sets the tab index of the control. + * Pass 0 if you want to disable tab index. + * @param integer the tab index to be set + */ + public function setTabIndex($value) + { + $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0); + } + + /** + * Returns the tag name used for this control. + * By default, the tag name is 'span'. + * You can override this method to provide customized tag names. + * @return string tag name of the control to be rendered + */ + protected function getTagName() + { + return 'span'; + } + + /** + * @return string the tooltip of the control + */ + public function getToolTip() + { + return $this->getViewState('ToolTip',''); + } + + /** + * Sets the tooltip of the control. + * Pass an empty string if you want to disable tooltip. + * @param string the tooltip to be set + */ + public function setToolTip($value) + { + $this->setViewState('ToolTip',$value,''); + } + + /** + * @return string the width of the control + */ + public function getWidth() + { + if($style=$this->getViewState('Style',null)) + return $style->getWidth(); + else + return ''; + } + + /** + * @param string the width of the control + */ + public function setWidth($value) + { + $this->getStyle()->setWidth($value); + } + + + /** + * If your subclass overrides the onPreRender method be sure to call + * this method through parent::onPreRender($param); so your sub-class can be decorated, + * among other things. + * @param TEventParameter event parameter to be passed to the event handlers + */ + public function onPreRender($param) { + if($decorator = $this->getDecorator(false)) + $decorator->instantiate(); + + parent::onPreRender($param); + } + + /** + * Adds attribute name-value pairs to renderer. + * By default, the method will render 'id', 'accesskey', 'disabled', + * 'tabindex', 'title' and all custom attributes. + * The method can be overriden to provide customized attribute rendering. + * @param THtmlWriter the writer used for the rendering purpose + */ + protected function addAttributesToRender($writer) + { + if($this->getID()!=='' || $this->getEnsureId()) + $writer->addAttribute('id',$this->getClientID()); + if(($accessKey=$this->getAccessKey())!=='') + $writer->addAttribute('accesskey',$accessKey); + if(!$this->getEnabled()) + $writer->addAttribute('disabled','disabled'); + if(($tabIndex=$this->getTabIndex())>0) + $writer->addAttribute('tabindex',"$tabIndex"); + if(($toolTip=$this->getToolTip())!=='') + $writer->addAttribute('title',$toolTip); + if($style=$this->getViewState('Style',null)) + $style->addAttributesToRender($writer); + if($this->getHasAttributes()) + { + foreach($this->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + } + + /** + * Renders the control. + * This method overrides the parent implementation by replacing it with + * the following sequence: + * - {@link renderBeginTag} + * - {@link renderContents} + * - {@link renderEndTag} + * @param THtmlWriter the writer used for the rendering purpose + */ + public function render($writer) + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + + /** + * Renders the openning tag for the control (including attributes) + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderBeginTag($writer) + { + if($decorator = $this->getDecorator(false)) { + $decorator->renderPreTagText($writer); + $this->addAttributesToRender($writer); + $writer->renderBeginTag($this->getTagName()); + $decorator->renderPreContentsText($writer); + } else { + $this->addAttributesToRender($writer); + $writer->renderBeginTag($this->getTagName()); + } + } + + /** + * Renders the body content enclosed between the control tag. + * By default, child controls and text strings will be rendered. + * You can override this method to provide customized content rendering. + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderContents($writer) + { + parent::renderChildren($writer); + } + + /** + * Renders the closing tag for the control + * @param THtmlWriter the writer used for the rendering purpose + */ + public function renderEndTag($writer) + { + if($decorator = $this->getDecorator(false)) { + $decorator->renderPostContentsText($writer); + $writer->renderEndTag(); + $decorator->renderPostTagText($writer); + } else + $writer->renderEndTag($writer); + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TWebControlAdapter.php b/gui/baculum/framework/Web/UI/WebControls/TWebControlAdapter.php new file mode 100644 index 0000000000..5de1541945 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TWebControlAdapter.php @@ -0,0 +1,71 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TWebControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +/** + * TWebControlAdapter class + * + * TWebControlAdapter is the base class for adapters that customize + * rendering for the Web control to which the adapter is attached. + * It may be used to modify the default markup or behavior for specific + * browsers. + * + * @author Qiang Xue + * @version $Id: TWebControlAdapter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWebControlAdapter extends TControlAdapter +{ + /** + * Renders the control to which the adapter is attached. + * It calls {@link renderBeginTag}, {@link renderContents} and + * {@link renderEndTag} in order. + * @param THtmlWriter writer for the rendering purpose + */ + public function render($writer) + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + + /** + * Renders the openning tag for the attached control. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderBeginTag($writer) + { + $this->getControl()->renderBeginTag($writer); + } + + /** + * Renders the body contents within the attached control tag. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderContents($writer) + { + $this->getControl()->renderContents($writer); + } + + /** + * Renders the closing tag for the attached control. + * Default implementation calls the attached control's corresponding method. + * @param THtmlWriter writer for the rendering purpose + */ + public function renderEndTag($writer) + { + $this->getControl()->renderEndTag($writer); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TWebControlDecorator.php b/gui/baculum/framework/Web/UI/WebControls/TWebControlDecorator.php new file mode 100644 index 0000000000..9b1c365286 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TWebControlDecorator.php @@ -0,0 +1,384 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TWebControlDecorator.php 2541 2008-10-21 15:05:13Z qiang.xue $ + * @package System.Web.UI.WebControls + */ + + +/** + * TWebControlDecorator class + * + * A TWebControlDecorator can be applied to a {@link TWebControl} to customize its rendering. + * TWebControlDecorator can add custom html code before and after both the open and close + * tag of a {@link TWebControl}. + * The html code can be an user-defined text or an external template file that will be + * instantiated and rendered in place. + * + * This is an easy way to have your look and feel depend upon the theme instead of writing + * specific html in your templates to achieve your website desires. + * Here is an example of how to code your theme skin: + * + * + * + * + *
      + * + * + * + *
      + * + *
      + *
      + * + * The order of the inclusion of the decoration into the page goes like this: + * * PreTagTemplate + * * PreTagText + * * TWebControl Open Tag Rendered + * * PreContentsText + * * PreContentsTemplate + * * TWebControl Children Rendered + * * PostContentsTemplate + * * PostContentsText + * * TWebControl CloseTag Rendered + * * PostTagText + * * PostTagTemplate + * + * + * @author Brad Anderson + * @version $Id: TWebControlDecorator.php 2541 2008-10-21 15:05:13Z qiang.xue $ + * @package System.Web.UI.WebControls + * @since 3.2 + */ + +class TWebControlDecorator extends TComponent { + + /** + * @var boolean tells if there should only be decoration around the inner content + */ + private $_internalonly; + + /** + * @var boolean tells if the decoration uses state in its templates. If there are no templates + * in the instance of the decoration this variable is unused. + */ + private $_usestate = false; + + /** + * @var TWebControl the control to decorate + */ + private $_control; + + /** + * @var TControl to tell the decorator where to place the outer controls + */ + private $_outercontrol; + + /** + * @var boolean This tells if the Templates have been + */ + private $_addedTemplateDecoration=false; + + + /** + * @var string the text that goes before the open tag + */ + private $_pretagtext = ''; + /** + * @var string the text that goes after the open tag + */ + private $_precontentstext = ''; + /** + * @var string the text that goes before the close tag + */ + private $_postcontentstext = ''; + /** + * @var string the text that goes after the close tag + */ + private $_posttagtext = ''; + + + + /** + * @var TTemplate the template that goes before the open tag + */ + private $_pretagtemplate; + /** + * @var TTemplate the template that goes after the open tag + */ + private $_precontentstemplate; + /** + * @var TTemplate the template that goes before the close tag + */ + private $_postcontentstemplate; + /** + * @var TTemplate the template that goes after the close tag + */ + private $_posttagtemplate; + + /** + * Constructor. + * Initializes the control . + * @param TWebControl The control that is to be decorated. + * @param boolean whether decoration is just around the inner content + */ + public function __construct($control, $onlyinternal = false) { + $this->_control = $control; + $this->_internalonly = $onlyinternal; + } + + /** + * @return boolean if the templates in this decoration need state. This defaults to false + */ + public function getUseState() + { + return $this->_usestate; + } + + /** + * @param boolean $value true to tell the decoration that the templates need state and should be + * placed in a control step before the state is saved. + */ + public function setUseState($value) + { + $this->_usestate = TPropertyValue::ensureBoolean($value); + } + + /** + * @return string gets the text before the open tag in the TWebControl + */ + public function getPreTagText() { + return $this->_pretagtext; + } + + /** + * @param string sets the text before the open tag in the TWebControl + */ + public function setPreTagText($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_pretagtext = TPropertyValue::ensureString($value); + } + + + /** + * @return string the text after the open tag in the TWebControl + */ + public function getPreContentsText() { + return $this->_precontentstext; + } + + /** + * @param string sets the text after the open tag in the TWebControl + */ + public function setPreContentsText($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_precontentstext = TPropertyValue::ensureString($value); + } + + + /** + * @return string the text before the close tag in the TWebControl + */ + public function getPostContentsText() { + return $this->_postcontentstext; + } + + /** + * @param string sets the text before the close tag in the TWebControl + */ + public function setPostContentsText($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_postcontentstext = TPropertyValue::ensureString($value); + } + + + /** + * @return string the text before the close tag in the TWebControl + */ + public function getPostTagText() { + return $this->_posttagtext; + } + + /** + * @param string sets the text after the close tag in the TWebControl + */ + public function setPostTagText($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_posttagtext = TPropertyValue::ensureString($value); + } + + + /** + * @return TTemplate|null the template before the open tag in the TWebControl. Defaults to null. + */ + public function getPreTagTemplate() { + return $this->_pretagtemplate; + } + + /** + * @param TTemplate sets the template before the open tag in the TWebControl + */ + public function setPreTagTemplate($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_pretagtemplate = $value; + } + + + /** + * @return TTemplate|null the template after the open tag in the TWebControl. Defaults to null. + */ + public function getPreContentsTemplate() { + return $this->_precontentstemplate; + } + + /** + * @param TTemplate sets the template after the open tag in the TWebControl + */ + public function setPreContentsTemplate($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_precontentstemplate = $value; + } + + + /** + * @return TTemplate|null the template before the close tag in the TWebControl. Defaults to null. + */ + public function getPostContentsTemplate() { + return $this->_postcontentstemplate; + } + + /** + * @param TTemplate sets the template before the close tag in the TWebControl + */ + public function setPostContentsTemplate($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_postcontentstemplate = $value; + } + + + /** + * @return TTemplate|null the template after the close tag in the TWebControl. Defaults to null. + */ + public function getPostTagTemplate() { + return $this->_posttagtemplate; + } + + /** + * @param TTemplate sets the template before the close tag in the TWebControl + */ + public function setPostTagTemplate($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_posttagtemplate = $value; + } + + /** + * this is a framework call. The Text decoration can't + * influence the object hierarchy because they are rendered into into the writer directly. + * This call attaches the ensureTemplateDecoration to the TPage onSaveStateComplete so + * these controls don't have page states. This is as close to not influencing the page as possible. + */ + public function instantiate($outercontrol = null) { + if($this->getPreTagTemplate() || $this->getPreContentsTemplate() || + $this->getPostContentsTemplate() || $this->getPostTagTemplate()) { + + $this->_outercontrol = $outercontrol; + if($this->getUseState()) + $this->ensureTemplateDecoration(); + else + $this->_control->getPage()->onSaveStateComplete[] = array($this, 'ensureTemplateDecoration'); + } + } + + + /** + * This method places the templates around the open and close tag. This takes a parameter which is + * to specify the control to get the outer template decoration. If no outer control is specified + * @param TComponent this indicates the component or control to get the outer tag elements, just in case it's + * different than attached TWebControl. If none is provided, the outer templates default to the attached + * control + * @return boolean returns true if the template decorations have been added + */ + public function ensureTemplateDecoration($sender=null, $param=null) { + + $control = $this->_control; + $outercontrol = $this->_outercontrol; + if($outercontrol === null) + $outercontrol = $control; + + if($this->_addedTemplateDecoration) + return $this->_addedTemplateDecoration; + + $this->_addedTemplateDecoration = true; + + if($this->getPreContentsTemplate()) + { + $precontents = Prado::createComponent('TCompositeControl'); + $this->getPreContentsTemplate()->instantiateIn($precontents); + $control->getControls()->insertAt(0, $precontents); + } + + if($this->getPostContentsTemplate()) + { + $postcontents = Prado::createComponent('TCompositeControl'); + $this->getPostContentsTemplate()->instantiateIn($postcontents); + $control->getControls()->add($postcontents); + } + + if(!$outercontrol->getParent()) + return $this->_addedTemplateDecoration; + + + if($this->getPreTagTemplate()) + { + $pretag = Prado::createComponent('TCompositeControl'); + $this->getPreTagTemplate()->instantiateIn($pretag); + $outercontrol->getParent()->getControls()->insertBefore($outercontrol, $pretag); + } + + if($this->getPostTagTemplate()) + { + $posttag = Prado::createComponent('TCompositeControl'); + $this->getPostTagTemplate()->instantiateIn($posttag); + $outercontrol->getParent()->getControls()->insertAfter($outercontrol, $posttag); + } + return true; + } + + + /** + * This method places the pre tag text into the {@link TTextWriter} + * @param {@link TTextWriter} the writer to which the text is written + */ + public function renderPreTagText($writer) { + $writer->write($this->getPreTagText()); + } + + /** + * This method places the pre contents text into the {@link TTextWriter} + * @param {@link TTextWriter} the writer to which the text is written + */ + public function renderPreContentsText($writer) { + $writer->write($this->getPreContentsText()); + } + + /** + * This method places the post contents text into the {@link TTextWriter} + * @param {@link TTextWriter} the writer to which the text is written + */ + public function renderPostContentsText($writer) { + $writer->write($this->getPostContentsText()); + } + + /** + * This method places the post tag text into the {@link TTextWriter} + * @param {@link TTextWriter} the writer to which the text is written + */ + public function renderPostTagText($writer) { + $writer->write($this->getPostTagText()); + } +} diff --git a/gui/baculum/framework/Web/UI/WebControls/TWizard.php b/gui/baculum/framework/Web/UI/WebControls/TWizard.php new file mode 100644 index 0000000000..67cbc4e4cb --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TWizard.php @@ -0,0 +1,2161 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + */ + +Prado::using('System.Web.UI.WebControls.TMultiView'); +Prado::using('System.Web.UI.WebControls.TPanel'); +Prado::using('System.Web.UI.WebControls.TButton'); +Prado::using('System.Web.UI.WebControls.TLinkButton'); +Prado::using('System.Web.UI.WebControls.TImageButton'); +Prado::using('System.Web.UI.WebControls.TDataList'); +Prado::using('System.Web.UI.WebControls.TWizardNavigationButtonStyle'); + +/** + * Class TWizard. + * + * TWizard splits a large form and presents the user with a series of smaller + * forms to complete. TWizard is analogous to the installation wizard commonly + * used to install software in Windows. + * + * The smaller forms are called wizard steps ({@link TWizardStep}, which can be accessed via + * {@link getWizardSteps WizardSteps}. In template, wizard steps can be added + * into a wizard using the following syntax, + * + * + * + * content in step 1, may contain other controls + * + * + * content in step 2, may contain other controls + * + * + * + * + * Each wizard step can be one of the following types: + * - Start : the first step in the wizard. + * - Step : the internal steps in the wizard. + * - Finish : the last step that allows user interaction. + * - Complete : the step that shows a summary to user (no interaction is allowed). + * - Auto : the step type is determined by wizard automatically. + * At any time, only one step is visible to end-users, which can be obtained + * by {@link getActiveStep ActiveStep}. Its index in the step collection is given by + * {@link getActiveStepIndex ActiveStepIndex}. + * + * Wizard content can be customized in many ways. + * + * The layout of a wizard consists of four parts: header, step content, navigation + * and side bar. Their content are affected by the following properties, respectively, + * - header: {@link setHeaderText HeaderText} and {@link setHeaderTemplate HeaderTemplate}. + * If both are present, the latter takes precedence. + * - step: {@link getWizardSteps WizardSteps}. + * - navigation: {@link setStartNavigationTemplate StartNavigationTemplate}, + * {@link setStepNavigationTemplate StepNavigationTemplate}, + * {@link setFinishNavigationTemplate FinishNavigationTemplate}. + * Default templates will be used if above templates are not set. + * - side bar: {@link setSideBarTemplate SideBarTemplate}. + * A default template will be used if this template is not set. + * Its visibility is toggled by {@link setShowSideBar ShowSideBar}. + * + * The style of these wizard layout components can be customized via the following style properties, + * - header: {@link getHeaderStyle HeaderStyle}. + * - step: {@link getStepStyle StepStyle}. + * - navigation: {@link getNavigationStyle NavigationStyle}, + * {@link getStartNextButtonStyle StartNextButtonStyle}, + * {@link getStepNextButtonStyle StepNextButtonStyle}, + * {@link getStepPreviousButtonStyle StepPreviousButtonStyle}, + * {@link getFinishPreviousButtonStyle FinishPreviousButtonStyle}, + * {@link getFinishCompleteButtonStyle FinishCompleteButtonStyle}, + * {@link getCancelButtonStyle CancelButtonStyle}. + * - side bar: {@link getSideBarStyle SideBarStyle} and {@link getSideBarButtonStyle SideBarButtonStyle}. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizard extends TWebControl implements INamingContainer +{ + /** + * Wizard step types. + * @deprecated deprecated since version 3.0.4 (use TWizardStepType constants instead) + */ + const ST_AUTO='Auto'; + const ST_START='Start'; + const ST_STEP='Step'; + const ST_FINISH='Finish'; + const ST_COMPLETE='Complete'; + /** + * Navigation commands. + */ + const CMD_PREVIOUS='PreviousStep'; + const CMD_NEXT='NextStep'; + const CMD_CANCEL='Cancel'; + const CMD_COMPLETE='Complete'; + const CMD_MOVETO='MoveTo'; + /** + * Side bar button ID + */ + const ID_SIDEBAR_BUTTON='SideBarButton'; + /** + * Side bar data list + */ + const ID_SIDEBAR_LIST='SideBarList'; + + /** + * @var TMultiView multiview that contains the wizard steps + */ + private $_multiView=null; + /** + * @var mixed navigation template for the start step. + */ + private $_startNavigationTemplate=null; + /** + * @var mixed navigation template for internal steps. + */ + private $_stepNavigationTemplate=null; + /** + * @var mixed navigation template for the finish step. + */ + private $_finishNavigationTemplate=null; + /** + * @var mixed template for wizard header. + */ + private $_headerTemplate=null; + /** + * @var mixed template for the side bar. + */ + private $_sideBarTemplate=null; + /** + * @var TWizardStepCollection + */ + private $_wizardSteps=null; + /** + * @var TPanel container of the wizard header + */ + private $_header; + /** + * @var TPanel container of the wizard step content + */ + private $_stepContent; + /** + * @var TPanel container of the wizard side bar + */ + private $_sideBar; + /** + * @var TPanel navigation panel + */ + private $_navigation; + /** + * @var TWizardNavigationContainer container of the start navigation + */ + private $_startNavigation; + /** + * @var TWizardNavigationContainer container of the step navigation + */ + private $_stepNavigation; + /** + * @var TWizardNavigationContainer container of the finish navigation + */ + private $_finishNavigation; + /** + * @var boolean whether ActiveStepIndex was already set + */ + private $_activeStepIndexSet=false; + /** + * @var TDataList side bar data list. + */ + private $_sideBarDataList; + /** + * @var boolean whether navigation should be cancelled (a status set in OnSideBarButtonClick) + */ + private $_cancelNavigation=false; + + /** + * @return string tag name for the wizard + */ + protected function getTagName() + { + return 'div'; + } + + /** + * Adds {@link TWizardStep} objects into step collection. + * This method overrides the parent implementation and is + * invoked when template is being instantiated. + * @param mixed object instantiated in template + */ + public function addParsedObject($object) + { + if($object instanceof TWizardStep) + $this->getWizardSteps()->add($object); + } + + /** + * @return TWizardStep the currently active wizard step + */ + public function getActiveStep() + { + return $this->getMultiView()->getActiveView(); + } + + /** + * @param TWizardStep step to be activated + * @throws TInvalidOperationException if the step is not in the wizard step collection + */ + public function setActiveStep($step) + { + if(($index=$this->getWizardSteps()->indexOf($step))<0) + throw new TInvalidOperationException('wizard_step_invalid'); + $this->setActiveStepIndex($index); + } + + /** + * @return integer the zero-based index of the active wizard step + */ + public function getActiveStepIndex() + { + return $this->getMultiView()->getActiveViewIndex(); + } + + /** + * @param integer the zero-based index of the wizard step to be activated + */ + public function setActiveStepIndex($value) + { + $value=TPropertyValue::ensureInteger($value); + $multiView=$this->getMultiView(); + if($multiView->getActiveViewIndex()!==$value) + { + $multiView->setActiveViewIndex($value); + $this->_activeStepIndexSet=true; + if($this->_sideBarDataList!==null && $this->getSideBarTemplate()!==null) + { + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + } + } + } + + /** + * @return TWizardStepCollection collection of wizard steps + */ + public function getWizardSteps() + { + if($this->_wizardSteps===null) + $this->_wizardSteps=new TWizardStepCollection($this); + return $this->_wizardSteps; + } + + /** + * @return boolean whether to display a cancel button in each wizard step. Defaults to false. + */ + public function getShowCancelButton() + { + return $this->getViewState('ShowCancelButton',false); + } + + /** + * @param boolean whether to display a cancel button in each wizard step. + */ + public function setShowCancelButton($value) + { + $this->setViewState('ShowCancelButton',TPropertyValue::ensureBoolean($value),false); + } + + /** + * @return boolean whether to display a side bar that contains links to wizard steps. Defaults to true. + */ + public function getShowSideBar() + { + return $this->getViewState('ShowSideBar',true); + } + + /** + * @param boolean whether to display a side bar that contains links to wizard steps. + */ + public function setShowSideBar($value) + { + $this->setViewState('ShowSideBar',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return ITemplate navigation template for the start step. Defaults to null. + */ + public function getStartNavigationTemplate() + { + return $this->_startNavigationTemplate; + } + + /** + * @param ITemplate navigation template for the start step. + */ + public function setStartNavigationTemplate($value) + { + $this->_startNavigationTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate navigation template for internal steps. Defaults to null. + */ + public function getStepNavigationTemplate() + { + return $this->_stepNavigationTemplate; + } + + /** + * @param ITemplate navigation template for internal steps. + */ + public function setStepNavigationTemplate($value) + { + $this->_stepNavigationTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate navigation template for the finish step. Defaults to null. + */ + public function getFinishNavigationTemplate() + { + return $this->_finishNavigationTemplate; + } + + /** + * @param ITemplate navigation template for the finish step. + */ + public function setFinishNavigationTemplate($value) + { + $this->_finishNavigationTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate template for wizard header. Defaults to null. + */ + public function getHeaderTemplate() + { + return $this->_headerTemplate; + } + + /** + * @param ITemplate template for wizard header. + */ + public function setHeaderTemplate($value) + { + $this->_headerTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return ITemplate template for the side bar. Defaults to null. + */ + public function getSideBarTemplate() + { + return $this->_sideBarTemplate; + } + + /** + * @param ITemplate template for the side bar. + */ + public function setSideBarTemplate($value) + { + $this->_sideBarTemplate=$value; + $this->requiresControlsRecreation(); + } + + /** + * @return string header text. Defaults to ''. + */ + public function getHeaderText() + { + return $this->getViewState('HeaderText',''); + } + + /** + * @param string header text. + */ + public function setHeaderText($value) + { + $this->setViewState('HeaderText',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the URL that the browser will be redirected to if the cancel button in the + * wizard is clicked. Defaults to ''. + */ + public function getCancelDestinationUrl() + { + return $this->getViewState('CancelDestinationUrl',''); + } + + /** + * @param string the URL that the browser will be redirected to if the cancel button in the + * wizard is clicked. + */ + public function setCancelDestinationUrl($value) + { + $this->setViewState('CancelDestinationUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return string the URL that the browser will be redirected to if the wizard finishes. + * Defaults to ''. + */ + public function getFinishDestinationUrl() + { + return $this->getViewState('FinishDestinationUrl',''); + } + + /** + * @param string the URL that the browser will be redirected to if the wizard finishes. + */ + public function setFinishDestinationUrl($value) + { + $this->setViewState('FinishDestinationUrl',TPropertyValue::ensureString($value),''); + } + + /** + * @return TStyle the style for the buttons displayed in the side bar. + */ + public function getSideBarButtonStyle() + { + if(($style=$this->getViewState('SideBarButtonStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('SideBarButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TStyle the style common for all navigation buttons. + */ + public function getNavigationButtonStyle() + { + if(($style=$this->getViewState('NavigationButtonStyle',null))===null) + { + $style=new TStyle; + $this->setViewState('NavigationButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the next button in the start wizard step. + */ + public function getStartNextButtonStyle() + { + if(($style=$this->getViewState('StartNextButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Next'); + $this->setViewState('StartNextButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the next button in each internal wizard step. + */ + public function getStepNextButtonStyle() + { + if(($style=$this->getViewState('StepNextButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Next'); + $this->setViewState('StepNextButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step. + */ + public function getStepPreviousButtonStyle() + { + if(($style=$this->getViewState('StepPreviousButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Previous'); + $this->setViewState('StepPreviousButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the complete button in the finish wizard step. + */ + public function getFinishCompleteButtonStyle() + { + if(($style=$this->getViewState('FinishCompleteButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Complete'); + $this->setViewState('FinishCompleteButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step. + */ + public function getFinishPreviousButtonStyle() + { + if(($style=$this->getViewState('FinishPreviousButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Previous'); + $this->setViewState('FinishPreviousButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TWizardNavigationButtonStyle the style for the cancel button + */ + public function getCancelButtonStyle() + { + if(($style=$this->getViewState('CancelButtonStyle',null))===null) + { + $style=new TWizardNavigationButtonStyle; + $style->setButtonText('Cancel'); + $this->setViewState('CancelButtonStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for the side bar. + */ + public function getSideBarStyle() + { + if(($style=$this->getViewState('SideBarStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('SideBarStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for the header. + */ + public function getHeaderStyle() + { + if(($style=$this->getViewState('HeaderStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('HeaderStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for each internal wizard step. + */ + public function getStepStyle() + { + if(($style=$this->getViewState('StepStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('StepStyle',$style,null); + } + return $style; + } + + /** + * @return TPanelStyle the style for the navigation panel. + */ + public function getNavigationStyle() + { + if(($style=$this->getViewState('NavigationStyle',null))===null) + { + $style=new TPanelStyle; + $this->setViewState('NavigationStyle',$style,null); + } + return $style; + } + + /** + * @return boolean whether to use default layout to arrange side bar and the rest wizard components. Defaults to true. + */ + public function getUseDefaultLayout() + { + return $this->getViewState('UseDefaultLayout',true); + } + + /** + * @param boolean whether to use default layout to arrange side bar and the rest wizard components. + * If true, an HTML table will be used which places the side bar in the left cell + * while the rest components in the right cell. + */ + public function setUseDefaultLayout($value) + { + $this->setViewState('UseDefaultLayout',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return TPanel container of the wizard header + */ + public function getHeader() + { + return $this->_header; + } + + /** + * @return TPanel container of the wizard step content + */ + public function getStepContent() + { + return $this->_stepContent; + } + + /** + * @return TPanel container of the wizard side bar + */ + public function getSideBar() + { + return $this->_sideBar; + } + + /** + * @return TWizardNavigationContainer container of the start navigation + */ + public function getStartNavigation() + { + return $this->_startNavigation; + } + + /** + * @return TWizardNavigationContainer container of the step navigation + */ + public function getStepNavigation() + { + return $this->_stepNavigation; + } + + /** + * @return TWizardNavigationContainer container of the finish navigation + */ + public function getFinishNavigation() + { + return $this->_finishNavigation; + } + + /** + * Raises OnActiveStepChanged event. + * This event is raised when the current visible step is changed in the + * wizard. + * @param TEventParameter event parameter + */ + public function onActiveStepChanged($param) + { + $this->raiseEvent('OnActiveStepChanged',$this,$param); + } + + /** + * Raises OnCancelButtonClick event. + * This event is raised when a cancel navigation button is clicked in the + * current active step. + * @param TEventParameter event parameter + */ + public function onCancelButtonClick($param) + { + $this->raiseEvent('OnCancelButtonClick',$this,$param); + if(($url=$this->getCancelDestinationUrl())!=='') + $this->getResponse()->redirect($url); + } + + /** + * Raises OnCompleteButtonClick event. + * This event is raised when a finish navigation button is clicked in the + * current active step. + * @param TWizardNavigationEventParameter event parameter + */ + public function onCompleteButtonClick($param) + { + $this->raiseEvent('OnCompleteButtonClick',$this,$param); + if(($url=$this->getFinishDestinationUrl())!=='') + $this->getResponse()->redirect($url); + } + + /** + * Raises OnNextButtonClick event. + * This event is raised when a next navigation button is clicked in the + * current active step. + * @param TWizardNavigationEventParameter event parameter + */ + public function onNextButtonClick($param) + { + $this->raiseEvent('OnNextButtonClick',$this,$param); + } + + /** + * Raises OnPreviousButtonClick event. + * This event is raised when a previous navigation button is clicked in the + * current active step. + * @param TWizardNavigationEventParameter event parameter + */ + public function onPreviousButtonClick($param) + { + $this->raiseEvent('OnPreviousButtonClick',$this,$param); + } + + /** + * Raises OnSideBarButtonClick event. + * This event is raised when a link button in the side bar is clicked. + * @param TWizardNavigationEventParameter event parameter + */ + public function onSideBarButtonClick($param) + { + $this->raiseEvent('OnSideBarButtonClick',$this,$param); + } + + /** + * Returns the multiview that holds the wizard steps. + * This method should only be used by control developers. + * @return TMultiView the multiview holding wizard steps + */ + public function getMultiView() + { + if($this->_multiView===null) + { + $this->_multiView=new TMultiView; + $this->_multiView->setID('WizardMultiView'); + $this->_multiView->attachEventHandler('OnActiveViewChanged',array($this,'onActiveStepChanged')); + $this->_multiView->ignoreBubbleEvents(); + } + return $this->_multiView; + } + + /** + * Adds a wizard step to the multiview. + * This method should only be used by control developers. + * It is invoked when a step is added into the step collection of the wizard. + * @param TWizardStep wizard step to be added into multiview. + */ + public function addedWizardStep($step) + { + if(($wizard=$step->getWizard())!==null) + $wizard->getWizardSteps()->remove($step); + $step->setWizard($this); + $this->wizardStepsChanged(); + } + + /** + * Removes a wizard step from the multiview. + * This method should only be used by control developers. + * It is invoked when a step is removed from the step collection of the wizard. + * @param TWizardStep wizard step to be removed from multiview. + */ + public function removedWizardStep($step) + { + $step->setWizard(null); + $this->wizardStepsChanged(); + } + + /** + * Creates the child controls of the wizard. + * This method overrides the parent implementation. + * @param TEventParameter event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $this->ensureChildControls(); + $this->setEnsureId(true); + if($this->getActiveStepIndex()<0 && $this->getWizardSteps()->getCount()>0) + $this->setActiveStepIndex(0); + } + + /** + * Saves the current active step index into history. + * This method is invoked by the framework when the control state is being saved. + */ + public function saveState() + { + $index=$this->getActiveStepIndex(); + $history=$this->getHistory(); + if(!$history->getCount() || $history->peek()!==$index) + $history->push($index); + } + + /** + * Indicates the wizard needs to recreate all child controls. + */ + protected function requiresControlsRecreation() + { + if($this->getChildControlsCreated()) + $this->setChildControlsCreated(false); + } + + /** + * Renders the wizard. + * @param THtmlWriter + */ + public function render($writer) + { + $this->ensureChildControls(); + if($this->getHasControls()) + { + if($this->getUseDefaultLayout()) + { + $this->applyControlProperties(); + $this->renderBeginTag($writer); + $writer->write("\n\n
      \n"); + $this->_sideBar->renderControl($writer); + $writer->write("\n\n"); + $this->_header->renderControl($writer); + $this->_stepContent->renderControl($writer); + $this->_navigation->renderControl($writer); + $writer->write("\n
      \n"); + $this->renderEndTag($writer); + } + else + { + $this->applyControlProperties(); + $this->renderBeginTag($writer); + $this->_sideBar->renderControl($writer); + $this->_header->renderControl($writer); + $this->_stepContent->renderControl($writer); + $this->_navigation->renderControl($writer); + $this->renderEndTag($writer); + } + } + } + + /** + * Applies various properties to the components of wizard + */ + protected function applyControlProperties() + { + $this->applyHeaderProperties(); + $this->applySideBarProperties(); + $this->applyStepContentProperties(); + $this->applyNavigationProperties(); + } + + /** + * Applies properties to the wizard header + */ + protected function applyHeaderProperties() + { + if(($style=$this->getViewState('HeaderStyle',null))!==null) + $this->_header->getStyle()->mergeWith($style); + if($this->getHeaderTemplate()===null) + { + $this->_header->getControls()->clear(); + $this->_header->getControls()->add($this->getHeaderText()); + } + } + + /** + * Applies properties to the wizard sidebar + */ + protected function applySideBarProperties() + { + $this->_sideBar->setVisible($this->getShowSideBar()); + if($this->_sideBarDataList!==null && $this->getShowSideBar()) + { + $this->_sideBarDataList->setDataSource($this->getWizardSteps()); + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + if(($style=$this->getViewState('SideBarButtonStyle',null))!==null) + { + foreach($this->_sideBarDataList->getItems() as $item) + { + if(($button=$item->findControl('SideBarButton'))!==null) + $button->getStyle()->mergeWith($style); + } + } + } + if(($style=$this->getViewState('SideBarStyle',null))!==null) + $this->_sideBar->getStyle()->mergeWith($style); + } + + /** + * Applies properties to the wizard step content + */ + protected function applyStepContentProperties() + { + if(($style=$this->getViewState('StepStyle',null))!==null) + $this->_stepContent->getStyle()->mergeWith($style); + } + + /** + * Apply properties to various navigation panels. + */ + protected function applyNavigationProperties() + { + $wizardSteps=$this->getWizardSteps(); + $activeStep=$this->getActiveStep(); + $activeStepIndex=$this->getActiveStepIndex(); + + if(!$this->_navigation) + return; + else if($activeStepIndex<0 || $activeStepIndex>=$wizardSteps->getCount()) + { + $this->_navigation->setVisible(false); + return; + } + + // set visibility of different types of navigation panel + $showStandard=true; + foreach($wizardSteps as $step) + { + if(($step instanceof TTemplatedWizardStep) && ($container=$step->getNavigationContainer())!==null) + { + if($activeStep===$step) + { + $container->setVisible(true); + $showStandard=false; + } + else + $container->setVisible(false); + } + } + $activeStepType=$this->getStepType($activeStep); + if($activeStepType===TWizardStepType::Complete) + { + $this->_sideBar->setVisible(false); + $this->_header->setVisible(false); + } + $this->_startNavigation->setVisible($showStandard && $activeStepType===self::ST_START); + $this->_stepNavigation->setVisible($showStandard && $activeStepType===self::ST_STEP); + $this->_finishNavigation->setVisible($showStandard && $activeStepType===self::ST_FINISH); + + if(($navigationStyle=$this->getViewState('NavigationStyle',null))!==null) + $this->_navigation->getStyle()->mergeWith($navigationStyle); + + $displayCancelButton=$this->getShowCancelButton(); + $cancelButtonStyle=$this->getCancelButtonStyle(); + $buttonStyle=$this->getViewState('NavigationButtonStyle',null); + if($buttonStyle!==null) + $cancelButtonStyle->mergeWith($buttonStyle); + + // apply styles to start navigation buttons + if(($cancelButton=$this->_startNavigation->getCancelButton())!==null) + { + $cancelButton->setVisible($displayCancelButton); + $cancelButtonStyle->apply($cancelButton); + } + if(($button=$this->_startNavigation->getNextButton())!==null) + { + $button->setVisible(true); + $style=$this->getStartNextButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + if($activeStepType===TWizardStepType::Start) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + } + + // apply styles to finish navigation buttons + if(($cancelButton=$this->_finishNavigation->getCancelButton())!==null) + { + $cancelButton->setVisible($displayCancelButton); + $cancelButtonStyle->apply($cancelButton); + } + if(($button=$this->_finishNavigation->getPreviousButton())!==null) + { + $button->setVisible($this->allowNavigationToPreviousStep()); + $style=$this->getFinishPreviousButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + } + if(($button=$this->_finishNavigation->getCompleteButton())!==null) + { + $button->setVisible(true); + $style=$this->getFinishCompleteButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + if($activeStepType===TWizardStepType::Finish) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + } + + // apply styles to step navigation buttons + if(($cancelButton=$this->_stepNavigation->getCancelButton())!==null) + { + $cancelButton->setVisible($displayCancelButton); + $cancelButtonStyle->apply($cancelButton); + } + if(($button=$this->_stepNavigation->getPreviousButton())!==null) + { + $button->setVisible($this->allowNavigationToPreviousStep()); + $style=$this->getStepPreviousButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + } + if(($button=$this->_stepNavigation->getNextButton())!==null) + { + $button->setVisible(true); + $style=$this->getStepNextButtonStyle(); + if($buttonStyle!==null) + $style->mergeWith($buttonStyle); + $style->apply($button); + if($activeStepType===TWizardStepType::Step) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + } + } + + /** + * @return TStack history containing step indexes that were navigated before + */ + protected function getHistory() + { + if(($history=$this->getControlState('History',null))===null) + { + $history=new TStack; + $this->setControlState('History',$history); + } + return $history; + } + + /** + * Determines the type of the specified wizard step. + * @param TWizardStep + * @return TWizardStepType type of the step + */ + protected function getStepType($wizardStep) + { + if(($type=$wizardStep->getStepType())===TWizardStepType::Auto) + { + $steps=$this->getWizardSteps(); + if(($index=$steps->indexOf($wizardStep))>=0) + { + $stepCount=$steps->getCount(); + if($stepCount===1 || ($index<$stepCount-1 && $steps->itemAt($index+1)->getStepType()===TWizardStepType::Complete)) + return TWizardStepType::Finish; + else if($index===0) + return TWizardStepType::Start; + else if($index===$stepCount-1) + return TWizardStepType::Finish; + else + return TWizardStepType::Step; + } + else + return $type; + } + else + return $type; + } + + /** + * Clears up everything within the wizard. + */ + protected function reset() + { + $this->getControls()->clear(); + $this->_header=null; + $this->_stepContent=null; + $this->_sideBar=null; + $this->_sideBarDataList=null; + $this->_navigation=null; + $this->_startNavigation=null; + $this->_stepNavigation=null; + $this->_finishNavigation=null; + } + + /** + * Creates child controls within the wizard + */ + public function createChildControls() + { + $this->reset(); + $this->createSideBar(); + $this->createHeader(); + $this->createStepContent(); + $this->createNavigation(); +// $this->clearChildState(); + } + + /** + * Creates the wizard header. + */ + protected function createHeader() + { + $this->_header=new TPanel; + if(($template=$this->getHeaderTemplate())!==null) + $template->instantiateIn($this->_header); + else + $this->_header->getControls()->add($this->getHeaderText()); + $this->getControls()->add($this->_header); + } + + /** + * Creates the wizard side bar + */ + protected function createSideBar() + { + if($this->getShowSideBar()) + { + if(($template=$this->getSideBarTemplate())===null) + $template=new TWizardSideBarTemplate; + $this->_sideBar=new TPanel; + $template->instantiateIn($this->_sideBar); + $this->getControls()->add($this->_sideBar); + + if(($this->_sideBarDataList=$this->_sideBar->findControl(self::ID_SIDEBAR_LIST))!==null) + { + $this->_sideBarDataList->attachEventHandler('OnItemCommand',array($this,'dataListItemCommand')); + $this->_sideBarDataList->attachEventHandler('OnItemDataBound',array($this,'dataListItemDataBound')); + $this->_sideBarDataList->setDataSource($this->getWizardSteps()); + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + } + } + else + { + $this->_sideBar=new TPanel; + $this->getControls()->add($this->_sideBar); + } + } + + /** + * Event handler for sidebar datalist's OnItemCommand event. + * This method is used internally by wizard. It mainly + * sets the active step index according to the button clicked in the sidebar. + * @param mixed sender of the event + * @param TDataListCommandEventParameter + */ + public function dataListItemCommand($sender,$param) + { + $item=$param->getItem(); + if($param->getCommandName()===self::CMD_MOVETO) + { + $stepIndex=$this->getActiveStepIndex(); + $newStepIndex=TPropertyValue::ensureInteger($param->getCommandParameter()); + $navParam=new TWizardNavigationEventParameter($stepIndex); + $navParam->setNextStepIndex($newStepIndex); + + // if the button clicked causes validation which fails, + // by default we will cancel navigation to the new step + $button=$param->getCommandSource(); + if(($button instanceof IButtonControl) && $button->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid()) + $navParam->setCancelNavigation(true); + + $this->_activeStepIndexSet=false; + $this->onSideBarButtonClick($navParam); + $this->_cancelNavigation=$navParam->getCancelNavigation(); + if(!$this->_cancelNavigation) + { + if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($newStepIndex)) + $this->setActiveStepIndex($newStepIndex); + } + else + $this->setActiveStepIndex($stepIndex); + } + } + + /** + * Event handler for sidebar datalist's OnItemDataBound event. + * This method is used internally by wizard. It mainly configures + * the buttons in the sidebar datalist. + * @param mixed sender of the event + * @param TDataListItemEventParameter + */ + public function dataListItemDataBound($sender,$param) + { + $item=$param->getItem(); + $itemType=$item->getItemType(); + if($itemType==='Item' || $itemType==='AlternatingItem' || $itemType==='SelectedItem' || $itemType==='EditItem') + { + if(($button=$item->findControl(self::ID_SIDEBAR_BUTTON))!==null) + { + $step=$item->getData(); + if(($this->getStepType($step)===TWizardStepType::Complete)) + $button->setEnabled(false); + if(($title=$step->getTitle())!=='') + $button->setText($title); + else + $button->setText($step->getID(false)); + $index=$this->getWizardSteps()->indexOf($step); + $button->setCommandName(self::CMD_MOVETO); + $button->setCommandParameter("$index"); + } + } + } + + /** + * Creates wizard step content. + */ + protected function createStepContent() + { + foreach($this->getWizardSteps() as $step) + { + if($step instanceof TTemplatedWizardStep) + $step->ensureChildControls(); + } + $multiView=$this->getMultiView(); + $this->_stepContent=new TPanel; + $this->_stepContent->getControls()->add($multiView); + $this->getControls()->add($this->_stepContent); + if($multiView->getViews()->getCount()) + $multiView->setActiveViewIndex(0); + } + + /** + * Creates navigation panel. + */ + protected function createNavigation() + { + $this->_navigation=new TPanel; + $this->getControls()->add($this->_navigation); + $controls=$this->_navigation->getControls(); + foreach($this->getWizardSteps() as $step) + { + if($step instanceof TTemplatedWizardStep) + { + $step->instantiateNavigationTemplate(); + if(($panel=$step->getNavigationContainer())!==null) + $controls->add($panel); + } + } + $this->_startNavigation=$this->createStartNavigation(); + $controls->add($this->_startNavigation); + $this->_stepNavigation=$this->createStepNavigation(); + $controls->add($this->_stepNavigation); + $this->_finishNavigation=$this->createFinishNavigation(); + $controls->add($this->_finishNavigation); + } + + /** + * Creates start navigation panel. + */ + protected function createStartNavigation() + { + if(($template=$this->getStartNavigationTemplate())===null) + $template=new TWizardStartNavigationTemplate($this); + $navigation=new TWizardNavigationContainer; + $template->instantiateIn($navigation); + return $navigation; + } + + /** + * Creates step navigation panel. + */ + protected function createStepNavigation() + { + if(($template=$this->getStepNavigationTemplate())===null) + $template=new TWizardStepNavigationTemplate($this); + $navigation=new TWizardNavigationContainer; + $template->instantiateIn($navigation); + return $navigation; + } + + /** + * Creates finish navigation panel. + */ + protected function createFinishNavigation() + { + if(($template=$this->getFinishNavigationTemplate())===null) + $template=new TWizardFinishNavigationTemplate($this); + $navigation=new TWizardNavigationContainer; + $template->instantiateIn($navigation); + return $navigation; + } + + /** + * Updates the sidebar datalist if any. + * This method is invoked when any wizard step is changed. + */ + public function wizardStepsChanged() + { + if($this->_sideBarDataList!==null) + { + $this->_sideBarDataList->setDataSource($this->getWizardSteps()); + $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex()); + $this->_sideBarDataList->dataBind(); + } + } + + /** + * Determines the index of the previous step based on history. + * @param boolean whether the first item in the history stack should be popped + * up after calling this method. + */ + protected function getPreviousStepIndex($popStack) + { + $history=$this->getHistory(); + if($history->getCount()>=0) + { + $activeStepIndex=$this->getActiveStepIndex(); + $previousStepIndex=-1; + if($popStack) + { + $previousStepIndex=$history->pop(); + if($activeStepIndex===$previousStepIndex && $history->getCount()>0) + $previousStepIndex=$history->pop(); + } + else + { + $previousStepIndex=$history->peek(); + if($activeStepIndex===$previousStepIndex && $history->getCount()>1) + { + $saveIndex=$history->pop(); + $previousStepIndex=$history->peek(); + $history->push($saveIndex); + } + } + return $activeStepIndex===$previousStepIndex ? -1 : $previousStepIndex; + } + else + return -1; + } + + /** + * @return boolean whether navigation to the previous step is allowed + */ + protected function allowNavigationToPreviousStep() + { + if(($index=$this->getPreviousStepIndex(false))!==-1) + return $this->getWizardSteps()->itemAt($index)->getAllowReturn(); + else + return false; + } + + /** + * @param integer index of the step + * @return boolean whether navigation to the specified step is allowed + */ + protected function allowNavigationToStep($index) + { + if($this->getHistory()->contains($index)) + return $this->getWizardSteps()->itemAt($index)->getAllowReturn(); + else + return true; + } + + /** + * Handles bubbled events. + * This method mainly translate certain command events into + * wizard-specific events. + * @param mixed sender of the original command event + * @param TEventParameter event parameter + * @throws TInvalidDataValueException if a navigation command is associated with an invalid parameter + */ + public function bubbleEvent($sender,$param) + { + if($param instanceof TCommandEventParameter) + { + $command=$param->getCommandName(); + if(strcasecmp($command,self::CMD_CANCEL)===0) + { + $this->onCancelButtonClick($param); + return true; + } + + $type=$this->getStepType($this->getActiveStep()); + $index=$this->getActiveStepIndex(); + $navParam=new TWizardNavigationEventParameter($index); + if(($sender instanceof IButtonControl) && $sender->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid()) + $navParam->setCancelNavigation(true); + + $handled=false; + $movePrev=false; + $this->_activeStepIndexSet=false; + + if(strcasecmp($command,self::CMD_NEXT)===0) + { + if($type!==self::ST_START && $type!==self::ST_STEP) + throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_NEXT); + if($index<$this->getWizardSteps()->getCount()-1) + $navParam->setNextStepIndex($index+1); + $this->onNextButtonClick($navParam); + $handled=true; + } + else if(strcasecmp($command,self::CMD_PREVIOUS)===0) + { + if($type!==self::ST_FINISH && $type!==self::ST_STEP) + throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_PREVIOUS); + $movePrev=true; + if(($prevIndex=$this->getPreviousStepIndex(false))>=0) + $navParam->setNextStepIndex($prevIndex); + $this->onPreviousButtonClick($navParam); + $handled=true; + } + else if(strcasecmp($command,self::CMD_COMPLETE)===0) + { + if($type!==self::ST_FINISH) + throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_COMPLETE); + if($index<$this->getWizardSteps()->getCount()-1) + $navParam->setNextStepIndex($index+1); + $this->onCompleteButtonClick($navParam); + $handled=true; + } + else if(strcasecmp($command,self::CMD_MOVETO)===0) + { + if($this->_cancelNavigation) // may be set in onSideBarButtonClick + $navParam->setCancelNavigation(true); + $requestedStep=$param->getCommandParameter(); + if (!is_numeric($requestedStep)) + { + $requestedIndex=-1; + foreach ($this->getWizardSteps() as $index=>$step) + if ($step->getId()===$requestedStep) + { + $requestedIndex=$index; + break; + } + if ($requestedIndex<0) + throw new TConfigurationException('wizard_step_invalid'); + } + else + $requestedIndex=TPropertyValue::ensureInteger($requestedStep); + $navParam->setNextStepIndex($requestedIndex); + $handled=true; + } + + if($handled) + { + if(!$navParam->getCancelNavigation()) + { + $nextStepIndex=$navParam->getNextStepIndex(); + if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($nextStepIndex)) + { + if($movePrev) + $this->getPreviousStepIndex(true); // pop out the previous move from history + $this->setActiveStepIndex($nextStepIndex); + } + } + else + $this->setActiveStepIndex($index); + return true; + } + } + return false; + } +} + + +/** + * TWizardStep class. + * + * TWizardStep represents a wizard step. The wizard owning the step + * can be obtained by {@link getWizard Wizard}. + * To specify the type of the step, set {@link setStepType StepType}; + * For step title, set {@link setTitle Title}. If a step can be re-visited, + * set {@link setAllowReturn AllowReturn} to true. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStep extends TView +{ + private $_wizard; + + /** + * @return TWizard the wizard owning this step + */ + public function getWizard() + { + return $this->_wizard; + } + + /** + * Sets the wizard owning this step. + * This method is used internally by {@link TWizard}. + * @param TWizard the wizard owning this step + */ + public function setWizard($wizard) + { + $this->_wizard=$wizard; + } + + /** + * @return string the title for this step. + */ + public function getTitle() + { + return $this->getViewState('Title',''); + } + + /** + * @param string the title for this step. + */ + public function setTitle($value) + { + $this->setViewState('Title',$value,''); + if($this->_wizard) + $this->_wizard->wizardStepsChanged(); + } + + /** + * @return boolean whether this step can be re-visited. Default to true. + */ + public function getAllowReturn() + { + return $this->getViewState('AllowReturn',true); + } + + /** + * @param boolean whether this step can be re-visited. + */ + public function setAllowReturn($value) + { + $this->setViewState('AllowReturn',TPropertyValue::ensureBoolean($value),true); + } + + /** + * @return TWizardStepType the wizard step type. Defaults to TWizardStepType::Auto. + */ + public function getStepType() + { + return $this->getViewState('StepType',TWizardStepType::Auto); + } + + /** + * @param TWizardStepType the wizard step type. + */ + public function setStepType($type) + { + $type=TPropertyValue::ensureEnum($type,'TWizardStepType'); + if($type!==$this->getStepType()) + { + $this->setViewState('StepType',$type,TWizardStepType::Auto); + if($this->_wizard) + $this->_wizard->wizardStepsChanged(); + } + } +} + + +/** + * TCompleteWizardStep class. + * + * TCompleteWizardStep represents a wizard step of type TWizardStepType::Complete. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TCompleteWizardStep extends TWizardStep +{ + /** + * @return TWizardStepType the wizard step type. Always TWizardStepType::Complete. + */ + public function getStepType() + { + return TWizardStepType::Complete; + } + + /** + * @param string the wizard step type. + * @throws TInvalidOperationException whenever this method is invoked. + */ + public function setStepType($value) + { + throw new TInvalidOperationException('completewizardstep_steptype_readonly'); + } +} + + +/** + * TTemplatedWizardStep class. + * + * TTemplatedWizardStep represents a wizard step whose content and navigation + * can be customized using templates. To customize the step content, specify + * {@link setContentTemplate ContentTemplate}. To customize navigation specific + * to the step, specify {@link setNavigationTemplate NavigationTemplate}. Note, + * if the navigation template is not specified, default navigation will be used. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TTemplatedWizardStep extends TWizardStep implements INamingContainer +{ + /** + * @var ITemplate the template for displaying the navigation UI of a wizard step. + */ + private $_navigationTemplate=null; + /** + * @var ITemplate the template for displaying the content within the wizard step. + */ + private $_contentTemplate=null; + /** + * @var TWizardNavigationContainer + */ + private $_navigationContainer=null; + + /** + * Creates child controls. + * This method mainly instantiates the content template, if any. + */ + public function createChildControls() + { + $this->getControls()->clear(); + if($this->_contentTemplate) + $this->_contentTemplate->instantiateIn($this); + } + + /** + * Ensures child controls are created. + * @param mixed event parameter + */ + public function onInit($param) + { + parent::onInit($param); + $this->ensureChildControls(); + } + + /** + * @return ITemplate the template for the content of the wizard step. + */ + public function getContentTemplate() + { + return $this->_contentTemplate; + } + + /** + * @param ITemplate the template for the content of the wizard step. + */ + public function setContentTemplate($value) + { + $this->_contentTemplate=$value; + } + + /** + * @return ITemplate the template for displaying the navigation UI of a wizard step. Defaults to null. + */ + public function getNavigationTemplate() + { + return $this->_navigationTemplate; + } + + /** + * @param ITemplate the template for displaying the navigation UI of a wizard step. + */ + public function setNavigationTemplate($value) + { + $this->_navigationTemplate=$value; + } + + /** + * @return TWizardNavigationContainer the control containing the navigation. + * It could be null if no navigation template is specified. + */ + public function getNavigationContainer() + { + return $this->_navigationContainer; + } + + /** + * Instantiates the navigation template if any + */ + public function instantiateNavigationTemplate() + { + if(!$this->_navigationContainer && $this->_navigationTemplate) + { + $this->_navigationContainer=new TWizardNavigationContainer; + $this->_navigationTemplate->instantiateIn($this->_navigationContainer); + } + } +} + + +/** + * TWizardStepCollection class. + * + * TWizardStepCollection represents the collection of wizard steps owned + * by a {@link TWizard}. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStepCollection extends TList +{ + /** + * @var TWizard + */ + private $_wizard; + + /** + * Constructor. + * @param TWizard wizard that owns this collection + */ + public function __construct(TWizard $wizard) + { + $this->_wizard=$wizard; + } + + /** + * Inserts an item at the specified position. + * This method overrides the parent implementation by checking if + * the item being added is a {@link TWizardStep}. + * @param integer the speicified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item being added is not TWizardStep. + */ + public function insertAt($index,$item) + { + if($item instanceof TWizardStep) + { + parent::insertAt($index,$item); + $this->_wizard->getMultiView()->getViews()->insertAt($index,$item); + $this->_wizard->addedWizardStep($item); + } + else + throw new TInvalidDataTypeException('wizardstepcollection_wizardstep_required'); + } + + /** + * Removes an item at the specified position. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $step=parent::removeAt($index); + $this->_wizard->getMultiView()->getViews()->remove($step); + $this->_wizard->removedWizardStep($step); + return $step; + } +} + + +/** + * TWizardNavigationContainer class. + * + * TWizardNavigationContainer represents a control containing + * a wizard navigation. The navigation may contain a few buttons, including + * {@link getPreviousButton PreviousButton}, {@link getNextButton NextButton}, + * {@link getCancelButton CancelButton}, {@link getCompleteButton CompleteButton}. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationContainer extends TControl implements INamingContainer +{ + private $_previousButton=null; + private $_nextButton=null; + private $_cancelButton=null; + private $_completeButton=null; + + /** + * @return mixed the previous button + */ + public function getPreviousButton() + { + return $this->_previousButton; + } + + /** + * @param mixed the previous button + */ + public function setPreviousButton($value) + { + $this->_previousButton=$value; + } + + /** + * @return mixed the next button + */ + public function getNextButton() + { + return $this->_nextButton; + } + + /** + * @param mixed the next button + */ + public function setNextButton($value) + { + $this->_nextButton=$value; + } + + /** + * @return mixed the cancel button + */ + public function getCancelButton() + { + return $this->_cancelButton; + } + + /** + * @param mixed the cancel button + */ + public function setCancelButton($value) + { + $this->_cancelButton=$value; + } + + /** + * @return mixed the complete button + */ + public function getCompleteButton() + { + return $this->_completeButton; + } + + /** + * @param mixed the complete button + */ + public function setCompleteButton($value) + { + $this->_completeButton=$value; + } +} + + +/** + * TWizardNavigationEventParameter class. + * + * TWizardNavigationEventParameter represents the parameter for + * {@link TWizard}'s navigation events. + * + * The index of the currently active step can be obtained from + * {@link getCurrentStepIndex CurrentStepIndex}, while the index + * of the candidate new step is in {@link getNextStepIndex NextStepIndex}. + * By modifying {@link setNextStepIndex NextStepIndex}, the new step + * can be changed to another one. If there is anything wrong with + * the navigation and it is not wanted, set {@link setCancelNavigation CancelNavigation} + * to true. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationEventParameter extends TEventParameter +{ + private $_cancel=false; + private $_currentStep; + private $_nextStep; + + /** + * Constructor. + * @param integer current step index + */ + public function __construct($currentStep) + { + $this->_currentStep=$currentStep; + $this->_nextStep=$currentStep; + } + + /** + * @return integer the zero-based index of the currently active step. + */ + public function getCurrentStepIndex() + { + return $this->_currentStep; + } + + /** + * @return integer the zero-based index of the next step. Default to {@link getCurrentStepIndex CurrentStepIndex}. + */ + public function getNextStepIndex() + { + return $this->_nextStep; + } + + /** + * @param integer the zero-based index of the next step. + */ + public function setNextStepIndex($index) + { + $this->_nextStep=TPropertyValue::ensureInteger($index); + } + + /** + * @return boolean whether navigation to the next step should be canceled. Default to false. + */ + public function getCancelNavigation() + { + return $this->_cancel; + } + + /** + * @param boolean whether navigation to the next step should be canceled. + */ + public function setCancelNavigation($value) + { + $this->_cancel=TPropertyValue::ensureBoolean($value); + } +} + +/** + * TWizardSideBarTemplate class. + * TWizardSideBarTemplate is the default template for wizard sidebar. + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardSideBarTemplate extends TComponent implements ITemplate +{ + /** + * Instantiates the template. + * It creates a {@link TDataList} control. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $dataList=new TDataList; + $dataList->setID(TWizard::ID_SIDEBAR_LIST); + $dataList->getSelectedItemStyle()->getFont()->setBold(true); + $dataList->setItemTemplate(new TWizardSideBarListItemTemplate); + $parent->getControls()->add($dataList); + } +} + +/** + * TWizardSideBarListItemTemplate class. + * TWizardSideBarListItemTemplate is the default template for each item in the sidebar datalist. + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardSideBarListItemTemplate extends TComponent implements ITemplate +{ + /** + * Instantiates the template. + * It creates a {@link TLinkButton}. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $button=new TLinkButton; + $button->setID(TWizard::ID_SIDEBAR_BUTTON); + $parent->getControls()->add($button); + } +} + +/** + * TWizardNavigationTemplate class. + * TWizardNavigationTemplate is the base class for various navigation templates. + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationTemplate extends TComponent implements ITemplate +{ + private $_wizard; + + /** + * Constructor. + * @param TWizard the wizard owning this template + */ + public function __construct($wizard) + { + $this->_wizard=$wizard; + } + + /** + * @return TWizard the wizard owning this template + */ + public function getWizard() + { + return $this->_wizard; + } + + /** + * Instantiates the template. + * Derived classes should override this method. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + } + + /** + * Creates a navigation button. + * It creates a {@link TButton}, {@link TLinkButton}, or {@link TImageButton}, + * depending on the given parameters. + * @param TWizardNavigationButtonStyle button style + * @param boolean whether the button should cause validation + * @param string command name for the button's OnCommand event + * @throws TInvalidDataValueException if the button type is not recognized + */ + protected function createNavigationButton($buttonStyle,$causesValidation,$commandName) + { + switch($buttonStyle->getButtonType()) + { + case TWizardNavigationButtonType::Button: + $button=new TButton; + break; + case TWizardNavigationButtonType::Link: + $button=new TLinkButton; + break; + case TWizardNavigationButtonType::Image: + $button=new TImageButton; + $button->setImageUrl($buttonStyle->getImageUrl()); + break; + default: + throw new TInvalidDataValueException('wizard_buttontype_unknown',$buttonStyle->getButtonType()); + } + $button->setText($buttonStyle->getButtonText()); + $button->setCausesValidation($causesValidation); + $button->setCommandName($commandName); + return $button; + } +} + +/** + * TWizardStartNavigationTemplate class. + * TWizardStartNavigationTemplate is the template used as default wizard start navigation panel. + * It consists of two buttons, Next and Cancel. + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStartNavigationTemplate extends TWizardNavigationTemplate +{ + /** + * Instantiates the template. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $nextButton=$this->createNavigationButton($this->getWizard()->getStartNextButtonStyle(),true,TWizard::CMD_NEXT); + $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); + + $controls=$parent->getControls(); + $controls->add($nextButton); + $controls->add("\n"); + $controls->add($cancelButton); + + $parent->setNextButton($nextButton); + $parent->setCancelButton($cancelButton); + } +} + +/** + * TWizardFinishNavigationTemplate class. + * TWizardFinishNavigationTemplate is the template used as default wizard finish navigation panel. + * It consists of three buttons, Previous, Complete and Cancel. + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardFinishNavigationTemplate extends TWizardNavigationTemplate +{ + /** + * Instantiates the template. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $previousButton=$this->createNavigationButton($this->getWizard()->getFinishPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS); + $completeButton=$this->createNavigationButton($this->getWizard()->getFinishCompleteButtonStyle(),true,TWizard::CMD_COMPLETE); + $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); + + $controls=$parent->getControls(); + $controls->add($previousButton); + $controls->add("\n"); + $controls->add($completeButton); + $controls->add("\n"); + $controls->add($cancelButton); + + $parent->setPreviousButton($previousButton); + $parent->setCompleteButton($completeButton); + $parent->setCancelButton($cancelButton); + } +} + +/** + * TWizardStepNavigationTemplate class. + * TWizardStepNavigationTemplate is the template used as default wizard step navigation panel. + * It consists of three buttons, Previous, Next and Cancel. + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardStepNavigationTemplate extends TWizardNavigationTemplate +{ + /** + * Instantiates the template. + * @param TControl parent to hold the content within the template + */ + public function instantiateIn($parent) + { + $previousButton=$this->createNavigationButton($this->getWizard()->getStepPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS); + $nextButton=$this->createNavigationButton($this->getWizard()->getStepNextButtonStyle(),true,TWizard::CMD_NEXT); + $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL); + + $controls=$parent->getControls(); + $controls->add($previousButton); + $controls->add("\n"); + $controls->add($nextButton); + $controls->add("\n"); + $controls->add($cancelButton); + + $parent->setPreviousButton($previousButton); + $parent->setNextButton($nextButton); + $parent->setCancelButton($cancelButton); + } +} + + +/** + * TWizardNavigationButtonType class. + * TWizardNavigationButtonType defines the enumerable type for the possible types of buttons + * that can be used in the navigation part of a {@link TWizard}. + * + * The following enumerable values are defined: + * - Button: a regular click button + * - Image: an image button + * - Link: a hyperlink button + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TWizardNavigationButtonType extends TEnumerable +{ + const Button='Button'; + const Image='Image'; + const Link='Link'; +} + + +/** + * TWizardStepType class. + * TWizardStepType defines the enumerable type for the possible types of {@link TWizard wizard} steps. + * + * The following enumerable values are defined: + * - Auto: the type is automatically determined based on the location of the wizard step in the whole step collection. + * - Complete: the step is the last summary step. + * - Start: the step is the first step + * - Step: the step is between the begin and the end steps. + * - Finish: the last step before the Complete step. + * + * @author Qiang Xue + * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TWizardStepType extends TEnumerable +{ + const Auto='Auto'; + const Complete='Complete'; + const Start='Start'; + const Step='Step'; + const Finish='Finish'; +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php b/gui/baculum/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php new file mode 100644 index 0000000000..f0873df8e8 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TWizardNavigationButtonStyle.php @@ -0,0 +1,155 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id $ + * @package System.Web.UI.WebControls + */ + +/** + * Includes TStyle class file + */ +Prado::using('System.Web.UI.WebControls.TStyle'); + +/** + * TWizardNavigationButtonStyle class. + * TWizardNavigationButtonStyle defines the style applied to a wizard navigation button. + * The button type can be specified via {@link setButtonType ButtonType}, which + * can be 'Button', 'Image' or 'Link'. + * If the button is an image button, {@link setImageUrl ImageUrl} will be + * used to load the image for the button. + * Otherwise, {@link setButtonText ButtonText} will be displayed as the button caption. + * + * @author Qiang Xue + * @version $Id: TWizardNavigationButtonStyle.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls + * @since 3.0 + */ +class TWizardNavigationButtonStyle extends TStyle +{ + private $_imageUrl=null; + private $_buttonText=null; + private $_buttonType=null; + + /** + * Sets the style attributes to default values. + * This method overrides the parent implementation by + * resetting additional TWizardNavigationButtonStyle specific attributes. + */ + public function reset() + { + parent::reset(); + $this->_imageUrl=null; + $this->_buttonText=null; + $this->_buttonType=null; + } + + /** + * Copies the fields in a new style to this style. + * If a style field is set in the new style, the corresponding field + * in this style will be overwritten. + * @param TStyle the new style + */ + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TWizardNavigationButtonStyle) + { + if($this->_imageUrl===null && $style->_imageUrl!==null) + $this->_imageUrl=$style->_imageUrl; + if($this->_buttonText===null && $style->_buttonText!==null) + $this->_buttonText=$style->_buttonText; + if($this->_buttonType===null && $style->_buttonType!==null) + $this->_buttonType=$style->_buttonType; + } + } + + /** + * Merges the style with a new one. + * If a style field is not set in this style, it will be overwritten by + * the new one. + * @param TStyle the new style + */ + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TWizardNavigationButtonStyle) + { + if($style->_imageUrl!==null) + $this->_imageUrl=$style->_imageUrl; + if($style->_buttonText!==null) + $this->_buttonText=$style->_buttonText; + if($style->_buttonType!==null) + $this->_buttonType=$style->_buttonType; + } + } + + /** + * @return string image URL for the image button + */ + public function getImageUrl() + { + return $this->_imageUrl===null?'':$this->_imageUrl; + } + + /** + * @param string image URL for the image button + */ + public function setImageUrl($value) + { + $this->_imageUrl=$value; + } + + /** + * @return string button caption + */ + public function getButtonText() + { + return $this->_buttonText===null?'':$this->_buttonText; + } + + /** + * @param string button caption + */ + public function setButtonText($value) + { + $this->_buttonText=$value; + } + + /** + * @return TWizardNavigationButtonType button type. Default to TWizardNavigationButtonType::Button. + */ + public function getButtonType() + { + return $this->_buttonType===null? TWizardNavigationButtonType::Button :$this->_buttonType; + } + + /** + * @param TWizardNavigationButtonType button type. + */ + public function setButtonType($value) + { + $this->_buttonType=TPropertyValue::ensureEnum($value,'TWizardNavigationButtonType'); + } + + /** + * Applies this style to the specified button + * @param mixed button to be applied with this style + */ + public function apply($button) + { + if($button instanceof TImageButton) + { + if($button->getImageUrl()==='') + $button->setImageUrl($this->getImageUrl()); + } + if($button->getText()==='') + $button->setText($this->getButtonText()); + $button->getStyle()->mergeWith($this); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/TXmlTransform.php b/gui/baculum/framework/Web/UI/WebControls/TXmlTransform.php new file mode 100644 index 0000000000..7b4d5cf746 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/TXmlTransform.php @@ -0,0 +1,193 @@ + + * @author Qiang Xue + * @link http://www.pradosoft.com + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @package System.Web.UI.WebControls + */ + +/** + * TXmlTransform class + * + * TXmlTransform uses the PHP's XSL extension to perform + * {@link http://www.w3.org/TR/xslt XSL transformations} using the + * {@link http://xmlsoft.org/XSLT/ libxslt library}. + * + * To associate an XML style sheet with TXmlTransform set the + * {@link setTransformPath TransformPath} property to the namespace or path to the style sheet + * or set the {@link setTransformContent TransformContent} property to the XML style sheet + * data as a string. + * + * To associate the XML data to be transformed set the {@link setDocumentPath DocumentPath} + * property to the namespace or path to the XML document or set the + * {@link setDocumentContent DocumentContent} property to the XML data as a string. + * + * To add additional parameters to the transformation process you can use the {@link getParameters Parameters} + * property. + * + * @author Knut Urdalen + * @author Qiang Xue + * @package System.Web.UI.WebControls + * @since 3.1 + */ +class TXmlTransform extends TControl { + + const EXT_XML_FILE = '.xml'; + const EXT_XSL_FILE = '.xsl'; + + /** + * Constructor + * + * Initializes the TXmlTransform object and ensure that the XSL extension is available + * @throws TConfigurationException If XSL extension is not available + */ + public function __construct() { + if(!class_exists('XSLTProcessor', false)) { + throw new TConfigurationException('xmltransform_xslextension_required'); + } + } + + /** + * @return string The path to the XML style sheet. + */ + public function getTransformPath() { + return $this->getViewState('TransformPath', ''); + } + + /** + * @param string The path to the XML style sheet. It must be in namespace format. + */ + public function setTransformPath($value) { + if(!is_file($value)) { + $value = Prado::getPathOfNamespace($value, self::EXT_XSL_FILE); + if($value === null) { + throw new TInvalidDataValueException('xmltransform_transformpath_invalid', $value); + } + } + $this->setViewState('TransformPath', $value, ''); + } + + /** + * @return string XML style sheet as string + */ + public function getTransformContent() { + return $this->getViewState('TransformContent', ''); + } + + /** + * @param string $value XML style sheet as string + */ + public function setTransformContent($value) { + $this->setViewState('TransformContent', $value, ''); + } + + /** + * @return string The path to the XML document. It must be in namespace format. + */ + public function getDocumentPath() { + return $this->getViewState('DocumentPath', ''); + } + + /** + * @param string Namespace or path to XML document + * @throws TInvalidDataValueException + */ + public function setDocumentPath($value) { + if(!is_file($value)) { + $value = Prado::getPathOfNamespace($value, self::EXT_XML_FILE); + if($value === null) { + throw new TInvalidDataValueException('xmltransform_documentpath_invalid', $value); + } + } + $this->setViewState('DocumentPath', $value, ''); + } + + /** + * @return string XML data + */ + public function getDocumentContent() { + return $this->getViewState('DocumentContent', ''); + } + + /** + * @param string $value XML data. If not empty, it takes precedence over {@link setDocumentPath DocumentPath}. + */ + public function setDocumentContent($value) { + $this->setViewState('DocumentContent', $value, ''); + } + + /** + * Returns the list of parameters to be applied to the transform. + * @return TAttributeCollection the list of custom parameters + */ + public function getParameters() { + if($params = $this->getViewState('Parameters',null)) { + return $params; + } else { + $params = new TAttributeCollection(); + $this->setViewState('Parameters', $params, null); + return $params; + } + } + + private function getTransformXmlDocument() { + if(($content = $this->getTransformContent()) !== '') { + $document = new DOMDocument(); + $document->loadXML($content); + return $document; + } else if(($path = $this->getTransformPath()) !== '') { + $document = new DOMDocument(); + $document->load($path); + return $document; + } else { + throw new TConfigurationException('xmltransform_transform_required'); + } + } + + private function getSourceXmlDocument() { + if(($content = $this->getDocumentContent()) !== '') { + $document = new DOMDocument(); + $document->loadXML($content); + return $document; + } else if(($path = $this->getDocumentPath()) !== '') { + $document = new DOMDocument(); + $document->load($path); + return $document; + } else { + return null; + } + } + + /** + * Performs XSL transformation and render the output. + * @param THtmlWriter The writer used for the rendering purpose + */ + public function render($writer) { + if(($document=$this->getSourceXmlDocument()) === null) { + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), new TTextWriter()); + parent::render($htmlWriter); + $document = new DOMDocument(); + $document->loadXML($htmlWriter->flush()); + } + $stylesheet = $this->getTransformXmlDocument(); + + // Perform XSL transformation + $xslt = new XSLTProcessor(); + $xslt->importStyleSheet($stylesheet); + + // Check for parameters + $parameters = $this->getParameters(); + foreach($parameters as $name => $value) { + $xslt->setParameter('', $name, $value); + } + $output = $xslt->transformToXML($document); + + // Write output + $writer->write($output); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSlider.css b/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSlider.css new file mode 100755 index 0000000000..458467eea1 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSlider.css @@ -0,0 +1,96 @@ +/* Base CSS Class for the slider */ +.Slider +{ + position: relative; + margin: 10px; + padding: 0; + background-color: Gainsboro; + border: 1px outset; +} + +/* Class for horizontal slider */ +.HorizontalSlider +{ + width: 210px; + height: 14px; + text-align: left; +} + +/* Class for vertical slider */ +.VerticalSlider +{ + height: 210px; + width: 14px; +} + +/* Class for Track */ +.Track +{ + border-style: inset; + outline-color: invert; + outline-style: none; + outline-width: 1px; + border-width: 1px; + position: absolute; + background-color: lightBlue; +} + +/* Horizontal Track */ +.HorizontalSlider .Track +{ + + height: 3px; + top: 5px; + width: 97%; + left: 1%; +} + +/* Vertical Track */ +.VerticalSlider .Track +{ + height: 97%; + width: 3px; + left: 5px; + top: 1%; +} + +/* Class for the progress indicator */ +.Progress +{ + background-color: red; +} + +/* Horizontal one, take 100% of track (parent) height*/ +.HorizontalSlider .Progress +{ + height: 100%; +} + +/* Vertical progress indicator, take 100% of track width*/ +.VerticalSlider .Progress +{ + width: 100% +} + +/* Class for handle */ +.Handle { + border: 0px none; + position: relative; + cursor: move; +} + +/* Horizontal slider handle */ +.HorizontalSlider .Handle +{ + width: 31px; + height: 14px; + background: transparent url("TSliderHandleHorizontal.png") no-repeat scroll center; +} + +/* Vertical slider handle */ +.VerticalSlider .Handle +{ + width: 14px; + height: 31px; + background: transparent url("TSliderHandleVertical.png") no-repeat scroll center; +} diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.png b/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.png new file mode 100644 index 0000000000..d9dc7ff91b Binary files /dev/null and b/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleHorizontal.png differ diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.png b/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.png new file mode 100644 index 0000000000..00532fbecf Binary files /dev/null and b/gui/baculum/framework/Web/UI/WebControls/assets/TSlider/TSliderHandleVertical.png differ diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/accordion.css b/gui/baculum/framework/Web/UI/WebControls/assets/accordion.css new file mode 100644 index 0000000000..842868e7e9 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/assets/accordion.css @@ -0,0 +1,28 @@ + +div.accordion { + position: relative; +} + +div.accordion-header { + padding: 3px; + z-index: 10; position: relative; + cursor: pointer; + background-color: #5577aa; + color: white; font-weight: bold; + border: 1px solid; +} + +div.accordion-header-active { + padding: 3px; + z-index: 10; position: relative; + cursor: pointer; + background-color: #334477; + color: red; font-weight: bold; + border: 1px solid red; +} + +div.accordion-view { + overflow: auto; + padding-left: 3px; padding-right: 3px; + margin-bottom: 2px; +} diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/captcha.php b/gui/baculum/framework/Web/UI/WebControls/assets/captcha.php new file mode 100644 index 0000000000..ac71d696c7 --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/assets/captcha.php @@ -0,0 +1,224 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: captcha.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Web.UI.WebControls.assets + */ + +define('THEME_OPAQUE_BACKGROUND',0x0001); +define('THEME_NOISY_BACKGROUND',0x0002); +define('THEME_HAS_GRID',0x0004); +define('THEME_HAS_SCRIBBLE',0x0008); +define('THEME_MORPH_BACKGROUND',0x0010); +define('THEME_SHADOWED_TEXT',0x0020); + +require_once(dirname(__FILE__).'/captcha_key.php'); + +$token='error'; +$theme=0; + +if(isset($_GET['options'])) +{ + $str=base64_decode($_GET['options']); + if(strlen($str)>32) + { + $hash=substr($str,0,32); + $str=substr($str,32); + if(md5($privateKey.$str)===$hash) + { + $options=unserialize($str); + $publicKey=$options['publicKey']; + $tokenLength=$options['tokenLength']; + $caseSensitive=$options['caseSensitive']; + $alphabet=$options['alphabet']; + $fontSize=$options['fontSize']; + $theme=$options['theme']; + if(($randomSeed=$options['randomSeed'])>0) + srand($randomSeed); + else + srand((int)(microtime()*1000000)); + $token=generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive); + } + } +} + +displayToken($token,$fontSize,$theme); + +function generateToken($publicKey,$privateKey,$alphabet,$tokenLength,$caseSensitive) +{ + $token=substr(hash2string(md5($publicKey.$privateKey),$alphabet).hash2string(md5($privateKey.$publicKey),$alphabet),0,$tokenLength); + return $caseSensitive?$token:strtoupper($token); +} + +function hash2string($hex,$alphabet) +{ + if(strlen($alphabet)<2) + $alphabet='234578adefhijmnrtABDEFGHJLMNRT'; + $hexLength=strlen($hex); + $base=strlen($alphabet); + $result=''; + for($i=0;$i<$hexLength;$i+=6) + { + $number=hexdec(substr($hex,$i,6)); + while($number) + { + $result.=$alphabet[$number%$base]; + $number=floor($number/$base); + } + } + return $result; +} + +function displayToken($token,$fontSize,$theme) +{ + if(($fontSize=(int)$fontSize)<22) + $fontSize=22; + if($fontSize>100) + $fontSize=100; + $length=strlen($token); + $padding=10; + $fontWidth=$fontSize; + $fontHeight=floor($fontWidth*1.5); + $width=$fontWidth*$length+$padding*2; + $height=$fontHeight; + $image=imagecreatetruecolor($width,$height); + + addBackground + ( + $image, $width, $height, + $theme&THEME_OPAQUE_BACKGROUND, + $theme&THEME_NOISY_BACKGROUND, + $theme&THEME_HAS_GRID, + $theme&THEME_HAS_SCRIBBLE, + $theme&THEME_MORPH_BACKGROUND + ); + + $font=dirname(__FILE__).DIRECTORY_SEPARATOR.'verase.ttf'; + + if(function_exists('imagefilter')) + imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR); + + $hasShadow=($theme&THEME_SHADOWED_TEXT); + for($i=0;$i<$length;$i++) + { + $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); + $size=rand($fontWidth-10,$fontWidth); + $angle=rand(-30,30); + $x=$padding+$i*$fontWidth; + $y=rand($fontHeight-15,$fontHeight-10); + imagettftext($image,$size,$angle,$x,$y,$color,$font,$token[$i]); + if($hasShadow) + imagettftext($image,$size,$angle,$x+2,$y+2,$color,$font,$token[$i]); + imagecolordeallocate($image,$color); + } + + header('Content-Type: image/png'); + imagepng($image); + imagedestroy($image); +} + +function addBackground($image,$width,$height,$opaque,$noisy,$hasGrid,$hasScribble,$morph) +{ + $background=imagecreatetruecolor($width*2,$height*2); + $white=imagecolorallocate($background,255,255,255); + imagefill($background,0,0,$white); + + if($opaque) + imagefill($background,0,0,imagecolorallocate($background,100,100,100)); + + if($noisy) + addNoise($background,$width*2,$height*2); + + if($hasGrid) + addGrid($background,$width*2,$height*2); + + if($hasScribble) + addScribble($background,$width*2,$height*2); + + if($morph) + morphImage($background,$width*2,$height*2); + + imagecopy($image,$background,0,0,30,30,$width,$height); + + if(!$opaque) + imagecolortransparent($image,$white); +} + +function addNoise($image,$width,$height) +{ + for($x=0;$x<$width;++$x) + { + for($y=0;$y<$height;++$y) + { + if(rand(0,100)<25) + { + $color=imagecolorallocate($image,rand(150,220),rand(150,220),rand(150,220)); + imagesetpixel($image,$x,$y,$color); + imagecolordeallocate($image,$color); + } + } + } +} + +function addGrid($image,$width,$height) +{ + for($i=0;$i<$width;$i+=rand(15,25)) + { + imagesetthickness($image,rand(2,6)); + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + imageline($image,$i+rand(-10,20),0,$i+rand(-10,20),$height,$color); + imagecolordeallocate($image,$color); + } + for($i=0;$i<$height;$i+=rand(15,25)) + { + imagesetthickness($image,rand(2,6)); + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + imageline($image,0,$i+rand(-10,20),$width,$i+rand(-10,20),$color); + imagecolordeallocate($image,$color); + } +} + +function addScribble($image,$width,$height) +{ + for($i=0;$i<8;$i++) + { + $color=imagecolorallocate($image,rand(100,180),rand(100,180),rand(100,180)); + $points=array(); + for($j=1;$j=$height) $y=$height-5; + if($y<0) $y=5; + imagecopy($tempImage,$image,$x,0,$x,$y,$chunk,$height); + } + for($x=$y=0;$y<$height;$y+=$chunk) + { + $chunk=rand(1,5); + $x+=rand(-1,1); + if($x>=$width) $x=$width-5; + if($x<0) $x=5; + imagecopy($image,$tempImage,$x,$y,0,$y,$width,$chunk); + } +} + diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/keyboard.css b/gui/baculum/framework/Web/UI/WebControls/assets/keyboard.css new file mode 100644 index 0000000000..ad88d76baf --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/assets/keyboard.css @@ -0,0 +1,80 @@ +div.Keyboard +{ + -moz-background-clip: -moz-initial; + -moz-background-origin: -moz-initial; + -moz-background-inline-policy: -moz-initial; + -moz-user-select: none; + + display: block; + position: absolute; + border: 1px solid #698977; + background-color: #DDDDDD; + z-index: 1000 !important; + visibility: hidden; + padding: 4px 4px 4px 4px; + text-align: center; +} + +div.Keyboard div.Line +{ + width: 267px; + height: 18px; +} + +div.Keyboard div.Key +{ + width: 17px; + height: 17px; + margin: 1px 0px 0px 1px; + overflow: hidden; + float: left; + display: inline; +} + +div.Keyboard div.Key div.Key1, +div.Keyboard div.Key div.Key2 +{ + overflow: hidden; + border: 1px solid #A5A5A5; + background-color: #F7F7F7; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + color: #556C5F; + font-weight: normal; + text-align: center; + line-height: 15px; + vertical-align: middle; + display: block; + cursor: pointer; +} + +div.Keyboard div.Key div.Hover +{ + color: #E02C03; + border-color: #E0431D; +} + +div.Keyboard div.Key div.Active +{ + color: #FFFFFF; + background-color: #E0431D; +} + +div.Keyboard.Shift div.Key div.Key1, +div.Keyboard.Caps div.L div.Key1, +div.Keyboard.Off div.Key div.Key2 +{ + display: none; +} + +div.Keyboard.Caps div.L div.Key2 +{ + display: block; +} + +div.Keyboard div.Bksp {width: 31px;} +div.Keyboard div.Exit {width: 33px;} +div.Keyboard div.Caps {width: 33px;} +div.Keyboard div.\\ {width: 24px;} +div.Keyboard div.Del {width: 24px;} +div.Keyboard div.Shift {width: 42px;} \ No newline at end of file diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/tabpanel.css b/gui/baculum/framework/Web/UI/WebControls/assets/tabpanel.css new file mode 100644 index 0000000000..43a42c60be --- /dev/null +++ b/gui/baculum/framework/Web/UI/WebControls/assets/tabpanel.css @@ -0,0 +1,71 @@ +.tab-panel +{ + position: relative; + float: left; + width: 100%; +} + +.tab-normal +{ + font-family: Verdana, Helvetica, Arial; + font-size: 12px; + display: inline; + margin: 1px -5px 1px 5px; + float: left; + padding: 3px 6px 4px 6px; + background: rgb(234,242,255); + border: 1px solid; + border-color: rgb(120,172,255); + z-index: 1; + position: relative; + top: 0; +} + +.tab-active +{ + position: relative; + display: inline; + float: left; + + font-family: Verdana, Helvetica, Arial; + font-size: 12px; + + border: 1px solid rgb(120,172,255); + border-bottom: 0; + z-index: 3; + padding: 2px 6px 8px 6px; + margin: 1px -6px -2px 0px; + top: -2px; + background: white; +} + +.tab-view +{ + clear: both; + border: 1px solid rgb(120,172,255); + z-index: 2; + position: relative; + top: -2px; + padding: 10px; +} + +.tab-active a +{ + color: black; + text-decoration: none; + font-weight: bold; +} + +.tab-normal a +{ + color: gray; + text-decoration: none; + font-weight: bold; +} + +.tab-normal a:hover, .tab-normal a:focus +{ + color: rgb(120,172,255); + text-decoration: none; + font-weight: bold; +} \ No newline at end of file diff --git a/gui/baculum/framework/Web/UI/WebControls/assets/verase.ttf b/gui/baculum/framework/Web/UI/WebControls/assets/verase.ttf new file mode 100644 index 0000000000..4b4ecc6667 Binary files /dev/null and b/gui/baculum/framework/Web/UI/WebControls/assets/verase.ttf differ diff --git a/gui/baculum/framework/Xml/TXmlDocument.php b/gui/baculum/framework/Xml/TXmlDocument.php new file mode 100644 index 0000000000..b0252ae899 --- /dev/null +++ b/gui/baculum/framework/Xml/TXmlDocument.php @@ -0,0 +1,569 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: TXmlDocument.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Xml + */ + +/** + * TXmlElement class. + * + * TXmlElement represents an XML element node. + * You can obtain its tag-name, attributes, text between the opening and closing + * tags via the TagName, Attributes, and Value properties, respectively. + * You can also retrieve its parent and child elements by Parent and Elements + * properties, respectively. + * + * TBD: xpath + * + * @author Qiang Xue + * @version $Id: TXmlDocument.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Xml + * @since 3.0 + */ +class TXmlElement extends TComponent +{ + /** + * @var TXmlElement parent of this element + */ + private $_parent=null; + /** + * @var string tag-name of this element + */ + private $_tagName='unknown'; + /** + * @var string text enclosed between opening and closing tags of this element + */ + private $_value=''; + /** + * @var TXmlElementList list of child elements of this element + */ + private $_elements=null; + /** + * @var TMap attributes of this element + */ + private $_attributes=null; + + /** + * Constructor. + * @param string tag-name for this element + */ + public function __construct($tagName) + { + $this->setTagName($tagName); + } + + /** + * @return TXmlElement parent element of this element + */ + public function getParent() + { + return $this->_parent; + } + + /** + * @param TXmlElement parent element of this element + */ + public function setParent($parent) + { + $this->_parent=$parent; + } + + /** + * @return string tag-name of this element + */ + public function getTagName() + { + return $this->_tagName; + } + + /** + * @param string tag-name of this element + */ + public function setTagName($tagName) + { + $this->_tagName=$tagName; + } + + /** + * @return string text enclosed between opening and closing tag of this element + */ + public function getValue() + { + return $this->_value; + } + + /** + * @param string text enclosed between opening and closing tag of this element + */ + public function setValue($value) + { + $this->_value=TPropertyValue::ensureString($value); + } + + /** + * @return boolean true if this element has child elements + */ + public function getHasElement() + { + return $this->_elements!==null && $this->_elements->getCount()>0; + } + + /** + * @return boolean true if this element has attributes + */ + public function getHasAttribute() + { + return $this->_attributes!==null && $this->_attributes->getCount()>0; + } + + /** + * @return string the attribute specified by the name, null if no such attribute + */ + public function getAttribute($name) + { + if($this->_attributes!==null) + return $this->_attributes->itemAt($name); + else + return null; + } + + /** + * @param string attribute name + * @param string attribute value + */ + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,TPropertyValue::ensureString($value)); + } + + /** + * @return TXmlElementList list of child elements + */ + public function getElements() + { + if(!$this->_elements) + $this->_elements=new TXmlElementList($this); + return $this->_elements; + } + + /** + * @return TMap list of attributes + */ + public function getAttributes() + { + if(!$this->_attributes) + $this->_attributes=new TMap; + return $this->_attributes; + } + + /** + * @return TXmlElement the first child element that has the specified tag-name, null if not found + */ + public function getElementByTagName($tagName) + { + if($this->_elements) + { + foreach($this->_elements as $element) + if($element->_tagName===$tagName) + return $element; + } + return null; + } + + /** + * @return TList list of all child elements that have the specified tag-name + */ + public function getElementsByTagName($tagName) + { + $list=new TList; + if($this->_elements) + { + foreach($this->_elements as $element) + if($element->_tagName===$tagName) + $list->add($element); + } + return $list; + } + + /** + * @return string string representation of this element + */ + public function toString($indent=0) + { + $attr=''; + if($this->_attributes!==null) + { + foreach($this->_attributes as $name=>$value) + { + $value=$this->xmlEncode($value); + $attr.=" $name=\"$value\""; + } + } + $prefix=str_repeat(' ',$indent*4); + if($this->getHasElement()) + { + $str=$prefix."<{$this->_tagName}$attr>\n"; + foreach($this->getElements() as $element) + $str.=$element->toString($indent+1)."\n"; + $str.=$prefix."_tagName}>"; + return $str; + } + else if(($value=$this->getValue())!=='') + { + $value=$this->xmlEncode($value); + return $prefix."<{$this->_tagName}$attr>$value_tagName}>"; + } + else + return $prefix."<{$this->_tagName}$attr />"; + } + + /** + * Magic-method override. Called whenever this element is used as a string. + * + * $element = new TXmlElement('tag'); + * echo $element; + * + * or + * + * $element = new TXmlElement('tag'); + * $xml = (string)$element; + * + * @return string string representation of this element + */ + public function __toString() + { + return $this->toString(); + } + + private function xmlEncode($str) + { + return strtr($str,array( + '>'=>'>', + '<'=>'<', + '&'=>'&', + '"'=>'"', + "\r"=>' ', + "\t"=>' ', + "\n"=>' ')); + } +} + +/** + * TXmlDocument class. + * + * TXmlDocument represents a DOM representation of an XML file. + * Besides all properties and methods inherited from {@link TXmlElement}, + * you can load an XML file or string by {@link loadFromFile} or {@link loadFromString}. + * You can also get the version and encoding of the XML document by + * the Version and Encoding properties. + * + * To construct an XML string, you may do the following: + * + * $doc=new TXmlDocument('1.0','utf-8'); + * $doc->TagName='Root'; + * + * $proc=new TXmlElement('Proc'); + * $proc->setAttribute('Name','xxxx'); + * $doc->Elements[]=$proc; + * + * $query=new TXmlElement('Query'); + * $query->setAttribute('ID','xxxx'); + * $proc->Elements[]=$query; + * + * $attr=new TXmlElement('Attr'); + * $attr->setAttribute('Name','aaa'); + * $attr->Value='1'; + * $query->Elements[]=$attr; + * + * $attr=new TXmlElement('Attr'); + * $attr->setAttribute('Name','bbb'); + * $attr->Value='1'; + * $query->Elements[]=$attr; + * + * The above code represents the following XML string: + * + * + * + * + * + * 1 + * 1 + * + * + * + * + * + * @author Qiang Xue + * @version $Id: TXmlDocument.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Xml + * @since 3.0 + */ +class TXmlDocument extends TXmlElement +{ + /** + * @var string version of this XML document + */ + private $_version; + /** + * @var string encoding of this XML document + */ + private $_encoding; + + /** + * Constructor. + * @param string version of this XML document + * @param string encoding of this XML document + */ + public function __construct($version='1.0',$encoding='') + { + parent::__construct(''); + $this->setVersion($version); + $this->setEncoding($encoding); + } + + /** + * @return string version of this XML document + */ + public function getVersion() + { + return $this->_version; + } + + /** + * @param string version of this XML document + */ + public function setVersion($version) + { + $this->_version=$version; + } + + /** + * @return string encoding of this XML document + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * @param string encoding of this XML document + */ + public function setEncoding($encoding) + { + $this->_encoding=$encoding; + } + + /** + * Loads and parses an XML document. + * @param string the XML file path + * @return boolean whether the XML file is parsed successfully + * @throws TIOException if the file fails to be opened. + */ + public function loadFromFile($file) + { + if(($str=@file_get_contents($file))!==false) + return $this->loadFromString($str); + else + throw new TIOException('xmldocument_file_read_failed',$file); + } + + /** + * Loads and parses an XML string. + * The version and encoding will be determined based on the parsing result. + * @param string the XML string + * @return boolean whether the XML string is parsed successfully + */ + public function loadFromString($string) + { + // TODO: since PHP 5.1, we can get parsing errors and throw them as exception + $doc=new DOMDocument(); + if($doc->loadXML($string)===false) + return false; + + $this->setEncoding($doc->encoding); + $this->setVersion($doc->version); + + $element=$doc->documentElement; + $this->setTagName($element->tagName); + $this->setValue($element->nodeValue); + $elements=$this->getElements(); + $attributes=$this->getAttributes(); + $elements->clear(); + $attributes->clear(); + + static $bSimpleXml; + if($bSimpleXml === null) + $bSimpleXml = (boolean)function_exists('simplexml_load_string'); + + if($bSimpleXml) + { + $simpleDoc = simplexml_load_string($string); + $docNamespaces = $simpleDoc->getDocNamespaces(false); + $simpleDoc = null; + foreach($docNamespaces as $prefix => $uri) + { + if($prefix === '') + $attributes->add('xmlns', $uri); + else + $attributes->add('xmlns:'.$prefix, $uri); + } + } + + foreach($element->attributes as $name=>$attr) + $attributes->add(($attr->prefix === '' ? '' : $attr->prefix . ':') .$name,$attr->value); + foreach($element->childNodes as $child) + { + if($child instanceof DOMElement) + $elements->add($this->buildElement($child)); + } + + return true; + } + + /** + * Saves this XML document as an XML file. + * @param string the name of the file to be stored with XML output + * @throws TIOException if the file cannot be written + */ + public function saveToFile($file) + { + if(($fw=fopen($file,'w'))!==false) + { + fwrite($fw,$this->saveToString()); + fclose($fw); + } + else + throw new TIOException('xmldocument_file_write_failed',$file); + } + + /** + * Saves this XML document as an XML string + * @return string the XML string of this XML document + */ + public function saveToString() + { + $version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"'; + $encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"'; + return "\n".$this->toString(0); + } + + /** + * Magic-method override. Called whenever this document is used as a string. + * + * $document = new TXmlDocument(); + * $document->TagName = 'root'; + * echo $document; + * + * or + * + * $document = new TXmlDocument(); + * $document->TagName = 'root'; + * $xml = (string)$document; + * + * @return string string representation of this document + */ + public function __toString() + { + return $this->saveToString(); + } + + /** + * Recursively converts DOM XML nodes into TXmlElement + * @param DOMXmlNode the node to be converted + * @return TXmlElement the converted TXmlElement + */ + private function buildElement($node) + { + $element=new TXmlElement($node->tagName); + $element->setValue($node->nodeValue); + foreach($node->attributes as $name=>$attr) + $element->getAttributes()->add(($attr->prefix === '' ? '' : $attr->prefix . ':') . $name,$attr->value); + + foreach($node->childNodes as $child) + { + if($child instanceof DOMElement) + $element->getElements()->add($this->buildElement($child)); + } + return $element; + } +} + + +/** + * TXmlElementList class. + * + * TXmlElementList represents a collection of {@link TXmlElement}. + * You may manipulate the collection with the operations defined in {@link TList}. + * + * @author Qiang Xue + * @version $Id: TXmlDocument.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System.Xml + * @since 3.0 + */ +class TXmlElementList extends TList +{ + /** + * @var TXmlElement owner of this list + */ + private $_o; + + /** + * Constructor. + * @param TXmlElement owner of this list + */ + public function __construct(TXmlElement $owner) + { + $this->_o=$owner; + } + + /** + * @return TXmlElement owner of this list + */ + protected function getOwner() + { + return $this->_o; + } + + /** + * Inserts an item at the specified position. + * This overrides the parent implementation by performing additional + * operations for each newly added TXmlElement object. + * @param integer the specified position. + * @param mixed new item + * @throws TInvalidDataTypeException if the item to be inserted is not a TXmlElement object. + */ + public function insertAt($index,$item) + { + if($item instanceof TXmlElement) + { + parent::insertAt($index,$item); + if($item->getParent()!==null) + $item->getParent()->getElements()->remove($item); + $item->setParent($this->_o); + } + else + throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required'); + } + + /** + * Removes an item at the specified position. + * This overrides the parent implementation by performing additional + * cleanup work when removing a TXmlElement object. + * @param integer the index of the item to be removed. + * @return mixed the removed item. + */ + public function removeAt($index) + { + $item=parent::removeAt($index); + if($item instanceof TXmlElement) + $item->setParent(null); + return $item; + } +} + diff --git a/gui/baculum/framework/interfaces.php b/gui/baculum/framework/interfaces.php new file mode 100644 index 0000000000..d05123ba20 --- /dev/null +++ b/gui/baculum/framework/interfaces.php @@ -0,0 +1,381 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * IModule interface. + * + * This interface must be implemented by application modules. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface IModule +{ + /** + * Initializes the module. + * @param TXmlElement the configuration for the module + */ + public function init($config); + /** + * @return string ID of the module + */ + public function getID(); + /** + * @param string ID of the module + */ + public function setID($id); +} + +/** + * IService interface. + * + * This interface must be implemented by services. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface IService +{ + /** + * Initializes the service. + * @param TXmlElement the configuration for the service + */ + public function init($config); + /** + * @return string ID of the service + */ + public function getID(); + /** + * @param string ID of the service + */ + public function setID($id); + /** + * @return boolean whether the service is enabled + */ + public function getEnabled(); + /** + * @param boolean whether the service is enabled + */ + public function setEnabled($value); + /** + * Runs the service. + */ + public function run(); +} + +/** + * ITextWriter interface. + * + * This interface must be implemented by writers. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface ITextWriter +{ + /** + * Writes a string. + * @param string string to be written + */ + public function write($str); + /** + * Flushes the content that has been written. + */ + public function flush(); +} + + +/** + * IUser interface. + * + * This interface must be implemented by user objects. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface IUser +{ + /** + * @return string username + */ + public function getName(); + /** + * @param string username + */ + public function setName($value); + /** + * @return boolean if the user is a guest + */ + public function getIsGuest(); + /** + * @param boolean if the user is a guest + */ + public function setIsGuest($value); + /** + * @return array list of roles that the user is of + */ + public function getRoles(); + /** + * @return array|string list of roles that the user is of. If it is a string, roles are assumed by separated by comma + */ + public function setRoles($value); + /** + * @param string role to be tested + * @return boolean whether the user is of this role + */ + public function isInRole($role); + /** + * @return string user data that is serialized and will be stored in session + */ + public function saveToString(); + /** + * @param string user data that is serialized and restored from session + * @return IUser the user object + */ + public function loadFromString($string); +} + +/** + * IStatePersister class. + * + * This interface must be implemented by all state persister classes (such as + * {@link TPageStatePersister}, {@link TApplicationStatePersister}. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface IStatePersister +{ + /** + * Loads state from a persistent storage. + * @return mixed the state + */ + public function load(); + /** + * Saves state into a persistent storage. + * @param mixed the state to be saved + */ + public function save($state); +} + + +/** + * ICache interface. + * + * This interface must be implemented by cache managers. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface ICache +{ + /** + * Retrieves a value from cache with a specified key. + * @param string a key identifying the cached value + * @return mixed the value stored in cache, false if the value is not in the cache or expired. + */ + public function get($id); + /** + * Stores a value identified by a key into cache. + * If the cache already contains such a key, the existing value and + * expiration time will be replaced with the new ones. + * + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labelled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function set($id,$value,$expire=0,$dependency=null); + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * Nothing will be done if the cache already contains the key. + * @param string the key identifying the value to be cached + * @param mixed the value to be cached + * @param integer the number of seconds in which the cached value will expire. 0 means never expire. + * @param ICacheDependency dependency of the cached item. If the dependency changes, the item is labelled invalid. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + public function add($id,$value,$expire=0,$dependency=null); + /** + * Deletes a value with the specified key from cache + * @param string the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + public function delete($id); + /** + * Deletes all values from cache. + * Be careful of performing this operation if the cache is shared by multiple applications. + */ + public function flush(); +} + +/** + * ICacheDependency interface. + * + * This interface must be implemented by classes meant to be used as + * cache dependencies. + * + * Classes implementing this interface must support serialization and unserialization. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface ICacheDependency +{ + /** + * @return boolean whether the dependency has changed. Defaults to false. + */ + public function getHasChanged(); +} + +/** + * IRenderable interface. + * + * This interface must be implemented by classes that can be rendered + * to end-users. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface IRenderable +{ + /** + * Renders the component to end-users. + * @param ITextWriter writer for the rendering purpose + */ + public function render($writer); +} + +/** + * IBindable interface. + * + * This interface must be implemented by classes that are capable of performing databinding. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ +interface IBindable +{ + /** + * Performs databinding. + */ + public function dataBind(); +} + +/** + * IStyleable interface. + * + * This interface should be implemented by classes that support CSS styles. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.1.0 + */ +interface IStyleable +{ + /** + * @return boolean whether the object has defined any style information + */ + public function getHasStyle(); + /** + * @return TStyle the object representing the css style of the object + */ + public function getStyle(); + /** + * Removes all styles associated with the object + */ + public function clearStyle(); +} + +/** + * IActiveControl interface. + * + * Active controls must implement IActiveControl interface. + * + * @author Wei Zhuo + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.1 + */ +interface IActiveControl +{ + /** + * @return TBaseActiveControl Active control properties. + */ + public function getActiveControl(); +} + +/** + * ICallbackEventHandler interface. + * + * If a control wants to respond to callback event, it must implement this + * interface. + * + * @author Wei Zhuo + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.1 + */ +interface ICallbackEventHandler +{ + /** + * Raises callback event. The implementation of this function should raise + * appropriate event(s) (e.g. OnClick, OnCommand) indicating the component + * is responsible for the callback event. + * @param TCallbackEventParameter the parameter associated with the callback event + */ + public function raiseCallbackEvent($eventArgument); +} + +/** + * IDataRenderer interface. + * + * If a control wants to be used a renderer for another data-bound control, + * this interface must be implemented. + * + * @author Qiang Xue + * @version $Id: interfaces.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.1 + */ +interface IDataRenderer +{ + /** + * @return mixed the data bound to this object + */ + public function getData(); + + /** + * @param mixed the data to be bound to this object + */ + public function setData($value); +} diff --git a/gui/baculum/framework/powered.gif b/gui/baculum/framework/powered.gif new file mode 100644 index 0000000000..abad3b17bd Binary files /dev/null and b/gui/baculum/framework/powered.gif differ diff --git a/gui/baculum/framework/powered2.gif b/gui/baculum/framework/powered2.gif new file mode 100644 index 0000000000..c31c3fb77a Binary files /dev/null and b/gui/baculum/framework/powered2.gif differ diff --git a/gui/baculum/framework/prado.php b/gui/baculum/framework/prado.php new file mode 100644 index 0000000000..0bb11789ec --- /dev/null +++ b/gui/baculum/framework/prado.php @@ -0,0 +1,66 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2013 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id: prado.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + */ + +/** + * Includes the PradoBase class file + */ +require_once(dirname(__FILE__).'/PradoBase.php'); + +/** + * Defines Prado class if not defined. + */ +if(!class_exists('Prado',false)) +{ + /** + * Prado class. + * + * @author Qiang Xue + * @version $Id: prado.php 3245 2013-01-07 20:23:32Z ctrlaltca $ + * @package System + * @since 3.0 + */ + class Prado extends PradoBase + { + } +} + +/** + * Registers the autoload function. + * Since Prado::autoload will report a fatal error if the class file + * cannot be found, if you have multiple autoloaders, Prado::autoload + * should be registered in the last. + */ +spl_autoload_register(array('Prado','autoload')); + +/** + * Initializes error and exception handlers + */ +Prado::initErrorHandlers(); + +/** + * Includes TApplication class file + */ +require_once(dirname(__FILE__).'/TApplication.php'); + +/** + * Includes TShellApplication class file + */ +require_once(dirname(__FILE__).'/TShellApplication.php'); + diff --git a/gui/baculum/framework/pradolite.php b/gui/baculum/framework/pradolite.php new file mode 100644 index 0000000000..d437c89fc3 --- /dev/null +++ b/gui/baculum/framework/pradolite.php @@ -0,0 +1,11155 @@ +PRADO_DIR); + private static $_usings=array(); + private static $_application=null; + private static $_logger=null; + protected static $classExists = array(); + public static function getVersion() + { + return '3.2.3'; + } + public static function initErrorHandlers() + { + set_error_handler(array('PradoBase','phpErrorHandler')); + set_exception_handler(array('PradoBase','exceptionHandler')); + } + public static function autoload($className) + { + include_once($className.self::CLASS_FILE_EXT); + if(!class_exists($className,false) && !interface_exists($className,false)) + self::fatalError("Class file for '$className' cannot be found."); + } + public static function poweredByPrado($logoType=0) + { + $logoName=$logoType==1?'powered2':'powered'; + if(self::$_application!==null) + { + $am=self::$_application->getAssetManager(); + $url=$am->publishFilePath(self::getPathOfNamespace('System.'.$logoName,'.gif')); + } + else + $url='http://www.pradosoft.com/images/'.$logoName.'.gif'; + return 'Powered by PRADO'; + } + public static function phpErrorHandler($errno,$errstr,$errfile,$errline) + { + if(error_reporting() & $errno) + throw new TPhpErrorException($errno,$errstr,$errfile,$errline); + } + public static function exceptionHandler($exception) + { + if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null) + { + $errorHandler->handleError(null,$exception); + } + else + { + echo $exception; + } + exit(1); + } + public static function setApplication($application) + { + if(self::$_application!==null && !defined('PRADO_TEST_RUN')) + throw new TInvalidOperationException('prado_application_singleton_required'); + self::$_application=$application; + } + public static function getApplication() + { + return self::$_application; + } + public static function getFrameworkPath() + { + return PRADO_DIR; + } + public static function serialize($data) + { + return serialize($data); + } + public static function unserialize($str) + { + return unserialize($str); + } + public static function createComponent($type) + { + if(!isset(self::$classExists[$type])) + self::$classExists[$type] = class_exists($type, false); + if( !isset(self::$_usings[$type]) && !self::$classExists[$type]) { + self::using($type); + self::$classExists[$type] = class_exists($type, false); + } + if( ($pos = strrpos($type, '.')) !== false) + $type = substr($type,$pos+1); + if(($n=func_num_args())>1) + { + $args = func_get_args(); + switch($n) { + case 2: + return new $type($args[1]); + break; + case 3: + return new $type($args[1], $args[2]); + break; + case 4: + return new $type($args[1], $args[2], $args[3]); + break; + case 5: + return new $type($args[1], $args[2], $args[3], $args[4]); + break; + default: + $s='$args[1]'; + for($i=2;$i<$n;++$i) + $s.=",\$args[$i]"; + eval("\$component=new $type($s);"); + return $component; + break; + } + } + else + return new $type; + } + public static function using($namespace,$checkClassExistence=true) + { + if(isset(self::$_usings[$namespace]) || class_exists($namespace,false)) + return; + if(($pos=strrpos($namespace,'.'))===false) { + try + { + include_once($namespace.self::CLASS_FILE_EXT); + } + catch(Exception $e) + { + if($checkClassExistence && !class_exists($namespace,false)) + throw new TInvalidOperationException('prado_component_unknown',$namespace,$e->getMessage()); + else + throw $e; + } + } + else if(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) + { + $className=substr($namespace,$pos+1); + if($className==='*') { + self::$_usings[$namespace]=$path; + set_include_path(get_include_path().PATH_SEPARATOR.$path); + } + else { + self::$_usings[$namespace]=$path; + if(!$checkClassExistence || !class_exists($className,false)) + { + try + { + include_once($path); + } + catch(Exception $e) + { + if($checkClassExistence && !class_exists($className,false)) + throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage()); + else + throw $e; + } + } + } + } + else + throw new TInvalidDataValueException('prado_using_invalid',$namespace); + } + public static function getPathOfNamespace($namespace, $ext='') + { + if(self::CLASS_FILE_EXT === $ext || empty($ext)) + { + if(isset(self::$_usings[$namespace])) + return self::$_usings[$namespace]; + if(isset(self::$_aliases[$namespace])) + return self::$_aliases[$namespace]; + } + $segs = explode('.',$namespace); + $alias = array_shift($segs); + if(null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias))) + return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file === '*') ? '' : DIRECTORY_SEPARATOR.$file.$ext); + return null; + } + public static function getPathOfAlias($alias) + { + return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null; + } + protected static function getPathAliases() + { + return self::$_aliases; + } + public static function setPathOfAlias($alias,$path) + { + if(isset(self::$_aliases[$alias]) && !defined('PRADO_TEST_RUN')) + throw new TInvalidOperationException('prado_alias_redefined',$alias); + else if(($rp=realpath($path))!==false && is_dir($rp)) + { + if(strpos($alias,'.')===false) + self::$_aliases[$alias]=$rp; + else + throw new TInvalidDataValueException('prado_aliasname_invalid',$alias); + } + else + throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path); + } + public static function fatalError($msg) + { + echo '

      Fatal Error

      '; + echo '

      '.$msg.'

      '; + if(!function_exists('debug_backtrace')) + return; + echo '

      Debug Backtrace

      '; + echo '
      ';
      +		$index=-1;
      +		foreach(debug_backtrace() as $t)
      +		{
      +			$index++;
      +			if($index==0)  				continue;
      +			echo '#'.$index.' ';
      +			if(isset($t['file']))
      +				echo basename($t['file']) . ':' . $t['line'];
      +			else
      +				 echo '';
      +			echo ' -- ';
      +			if(isset($t['class']))
      +				echo $t['class'] . $t['type'];
      +			echo $t['function'] . '(';
      +			if(isset($t['args']) && sizeof($t['args']) > 0)
      +			{
      +				$count=0;
      +				foreach($t['args'] as $item)
      +				{
      +					if(is_string($item))
      +					{
      +						$str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
      +						if (strlen($item) > 70)
      +							echo "'". substr($str, 0, 70) . "...'";
      +						else
      +							echo "'" . $str . "'";
      +					}
      +					else if (is_int($item) || is_float($item))
      +						echo $item;
      +					else if (is_object($item))
      +						echo get_class($item);
      +					else if (is_array($item))
      +						echo 'array(' . count($item) . ')';
      +					else if (is_bool($item))
      +						echo $item ? 'true' : 'false';
      +					else if ($item === null)
      +						echo 'NULL';
      +					else if (is_resource($item))
      +						echo get_resource_type($item);
      +					$count++;
      +					if (count($t['args']) > $count)
      +						echo ', ';
      +				}
      +			}
      +			echo ")\n";
      +		}
      +		echo '
      '; + exit(1); + } + public static function getUserLanguages() + { + static $languages=null; + if($languages===null) + { + if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) + $languages[0]='en'; + else + { + $languages=array(); + foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) + { + $array=explode(';q=',trim($language)); + $languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0; + } + arsort($languages); + $languages=array_keys($languages); + if(empty($languages)) + $languages[0]='en'; + } + } + return $languages; + } + public static function getPreferredLanguage() + { + static $language=null; + if($language===null) + { + $langs=Prado::getUserLanguages(); + $lang=explode('-',$langs[0]); + if(empty($lang[0]) || !ctype_alpha($lang[0])) + $language='en'; + else + $language=$lang[0]; + } + return $language; + } + public static function trace($msg,$category='Uncategorized',$ctl=null) + { + if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance) + return; + if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug) + { + $trace=debug_backtrace(); + if(isset($trace[0]['file']) && isset($trace[0]['line'])) + $msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})"; + $level=TLogger::DEBUG; + } + else + $level=TLogger::INFO; + self::log($msg,$level,$category,$ctl); + } + public static function log($msg,$level=TLogger::INFO,$category='Uncategorized',$ctl=null) + { + if(self::$_logger===null) + self::$_logger=new TLogger; + self::$_logger->log($msg,$level,$category,$ctl); + } + public static function getLogger() + { + if(self::$_logger===null) + self::$_logger=new TLogger; + return self::$_logger; + } + public static function varDump($var,$depth=10,$highlight=false) + { + Prado::using('System.Util.TVarDumper'); + return TVarDumper::dump($var,$depth,$highlight); + } + public static function localize($text, $parameters=array(), $catalogue=null, $charset=null) + { + Prado::using('System.I18N.Translation'); + $app = Prado::getApplication()->getGlobalization(false); + $params = array(); + foreach($parameters as $key => $value) + $params['{'.$key.'}'] = $value; + if($app===null || ($config = $app->getTranslationConfiguration())===null) + return strtr($text, $params); + if ($catalogue===null) + $catalogue=isset($config['catalogue'])?$config['catalogue']:'messages'; + Translation::init($catalogue); + $appCharset = $app===null ? '' : $app->getCharset(); + $defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset(); + if(empty($charset)) $charset = $appCharset; + if(empty($charset)) $charset = $defaultCharset; + return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset); + } +} +PradoBase::using('System.TComponent'); +PradoBase::using('System.Exceptions.TException'); +PradoBase::using('System.Util.TLogger'); +if(!class_exists('Prado',false)) +{ + class Prado extends PradoBase + { + } +} +spl_autoload_register(array('Prado','autoload')); +Prado::initErrorHandlers(); +interface IModule +{ + public function init($config); + public function getID(); + public function setID($id); +} +interface IService +{ + public function init($config); + public function getID(); + public function setID($id); + public function getEnabled(); + public function setEnabled($value); + public function run(); +} +interface ITextWriter +{ + public function write($str); + public function flush(); +} +interface IUser +{ + public function getName(); + public function setName($value); + public function getIsGuest(); + public function setIsGuest($value); + public function getRoles(); + public function setRoles($value); + public function isInRole($role); + public function saveToString(); + public function loadFromString($string); +} +interface IStatePersister +{ + public function load(); + public function save($state); +} +interface ICache +{ + public function get($id); + public function set($id,$value,$expire=0,$dependency=null); + public function add($id,$value,$expire=0,$dependency=null); + public function delete($id); + public function flush(); +} +interface ICacheDependency +{ + public function getHasChanged(); +} +interface IRenderable +{ + public function render($writer); +} +interface IBindable +{ + public function dataBind(); +} +interface IStyleable +{ + public function getHasStyle(); + public function getStyle(); + public function clearStyle(); +} +interface IActiveControl +{ + public function getActiveControl(); +} +interface ICallbackEventHandler +{ + public function raiseCallbackEvent($eventArgument); +} +interface IDataRenderer +{ + public function getData(); + public function setData($value); +} +class TApplicationComponent extends TComponent +{ + public function getApplication() + { + return Prado::getApplication(); + } + public function getService() + { + return Prado::getApplication()->getService(); + } + public function getRequest() + { + return Prado::getApplication()->getRequest(); + } + public function getResponse() + { + return Prado::getApplication()->getResponse(); + } + public function getSession() + { + return Prado::getApplication()->getSession(); + } + public function getUser() + { + return Prado::getApplication()->getUser(); + } + public function publishAsset($assetPath,$className=null) + { + if($className===null) + $className=get_class($this); + $class=new ReflectionClass($className); + $fullPath=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$assetPath; + return $this->publishFilePath($fullPath); + } + public function publishFilePath($fullPath, $checkTimestamp=false) + { + return Prado::getApplication()->getAssetManager()->publishFilePath($fullPath, $checkTimestamp); + } +} +abstract class TModule extends TApplicationComponent implements IModule +{ + private $_id; + public function init($config) + { + } + public function getID() + { + return $this->_id; + } + public function setID($value) + { + $this->_id=$value; + } +} +abstract class TService extends TApplicationComponent implements IService +{ + private $_id; + private $_enabled=true; + public function init($config) + { + } + public function getID() + { + return $this->_id; + } + public function setID($value) + { + $this->_id=$value; + } + public function getEnabled() + { + return $this->_enabled; + } + public function setEnabled($value) + { + $this->_enabled=TPropertyValue::ensureBoolean($value); + } + public function run() + { + } +} +class TErrorHandler extends TModule +{ + const ERROR_FILE_NAME='error'; + const EXCEPTION_FILE_NAME='exception'; + const SOURCE_LINES=12; + private $_templatePath=null; + public function init($config) + { + $this->getApplication()->setErrorHandler($this); + } + public function getErrorTemplatePath() + { + if($this->_templatePath===null) + $this->_templatePath=Prado::getFrameworkPath().'/Exceptions/templates'; + return $this->_templatePath; + } + public function setErrorTemplatePath($value) + { + if(($templatePath=Prado::getPathOfNamespace($value))!==null && is_dir($templatePath)) + $this->_templatePath=$templatePath; + else + throw new TConfigurationException('errorhandler_errortemplatepath_invalid',$value); + } + public function handleError($sender,$param) + { + static $handling=false; + restore_error_handler(); + restore_exception_handler(); + if($handling) + $this->handleRecursiveError($param); + else + { + $handling=true; + if(($response=$this->getResponse())!==null) + $response->clear(); + if(!headers_sent()) + header('Content-Type: text/html; charset=UTF-8'); + if($param instanceof THttpException) + $this->handleExternalError($param->getStatusCode(),$param); + else if($this->getApplication()->getMode()===TApplicationMode::Debug) + $this->displayException($param); + else + $this->handleExternalError(500,$param); + } + } + protected static function hideSecurityRelated($value, $exception=null) + { + $aRpl = array(); + if($exception !== null && $exception instanceof Exception) + { + $aTrace = $exception->getTrace(); + foreach($aTrace as $item) + { + if(isset($item['file'])) + $aRpl[dirname($item['file']) . DIRECTORY_SEPARATOR] = '' . DIRECTORY_SEPARATOR; + } + } + $aRpl[$_SERVER['DOCUMENT_ROOT']] = '${DocumentRoot}'; + $aRpl[str_replace('/', DIRECTORY_SEPARATOR, $_SERVER['DOCUMENT_ROOT'])] = '${DocumentRoot}'; + $aRpl[PRADO_DIR . DIRECTORY_SEPARATOR] = '${PradoFramework}' . DIRECTORY_SEPARATOR; + if(isset($aRpl[DIRECTORY_SEPARATOR])) unset($aRpl[DIRECTORY_SEPARATOR]); + $aRpl = array_reverse($aRpl, true); + return str_replace(array_keys($aRpl), $aRpl, $value); + } + protected function handleExternalError($statusCode,$exception) + { + if(!($exception instanceof THttpException)) + error_log($exception->__toString()); + $content=$this->getErrorTemplate($statusCode,$exception); + $serverAdmin=isset($_SERVER['SERVER_ADMIN'])?$_SERVER['SERVER_ADMIN']:''; + $isDebug = $this->getApplication()->getMode()===TApplicationMode::Debug; + $errorMessage = $exception->getMessage(); + if($isDebug) + $version=$_SERVER['SERVER_SOFTWARE'].' PRADO/'.Prado::getVersion(); + else + { + $version=''; + $errorMessage = self::hideSecurityRelated($errorMessage, $exception); + } + $tokens=array( + '%%StatusCode%%' => "$statusCode", + '%%ErrorMessage%%' => htmlspecialchars($errorMessage), + '%%ServerAdmin%%' => $serverAdmin, + '%%Version%%' => $version, + '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) + ); + $CGI=substr(php_sapi_name(), 0, 3) == 'cgi'; if($isDebug) + { + if ($CGI) + header("Status: $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode)); + else + header("HTTP/1.0 $statusCode ".$exception->getMessage(), true, TPropertyValue::ensureInteger($statusCode)); + } else { + if ($CGI) + header("Status: $statusCode", true, TPropertyValue::ensureInteger($statusCode)); + else + header("HTTP/1.0 $statusCode", true, TPropertyValue::ensureInteger($statusCode)); + } + echo strtr($content,$tokens); + } + protected function handleRecursiveError($exception) + { + if($this->getApplication()->getMode()===TApplicationMode::Debug) + { + echo "Recursive Error\n"; + echo "

      Recursive Error

      \n"; + echo "
      ".$exception->__toString()."
      \n"; + echo ""; + } + else + { + error_log("Error happened while processing an existing error:\n".$exception->__toString()); + header('HTTP/1.0 500 Internal Error'); + } + } + protected function displayException($exception) + { + if(php_sapi_name()==='cli') + { + echo $exception->getMessage()."\n"; + echo $exception->getTraceAsString(); + return; + } + if($exception instanceof TTemplateException) + { + $fileName=$exception->getTemplateFile(); + $lines=empty($fileName)?explode("\n",$exception->getTemplateSource()):@file($fileName); + $source=$this->getSourceCode($lines,$exception->getLineNumber()); + if($fileName==='') + $fileName='---embedded template---'; + $errorLine=$exception->getLineNumber(); + } + else + { + if(($trace=$this->getExactTrace($exception))!==null) + { + $fileName=$trace['file']; + $errorLine=$trace['line']; + } + else + { + $fileName=$exception->getFile(); + $errorLine=$exception->getLine(); + } + $source=$this->getSourceCode(@file($fileName),$errorLine); + } + if($this->getApplication()->getMode()===TApplicationMode::Debug) + $version=$_SERVER['SERVER_SOFTWARE'].' PRADO/'.Prado::getVersion(); + else + $version=''; + $tokens=array( + '%%ErrorType%%' => get_class($exception), + '%%ErrorMessage%%' => $this->addLink(htmlspecialchars($exception->getMessage())), + '%%SourceFile%%' => htmlspecialchars($fileName).' ('.$errorLine.')', + '%%SourceCode%%' => $source, + '%%StackTrace%%' => htmlspecialchars($exception->getTraceAsString()), + '%%Version%%' => $version, + '%%Time%%' => @strftime('%Y-%m-%d %H:%M',time()) + ); + $content=$this->getExceptionTemplate($exception); + echo strtr($content,$tokens); + } + protected function getExceptionTemplate($exception) + { + $lang=Prado::getPreferredLanguage(); + $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'-'.$lang.'.html'; + if(!is_file($exceptionFile)) + $exceptionFile=Prado::getFrameworkPath().'/Exceptions/templates/'.self::EXCEPTION_FILE_NAME.'.html'; + if(($content=@file_get_contents($exceptionFile))===false) + die("Unable to open exception template file '$exceptionFile'."); + return $content; + } + protected function getErrorTemplate($statusCode,$exception) + { + $base=$this->getErrorTemplatePath().DIRECTORY_SEPARATOR.self::ERROR_FILE_NAME; + $lang=Prado::getPreferredLanguage(); + if(is_file("$base$statusCode-$lang.html")) + $errorFile="$base$statusCode-$lang.html"; + else if(is_file("$base$statusCode.html")) + $errorFile="$base$statusCode.html"; + else if(is_file("$base-$lang.html")) + $errorFile="$base-$lang.html"; + else + $errorFile="$base.html"; + if(($content=@file_get_contents($errorFile))===false) + die("Unable to open error template file '$errorFile'."); + return $content; + } + private function getExactTrace($exception) + { + $trace=$exception->getTrace(); + $result=null; + if($exception instanceof TPhpErrorException) + $result=isset($trace[0]['file'])?$trace[0]:$trace[1]; + else if($exception instanceof TInvalidOperationException) + { + if(($result=$this->getPropertyAccessTrace($trace,'__get'))===null) + $result=$this->getPropertyAccessTrace($trace,'__set'); + } + if($result!==null && strpos($result['file'],': eval()\'d code')!==false) + return null; + return $result; + } + private function getPropertyAccessTrace($trace,$pattern) + { + $result=null; + foreach($trace as $t) + { + if(isset($t['function']) && $t['function']===$pattern) + $result=$t; + else + break; + } + return $result; + } + private function getSourceCode($lines,$errorLine) + { + $beginLine=$errorLine-self::SOURCE_LINES>=0?$errorLine-self::SOURCE_LINES:0; + $endLine=$errorLine+self::SOURCE_LINES<=count($lines)?$errorLine+self::SOURCE_LINES:count($lines); + $source=''; + for($i=$beginLine;$i<$endLine;++$i) + { + if($i===$errorLine-1) + { + $line=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + $source.="
      ".$line."
      "; + } + else + $source.=htmlspecialchars(sprintf("%04d: %s",$i+1,str_replace("\t",' ',$lines[$i]))); + } + return $source; + } + private function addLink($message) + { + $baseUrl='http://www.pradosoft.com/docs/classdoc'; + return preg_replace('/\b(T[A-Z]\w+)\b/',"\${1}",$message); + } +} +class TList extends TComponent implements IteratorAggregate,ArrayAccess,Countable +{ + private $_d=array(); + private $_c=0; + private $_r=false; + public function __construct($data=null,$readOnly=false) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + } + public function getReadOnly() + { + return $this->_r; + } + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + public function count() + { + return $this->getCount(); + } + public function getCount() + { + return $this->_c; + } + public function itemAt($index) + { + if($index>=0 && $index<$this->_c) + return $this->_d[$index]; + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + public function add($item) + { + $this->insertAt($this->_c,$item); + return $this->_c-1; + } + public function insertAt($index,$item) + { + if(!$this->_r) + { + if($index===$this->_c) + $this->_d[$this->_c++]=$item; + else if($index>=0 && $index<$this->_c) + { + array_splice($this->_d,$index,0,array($item)); + $this->_c++; + } + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + public function remove($item) + { + if(!$this->_r) + { + if(($index=$this->indexOf($item))>=0) + { + $this->removeAt($index); + return $index; + } + else + throw new TInvalidDataValueException('list_item_inexistent'); + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + public function removeAt($index) + { + if(!$this->_r) + { + if($index>=0 && $index<$this->_c) + { + $this->_c--; + if($index===$this->_c) + return array_pop($this->_d); + else + { + $item=$this->_d[$index]; + array_splice($this->_d,$index,1); + return $item; + } + } + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + public function clear() + { + for($i=$this->_c-1;$i>=0;--$i) + $this->removeAt($i); + } + public function contains($item) + { + return $this->indexOf($item)>=0; + } + public function indexOf($item) + { + if(($index=array_search($item,$this->_d,true))===false) + return -1; + else + return $index; + } + public function insertBefore($baseitem, $item) + { + if(!$this->_r) + { + if(($index = $this->indexOf($baseitem)) == -1) + throw new TInvalidDataValueException('list_item_inexistent'); + $this->insertAt($index, $item); + return $index; + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + public function insertAfter($baseitem, $item) + { + if(!$this->_r) + { + if(($index = $this->indexOf($baseitem)) == -1) + throw new TInvalidDataValueException('list_item_inexistent'); + $this->insertAt($index + 1, $item); + return $index + 1; + } + else + throw new TInvalidOperationException('list_readonly',get_class($this)); + } + public function toArray() + { + return $this->_d; + } + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + if($this->_c>0) + $this->clear(); + foreach($data as $item) + $this->add($item); + } + else if($data!==null) + throw new TInvalidDataTypeException('list_data_not_iterable'); + } + public function mergeWith($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + foreach($data as $item) + $this->add($item); + } + else if($data!==null) + throw new TInvalidDataTypeException('list_data_not_iterable'); + } + public function offsetExists($offset) + { + return ($offset>=0 && $offset<$this->_c); + } + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + public function offsetSet($offset,$item) + { + if($offset===null || $offset===$this->_c) + $this->insertAt($this->_c,$item); + else + { + $this->removeAt($offset); + $this->insertAt($offset,$item); + } + } + public function offsetUnset($offset) + { + $this->removeAt($offset); + } +} +class TListIterator implements Iterator +{ + private $_d; + private $_i; + private $_c; + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + public function rewind() + { + $this->_i=0; + } + public function key() + { + return $this->_i; + } + public function current() + { + return $this->_d[$this->_i]; + } + public function next() + { + $this->_i++; + } + public function valid() + { + return $this->_i<$this->_c; + } +} +abstract class TCache extends TModule implements ICache, ArrayAccess +{ + private $_prefix=null; + private $_primary=true; + public function init($config) + { + if($this->_prefix===null) + $this->_prefix=$this->getApplication()->getUniqueID(); + if($this->_primary) + { + if($this->getApplication()->getCache()===null) + $this->getApplication()->setCache($this); + else + throw new TConfigurationException('cache_primary_duplicated',get_class($this)); + } + } + public function getPrimaryCache() + { + return $this->_primary; + } + public function setPrimaryCache($value) + { + $this->_primary=TPropertyValue::ensureBoolean($value); + } + public function getKeyPrefix() + { + return $this->_prefix; + } + public function setKeyPrefix($value) + { + $this->_prefix=$value; + } + protected function generateUniqueKey($key) + { + return md5($this->_prefix.$key); + } + public function get($id) + { + if(($data=$this->getValue($this->generateUniqueKey($id)))!==false) + { + if(!is_array($data)) + return false; + if(!($data[1] instanceof ICacheDependency) || !$data[1]->getHasChanged()) + return $data[0]; + } + return false; + } + public function set($id,$value,$expire=0,$dependency=null) + { + if(empty($value) && $expire === 0) + $this->delete($id); + else + { + $data=array($value,$dependency); + return $this->setValue($this->generateUniqueKey($id),$data,$expire); + } + } + public function add($id,$value,$expire=0,$dependency=null) + { + if(empty($value) && $expire === 0) + return false; + $data=array($value,$dependency); + return $this->addValue($this->generateUniqueKey($id),$data,$expire); + } + public function delete($id) + { + return $this->deleteValue($this->generateUniqueKey($id)); + } + public function flush() + { + throw new TNotSupportedException('cache_flush_unsupported'); + } + abstract protected function getValue($key); + abstract protected function setValue($key,$value,$expire); + abstract protected function addValue($key,$value,$expire); + abstract protected function deleteValue($key); + public function offsetExists($id) + { + return $this->get($id) !== false; + } + public function offsetGet($id) + { + return $this->get($id); + } + public function offsetSet($id, $value) + { + $this->set($id, $value); + } + public function offsetUnset($id) + { + $this->delete($id); + } +} +abstract class TCacheDependency extends TComponent implements ICacheDependency +{ +} +class TFileCacheDependency extends TCacheDependency +{ + private $_fileName; + private $_timestamp; + public function __construct($fileName) + { + $this->setFileName($fileName); + } + public function getFileName() + { + return $this->_fileName; + } + public function setFileName($value) + { + $this->_fileName=$value; + $this->_timestamp=@filemtime($value); + } + public function getTimestamp() + { + return $this->_timestamp; + } + public function getHasChanged() + { + return @filemtime($this->_fileName)!==$this->_timestamp; + } +} +class TDirectoryCacheDependency extends TCacheDependency +{ + private $_recursiveCheck=true; + private $_recursiveLevel=-1; + private $_timestamps; + private $_directory; + public function __construct($directory) + { + $this->setDirectory($directory); + } + public function getDirectory() + { + return $this->_directory; + } + public function setDirectory($directory) + { + if(($path=realpath($directory))===false || !is_dir($path)) + throw new TInvalidDataValueException('directorycachedependency_directory_invalid',$directory); + $this->_directory=$path; + $this->_timestamps=$this->generateTimestamps($path); + } + public function getRecursiveCheck() + { + return $this->_recursiveCheck; + } + public function setRecursiveCheck($value) + { + $this->_recursiveCheck=TPropertyValue::ensureBoolean($value); + } + public function getRecursiveLevel() + { + return $this->_recursiveLevel; + } + public function setRecursiveLevel($value) + { + $this->_recursiveLevel=TPropertyValue::ensureInteger($value); + } + public function getHasChanged() + { + return $this->generateTimestamps($this->_directory)!=$this->_timestamps; + } + protected function validateFile($fileName) + { + return true; + } + protected function validateDirectory($directory) + { + return true; + } + protected function generateTimestamps($directory,$level=0) + { + if(($dir=opendir($directory))===false) + throw new TIOException('directorycachedependency_directory_invalid',$directory); + $timestamps=array(); + while(($file=readdir($dir))!==false) + { + $path=$directory.DIRECTORY_SEPARATOR.$file; + if($file==='.' || $file==='..') + continue; + else if(is_dir($path)) + { + if(($this->_recursiveLevel<0 || $level<$this->_recursiveLevel) && $this->validateDirectory($path)) + $timestamps=array_merge($this->generateTimestamps($path,$level+1)); + } + else if($this->validateFile($path)) + $timestamps[$path]=filemtime($path); + } + closedir($dir); + return $timestamps; + } +} +class TGlobalStateCacheDependency extends TCacheDependency +{ + private $_stateName; + private $_stateValue; + public function __construct($name) + { + $this->setStateName($name); + } + public function getStateName() + { + return $this->_stateName; + } + public function setStateName($value) + { + $this->_stateName=$value; + $this->_stateValue=Prado::getApplication()->getGlobalState($value); + } + public function getHasChanged() + { + return $this->_stateValue!==Prado::getApplication()->getGlobalState($this->_stateName); + } +} +class TChainedCacheDependency extends TCacheDependency +{ + private $_dependencies=null; + public function getDependencies() + { + if($this->_dependencies===null) + $this->_dependencies=new TCacheDependencyList; + return $this->_dependencies; + } + public function getHasChanged() + { + if($this->_dependencies!==null) + { + foreach($this->_dependencies as $dependency) + if($dependency->getHasChanged()) + return true; + } + return false; + } +} +class TApplicationStateCacheDependency extends TCacheDependency +{ + public function getHasChanged() + { + return Prado::getApplication()->getMode()!==TApplicationMode::Performance; + } +} +class TCacheDependencyList extends TList +{ + public function insertAt($index,$item) + { + if($item instanceof ICacheDependency) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('cachedependencylist_cachedependency_required'); + } +} +class TTextWriter extends TComponent implements ITextWriter +{ + private $_str=''; + public function flush() + { + $str=$this->_str; + $this->_str=''; + return $str; + } + public function write($str) + { + $this->_str.=$str; + } + public function writeLine($str='') + { + $this->write($str."\n"); + } +} +class TPriorityList extends TList +{ + private $_d=array(); + private $_o=false; + private $_fd=null; + private $_c=0; + private $_dp=10; + private $_p=8; + public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) + { + parent::__construct(); + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + $this->setPrecision($precision); + $this->setDefaultPriority($defaultPriority); + } + public function count() + { + return $this->getCount(); + } + public function getCount() + { + return $this->_c; + } + public function getPriorityCount($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(!isset($this->_d[$priority]) || !is_array($this->_d[$priority])) + return false; + return count($this->_d[$priority]); + } + public function getDefaultPriority() + { + return $this->_dp; + } + protected function setDefaultPriority($value) + { + $this->_dp=(string)round(TPropertyValue::ensureFloat($value),$this->_p); + } + public function getPrecision() + { + return $this->_p; + } + protected function setPrecision($value) + { + $this->_p=TPropertyValue::ensureInteger($value); + } + public function getIterator() + { + return new ArrayIterator($this->flattenPriorities()); + } + public function getPriorities() + { + $this->sortPriorities(); + return array_keys($this->_d); + } + protected function sortPriorities() { + if(!$this->_o) { + ksort($this->_d,SORT_NUMERIC); + $this->_o=true; + } + } + protected function flattenPriorities() { + if(is_array($this->_fd)) + return $this->_fd; + $this->sortPriorities(); + $this->_fd=array(); + foreach($this->_d as $priority => $itemsatpriority) + $this->_fd=array_merge($this->_fd,$itemsatpriority); + return $this->_fd; + } + public function itemAt($index) + { + if($index>=0&&$index<$this->getCount()) { + $arr=$this->flattenPriorities(); + return $arr[$index]; + } else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + public function itemsAtPriority($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + return isset($this->_d[$priority])?$this->_d[$priority]:null; + } + public function itemAtIndexInPriority($index,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); + return !isset($this->_d[$priority])?false:( + isset($this->_d[$priority][$index])?$this->_d[$priority][$index]:false + ); + } + public function add($item,$priority=null) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + return $this->insertAtIndexInPriority($item,false,$priority,true); + } + public function insertAt($index,$item) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if(($priority=$this->priorityAt($index,true))!==false) + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); + else + throw new TInvalidDataValueException('list_index_invalid',$index); + } + public function insertAtIndexInPriority($item,$index=false,$priority=null,$preserveCache=false) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority), $this->_p); + if($preserveCache) { + $this->sortPriorities(); + $cc=0; + foreach($this->_d as $prioritykey=>$items) + if($prioritykey>=$priority) + break; + else + $cc+=count($items); + if($index===false&&isset($this->_d[$priority])) { + $c=count($this->_d[$priority]); + $c+=$cc; + $this->_d[$priority][]=$item; + } else if(isset($this->_d[$priority])) { + $c=$index+$cc; + array_splice($this->_d[$priority],$index,0,array($item)); + } else { + $c = $cc; + $this->_o = false; + $this->_d[$priority]=array($item); + } + if($this->_fd&&is_array($this->_fd)) array_splice($this->_fd,$c,0,array($item)); + } else { + $c=null; + if($index===false&&isset($this->_d[$priority])) { + $cc=count($this->_d[$priority]); + $this->_d[$priority][]=$item; + } else if(isset($this->_d[$priority])) { + $cc=$index; + array_splice($this->_d[$priority],$index,0,array($item)); + } else { + $cc=0; + $this->_o=false; + $this->_d[$priority]=array($item); + } + if($this->_fd&&is_array($this->_fd)&&count($this->_d)==1) + array_splice($this->_fd,$cc,0,array($item)); + else + $this->_fd=null; + } + $this->_c++; + return $c; + } + public function remove($item,$priority=false) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if(($p=$this->priorityOf($item,true))!==false) + { + if($priority!==false) { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if($p[0]!=$priority) + throw new TInvalidDataValueException('list_item_inexistent'); + } + $this->removeAtIndexInPriority($p[1],$p[0]); + return $p[2]; + } + else + throw new TInvalidDataValueException('list_item_inexistent'); + } + public function removeAt($index) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if(($priority=$this->priorityAt($index, true))!==false) + return $this->removeAtIndexInPriority($priority[1],$priority[0]); + throw new TInvalidDataValueException('list_index_invalid',$index); + } + public function removeAtIndexInPriority($index, $priority=null) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(!isset($this->_d[$priority])||$index<0||$index>=count($this->_d[$priority])) + throw new TInvalidDataValueException('list_item_inexistent'); + $value=array_splice($this->_d[$priority],$index,1); + $value=$value[0]; + if(!count($this->_d[$priority])) + unset($this->_d[$priority]); + $this->_c--; + $this->_fd=null; + return $value; + } + public function clear() + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + $d=array_reverse($this->_d,true); + foreach($this->_d as $priority=>$items) { + for($index=count($items)-1;$index>=0;$index--) + $this->removeAtIndexInPriority($index,$priority); + unset($this->_d[$priority]); + } + } + public function contains($item) + { + return $this->indexOf($item)>=0; + } + public function indexOf($item) + { + if(($index=array_search($item,$this->flattenPriorities(),true))===false) + return -1; + else + return $index; + } + public function priorityOf($item,$withindex = false) + { + $this->sortPriorities(); + $absindex = 0; + foreach($this->_d as $priority=>$items) { + if(($index=array_search($item,$items,true))!==false) { + $absindex+=$index; + return $withindex?array($priority,$index,$absindex, + 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; + } else + $absindex+=count($items); + } + return false; + } + public function priorityAt($index,$withindex = false) + { + if($index<0||$index>=$this->getCount()) + throw new TInvalidDataValueException('list_index_invalid',$index); + $absindex=$index; + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) { + if($index>=($c=count($items))) + $index-=$c; + else + return $withindex?array($priority,$index,$absindex, + 'priority'=>$priority,'index'=>$index,'absindex'=>$absindex):$priority; + } + return false; + } + public function insertBefore($indexitem, $item) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if(($priority=$this->priorityOf($indexitem,true))===false) + throw new TInvalidDataValueException('list_item_inexistent'); + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); + return $priority[2]; + } + public function insertAfter($indexitem, $item) + { + if($this->getReadOnly()) + throw new TInvalidOperationException('list_readonly',get_class($this)); + if(($priority=$this->priorityOf($indexitem,true))===false) + throw new TInvalidDataValueException('list_item_inexistent'); + $this->insertAtIndexInPriority($item,$priority[1]+1,$priority[0]); + return $priority[2]+1; + } + public function toArray() + { + return $this->flattenPriorities(); + } + public function toPriorityArray() + { + $this->sortPriorities(); + return $this->_d; + } + public function toArrayBelowPriority($priority,$inclusive=false) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) + break; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + public function toArrayAbovePriority($priority,$inclusive=true) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) + continue; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + public function copyFrom($data) + { + if($data instanceof TPriorityList) + { + if($this->getCount()>0) + $this->clear(); + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $index=>$item) + $this->insertAtIndexInPriority($item,$index,$priority); + } + } else if(is_array($data)||$data instanceof Traversable) { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$item) + $this->add($item); + } else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + public function mergeWith($data) + { + if($data instanceof TPriorityList) + { + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $index=>$item) + $this->insertAtIndexInPriority($item,false,$priority); + } + } + else if(is_array($data)||$data instanceof Traversable) + { + foreach($data as $priority=>$item) + $this->add($item); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + public function offsetExists($offset) + { + return ($offset>=0&&$offset<$this->getCount()); + } + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + public function offsetSet($offset,$item) + { + if($offset===null) + return $this->add($item); + if($offset===$this->getCount()) { + $priority=$this->priorityAt($offset-1,true); + $priority[1]++; + } else { + $priority=$this->priorityAt($offset,true); + $this->removeAtIndexInPriority($priority[1],$priority[0]); + } + $this->insertAtIndexInPriority($item,$priority[1],$priority[0]); + } + public function offsetUnset($offset) + { + $this->removeAt($offset); + } +} +class TMap extends TComponent implements IteratorAggregate,ArrayAccess,Countable +{ + private $_d=array(); + private $_r=false; + public function __construct($data=null,$readOnly=false) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + } + public function getReadOnly() + { + return $this->_r; + } + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + public function count() + { + return $this->getCount(); + } + public function getCount() + { + return count($this->_d); + } + public function getKeys() + { + return array_keys($this->_d); + } + public function itemAt($key) + { + return isset($this->_d[$key]) ? $this->_d[$key] : null; + } + public function add($key,$value) + { + if(!$this->_r) + $this->_d[$key]=$value; + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + public function remove($key) + { + if(!$this->_r) + { + if(isset($this->_d[$key]) || array_key_exists($key,$this->_d)) + { + $value=$this->_d[$key]; + unset($this->_d[$key]); + return $value; + } + else + return null; + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + public function clear() + { + foreach(array_keys($this->_d) as $key) + $this->remove($key); + } + public function contains($key) + { + return isset($this->_d[$key]) || array_key_exists($key,$this->_d); + } + public function toArray() + { + return $this->_d; + } + public function copyFrom($data) + { + if(is_array($data) || $data instanceof Traversable) + { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + public function mergeWith($data) + { + if(is_array($data) || $data instanceof Traversable) + { + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + public function offsetExists($offset) + { + return $this->contains($offset); + } + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + public function offsetUnset($offset) + { + $this->remove($offset); + } +} +class TMapIterator implements Iterator +{ + private $_d; + private $_keys; + private $_key; + public function __construct(&$data) + { + $this->_d=&$data; + $this->_keys=array_keys($data); + } + public function rewind() + { + $this->_key=reset($this->_keys); + } + public function key() + { + return $this->_key; + } + public function current() + { + return $this->_d[$this->_key]; + } + public function next() + { + $this->_key=next($this->_keys); + } + public function valid() + { + return $this->_key!==false; + } +} +class TPriorityMap extends TMap +{ + private $_d=array(); + private $_r=false; + private $_o=false; + private $_fd=null; + private $_c=0; + private $_dp=10; + private $_p=8; + public function __construct($data=null,$readOnly=false,$defaultPriority=10,$precision=8) + { + if($data!==null) + $this->copyFrom($data); + $this->setReadOnly($readOnly); + $this->setPrecision($precision); + $this->setDefaultPriority($defaultPriority); + } + public function getReadOnly() + { + return $this->_r; + } + protected function setReadOnly($value) + { + $this->_r=TPropertyValue::ensureBoolean($value); + } + public function getDefaultPriority() + { + return $this->_dp; + } + protected function setDefaultPriority($value) + { + $this->_dp = (string)round(TPropertyValue::ensureFloat($value), $this->_p); + } + public function getPrecision() + { + return $this->_p; + } + protected function setPrecision($value) + { + $this->_p=TPropertyValue::ensureInteger($value); + } + public function getIterator() + { + return new ArrayIterator($this->flattenPriorities()); + } + protected function sortPriorities() { + if(!$this->_o) { + ksort($this->_d, SORT_NUMERIC); + $this->_o=true; + } + } + protected function flattenPriorities() { + if(is_array($this->_fd)) + return $this->_fd; + $this->sortPriorities(); + $this->_fd = array(); + foreach($this->_d as $priority => $itemsatpriority) + $this->_fd = array_merge($this->_fd, $itemsatpriority); + return $this->_fd; + } + public function count() + { + return $this->getCount(); + } + public function getCount() + { + return $this->_c; + } + public function getPriorityCount($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(!isset($this->_d[$priority])||!is_array($this->_d[$priority])) + return false; + return count($this->_d[$priority]); + } + public function getPriorities() + { + $this->sortPriorities(); + return array_keys($this->_d); + } + public function getKeys() + { + return array_keys($this->flattenPriorities()); + } + public function itemAt($key,$priority=false) + { + if($priority===false){ + $map=$this->flattenPriorities(); + return isset($map[$key])?$map[$key]:null; + } else { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + return (isset($this->_d[$priority])&&isset($this->_d[$priority][$key]))?$this->_d[$priority][$key]:null; + } + } + public function setPriorityAt($key,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + $oldpriority=$this->priorityAt($key); + if($oldpriority!==false&&$oldpriority!=$priority) { + $value=$this->remove($key,$oldpriority); + $this->add($key,$value,$priority); + } + return $oldpriority; + } + public function itemsAtPriority($priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + return isset($this->_d[$priority])?$this->_d[$priority]:null; + } + public function priorityOf($item) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(($index=array_search($item,$items,true))!==false) + return $priority; + return false; + } + public function priorityAt($key) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(array_key_exists($key,$items)) + return $priority; + return false; + } + public function add($key,$value,$priority=null) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(!$this->_r) + { + foreach($this->_d as $innerpriority=>$items) + if(array_key_exists($key,$items)) + { + unset($this->_d[$innerpriority][$key]); + $this->_c--; + if(count($this->_d[$innerpriority])===0) + unset($this->_d[$innerpriority]); + } + if(!isset($this->_d[$priority])) { + $this->_d[$priority]=array($key=>$value); + $this->_o=false; + } + else + $this->_d[$priority][$key]=$value; + $this->_c++; + $this->_fd=null; + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + return $priority; + } + public function remove($key,$priority=false) + { + if(!$this->_r) + { + if($priority===null) + $priority=$this->getDefaultPriority(); + if($priority===false) + { + $this->sortPriorities(); + foreach($this->_d as $priority=>$items) + if(array_key_exists($key,$items)) + { + $value=$this->_d[$priority][$key]; + unset($this->_d[$priority][$key]); + $this->_c--; + if(count($this->_d[$priority])===0) + { + unset($this->_d[$priority]); + $this->_o=false; + } + $this->_fd=null; + return $value; + } + return null; + } + else + { + $priority=(string)round(TPropertyValue::ensureFloat($priority),$this->_p); + if(isset($this->_d[$priority])&&(isset($this->_d[$priority][$key])||array_key_exists($key,$this->_d[$priority]))) + { + $value=$this->_d[$priority][$key]; + unset($this->_d[$priority][$key]); + $this->_c--; + if(count($this->_d[$priority])===0) { + unset($this->_d[$priority]); + $this->_o=false; + } + $this->_fd=null; + return $value; + } + else + return null; + } + } + else + throw new TInvalidOperationException('map_readonly',get_class($this)); + } + public function clear() + { + foreach($this->_d as $priority=>$items) + foreach(array_keys($items) as $key) + $this->remove($key); + } + public function contains($key) + { + $map=$this->flattenPriorities(); + return isset($map[$key])||array_key_exists($key,$map); + } + public function toArray() + { + return $this->flattenPriorities(); + } + public function toArrayBelowPriority($priority,$inclusive=false) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority>=$priority)||$itemspriority>$priority) + break; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + public function toArrayAbovePriority($priority,$inclusive=true) + { + $this->sortPriorities(); + $items=array(); + foreach($this->_d as $itemspriority=>$itemsatpriority) + { + if((!$inclusive&&$itemspriority<=$priority)||$itemspriority<$priority) + continue; + $items=array_merge($items,$itemsatpriority); + } + return $items; + } + public function copyFrom($data) + { + if($data instanceof TPriorityMap) + { + if($this->getCount()>0) + $this->clear(); + foreach($data->getPriorities() as $priority) { + foreach($data->itemsAtPriority($priority) as $key => $value) { + $this->add($key,$value,$priority); + } + } + } + else if(is_array($data)||$data instanceof Traversable) + { + if($this->getCount()>0) + $this->clear(); + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + public function mergeWith($data) + { + if($data instanceof TPriorityMap) + { + foreach($data->getPriorities() as $priority) + { + foreach($data->itemsAtPriority($priority) as $key => $value) + $this->add($key,$value,$priority); + } + } + else if(is_array($data)||$data instanceof Traversable) + { + foreach($data as $key=>$value) + $this->add($key,$value); + } + else if($data!==null) + throw new TInvalidDataTypeException('map_data_not_iterable'); + } + public function offsetExists($offset) + { + return $this->contains($offset); + } + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + public function offsetUnset($offset) + { + $this->remove($offset); + } +} +class TStack extends TComponent implements IteratorAggregate,Countable +{ + private $_d=array(); + private $_c=0; + public function __construct($data=null) + { + if($data!==null) + $this->copyFrom($data); + } + public function toArray() + { + return $this->_d; + } + public function copyFrom($data) + { + if(is_array($data) || ($data instanceof Traversable)) + { + $this->clear(); + foreach($data as $item) + { + $this->_d[]=$item; + ++$this->_c; + } + } + else if($data!==null) + throw new TInvalidDataTypeException('stack_data_not_iterable'); + } + public function clear() + { + $this->_c=0; + $this->_d=array(); + } + public function contains($item) + { + return array_search($item,$this->_d,true)!==false; + } + public function peek() + { + if($this->_c===0) + throw new TInvalidOperationException('stack_empty'); + else + return $this->_d[$this->_c-1]; + } + public function pop() + { + if($this->_c===0) + throw new TInvalidOperationException('stack_empty'); + else + { + --$this->_c; + return array_pop($this->_d); + } + } + public function push($item) + { + ++$this->_c; + $this->_d[] = $item; + } + public function getIterator() + { + return new ArrayIterator( $this->_d ); + } + public function getCount() + { + return $this->_c; + } + public function count() + { + return $this->getCount(); + } +} +class TStackIterator implements Iterator +{ + private $_d; + private $_i; + private $_c; + public function __construct(&$data) + { + $this->_d=&$data; + $this->_i=0; + $this->_c=count($this->_d); + } + public function rewind() + { + $this->_i=0; + } + public function key() + { + return $this->_i; + } + public function current() + { + return $this->_d[$this->_i]; + } + public function next() + { + $this->_i++; + } + public function valid() + { + return $this->_i<$this->_c; + } +} +class TXmlElement extends TComponent +{ + private $_parent=null; + private $_tagName='unknown'; + private $_value=''; + private $_elements=null; + private $_attributes=null; + public function __construct($tagName) + { + $this->setTagName($tagName); + } + public function getParent() + { + return $this->_parent; + } + public function setParent($parent) + { + $this->_parent=$parent; + } + public function getTagName() + { + return $this->_tagName; + } + public function setTagName($tagName) + { + $this->_tagName=$tagName; + } + public function getValue() + { + return $this->_value; + } + public function setValue($value) + { + $this->_value=TPropertyValue::ensureString($value); + } + public function getHasElement() + { + return $this->_elements!==null && $this->_elements->getCount()>0; + } + public function getHasAttribute() + { + return $this->_attributes!==null && $this->_attributes->getCount()>0; + } + public function getAttribute($name) + { + if($this->_attributes!==null) + return $this->_attributes->itemAt($name); + else + return null; + } + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,TPropertyValue::ensureString($value)); + } + public function getElements() + { + if(!$this->_elements) + $this->_elements=new TXmlElementList($this); + return $this->_elements; + } + public function getAttributes() + { + if(!$this->_attributes) + $this->_attributes=new TMap; + return $this->_attributes; + } + public function getElementByTagName($tagName) + { + if($this->_elements) + { + foreach($this->_elements as $element) + if($element->_tagName===$tagName) + return $element; + } + return null; + } + public function getElementsByTagName($tagName) + { + $list=new TList; + if($this->_elements) + { + foreach($this->_elements as $element) + if($element->_tagName===$tagName) + $list->add($element); + } + return $list; + } + public function toString($indent=0) + { + $attr=''; + if($this->_attributes!==null) + { + foreach($this->_attributes as $name=>$value) + { + $value=$this->xmlEncode($value); + $attr.=" $name=\"$value\""; + } + } + $prefix=str_repeat(' ',$indent*4); + if($this->getHasElement()) + { + $str=$prefix."<{$this->_tagName}$attr>\n"; + foreach($this->getElements() as $element) + $str.=$element->toString($indent+1)."\n"; + $str.=$prefix."_tagName}>"; + return $str; + } + else if(($value=$this->getValue())!=='') + { + $value=$this->xmlEncode($value); + return $prefix."<{$this->_tagName}$attr>$value_tagName}>"; + } + else + return $prefix."<{$this->_tagName}$attr />"; + } + public function __toString() + { + return $this->toString(); + } + private function xmlEncode($str) + { + return strtr($str,array( + '>'=>'>', + '<'=>'<', + '&'=>'&', + '"'=>'"', + "\r"=>' ', + "\t"=>' ', + "\n"=>' ')); + } +} +class TXmlDocument extends TXmlElement +{ + private $_version; + private $_encoding; + public function __construct($version='1.0',$encoding='') + { + parent::__construct(''); + $this->setVersion($version); + $this->setEncoding($encoding); + } + public function getVersion() + { + return $this->_version; + } + public function setVersion($version) + { + $this->_version=$version; + } + public function getEncoding() + { + return $this->_encoding; + } + public function setEncoding($encoding) + { + $this->_encoding=$encoding; + } + public function loadFromFile($file) + { + if(($str=@file_get_contents($file))!==false) + return $this->loadFromString($str); + else + throw new TIOException('xmldocument_file_read_failed',$file); + } + public function loadFromString($string) + { + $doc=new DOMDocument(); + if($doc->loadXML($string)===false) + return false; + $this->setEncoding($doc->encoding); + $this->setVersion($doc->version); + $element=$doc->documentElement; + $this->setTagName($element->tagName); + $this->setValue($element->nodeValue); + $elements=$this->getElements(); + $attributes=$this->getAttributes(); + $elements->clear(); + $attributes->clear(); + static $bSimpleXml; + if($bSimpleXml === null) + $bSimpleXml = (boolean)function_exists('simplexml_load_string'); + if($bSimpleXml) + { + $simpleDoc = simplexml_load_string($string); + $docNamespaces = $simpleDoc->getDocNamespaces(false); + $simpleDoc = null; + foreach($docNamespaces as $prefix => $uri) + { + if($prefix === '') + $attributes->add('xmlns', $uri); + else + $attributes->add('xmlns:'.$prefix, $uri); + } + } + foreach($element->attributes as $name=>$attr) + $attributes->add(($attr->prefix === '' ? '' : $attr->prefix . ':') .$name,$attr->value); + foreach($element->childNodes as $child) + { + if($child instanceof DOMElement) + $elements->add($this->buildElement($child)); + } + return true; + } + public function saveToFile($file) + { + if(($fw=fopen($file,'w'))!==false) + { + fwrite($fw,$this->saveToString()); + fclose($fw); + } + else + throw new TIOException('xmldocument_file_write_failed',$file); + } + public function saveToString() + { + $version=empty($this->_version)?' version="1.0"':' version="'.$this->_version.'"'; + $encoding=empty($this->_encoding)?'':' encoding="'.$this->_encoding.'"'; + return "\n".$this->toString(0); + } + public function __toString() + { + return $this->saveToString(); + } + private function buildElement($node) + { + $element=new TXmlElement($node->tagName); + $element->setValue($node->nodeValue); + foreach($node->attributes as $name=>$attr) + $element->getAttributes()->add(($attr->prefix === '' ? '' : $attr->prefix . ':') . $name,$attr->value); + foreach($node->childNodes as $child) + { + if($child instanceof DOMElement) + $element->getElements()->add($this->buildElement($child)); + } + return $element; + } +} +class TXmlElementList extends TList +{ + private $_o; + public function __construct(TXmlElement $owner) + { + $this->_o=$owner; + } + protected function getOwner() + { + return $this->_o; + } + public function insertAt($index,$item) + { + if($item instanceof TXmlElement) + { + parent::insertAt($index,$item); + if($item->getParent()!==null) + $item->getParent()->getElements()->remove($item); + $item->setParent($this->_o); + } + else + throw new TInvalidDataTypeException('xmlelementlist_xmlelement_required'); + } + public function removeAt($index) + { + $item=parent::removeAt($index); + if($item instanceof TXmlElement) + $item->setParent(null); + return $item; + } +} +class TAuthorizationRule extends TComponent +{ + private $_action; + private $_users; + private $_roles; + private $_verb; + private $_ipRules; + private $_everyone; + private $_guest; + private $_authenticated; + public function __construct($action,$users,$roles,$verb='',$ipRules='') + { + $action=strtolower(trim($action)); + if($action==='allow' || $action==='deny') + $this->_action=$action; + else + throw new TInvalidDataValueException('authorizationrule_action_invalid',$action); + $this->_users=array(); + $this->_roles=array(); + $this->_ipRules=array(); + $this->_everyone=false; + $this->_guest=false; + $this->_authenticated=false; + if(trim($users)==='') + $users='*'; + foreach(explode(',',$users) as $user) + { + if(($user=trim(strtolower($user)))!=='') + { + if($user==='*') + { + $this->_everyone=true; + break; + } + else if($user==='?') + $this->_guest=true; + else if($user==='@') + $this->_authenticated=true; + else + $this->_users[]=$user; + } + } + if(trim($roles)==='') + $roles='*'; + foreach(explode(',',$roles) as $role) + { + if(($role=trim(strtolower($role)))!=='') + $this->_roles[]=$role; + } + if(($verb=trim(strtolower($verb)))==='') + $verb='*'; + if($verb==='*' || $verb==='get' || $verb==='post') + $this->_verb=$verb; + else + throw new TInvalidDataValueException('authorizationrule_verb_invalid',$verb); + if(trim($ipRules)==='') + $ipRules='*'; + foreach(explode(',',$ipRules) as $ipRule) + { + if(($ipRule=trim($ipRule))!=='') + $this->_ipRules[]=$ipRule; + } + } + public function getAction() + { + return $this->_action; + } + public function getUsers() + { + return $this->_users; + } + public function getRoles() + { + return $this->_roles; + } + public function getVerb() + { + return $this->_verb; + } + public function getIPRules() + { + return $this->_ipRules; + } + public function getGuestApplied() + { + return $this->_guest || $this->_everyone; + } + public function getEveryoneApplied() + { + return $this->_everyone; + } + public function getAuthenticatedApplied() + { + return $this->_authenticated || $this->_everyone; + } + public function isUserAllowed(IUser $user,$verb,$ip) + { + if($this->isVerbMatched($verb) && $this->isIpMatched($ip) && $this->isUserMatched($user) && $this->isRoleMatched($user)) + return ($this->_action==='allow')?1:-1; + else + return 0; + } + private function isIpMatched($ip) + { + if(empty($this->_ipRules)) + return 1; + foreach($this->_ipRules as $rule) + { + if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && strncmp($ip,$rule,$pos)===0)) + return 1; + } + return 0; + } + private function isUserMatched($user) + { + return ($this->_everyone || ($this->_guest && $user->getIsGuest()) || ($this->_authenticated && !$user->getIsGuest()) || in_array(strtolower($user->getName()),$this->_users)); + } + private function isRoleMatched($user) + { + foreach($this->_roles as $role) + { + if($role==='*' || $user->isInRole($role)) + return true; + } + return false; + } + private function isVerbMatched($verb) + { + return ($this->_verb==='*' || strcasecmp($verb,$this->_verb)===0); + } +} +class TAuthorizationRuleCollection extends TList +{ + public function isUserAllowed($user,$verb,$ip) + { + if($user instanceof IUser) + { + $verb=strtolower(trim($verb)); + foreach($this as $rule) + { + if(($decision=$rule->isUserAllowed($user,$verb,$ip))!==0) + return ($decision>0); + } + return true; + } + else + return false; + } + public function insertAt($index,$item) + { + if($item instanceof TAuthorizationRule) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('authorizationrulecollection_authorizationrule_required'); + } +} +class TSecurityManager extends TModule +{ + const STATE_VALIDATION_KEY = 'prado:securitymanager:validationkey'; + const STATE_ENCRYPTION_KEY = 'prado:securitymanager:encryptionkey'; + private $_validationKey = null; + private $_encryptionKey = null; + private $_hashAlgorithm = 'sha1'; + private $_cryptAlgorithm = 'rijndael-256'; + private $_mbstring; + public function init($config) + { + $this->_mbstring=extension_loaded('mbstring'); + $this->getApplication()->setSecurityManager($this); + } + protected function generateRandomKey() + { + return sprintf('%08x%08x%08x%08x',mt_rand(),mt_rand(),mt_rand(),mt_rand()); + } + public function getValidationKey() + { + if(null === $this->_validationKey) { + if(null === ($this->_validationKey = $this->getApplication()->getGlobalState(self::STATE_VALIDATION_KEY))) { + $this->_validationKey = $this->generateRandomKey(); + $this->getApplication()->setGlobalState(self::STATE_VALIDATION_KEY, $this->_validationKey, null, true); + } + } + return $this->_validationKey; + } + public function setValidationKey($value) + { + if('' === $value) + throw new TInvalidDataValueException('securitymanager_validationkey_invalid'); + $this->_validationKey = $value; + } + public function getEncryptionKey() + { + if(null === $this->_encryptionKey) { + if(null === ($this->_encryptionKey = $this->getApplication()->getGlobalState(self::STATE_ENCRYPTION_KEY))) { + $this->_encryptionKey = $this->generateRandomKey(); + $this->getApplication()->setGlobalState(self::STATE_ENCRYPTION_KEY, $this->_encryptionKey, null, true); + } + } + return $this->_encryptionKey; + } + public function setEncryptionKey($value) + { + if('' === $value) + throw new TInvalidDataValueException('securitymanager_encryptionkey_invalid'); + $this->_encryptionKey = $value; + } + public function getValidation() + { + return $this->_hashAlgorithm; + } + public function getHashAlgorithm() + { + return $this->_hashAlgorithm; + } + public function setValidation($value) + { + $this->_hashAlgorithm = TPropertyValue::ensureEnum($value, 'TSecurityManagerValidationMode'); + } + public function setHashAlgorithm($value) + { + $this->_hashAlgorithm = TPropertyValue::ensureString($value); + } + public function getEncryption() + { + if(is_string($this->_cryptAlgorithm)) + return $this->_cryptAlgorithm; + return "3DES"; + } + public function setEncryption($value) + { + $this->_cryptAlgorithm = $value; + } + public function getCryptAlgorithm() + { + return $this->_cryptAlgorithm; + } + public function setCryptAlgorithm($value) + { + $this->_cryptAlgorithm = $value; + } + public function encrypt($data) + { + $module=$this->openCryptModule(); + $key = $this->substr(md5($this->getEncryptionKey()), 0, mcrypt_enc_get_key_size($module)); + srand(); + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); + mcrypt_generic_init($module, $key, $iv); + $encrypted = $iv.mcrypt_generic($module, $data); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return $encrypted; + } + public function decrypt($data) + { + $module=$this->openCryptModule(); + $key = $this->substr(md5($this->getEncryptionKey()), 0, mcrypt_enc_get_key_size($module)); + $ivSize = mcrypt_enc_get_iv_size($module); + $iv = $this->substr($data, 0, $ivSize); + mcrypt_generic_init($module, $key, $iv); + $decrypted = mdecrypt_generic($module, $this->substr($data, $ivSize, $this->strlen($data))); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return $decrypted; + } + protected function openCryptModule() + { + if(extension_loaded('mcrypt')) + { + if(is_array($this->_cryptAlgorithm)) + $module=@call_user_func_array('mcrypt_module_open',$this->_cryptAlgorithm); + else + $module=@mcrypt_module_open($this->_cryptAlgorithm,'', MCRYPT_MODE_CBC,''); + if($module===false) + throw new TNotSupportedException('securitymanager_mcryptextension_initfailed'); + return $module; + } + else + throw new TNotSupportedException('securitymanager_mcryptextension_required'); + } + public function hashData($data) + { + $hmac = $this->computeHMAC($data); + return $hmac.$data; + } + public function validateData($data) + { + $len=$this->strlen($this->computeHMAC('test')); + if($this->strlen($data) < $len) + return false; + $hmac = $this->substr($data, 0, $len); + $data2=$this->substr($data, $len, $this->strlen($data)); + return $hmac === $this->computeHMAC($data2) ? $data2 : false; + } + protected function computeHMAC($data) + { + $key = $this->getValidationKey(); + if(function_exists('hash_hmac')) + return hash_hmac($this->_hashAlgorithm, $data, $key); + if(!strcasecmp($this->_hashAlgorithm,'sha1')) + { + $pack = 'H40'; + $func = 'sha1'; + } else { + $pack = 'H32'; + $func = 'md5'; + } + $key = str_pad($func($key), 64, chr(0)); + return $func((str_repeat(chr(0x5C), 64) ^ substr($key, 0, 64)) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ substr($key, 0, 64)) . $data))); + } + private function strlen($string) + { + return $this->_mbstring ? mb_strlen($string,'8bit') : strlen($string); + } + private function substr($string,$start,$length) + { + return $this->_mbstring ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length); + } +} +class TSecurityManagerValidationMode extends TEnumerable +{ + const MD5 = 'MD5'; + const SHA1 = 'SHA1'; +} +class THttpUtility +{ + private static $_encodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + private static $_decodeTable=array('<'=>'<','>'=>'>','"'=>'"'); + private static $_stripTable=array('<'=>'','>'=>'','"'=>''); + public static function htmlEncode($s) + { + return strtr($s,self::$_encodeTable); + } + public static function htmlDecode($s) + { + return strtr($s,self::$_decodeTable); + } + public static function htmlStrip($s) + { + return strtr($s,self::$_stripTable); + } +} +class TJavaScript +{ + public static function renderScriptFiles($files) + { + $str=''; + foreach($files as $file) + $str.= self::renderScriptFile($file); + return $str; + } + public static function renderScriptFile($file) + { + return '\n"; + } + public static function renderScriptBlocks($scripts) + { + if(count($scripts)) + return "\n"; + else + return ''; + } + public static function renderScriptBlock($script) + { + return "\n"; + } + public static function quoteString($js) + { + return self::jsonEncode($js,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); + } + public static function quoteJsLiteral($js) + { + if($js instanceof TJavaScriptLiteral) + return $js; + else + return new TJavaScriptLiteral($js); + } + public static function quoteFunction($js) + { + return self::quoteJsLiteral($js); + } + public static function isJsLiteral($js) + { + return ($js instanceof TJavaScriptLiteral); + } + public static function isFunction($js) + { + return self::isJsLiteral($js); + } + public static function encode($value,$toMap=true,$encodeEmptyStrings=false) + { + if(is_string($value)) + return self::quoteString($value); + else if(is_bool($value)) + return $value?'true':'false'; + else if(is_array($value)) + { + $results=''; + if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1)) + { + foreach($value as $k=>$v) + { + if($v!=='' || $encodeEmptyStrings) + { + if($results!=='') + $results.=','; + $results.="'$k':".self::encode($v,$toMap,$encodeEmptyStrings); + } + } + return '{'.$results.'}'; + } + else + { + foreach($value as $v) + { + if($v!=='' || $encodeEmptyStrings) + { + if($results!=='') + $results.=','; + $results.=self::encode($v,$toMap, $encodeEmptyStrings); + } + } + return '['.$results.']'; + } + } + else if(is_integer($value)) + return "$value"; + else if(is_float($value)) + { + switch($value) + { + case -INF: + return 'Number.NEGATIVE_INFINITY'; + break; + case INF: + return 'Number.POSITIVE_INFINITY'; + break; + default: + $locale=localeConv(); + if($locale['decimal_point']=='.') + return "$value"; + else + return str_replace($locale['decimal_point'], '.', "$value"); + break; + } + } + else if(is_object($value)) + if ($value instanceof TJavaScriptLiteral) + return $value->toJavaScriptLiteral(); + else + return self::encode(get_object_vars($value),$toMap); + else if($value===null) + return 'null'; + else + return ''; + } + public static function jsonEncode($value, $options = 0) + { + if (is_string($value) && + ($g=Prado::getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + $value=iconv($enc, 'UTF-8', $value); + $s = @json_encode($value,$options); + self::checkJsonError(); + return $s; + } + public static function jsonDecode($value, $assoc = false, $depth = 512) + { + $s= @json_decode($value, $assoc, $depth); + self::checkJsonError(); + return $s; + } + private static function checkJsonError() + { + switch ($err = json_last_error()) + { + case JSON_ERROR_NONE: + return; + break; + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_SYNTAX: + $msg = 'Syntax error, malformed JSON'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + break; + } + throw new Exception("JSON error ($err): $msg"); + } + public static function JSMin($code) + { + Prado::using('System.Web.Javascripts.JSMin'); + return JSMin::minify($code); + } +} +class TUrlManager extends TModule +{ + public function constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems) + { + $url=$serviceID.'='.urlencode($serviceParam); + $amp=$encodeAmpersand?'&':'&'; + $request=$this->getRequest(); + if(is_array($getItems) || $getItems instanceof Traversable) + { + if($encodeGetItems) + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + $name=urlencode($name.'[]'); + foreach($value as $v) + $url.=$amp.$name.'='.urlencode($v); + } + else + $url.=$amp.urlencode($name).'='.urlencode($value); + } + } + else + { + foreach($getItems as $name=>$value) + { + if(is_array($value)) + { + foreach($value as $v) + $url.=$amp.$name.'[]='.$v; + } + else + $url.=$amp.$name.'='.$value; + } + } + } + switch($request->getUrlFormat()) + { + case THttpRequestUrlFormat::Path: + return $request->getApplicationUrl().'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); + case THttpRequestUrlFormat::HiddenPath: + return rtrim(dirname($request->getApplicationUrl()), '/').'/'.strtr($url,array($amp=>'/','?'=>'/','='=>$request->getUrlParamSeparator())); + default: + return $request->getApplicationUrl().'?'.$url; + } + } + public function parseUrl() + { + $request=$this->getRequest(); + $pathInfo=trim($request->getPathInfo(),'/'); + if(($request->getUrlFormat()===THttpRequestUrlFormat::Path || + $request->getUrlFormat()===THttpRequestUrlFormat::HiddenPath) && + $pathInfo!=='') + { + $separator=$request->getUrlParamSeparator(); + $paths=explode('/',$pathInfo); + $getVariables=array(); + foreach($paths as $path) + { + if(($path=trim($path))!=='') + { + if(($pos=strpos($path,$separator))!==false) + { + $name=substr($path,0,$pos); + $value=substr($path,$pos+1); + if(($pos=strpos($name,'[]'))!==false) + $getVariables[substr($name,0,$pos)][]=$value; + else + $getVariables[$name]=$value; + } + else + $getVariables[$path]=''; + } + } + return $getVariables; + } + else + return array(); + } +} +class THttpRequest extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule +{ + const CGIFIX__PATH_INFO = 1; + const CGIFIX__SCRIPT_NAME = 2; + private $_urlManager=null; + private $_urlManagerID=''; + private $_separator=','; + private $_serviceID=null; + private $_serviceParam=null; + private $_cookies=null; + private $_requestUri; + private $_pathInfo; + private $_cookieOnly=null; + private $_urlFormat=THttpRequestUrlFormat::Get; + private $_services; + private $_requestResolved=false; + private $_enableCookieValidation=false; + private $_cgiFix=0; + private $_enableCache=false; + private $_url=null; + private $_id; + private $_items=array(); + public function getID() + { + return $this->_id; + } + public function setID($value) + { + $this->_id=$value; + } + public function init($config) + { + if(php_sapi_name()==='cli') + { + $_SERVER['REMOTE_ADDR']='127.0.0.1'; + $_SERVER['REQUEST_METHOD']='GET'; + $_SERVER['SERVER_NAME']='localhost'; + $_SERVER['SERVER_PORT']=80; + $_SERVER['HTTP_USER_AGENT']=''; + } + if(isset($_SERVER['REQUEST_URI'])) + $this->_requestUri=$_SERVER['REQUEST_URI']; + else $this->_requestUri=$_SERVER['SCRIPT_NAME'].(empty($_SERVER['QUERY_STRING'])?'':'?'.$_SERVER['QUERY_STRING']); + if($this->_cgiFix&self::CGIFIX__PATH_INFO && isset($_SERVER['ORIG_PATH_INFO'])) + $this->_pathInfo=substr($_SERVER['ORIG_PATH_INFO'], strlen($_SERVER['SCRIPT_NAME'])); + elseif(isset($_SERVER['PATH_INFO'])) + $this->_pathInfo=$_SERVER['PATH_INFO']; + else if(strpos($_SERVER['PHP_SELF'],$_SERVER['SCRIPT_NAME'])===0 && $_SERVER['PHP_SELF']!==$_SERVER['SCRIPT_NAME']) + $this->_pathInfo=substr($_SERVER['PHP_SELF'],strlen($_SERVER['SCRIPT_NAME'])); + else + $this->_pathInfo=''; + if(get_magic_quotes_gpc()) + { + if(isset($_GET)) + $_GET=$this->stripSlashes($_GET); + if(isset($_POST)) + $_POST=$this->stripSlashes($_POST); + if(isset($_REQUEST)) + $_REQUEST=$this->stripSlashes($_REQUEST); + if(isset($_COOKIE)) + $_COOKIE=$this->stripSlashes($_COOKIE); + } + $this->getApplication()->setRequest($this); + } + public function stripSlashes(&$data) + { + return is_array($data)?array_map(array($this,'stripSlashes'),$data):stripslashes($data); + } + public function getUrl() + { + if($this->_url===null) + { + $secure=$this->getIsSecureConnection(); + $url=$secure?'https://':'http://'; + if(empty($_SERVER['HTTP_HOST'])) + { + $url.=$_SERVER['SERVER_NAME']; + $port=$_SERVER['SERVER_PORT']; + if(($port!=80 && !$secure) || ($port!=443 && $secure)) + $url.=':'.$port; + } + else + $url.=$_SERVER['HTTP_HOST']; + $url.=$this->getRequestUri(); + $this->_url=new TUri($url); + } + return $this->_url; + } + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + public function getEnableCache() + { + return $this->_enableCache; + } + protected function getCacheKey() + { + return $this->getID(); + } + protected function cacheUrlManager($manager) + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) + { + $dependencies = null; + if($this->getApplication()->getMode() !== TApplicationMode::Performance) + if ($manager instanceof TUrlMapping && $fn = $manager->getConfigFile()) + { + $fn = Prado::getPathOfNamespace($fn,$this->getApplication()->getConfigurationFileExt()); + $dependencies = new TFileCacheDependency($fn); + } + return $cache->set($this->getCacheKey(), $manager, 0, $dependencies); + } + } + return false; + } + protected function loadCachedUrlManager() + { + if($this->getEnableCache()) + { + $cache = $this->getApplication()->getCache(); + if($cache !== null) + { + $manager = $cache->get($this->getCacheKey()); + if($manager instanceof TUrlManager) + return $manager; + } + } + return null; + } + public function getUrlManager() + { + return $this->_urlManagerID; + } + public function setUrlManager($value) + { + $this->_urlManagerID=$value; + } + public function getUrlManagerModule() + { + if($this->_urlManager===null) + { + if(($this->_urlManager = $this->loadCachedUrlManager())===null) + { + if(empty($this->_urlManagerID)) + { + $this->_urlManager=new TUrlManager; + $this->_urlManager->init(null); + } + else + { + $this->_urlManager=$this->getApplication()->getModule($this->_urlManagerID); + if($this->_urlManager===null) + throw new TConfigurationException('httprequest_urlmanager_inexist',$this->_urlManagerID); + if(!($this->_urlManager instanceof TUrlManager)) + throw new TConfigurationException('httprequest_urlmanager_invalid',$this->_urlManagerID); + } + $this->cacheUrlManager($this->_urlManager); + } + } + return $this->_urlManager; + } + public function getUrlFormat() + { + return $this->_urlFormat; + } + public function setUrlFormat($value) + { + $this->_urlFormat=TPropertyValue::ensureEnum($value,'THttpRequestUrlFormat'); + } + public function getUrlParamSeparator() + { + return $this->_separator; + } + public function setUrlParamSeparator($value) + { + if(strlen($value)===1) + $this->_separator=$value; + else + throw new TInvalidDataValueException('httprequest_separator_invalid'); + } + public function getRequestType() + { + return isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:null; + } + public function getContentType($mimetypeOnly = true) + { + if(!isset($_SERVER['CONTENT_TYPE'])) + return null; + if($mimetypeOnly === true && ($_pos = strpos(';', $_SERVER['CONTENT_TYPE'])) !== false) + return substr($_SERVER['CONTENT_TYPE'], 0, $_pos); + return $_SERVER['CONTENT_TYPE']; + } + public function getIsSecureConnection() + { + return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'],'off'); + } + public function getPathInfo() + { + return $this->_pathInfo; + } + public function getQueryString() + { + return isset($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:null; + } + public function getHttpProtocolVersion() + { + return isset($_SERVER['SERVER_PROTOCOL'])?$_SERVER['SERVER_PROTOCOL']:null; + } + public function getHeaders($case=null) + { + static $result; + if($result === null && function_exists('apache_request_headers')) { + $result = apache_request_headers(); + } + elseif($result === null) { + $result = array(); + foreach($_SERVER as $key=>$value) { + if(strncasecmp($key, 'HTTP_', 5) !== 0) continue; + $key = str_replace(' ','-', ucwords(strtolower(str_replace('_',' ', substr($key, 5))))); + $result[$key] = $value; + } + } + if($case !== null) + return array_change_key_case($result, $case); + return $result; + } + public function getRequestUri() + { + return $this->_requestUri; + } + public function getBaseUrl($forceSecureConnection=null) + { + $url=$this->getUrl(); + $scheme=($forceSecureConnection)?"https": (($forceSecureConnection === null)?$url->getScheme():'http'); + $host=$url->getHost(); + if (($port=$url->getPort())) $host.=':'.$port; + return $scheme.'://'.$host; + } + public function getApplicationUrl() + { + if($this->_cgiFix&self::CGIFIX__SCRIPT_NAME && isset($_SERVER['ORIG_SCRIPT_NAME'])) + return $_SERVER['ORIG_SCRIPT_NAME']; + return isset($_SERVER['SCRIPT_NAME'])?$_SERVER['SCRIPT_NAME']:null; + } + public function getAbsoluteApplicationUrl($forceSecureConnection=null) + { + return $this->getBaseUrl($forceSecureConnection) . $this->getApplicationUrl(); + } + public function getApplicationFilePath() + { + return realpath(isset($_SERVER['SCRIPT_FILENAME'])?$_SERVER['SCRIPT_FILENAME']:null); + } + public function getServerName() + { + return isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:null; + } + public function getServerPort() + { + return isset($_SERVER['SERVER_PORT'])?$_SERVER['SERVER_PORT']:null; + } + public function getUrlReferrer() + { + return isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:null; + } + public function getBrowser() + { + try + { + return get_browser(); + } + catch(TPhpErrorException $e) + { + throw new TConfigurationException('httprequest_browscap_required'); + } + } + public function getUserAgent() + { + return isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:null; + } + public function getUserHostAddress() + { + return isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:null; + } + public function getUserHost() + { + return isset($_SERVER['REMOTE_HOST'])?$_SERVER['REMOTE_HOST']:null; + } + public function getAcceptTypes() + { + return isset($_SERVER['HTTP_ACCEPT'])?$_SERVER['HTTP_ACCEPT']:null; + } + public function getUserLanguages() + { + return Prado::getUserLanguages(); + } + public function getEnableCookieValidation() + { + return $this->_enableCookieValidation; + } + public function setEnableCookieValidation($value) + { + $this->_enableCookieValidation=TPropertyValue::ensureBoolean($value); + } + public function getCgiFix() + { + return $this->_cgiFix; + } + public function setCgiFix($value) + { + $this->_cgiFix=TPropertyValue::ensureInteger($value); + } + public function getCookies() + { + if($this->_cookies===null) + { + $this->_cookies=new THttpCookieCollection; + if($this->getEnableCookieValidation()) + { + $sm=$this->getApplication()->getSecurityManager(); + foreach($_COOKIE as $key=>$value) + { + if(($value=$sm->validateData($value))!==false) + $this->_cookies->add(new THttpCookie($key,$value)); + } + } + else + { + foreach($_COOKIE as $key=>$value) + $this->_cookies->add(new THttpCookie($key,$value)); + } + } + return $this->_cookies; + } + public function getUploadedFiles() + { + return $_FILES; + } + public function getServerVariables() + { + return $_SERVER; + } + public function getEnvironmentVariables() + { + return $_ENV; + } + public function constructUrl($serviceID,$serviceParam,$getItems=null,$encodeAmpersand=true,$encodeGetItems=true) + { + if ($this->_cookieOnly===null) + $this->_cookieOnly=(int)ini_get('session.use_cookies') && (int)ini_get('session.use_only_cookies'); + $url=$this->getUrlManagerModule()->constructUrl($serviceID,$serviceParam,$getItems,$encodeAmpersand,$encodeGetItems); + if(defined('SID') && SID != '' && !$this->_cookieOnly) + return $url . (strpos($url,'?')===false? '?' : ($encodeAmpersand?'&':'&')) . SID; + else + return $url; + } + protected function parseUrl() + { + return $this->getUrlManagerModule()->parseUrl(); + } + public function resolveRequest($serviceIDs) + { + $getParams=$this->parseUrl(); + foreach($getParams as $name=>$value) + $_GET[$name]=$value; + $this->_items=array_merge($_GET,$_POST); + $this->_requestResolved=true; + foreach($serviceIDs as $serviceID) + { + if($this->contains($serviceID)) + { + $this->setServiceID($serviceID); + $this->setServiceParameter($this->itemAt($serviceID)); + return $serviceID; + } + } + return null; + } + public function getRequestResolved() + { + return $this->_requestResolved; + } + public function getServiceID() + { + return $this->_serviceID; + } + public function setServiceID($value) + { + $this->_serviceID=$value; + } + public function getServiceParameter() + { + return $this->_serviceParam; + } + public function setServiceParameter($value) + { + $this->_serviceParam=$value; + } + public function getIterator() + { + return new TMapIterator($this->_items); + } + public function getCount() + { + return count($this->_items); + } + public function count() + { + return $this->getCount(); + } + public function getKeys() + { + return array_keys($this->_items); + } + public function itemAt($key) + { + return isset($this->_items[$key]) ? $this->_items[$key] : null; + } + public function add($key,$value) + { + $this->_items[$key]=$value; + } + public function remove($key) + { + if(isset($this->_items[$key]) || array_key_exists($key,$this->_items)) + { + $value=$this->_items[$key]; + unset($this->_items[$key]); + return $value; + } + else + return null; + } + public function clear() + { + foreach(array_keys($this->_items) as $key) + $this->remove($key); + } + public function contains($key) + { + return isset($this->_items[$key]) || array_key_exists($key,$this->_items); + } + public function toArray() + { + return $this->_items; + } + public function offsetExists($offset) + { + return $this->contains($offset); + } + public function offsetGet($offset) + { + return $this->itemAt($offset); + } + public function offsetSet($offset,$item) + { + $this->add($offset,$item); + } + public function offsetUnset($offset) + { + $this->remove($offset); + } +} +class THttpCookieCollection extends TList +{ + private $_o; + public function __construct($owner=null) + { + $this->_o=$owner; + } + public function insertAt($index,$item) + { + if($item instanceof THttpCookie) + { + parent::insertAt($index,$item); + if($this->_o instanceof THttpResponse) + $this->_o->addCookie($item); + } + else + throw new TInvalidDataTypeException('httpcookiecollection_httpcookie_required'); + } + public function removeAt($index) + { + $item=parent::removeAt($index); + if($this->_o instanceof THttpResponse) + $this->_o->removeCookie($item); + return $item; + } + public function itemAt($index) + { + if(is_integer($index)) + return parent::itemAt($index); + else + return $this->findCookieByName($index); + } + public function findCookieByName($name) + { + foreach($this as $cookie) + if($cookie->getName()===$name) + return $cookie; + return null; + } +} +class THttpCookie extends TComponent +{ + private $_domain=''; + private $_name; + private $_value=''; + private $_expire=0; + private $_path='/'; + private $_secure=false; + private $_httpOnly=false; + public function __construct($name,$value) + { + $this->_name=$name; + $this->_value=$value; + } + public function getDomain() + { + return $this->_domain; + } + public function setDomain($value) + { + $this->_domain=$value; + } + public function getExpire() + { + return $this->_expire; + } + public function setExpire($value) + { + $this->_expire=TPropertyValue::ensureInteger($value); + } + public function getHttpOnly() + { + return $this->_httpOnly; + } + public function setHttpOnly($value) + { + $this->_httpOnly = TPropertyValue::ensureBoolean($value); + } + public function getName() + { + return $this->_name; + } + public function setName($value) + { + $this->_name=$value; + } + public function getValue() + { + return $this->_value; + } + public function setValue($value) + { + $this->_value=$value; + } + public function getPath() + { + return $this->_path; + } + public function setPath($value) + { + $this->_path=$value; + } + public function getSecure() + { + return $this->_secure; + } + public function setSecure($value) + { + $this->_secure=TPropertyValue::ensureBoolean($value); + } +} +class TUri extends TComponent +{ + private static $_defaultPort=array( + 'ftp'=>21, + 'gopher'=>70, + 'http'=>80, + 'https'=>443, + 'news'=>119, + 'nntp'=>119, + 'wais'=>210, + 'telnet'=>23 + ); + private $_scheme; + private $_host; + private $_port; + private $_user; + private $_pass; + private $_path; + private $_query; + private $_fragment; + private $_uri; + public function __construct($uri) + { + if(($ret=@parse_url($uri))!==false) + { + $this->_scheme=isset($ret['scheme'])?$ret['scheme']:''; + $this->_host=isset($ret['host'])?$ret['host']:''; + $this->_port=isset($ret['port'])?$ret['port']:''; + $this->_user=isset($ret['user'])?$ret['user']:''; + $this->_pass=isset($ret['pass'])?$ret['pass']:''; + $this->_path=isset($ret['path'])?$ret['path']:''; + $this->_query=isset($ret['query'])?$ret['query']:''; + $this->_fragment=isset($ret['fragment'])?$ret['fragment']:''; + $this->_uri=$uri; + } + else + { + throw new TInvalidDataValueException('uri_format_invalid',$uri); + } + } + public function getUri() + { + return $this->_uri; + } + public function getScheme() + { + return $this->_scheme; + } + public function getHost() + { + return $this->_host; + } + public function getPort() + { + return $this->_port; + } + public function getUser() + { + return $this->_user; + } + public function getPassword() + { + return $this->_pass; + } + public function getPath() + { + return $this->_path; + } + public function getQuery() + { + return $this->_query; + } + public function getFragment() + { + return $this->_fragment; + } +} +class THttpRequestUrlFormat extends TEnumerable +{ + const Get='Get'; + const Path='Path'; + const HiddenPath='HiddenPath'; +} +class THttpResponseAdapter extends TApplicationComponent +{ + private $_response; + public function __construct($response) + { + $this->_response=$response; + } + public function getResponse() + { + return $this->_response; + } + public function flushContent() + { + $this->_response->flushContent(); + } + public function httpRedirect($url) + { + $this->_response->httpRedirect($url); + } + public function createNewHtmlWriter($type, $writer) + { + return $this->_response->createNewHtmlWriter($type,$writer); + } +} +class THttpResponse extends TModule implements ITextWriter +{ + const DEFAULT_CONTENTTYPE = 'text/html'; + const DEFAULT_CHARSET = 'UTF-8'; + private static $HTTP_STATUS_CODES = array( + 100 => 'Continue', 101 => 'Switching Protocols', + 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', + 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', + 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', + 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' + ); + private $_bufferOutput=true; + private $_initialized=false; + private $_cookies=null; + private $_status=200; + private $_reason='OK'; + private $_htmlWriterType='System.Web.UI.THtmlWriter'; + private $_contentType=null; + private $_charset=''; + private $_adapter; + private $_httpHeaderSent; + private $_contentTypeHeaderSent; + public function __destruct() + { + } + public function setAdapter(THttpResponseAdapter $adapter) + { + $this->_adapter=$adapter; + } + public function getAdapter() + { + return $this->_adapter; + } + public function getHasAdapter() + { + return $this->_adapter!==null; + } + public function init($config) + { + if($this->_bufferOutput) + ob_start(); + $this->_initialized=true; + $this->getApplication()->setResponse($this); + } + public function getCacheExpire() + { + return session_cache_expire(); + } + public function setCacheExpire($value) + { + session_cache_expire(TPropertyValue::ensureInteger($value)); + } + public function getCacheControl() + { + return session_cache_limiter(); + } + public function setCacheControl($value) + { + session_cache_limiter(TPropertyValue::ensureEnum($value,array('none','nocache','private','private_no_expire','public'))); + } + public function setContentType($type) + { + if ($this->_contentTypeHeaderSent) + throw new Exception('Unable to alter content-type as it has been already sent'); + $this->_contentType = $type; + } + public function getContentType() + { + return $this->_contentType; + } + public function getCharset() + { + return $this->_charset; + } + public function setCharset($charset) + { + $this->_charset = (strToLower($charset) === 'false') ? false : (string)$charset; + } + public function getBufferOutput() + { + return $this->_bufferOutput; + } + public function setBufferOutput($value) + { + if($this->_initialized) + throw new TInvalidOperationException('httpresponse_bufferoutput_unchangeable'); + else + $this->_bufferOutput=TPropertyValue::ensureBoolean($value); + } + public function getStatusCode() + { + return $this->_status; + } + public function setStatusCode($status, $reason=null) + { + if ($this->_httpHeaderSent) + throw new Exception('Unable to alter response as HTTP header already sent'); + $status=TPropertyValue::ensureInteger($status); + if(isset(self::$HTTP_STATUS_CODES[$status])) { + $this->_reason=self::$HTTP_STATUS_CODES[$status]; + }else{ + if($reason===null || $reason==='') { + throw new TInvalidDataValueException("response_status_reason_missing"); + } + $reason=TPropertyValue::ensureString($reason); + if(strpos($reason, "\r")!=false || strpos($reason, "\n")!=false) { + throw new TInvalidDataValueException("response_status_reason_barchars"); + } + $this->_reason=$reason; + } + $this->_status=$status; + } + public function getStatusReason() { + return $this->_reason; + } + public function getCookies() + { + if($this->_cookies===null) + $this->_cookies=new THttpCookieCollection($this); + return $this->_cookies; + } + public function write($str) + { + if (!$this->_bufferOutput and !$this->_httpHeaderSent) + $this->ensureHeadersSent(); + echo $str; + } + public function writeFile($fileName,$content=null,$mimeType=null,$headers=null,$forceDownload=true,$clientFileName=null,$fileSize=null) + { + static $defaultMimeTypes=array( + 'css'=>'text/css', + 'gif'=>'image/gif', + 'png'=>'image/png', + 'jpg'=>'image/jpeg', + 'jpeg'=>'image/jpeg', + 'htm'=>'text/html', + 'html'=>'text/html', + 'js'=>'javascript/js', + 'pdf'=>'application/pdf', + 'xls'=>'application/vnd.ms-excel', + ); + if($mimeType===null) + { + $mimeType='text/plain'; + if(function_exists('mime_content_type')) + $mimeType=mime_content_type($fileName); + else if(($ext=strrchr($fileName,'.'))!==false) + { + $ext=substr($ext,1); + if(isset($defaultMimeTypes[$ext])) + $mimeType=$defaultMimeTypes[$ext]; + } + } + if($clientFileName===null) + $clientFileName=basename($fileName); + else + $clientFileName=basename($clientFileName); + if($fileSize===null || $fileSize < 0) + $fileSize = ($content===null?filesize($fileName):strlen($content)); + $this->sendHttpHeader(); + if(is_array($headers)) + { + foreach($headers as $h) + header($h); + } + else + { + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header("Content-Type: $mimeType"); + $this->_contentTypeHeaderSent = true; + } + header('Content-Length: '.$fileSize); + header("Content-Disposition: " . ($forceDownload ? 'attachment' : 'inline') . "; filename=\"$clientFileName\""); + header('Content-Transfer-Encoding: binary'); + if($content===null) + readfile($fileName); + else + echo $content; + } + public function redirect($url) + { + if($this->getHasAdapter()) + $this->_adapter->httpRedirect($url); + else + $this->httpRedirect($url); + } + public function httpRedirect($url) + { + $this->ensureHeadersSent(); + if($url[0]==='/') + $url=$this->getRequest()->getBaseUrl().$url; + if ($this->_status >= 300 && $this->_status < 400) + header('Location: '.str_replace('&','&',$url), true, $this->_status); + else + header('Location: '.str_replace('&','&',$url)); + if(!$this->getApplication()->getRequestCompleted()) + $this->getApplication()->onEndRequest(); + exit(); + } + public function reload() + { + $this->redirect($this->getRequest()->getRequestUri()); + } + public function flush($continueBuffering = true) + { + if($this->getHasAdapter()) + $this->_adapter->flushContent($continueBuffering); + else + $this->flushContent($continueBuffering); + } + public function ensureHeadersSent() + { + $this->ensureHttpHeaderSent(); + $this->ensureContentTypeHeaderSent(); + } + public function flushContent($continueBuffering = true) + { + $this->ensureHeadersSent(); + if($this->_bufferOutput) + { + if (ob_get_length()>0) + { + if (!$continueBuffering) + { + $this->_bufferOutput = false; + ob_end_flush(); + } + else + ob_flush(); + flush(); + } + } + else + flush(); + } + protected function ensureHttpHeaderSent() + { + if (!$this->_httpHeaderSent) + $this->sendHttpHeader(); + } + protected function sendHttpHeader() + { + if (($version=$this->getRequest()->getHttpProtocolVersion())==='') + header (' ', true, $this->_status); + else + header($version.' '.$this->_status.' '.$this->_reason, true, $this->_status); + $this->_httpHeaderSent = true; + } + protected function ensureContentTypeHeaderSent() + { + if (!$this->_contentTypeHeaderSent) + $this->sendContentTypeHeader(); + } + protected function sendContentTypeHeader() + { + $contentType=$this->_contentType===null?self::DEFAULT_CONTENTTYPE:$this->_contentType; + $charset=$this->getCharset(); + if($charset === false) { + $this->appendHeader('Content-Type: '.$contentType); + return; + } + if($charset==='' && ($globalization=$this->getApplication()->getGlobalization(false))!==null) + $charset=$globalization->getCharset(); + if($charset==='') $charset = self::DEFAULT_CHARSET; + $this->appendHeader('Content-Type: '.$contentType.';charset='.$charset); + $this->_contentTypeHeaderSent = true; + } + public function getContents() + { + return $this->_bufferOutput?ob_get_contents():''; + } + public function clear() + { + if($this->_bufferOutput) + ob_clean(); + } + public function getHeaders($case=null) + { + $result = array(); + $headers = headers_list(); + foreach($headers as $header) { + $tmp = explode(':', $header); + $key = trim(array_shift($tmp)); + $value = trim(implode(':', $tmp)); + if(isset($result[$key])) + $result[$key] .= ', ' . $value; + else + $result[$key] = $value; + } + if($case !== null) + return array_change_key_case($result, $case); + return $result; + } + public function appendHeader($value, $replace=true) + { + header($value, $replace); + } + public function appendLog($message,$messageType=0,$destination='',$extraHeaders='') + { + error_log($message,$messageType,$destination,$extraHeaders); + } + public function addCookie($cookie) + { + $request=$this->getRequest(); + if($request->getEnableCookieValidation()) + { + $value=$this->getApplication()->getSecurityManager()->hashData($cookie->getValue()); + setcookie( + $cookie->getName(), + $value, + $cookie->getExpire(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + else { + setcookie( + $cookie->getName(), + $cookie->getValue(), + $cookie->getExpire(), + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + } + public function removeCookie($cookie) + { + setcookie( + $cookie->getName(), + null, + 0, + $cookie->getPath(), + $cookie->getDomain(), + $cookie->getSecure(), + $cookie->getHttpOnly() + ); + } + public function getHtmlWriterType() + { + return $this->_htmlWriterType; + } + public function setHtmlWriterType($value) + { + $this->_htmlWriterType=$value; + } + public function createHtmlWriter($type=null) + { + if($type===null) + $type=$this->getHtmlWriterType(); + if($this->getHasAdapter()) + return $this->_adapter->createNewHtmlWriter($type, $this); + else + return $this->createNewHtmlWriter($type, $this); + } + public function createNewHtmlWriter($type, $writer) + { + return Prado::createComponent($type, $writer); + } +} +class THttpSession extends TApplicationComponent implements IteratorAggregate,ArrayAccess,Countable,IModule +{ + private $_initialized=false; + private $_started=false; + private $_autoStart=false; + private $_cookie=null; + private $_id; + private $_customStorage=false; + public function getID() + { + return $this->_id; + } + public function setID($value) + { + $this->_id=$value; + } + public function init($config) + { + if($this->_autoStart) + $this->open(); + $this->_initialized=true; + $this->getApplication()->setSession($this); + register_shutdown_function(array($this, "close")); + } + public function open() + { + if(!$this->_started) + { + if($this->_customStorage) + session_set_save_handler(array($this,'_open'),array($this,'_close'),array($this,'_read'),array($this,'_write'),array($this,'_destroy'),array($this,'_gc')); + if($this->_cookie!==null) + session_set_cookie_params($this->_cookie->getExpire(),$this->_cookie->getPath(),$this->_cookie->getDomain(),$this->_cookie->getSecure()); + if(ini_get('session.auto_start')!=='1') + session_start(); + $this->_started=true; + } + } + public function close() + { + if($this->_started) + { + session_write_close(); + $this->_started=false; + } + } + public function destroy() + { + if($this->_started) + { + session_destroy(); + $this->_started=false; + } + } + public function regenerate($deleteOld=false) + { + $old = $this->getSessionID(); + session_regenerate_id($deleteOld); + return $old; + } + public function getIsStarted() + { + return $this->_started; + } + public function getSessionID() + { + return session_id(); + } + public function setSessionID($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_sessionid_unchangeable'); + else + session_id($value); + } + public function getSessionName() + { + return session_name(); + } + public function setSessionName($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_sessionname_unchangeable'); + else if(ctype_alnum($value)) + session_name($value); + else + throw new TInvalidDataValueException('httpsession_sessionname_invalid',$value); + } + public function getSavePath() + { + return session_save_path(); + } + public function setSavePath($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_savepath_unchangeable'); + else if(is_dir($value)) + session_save_path($value); + else + throw new TInvalidDataValueException('httpsession_savepath_invalid',$value); + } + public function getUseCustomStorage() + { + return $this->_customStorage; + } + public function setUseCustomStorage($value) + { + $this->_customStorage=TPropertyValue::ensureBoolean($value); + } + public function getCookie() + { + if($this->_cookie===null) + $this->_cookie=new THttpCookie($this->getSessionName(),$this->getSessionID()); + return $this->_cookie; + } + public function getCookieMode() + { + if(ini_get('session.use_cookies')==='0') + return THttpSessionCookieMode::None; + else if(ini_get('session.use_only_cookies')==='0') + return THttpSessionCookieMode::Allow; + else + return THttpSessionCookieMode::Only; + } + public function setCookieMode($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_cookiemode_unchangeable'); + else + { + $value=TPropertyValue::ensureEnum($value,'THttpSessionCookieMode'); + if($value===THttpSessionCookieMode::None) + ini_set('session.use_cookies','0'); + else if($value===THttpSessionCookieMode::Allow) + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','0'); + } + else + { + ini_set('session.use_cookies','1'); + ini_set('session.use_only_cookies','1'); + ini_set('session.use_trans_sid', 0); + } + } + } + public function getAutoStart() + { + return $this->_autoStart; + } + public function setAutoStart($value) + { + if($this->_initialized) + throw new TInvalidOperationException('httpsession_autostart_unchangeable'); + else + $this->_autoStart=TPropertyValue::ensureBoolean($value); + } + public function getGCProbability() + { + return TPropertyValue::ensureInteger(ini_get('session.gc_probability')); + } + public function setGCProbability($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_gcprobability_unchangeable'); + else + { + $value=TPropertyValue::ensureInteger($value); + if($value>=0 && $value<=100) + { + ini_set('session.gc_probability',$value); + ini_set('session.gc_divisor','100'); + } + else + throw new TInvalidDataValueException('httpsession_gcprobability_invalid',$value); + } + } + public function getUseTransparentSessionID() + { + return ini_get('session.use_trans_sid')==='1'; + } + public function setUseTransparentSessionID($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_transid_unchangeable'); + else + { + $value=TPropertyValue::ensureBoolean($value); + if ($value && $this->getCookieMode()==THttpSessionCookieMode::Only) + throw new TInvalidOperationException('httpsession_transid_cookieonly'); + ini_set('session.use_trans_sid',$value?'1':'0'); + } + } + public function getTimeout() + { + return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime')); + } + public function setTimeout($value) + { + if($this->_started) + throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable'); + else + ini_set('session.gc_maxlifetime',$value); + } + public function _open($savePath,$sessionName) + { + return true; + } + public function _close() + { + return true; + } + public function _read($id) + { + return ''; + } + public function _write($id,$data) + { + return true; + } + public function _destroy($id) + { + return true; + } + public function _gc($maxLifetime) + { + return true; + } + public function getIterator() + { + return new TSessionIterator; + } + public function getCount() + { + return count($_SESSION); + } + public function count() + { + return $this->getCount(); + } + public function getKeys() + { + return array_keys($_SESSION); + } + public function itemAt($key) + { + return isset($_SESSION[$key]) ? $_SESSION[$key] : null; + } + public function add($key,$value) + { + $_SESSION[$key]=$value; + } + public function remove($key) + { + if(isset($_SESSION[$key])) + { + $value=$_SESSION[$key]; + unset($_SESSION[$key]); + return $value; + } + else + return null; + } + public function clear() + { + foreach(array_keys($_SESSION) as $key) + unset($_SESSION[$key]); + } + public function contains($key) + { + return isset($_SESSION[$key]); + } + public function toArray() + { + return $_SESSION; + } + public function offsetExists($offset) + { + return isset($_SESSION[$offset]); + } + public function offsetGet($offset) + { + return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null; + } + public function offsetSet($offset,$item) + { + $_SESSION[$offset]=$item; + } + public function offsetUnset($offset) + { + unset($_SESSION[$offset]); + } +} +class TSessionIterator implements Iterator +{ + private $_keys; + private $_key; + public function __construct() + { + $this->_keys=array_keys($_SESSION); + } + public function rewind() + { + $this->_key=reset($this->_keys); + } + public function key() + { + return $this->_key; + } + public function current() + { + return isset($_SESSION[$this->_key])?$_SESSION[$this->_key]:null; + } + public function next() + { + do + { + $this->_key=next($this->_keys); + } + while(!isset($_SESSION[$this->_key]) && $this->_key!==false); + } + public function valid() + { + return $this->_key!==false; + } +} +class THttpSessionCookieMode extends TEnumerable +{ + const None='None'; + const Allow='Allow'; + const Only='Only'; +} +Prado::using('System.Web.UI.WebControls.*'); +class TAttributeCollection extends TMap +{ + private $_caseSensitive=false; + public function __get($name) + { + return $this->contains($name)?$this->itemAt($name):parent::__get($name); + } + public function __set($name,$value) + { + $this->add($name,$value); + } + public function getCaseSensitive() + { + return $this->_caseSensitive; + } + public function setCaseSensitive($value) + { + $this->_caseSensitive=TPropertyValue::ensureBoolean($value); + } + public function itemAt($key) + { + return parent::itemAt($this->_caseSensitive?$key:strtolower($key)); + } + public function add($key,$value) + { + parent::add($this->_caseSensitive?$key:strtolower($key),$value); + } + public function remove($key) + { + return parent::remove($this->_caseSensitive?$key:strtolower($key)); + } + public function contains($key) + { + return parent::contains($this->_caseSensitive?$key:strtolower($key)); + } + public function hasProperty($name) + { + return $this->contains($name) || parent::canGetProperty($name) || parent::canSetProperty($name); + } + public function canGetProperty($name) + { + return $this->contains($name) || parent::canGetProperty($name); + } + public function canSetProperty($name) + { + return true; + } +} +class TControlAdapter extends TApplicationComponent +{ + protected $_control; + public function __construct($control) + { + $this->_control=$control; + } + public function getControl() + { + return $this->_control; + } + public function getPage() + { + return $this->_control?$this->_control->getPage():null; + } + public function createChildControls() + { + $this->_control->createChildControls(); + } + public function loadState() + { + $this->_control->loadState(); + } + public function saveState() + { + $this->_control->saveState(); + } + public function onInit($param) + { + $this->_control->onInit($param); + } + public function onLoad($param) + { + $this->_control->onLoad($param); + } + public function onPreRender($param) + { + $this->_control->onPreRender($param); + } + public function onUnload($param) + { + $this->_control->onUnload($param); + } + public function render($writer) + { + $this->_control->render($writer); + } + public function renderChildren($writer) + { + $this->_control->renderChildren($writer); + } +} +class TControl extends TApplicationComponent implements IRenderable, IBindable +{ + const ID_FORMAT='/^[a-zA-Z_]\\w*$/'; + const ID_SEPARATOR='$'; + const CLIENT_ID_SEPARATOR='_'; + const AUTOMATIC_ID_PREFIX='ctl'; + const CS_CONSTRUCTED=0; + const CS_CHILD_INITIALIZED=1; + const CS_INITIALIZED=2; + const CS_STATE_LOADED=3; + const CS_LOADED=4; + const CS_PRERENDERED=5; + const IS_ID_SET=0x01; + const IS_DISABLE_VIEWSTATE=0x02; + const IS_SKIN_APPLIED=0x04; + const IS_STYLESHEET_APPLIED=0x08; + const IS_DISABLE_THEMING=0x10; + const IS_CHILD_CREATED=0x20; + const IS_CREATING_CHILD=0x40; + const RF_CONTROLS=0; const RF_CHILD_STATE=1; const RF_NAMED_CONTROLS=2; const RF_NAMED_CONTROLS_ID=3; const RF_SKIN_ID=4; const RF_DATA_BINDINGS=5; const RF_EVENTS=6; const RF_CONTROLSTATE=7; const RF_NAMED_OBJECTS=8; const RF_ADAPTER=9; const RF_AUTO_BINDINGS=10; + private $_id=''; + private $_uid; + private $_parent; + private $_page; + private $_namingContainer; + private $_tplControl; + private $_viewState=array(); + private $_tempState=array(); + private $_trackViewState=true; + private $_stage=0; + private $_flags=0; + private $_rf=array(); + public function __construct() + { + } + public function __get($name) + { + if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) + return $this->_rf[self::RF_NAMED_OBJECTS][$name]; + else + return parent::__get($name); + } + public function getHasAdapter() + { + return isset($this->_rf[self::RF_ADAPTER]); + } + public function getAdapter() + { + return isset($this->_rf[self::RF_ADAPTER])?$this->_rf[self::RF_ADAPTER]:null; + } + public function setAdapter(TControlAdapter $adapter) + { + $this->_rf[self::RF_ADAPTER]=$adapter; + } + public function getParent() + { + return $this->_parent; + } + public function getNamingContainer() + { + if(!$this->_namingContainer && $this->_parent) + { + if($this->_parent instanceof INamingContainer) + $this->_namingContainer=$this->_parent; + else + $this->_namingContainer=$this->_parent->getNamingContainer(); + } + return $this->_namingContainer; + } + public function getPage() + { + if(!$this->_page) + { + if($this->_parent) + $this->_page=$this->_parent->getPage(); + else if($this->_tplControl) + $this->_page=$this->_tplControl->getPage(); + } + return $this->_page; + } + public function setPage($page) + { + $this->_page=$page; + } + public function setTemplateControl($control) + { + $this->_tplControl=$control; + } + public function getTemplateControl() + { + if(!$this->_tplControl && $this->_parent) + $this->_tplControl=$this->_parent->getTemplateControl(); + return $this->_tplControl; + } + public function getSourceTemplateControl() + { + $control=$this; + while(($control instanceof TControl) && ($control=$control->getTemplateControl())!==null) + { + if(($control instanceof TTemplateControl) && $control->getIsSourceTemplateControl()) + return $control; + } + return $this->getPage(); + } + protected function getControlStage() + { + return $this->_stage; + } + protected function setControlStage($value) + { + $this->_stage=$value; + } + public function getID($hideAutoID=true) + { + if($hideAutoID) + return ($this->_flags & self::IS_ID_SET) ? $this->_id : ''; + else + return $this->_id; + } + public function setID($id) + { + if(!preg_match(self::ID_FORMAT,$id)) + throw new TInvalidDataValueException('control_id_invalid',get_class($this),$id); + $this->_id=$id; + $this->_flags |= self::IS_ID_SET; + $this->clearCachedUniqueID($this instanceof INamingContainer); + if($this->_namingContainer) + $this->_namingContainer->clearNameTable(); + } + public function getUniqueID() + { + if($this->_uid==='' || $this->_uid===null) { + $this->_uid=''; if($namingContainer=$this->getNamingContainer()) + { + if($this->getPage()===$namingContainer) + return ($this->_uid=$this->_id); + else if(($prefix=$namingContainer->getUniqueID())==='') + return $this->_id; + else + return ($this->_uid=$prefix.self::ID_SEPARATOR.$this->_id); + } + else return $this->_id; + } + else + return $this->_uid; + } + public function focus() + { + $this->getPage()->setFocus($this); + } + public function getClientID() + { + return strtr($this->getUniqueID(),self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); + } + public static function convertUniqueIdToClientId($uniqueID) + { + return strtr($uniqueID,self::ID_SEPARATOR,self::CLIENT_ID_SEPARATOR); + } + public function getSkinID() + { + return isset($this->_rf[self::RF_SKIN_ID])?$this->_rf[self::RF_SKIN_ID]:''; + } + public function setSkinID($value) + { + if(($this->_flags & self::IS_SKIN_APPLIED) || $this->_stage>=self::CS_CHILD_INITIALIZED) + throw new TInvalidOperationException('control_skinid_unchangeable',get_class($this)); + else + $this->_rf[self::RF_SKIN_ID]=$value; + } + public function getIsSkinApplied() + { + return ($this->_flags & self::IS_SKIN_APPLIED); + } + public function getEnableTheming() + { + if($this->_flags & self::IS_DISABLE_THEMING) + return false; + else + return $this->_parent?$this->_parent->getEnableTheming():true; + } + public function setEnableTheming($value) + { + if($this->_stage>=self::CS_CHILD_INITIALIZED) + throw new TInvalidOperationException('control_enabletheming_unchangeable',get_class($this),$this->getUniqueID()); + else if(TPropertyValue::ensureBoolean($value)) + $this->_flags &= ~self::IS_DISABLE_THEMING; + else + $this->_flags |= self::IS_DISABLE_THEMING; + } + public function getCustomData() + { + return $this->getViewState('CustomData',null); + } + public function setCustomData($value) + { + $this->setViewState('CustomData',$value,null); + } + public function getHasControls() + { + return isset($this->_rf[self::RF_CONTROLS]) && $this->_rf[self::RF_CONTROLS]->getCount()>0; + } + public function getControls() + { + if(!isset($this->_rf[self::RF_CONTROLS])) + $this->_rf[self::RF_CONTROLS]=$this->createControlCollection(); + return $this->_rf[self::RF_CONTROLS]; + } + protected function createControlCollection() + { + return $this->getAllowChildControls()?new TControlCollection($this):new TEmptyControlCollection($this); + } + public function getVisible($checkParents=true) + { + if($checkParents) + { + for($control=$this;$control;$control=$control->_parent) + if(!$control->getVisible(false)) + return false; + return true; + } + else + return $this->getViewState('Visible',true); + } + public function setVisible($value) + { + $this->setViewState('Visible',TPropertyValue::ensureBoolean($value),true); + } + public function getEnabled($checkParents=false) + { + if($checkParents) + { + for($control=$this;$control;$control=$control->_parent) + if(!$control->getViewState('Enabled',true)) + return false; + return true; + } + else + return $this->getViewState('Enabled',true); + } + public function setEnabled($value) + { + $this->setViewState('Enabled',TPropertyValue::ensureBoolean($value),true); + } + public function getHasAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->getCount()>0; + else + return false; + } + public function getAttributes() + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes; + else + { + $attributes=new TAttributeCollection; + $this->setViewState('Attributes',$attributes,null); + return $attributes; + } + } + public function hasAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->contains($name); + else + return false; + } + public function getAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->itemAt($name); + else + return null; + } + public function setAttribute($name,$value) + { + $this->getAttributes()->add($name,$value); + } + public function removeAttribute($name) + { + if($attributes=$this->getViewState('Attributes',null)) + return $attributes->remove($name); + else + return null; + } + public function getEnableViewState($checkParents=false) + { + if($checkParents) + { + for($control=$this;$control!==null;$control=$control->getParent()) + if($control->_flags & self::IS_DISABLE_VIEWSTATE) + return false; + return true; + } + else + return !($this->_flags & self::IS_DISABLE_VIEWSTATE); + } + public function setEnableViewState($value) + { + if(TPropertyValue::ensureBoolean($value)) + $this->_flags &= ~self::IS_DISABLE_VIEWSTATE; + else + $this->_flags |= self::IS_DISABLE_VIEWSTATE; + } + protected function getControlState($key,$defaultValue=null) + { + return isset($this->_rf[self::RF_CONTROLSTATE][$key])?$this->_rf[self::RF_CONTROLSTATE][$key]:$defaultValue; + } + protected function setControlState($key,$value,$defaultValue=null) + { + if($value===$defaultValue) + unset($this->_rf[self::RF_CONTROLSTATE][$key]); + else + $this->_rf[self::RF_CONTROLSTATE][$key]=$value; + } + protected function clearControlState($key) + { + unset($this->_rf[self::RF_CONTROLSTATE][$key]); + } + public function trackViewState($enabled) + { + $this->_trackViewState=TPropertyValue::ensureBoolean($enabled); + } + public function getViewState($key,$defaultValue=null) + { + if(isset($this->_viewState[$key])) + return $this->_viewState[$key]!==null?$this->_viewState[$key]:$defaultValue; + else if(isset($this->_tempState[$key])) + { + if(is_object($this->_tempState[$key]) && $this->_trackViewState) + $this->_viewState[$key]=$this->_tempState[$key]; + return $this->_tempState[$key]; + } + else + return $defaultValue; + } + public function setViewState($key,$value,$defaultValue=null) + { + if($this->_trackViewState) + { + $this->_viewState[$key]=$value; + unset($this->_tempState[$key]); + } + else + { + unset($this->_viewState[$key]); + $this->_tempState[$key]=$value; + } + } + public function clearViewState($key) + { + unset($this->_viewState[$key]); + unset($this->_tempState[$key]); + } + public function bindProperty($name,$expression) + { + $this->_rf[self::RF_DATA_BINDINGS][$name]=$expression; + } + public function unbindProperty($name) + { + unset($this->_rf[self::RF_DATA_BINDINGS][$name]); + } + public function autoBindProperty($name,$expression) + { + $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression; + } + public function dataBind() + { + $this->dataBindProperties(); + $this->onDataBinding(null); + $this->dataBindChildren(); + } + protected function dataBindProperties() + { + if(isset($this->_rf[self::RF_DATA_BINDINGS])) + { + if(($context=$this->getTemplateControl())===null) + $context=$this; + foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression) + $this->setSubProperty($property,$context->evaluateExpression($expression)); + } + } + protected function autoDataBindProperties() + { + if(isset($this->_rf[self::RF_AUTO_BINDINGS])) + { + if(($context=$this->getTemplateControl())===null) + $context=$this; + foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression) + $this->setSubProperty($property,$context->evaluateExpression($expression)); + } + } + protected function dataBindChildren() + { + if(isset($this->_rf[self::RF_CONTROLS])) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof IBindable) + $control->dataBind(); + } + } + final protected function getChildControlsCreated() + { + return ($this->_flags & self::IS_CHILD_CREATED)!==0; + } + final protected function setChildControlsCreated($value) + { + if($value) + $this->_flags |= self::IS_CHILD_CREATED; + else + { + if($this->getHasControls() && ($this->_flags & self::IS_CHILD_CREATED)) + $this->getControls()->clear(); + $this->_flags &= ~self::IS_CHILD_CREATED; + } + } + public function ensureChildControls() + { + if(!($this->_flags & self::IS_CHILD_CREATED) && !($this->_flags & self::IS_CREATING_CHILD)) + { + try + { + $this->_flags |= self::IS_CREATING_CHILD; + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->createChildControls(); + else + $this->createChildControls(); + $this->_flags &= ~self::IS_CREATING_CHILD; + $this->_flags |= self::IS_CHILD_CREATED; + } + catch(Exception $e) + { + $this->_flags &= ~self::IS_CREATING_CHILD; + $this->_flags |= self::IS_CHILD_CREATED; + throw $e; + } + } + } + public function createChildControls() + { + } + public function findControl($id) + { + $id=strtr($id,'.',self::ID_SEPARATOR); + $container=($this instanceof INamingContainer)?$this:$this->getNamingContainer(); + if(!$container || !$container->getHasControls()) + return null; + if(!isset($container->_rf[self::RF_NAMED_CONTROLS])) + { + $container->_rf[self::RF_NAMED_CONTROLS]=array(); + $container->fillNameTable($container,$container->_rf[self::RF_CONTROLS]); + } + if(($pos=strpos($id,self::ID_SEPARATOR))===false) + return isset($container->_rf[self::RF_NAMED_CONTROLS][$id])?$container->_rf[self::RF_NAMED_CONTROLS][$id]:null; + else + { + $cid=substr($id,0,$pos); + $sid=substr($id,$pos+1); + if(isset($container->_rf[self::RF_NAMED_CONTROLS][$cid])) + return $container->_rf[self::RF_NAMED_CONTROLS][$cid]->findControl($sid); + else + return null; + } + } + public function findControlsByType($type,$strict=true) + { + $controls=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if(is_object($control) && (get_class($control)===$type || (!$strict && ($control instanceof $type)))) + $controls[]=$control; + if(($control instanceof TControl) && $control->getHasControls()) + $controls=array_merge($controls,$control->findControlsByType($type,$strict)); + } + } + return $controls; + } + public function findControlsByID($id) + { + $controls=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + if($control->_id===$id) + $controls[]=$control; + $controls=array_merge($controls,$control->findControlsByID($id)); + } + } + } + return $controls; + } + public function clearNamingContainer() + { + unset($this->_rf[self::RF_NAMED_CONTROLS_ID]); + $this->clearNameTable(); + } + public function registerObject($name,$object) + { + if(isset($this->_rf[self::RF_NAMED_OBJECTS][$name])) + throw new TInvalidOperationException('control_object_reregistered',$name); + $this->_rf[self::RF_NAMED_OBJECTS][$name]=$object; + } + public function unregisterObject($name) + { + unset($this->_rf[self::RF_NAMED_OBJECTS][$name]); + } + public function isObjectRegistered($name) + { + return isset($this->_rf[self::RF_NAMED_OBJECTS][$name]); + } + public function getHasChildInitialized() + { + return $this->getControlStage() >= self::CS_CHILD_INITIALIZED; + } + public function getHasInitialized() + { + return $this->getControlStage() >= self::CS_INITIALIZED; + } + public function getHasLoadedPostData() + { + return $this->getControlStage() >= self::CS_STATE_LOADED; + } + public function getHasLoaded() + { + return $this->getControlStage() >= self::CS_LOADED; + } + public function getHasPreRendered() + { + return $this->getControlStage() >= self::CS_PRERENDERED; + } + public function getRegisteredObject($name) + { + return isset($this->_rf[self::RF_NAMED_OBJECTS][$name])?$this->_rf[self::RF_NAMED_OBJECTS][$name]:null; + } + public function getAllowChildControls() + { + return true; + } + public function addParsedObject($object) + { + $this->getControls()->add($object); + } + final protected function clearChildState() + { + unset($this->_rf[self::RF_CHILD_STATE]); + } + final protected function isDescendentOf($ancestor) + { + $control=$this; + while($control!==$ancestor && $control->_parent) + $control=$control->_parent; + return $control===$ancestor; + } + public function addedControl($control) + { + if($control->_parent) + $control->_parent->getControls()->remove($control); + $control->_parent=$this; + $control->_page=$this->getPage(); + $namingContainer=($this instanceof INamingContainer)?$this:$this->_namingContainer; + if($namingContainer) + { + $control->_namingContainer=$namingContainer; + if($control->_id==='') + $control->generateAutomaticID(); + else + $namingContainer->clearNameTable(); + $control->clearCachedUniqueID($control instanceof INamingContainer); + } + if($this->_stage>=self::CS_CHILD_INITIALIZED) + { + $control->initRecursive($namingContainer); + if($this->_stage>=self::CS_STATE_LOADED) + { + if(isset($this->_rf[self::RF_CHILD_STATE][$control->_id])) + { + $state=$this->_rf[self::RF_CHILD_STATE][$control->_id]; + unset($this->_rf[self::RF_CHILD_STATE][$control->_id]); + } + else + $state=null; + $control->loadStateRecursive($state,!($this->_flags & self::IS_DISABLE_VIEWSTATE)); + if($this->_stage>=self::CS_LOADED) + { + $control->loadRecursive(); + if($this->_stage>=self::CS_PRERENDERED) + $control->preRenderRecursive(); + } + } + } + } + public function removedControl($control) + { + if($this->_namingContainer) + $this->_namingContainer->clearNameTable(); + $control->unloadRecursive(); + $control->_parent=null; + $control->_page=null; + $control->_namingContainer=null; + $control->_tplControl=null; + if(!($control->_flags & self::IS_ID_SET)) + $control->_id=''; + else + unset($this->_rf[self::RF_NAMED_OBJECTS][$control->_id]); + $control->clearCachedUniqueID(true); + } + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + if($this->getHasControls()) + { + if($this instanceof INamingContainer) + $namingContainer=$this; + $page=$this->getPage(); + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + $control->_namingContainer=$namingContainer; + $control->_page=$page; + if($control->_id==='' && $namingContainer) + $control->generateAutomaticID(); + $control->initRecursive($namingContainer); + } + } + } + if($this->_stage_stage=self::CS_CHILD_INITIALIZED; + if(($page=$this->getPage()) && $this->getEnableTheming() && !($this->_flags & self::IS_SKIN_APPLIED)) + { + $page->applyControlSkin($this); + $this->_flags |= self::IS_SKIN_APPLIED; + } + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onInit(null); + else + $this->onInit(null); + $this->_stage=self::CS_INITIALIZED; + } + } + protected function loadRecursive() + { + if($this->_stage_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onLoad(null); + else + $this->onLoad(null); + } + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->loadRecursive(); + } + } + if($this->_stage_stage=self::CS_LOADED; + } + protected function preRenderRecursive() + { + $this->autoDataBindProperties(); + if($this->getVisible(false)) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onPreRender(null); + else + $this->onPreRender(null); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->preRenderRecursive(); + else if($control instanceof TCompositeLiteral) + $control->evaluateDynamicContent(); + } + } + $this->addToPostDataLoader(); + } + $this->_stage=self::CS_PRERENDERED; + } + protected function addToPostDataLoader() + { + if($this instanceof IPostBackDataHandler) + $this->getPage()->registerPostDataLoader($this); + } + protected function unloadRecursive() + { + if(!($this->_flags & self::IS_ID_SET)) + $this->_id=''; + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof TControl) + $control->unloadRecursive(); + } + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->onUnload(null); + else + $this->onUnload(null); + } + public function onInit($param) + { + $this->raiseEvent('OnInit',$this,$param); + } + public function onLoad($param) + { + $this->raiseEvent('OnLoad',$this,$param); + } + public function onDataBinding($param) + { + $this->raiseEvent('OnDataBinding',$this,$param); + } + public function onUnload($param) + { + $this->raiseEvent('OnUnload',$this,$param); + } + public function onPreRender($param) + { + $this->raiseEvent('OnPreRender',$this,$param); + } + protected function raiseBubbleEvent($sender,$param) + { + $control=$this; + while($control=$control->_parent) + { + if($control->bubbleEvent($sender,$param)) + break; + } + } + public function bubbleEvent($sender,$param) + { + return false; + } + public function broadcastEvent($name,$sender,$param) + { + $rootControl=(($page=$this->getPage())===null)?$this:$page; + $rootControl->broadcastEventInternal($name,$sender,new TBroadcastEventParameter($name,$param)); + } + private function broadcastEventInternal($name,$sender,$param) + { + if($this->hasEvent($name)) + $this->raiseEvent($name,$sender,$param->getParameter()); + if($this instanceof IBroadcastEventReceiver) + $this->broadcastEventReceived($sender,$param); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $control->broadcastEventInternal($name,$sender,$param); + } + } + } + protected function traverseChildControls($param,$preCallback=null,$postCallback=null) + { + if($preCallback!==null) + call_user_func($preCallback,$this,$param); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + $control->traverseChildControls($param,$preCallback,$postCallback); + } + } + } + if($postCallback!==null) + call_user_func($postCallback,$this,$param); + } + public function renderControl($writer) + { + if($this instanceof IActiveControl || $this->getVisible(false)) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->render($writer); + else + $this->render($writer); + } + } + public function render($writer) + { + $this->renderChildren($writer); + } + public function renderChildren($writer) + { + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if(is_string($control)) + $writer->write($control); + else if($control instanceof TControl) + $control->renderControl($writer); + else if($control instanceof IRenderable) + $control->render($writer); + } + } + } + public function saveState() + { + } + public function loadState() + { + } + protected function loadStateRecursive(&$state,$needViewState=true) + { + if(is_array($state)) + { + $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); + if(isset($state[1])) + { + $this->_rf[self::RF_CONTROLSTATE]=&$state[1]; + unset($state[1]); + } + else + unset($this->_rf[self::RF_CONTROLSTATE]); + if($needViewState) + { + if(isset($state[0])) + $this->_viewState=&$state[0]; + else + $this->_viewState=array(); + } + unset($state[0]); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + { + if(isset($state[$control->_id])) + { + $control->loadStateRecursive($state[$control->_id],$needViewState); + unset($state[$control->_id]); + } + } + } + } + if(!empty($state)) + $this->_rf[self::RF_CHILD_STATE]=&$state; + } + $this->_stage=self::CS_STATE_LOADED; + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->loadState(); + else + $this->loadState(); + } + protected function &saveStateRecursive($needViewState=true) + { + if(isset($this->_rf[self::RF_ADAPTER])) + $this->_rf[self::RF_ADAPTER]->saveState(); + else + $this->saveState(); + $needViewState=($needViewState && !($this->_flags & self::IS_DISABLE_VIEWSTATE)); + $state=array(); + if($this->getHasControls()) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + { + if($control instanceof TControl) + $state[$control->_id]=&$control->saveStateRecursive($needViewState); + } + } + if($needViewState && !empty($this->_viewState)) + $state[0]=&$this->_viewState; + if(isset($this->_rf[self::RF_CONTROLSTATE])) + $state[1]=&$this->_rf[self::RF_CONTROLSTATE]; + return $state; + } + public function applyStyleSheetSkin($page) + { + if($page && !($this->_flags & self::IS_STYLESHEET_APPLIED)) + { + $page->applyControlStyleSheet($this); + $this->_flags |= self::IS_STYLESHEET_APPLIED; + } + else if($this->_flags & self::IS_STYLESHEET_APPLIED) + throw new TInvalidOperationException('control_stylesheet_applied',get_class($this)); + } + private function clearCachedUniqueID($recursive) + { + if($recursive && $this->_uid!==null && isset($this->_rf[self::RF_CONTROLS])) + { + foreach($this->_rf[self::RF_CONTROLS] as $control) + if($control instanceof TControl) + $control->clearCachedUniqueID($recursive); + } + $this->_uid=null; + } + private function generateAutomaticID() + { + $this->_flags &= ~self::IS_ID_SET; + if(!isset($this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID])) + $this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]=0; + $id=$this->_namingContainer->_rf[self::RF_NAMED_CONTROLS_ID]++; + $this->_id=self::AUTOMATIC_ID_PREFIX . $id; + $this->_namingContainer->clearNameTable(); + } + private function clearNameTable() + { + unset($this->_rf[self::RF_NAMED_CONTROLS]); + } + private function fillNameTable($container,$controls) + { + foreach($controls as $control) + { + if($control instanceof TControl) + { + if($control->_id!=='') + { + if(isset($container->_rf[self::RF_NAMED_CONTROLS][$control->_id])) + throw new TInvalidDataValueException('control_id_nonunique',get_class($control),$control->_id); + else + $container->_rf[self::RF_NAMED_CONTROLS][$control->_id]=$control; + } + if(!($control instanceof INamingContainer) && $control->getHasControls()) + $this->fillNameTable($container,$control->_rf[self::RF_CONTROLS]); + } + } + } +} +class TControlCollection extends TList +{ + private $_o; + public function __construct(TControl $owner,$readOnly=false) + { + $this->_o=$owner; + parent::__construct(null,$readOnly); + } + protected function getOwner() + { + return $this->_o; + } + public function insertAt($index,$item) + { + if($item instanceof TControl) + { + parent::insertAt($index,$item); + $this->_o->addedControl($item); + } + else if(is_string($item) || ($item instanceof IRenderable)) + parent::insertAt($index,$item); + else + throw new TInvalidDataTypeException('controlcollection_control_required'); + } + public function removeAt($index) + { + $item=parent::removeAt($index); + if($item instanceof TControl) + $this->_o->removedControl($item); + return $item; + } + public function clear() + { + parent::clear(); + if($this->_o instanceof INamingContainer) + $this->_o->clearNamingContainer(); + } +} +class TEmptyControlCollection extends TControlCollection +{ + public function __construct(TControl $owner) + { + parent::__construct($owner,true); + } + public function insertAt($index,$item) + { + if(!is_string($item)) parent::insertAt($index,$item); } +} +interface INamingContainer +{ +} +interface IPostBackEventHandler +{ + public function raisePostBackEvent($param); +} +interface IPostBackDataHandler +{ + public function loadPostData($key,$values); + public function raisePostDataChangedEvent(); + public function getDataChanged(); +} +interface IValidator +{ + public function validate(); + public function getIsValid(); + public function setIsValid($value); + public function getErrorMessage(); + public function setErrorMessage($value); +} +interface IValidatable +{ + public function getValidationPropertyValue(); + public function getIsValid(); + public function setIsValid($value); +} +interface IBroadcastEventReceiver +{ + public function broadcastEventReceived($sender,$param); +} +interface ITheme +{ + public function applySkin($control); +} +interface ITemplate +{ + public function instantiateIn($parent); +} +interface IButtonControl +{ + public function getText(); + public function setText($value); + public function getCausesValidation(); + public function setCausesValidation($value); + public function getCommandName(); + public function setCommandName($value); + public function getCommandParameter(); + public function setCommandParameter($value); + public function getValidationGroup(); + public function setValidationGroup($value); + public function onClick($param); + public function onCommand($param); + public function setIsDefaultButton($value); + public function getIsDefaultButton(); +} +interface ISurroundable +{ + public function getSurroundingTagID(); +} +class TBroadcastEventParameter extends TEventParameter +{ + private $_name; + private $_param; + public function __construct($name='',$parameter=null) + { + $this->_name=$name; + $this->_param=$parameter; + } + public function getName() + { + return $this->_name; + } + public function setName($value) + { + $this->_name=$value; + } + public function getParameter() + { + return $this->_param; + } + public function setParameter($value) + { + $this->_param=$value; + } +} +class TCommandEventParameter extends TEventParameter +{ + private $_name; + private $_param; + public function __construct($name='',$parameter='') + { + $this->_name=$name; + $this->_param=$parameter; + } + public function getCommandName() + { + return $this->_name; + } + public function getCommandParameter() + { + return $this->_param; + } +} +class TCompositeLiteral extends TComponent implements IRenderable, IBindable +{ + const TYPE_EXPRESSION=0; + const TYPE_STATEMENTS=1; + const TYPE_DATABINDING=2; + private $_container=null; + private $_items=array(); + private $_expressions=array(); + private $_statements=array(); + private $_bindings=array(); + public function __construct($items) + { + $this->_items=array(); + $this->_expressions=array(); + $this->_statements=array(); + foreach($items as $id=>$item) + { + if(is_array($item)) + { + if($item[0]===self::TYPE_EXPRESSION) + $this->_expressions[$id]=$item[1]; + else if($item[0]===self::TYPE_STATEMENTS) + $this->_statements[$id]=$item[1]; + else if($item[0]===self::TYPE_DATABINDING) + $this->_bindings[$id]=$item[1]; + $this->_items[$id]=''; + } + else + $this->_items[$id]=$item; + } + } + public function getContainer() + { + return $this->_container; + } + public function setContainer(TComponent $value) + { + $this->_container=$value; + } + public function evaluateDynamicContent() + { + $context=$this->_container===null?$this:$this->_container; + foreach($this->_expressions as $id=>$expression) + $this->_items[$id]=$context->evaluateExpression($expression); + foreach($this->_statements as $id=>$statement) + $this->_items[$id]=$context->evaluateStatements($statement); + } + public function dataBind() + { + $context=$this->_container===null?$this:$this->_container; + foreach($this->_bindings as $id=>$binding) + $this->_items[$id]=$context->evaluateExpression($binding); + } + public function render($writer) + { + $writer->write(implode('',$this->_items)); + } +} +class TFont extends TComponent +{ + const IS_BOLD=0x01; + const IS_ITALIC=0x02; + const IS_OVERLINE=0x04; + const IS_STRIKEOUT=0x08; + const IS_UNDERLINE=0x10; + const IS_SET_BOLD=0x01000; + const IS_SET_ITALIC=0x02000; + const IS_SET_OVERLINE=0x04000; + const IS_SET_STRIKEOUT=0x08000; + const IS_SET_UNDERLINE=0x10000; + const IS_SET_SIZE=0x20000; + const IS_SET_NAME=0x40000; + private $_flags=0; + private $_name=''; + private $_size=''; + public function getBold() + { + return ($this->_flags & self::IS_BOLD)!==0; + } + public function setBold($value) + { + $this->_flags |= self::IS_SET_BOLD; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_BOLD; + else + $this->_flags &= ~self::IS_BOLD; + } + public function getItalic() + { + return ($this->_flags & self::IS_ITALIC)!==0; + } + public function setItalic($value) + { + $this->_flags |= self::IS_SET_ITALIC; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_ITALIC; + else + $this->_flags &= ~self::IS_ITALIC; + } + public function getOverline() + { + return ($this->_flags & self::IS_OVERLINE)!==0; + } + public function setOverline($value) + { + $this->_flags |= self::IS_SET_OVERLINE; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_OVERLINE; + else + $this->_flags &= ~self::IS_OVERLINE; + } + public function getSize() + { + return $this->_size; + } + public function setSize($value) + { + $this->_flags |= self::IS_SET_SIZE; + $this->_size=$value; + } + public function getStrikeout() + { + return ($this->_flags & self::IS_STRIKEOUT)!==0; + } + public function setStrikeout($value) + { + $this->_flags |= self::IS_SET_STRIKEOUT; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_STRIKEOUT; + else + $this->_flags &= ~self::IS_STRIKEOUT; + } + public function getUnderline() + { + return ($this->_flags & self::IS_UNDERLINE)!==0; + } + public function setUnderline($value) + { + $this->_flags |= self::IS_SET_UNDERLINE; + if(TPropertyValue::ensureBoolean($value)) + $this->_flags |= self::IS_UNDERLINE; + else + $this->_flags &= ~self::IS_UNDERLINE; + } + public function getName() + { + return $this->_name; + } + public function setName($value) + { + $this->_flags |= self::IS_SET_NAME; + $this->_name=$value; + } + public function getIsEmpty() + { + return !$this->_flags; + } + public function reset() + { + $this->_flags=0; + $this->_name=''; + $this->_size=''; + } + public function mergeWith($font) + { + if($font===null || $font->_flags===0) + return; + if(!($this->_flags & self::IS_SET_BOLD) && ($font->_flags & self::IS_SET_BOLD)) + $this->setBold($font->getBold()); + if(!($this->_flags & self::IS_SET_ITALIC) && ($font->_flags & self::IS_SET_ITALIC)) + $this->setItalic($font->getItalic()); + if(!($this->_flags & self::IS_SET_OVERLINE) && ($font->_flags & self::IS_SET_OVERLINE)) + $this->setOverline($font->getOverline()); + if(!($this->_flags & self::IS_SET_STRIKEOUT) && ($font->_flags & self::IS_SET_STRIKEOUT)) + $this->setStrikeout($font->getStrikeout()); + if(!($this->_flags & self::IS_SET_UNDERLINE) && ($font->_flags & self::IS_SET_UNDERLINE)) + $this->setUnderline($font->getUnderline()); + if(!($this->_flags & self::IS_SET_SIZE) && ($font->_flags & self::IS_SET_SIZE)) + $this->setSize($font->getSize()); + if(!($this->_flags & self::IS_SET_NAME) && ($font->_flags & self::IS_SET_NAME)) + $this->setName($font->getName()); + } + public function copyFrom($font) + { + if($font===null || $font->_flags===0) + return; + if($font->_flags & self::IS_SET_BOLD) + $this->setBold($font->getBold()); + if($font->_flags & self::IS_SET_ITALIC) + $this->setItalic($font->getItalic()); + if($font->_flags & self::IS_SET_OVERLINE) + $this->setOverline($font->getOverline()); + if($font->_flags & self::IS_SET_STRIKEOUT) + $this->setStrikeout($font->getStrikeout()); + if($font->_flags & self::IS_SET_UNDERLINE) + $this->setUnderline($font->getUnderline()); + if($font->_flags & self::IS_SET_SIZE) + $this->setSize($font->getSize()); + if($font->_flags & self::IS_SET_NAME) + $this->setName($font->getName()); + } + public function toString() + { + if($this->_flags===0) + return ''; + $str=''; + if($this->_flags & self::IS_SET_BOLD) + $str.='font-weight:'.(($this->_flags & self::IS_BOLD)?'bold;':'normal;'); + if($this->_flags & self::IS_SET_ITALIC) + $str.='font-style:'.(($this->_flags & self::IS_ITALIC)?'italic;':'normal;'); + $textDec=''; + if($this->_flags & self::IS_UNDERLINE) + $textDec.='underline'; + if($this->_flags & self::IS_OVERLINE) + $textDec.=' overline'; + if($this->_flags & self::IS_STRIKEOUT) + $textDec.=' line-through'; + $textDec=ltrim($textDec); + if($textDec!=='') + $str.='text-decoration:'.$textDec.';'; + if($this->_size!=='') + $str.='font-size:'.$this->_size.';'; + if($this->_name!=='') + $str.='font-family:'.$this->_name.';'; + return $str; + } + public function addAttributesToRender($writer) + { + if($this->_flags===0) + return; + if($this->_flags & self::IS_SET_BOLD) + $writer->addStyleAttribute('font-weight',(($this->_flags & self::IS_BOLD)?'bold':'normal')); + if($this->_flags & self::IS_SET_ITALIC) + $writer->addStyleAttribute('font-style',(($this->_flags & self::IS_ITALIC)?'italic':'normal')); + $textDec=''; + if($this->_flags & self::IS_UNDERLINE) + $textDec.='underline'; + if($this->_flags & self::IS_OVERLINE) + $textDec.=' overline'; + if($this->_flags & self::IS_STRIKEOUT) + $textDec.=' line-through'; + $textDec=ltrim($textDec); + if($textDec!=='') + $writer->addStyleAttribute('text-decoration',$textDec); + if($this->_size!=='') + $writer->addStyleAttribute('font-size',$this->_size); + if($this->_name!=='') + $writer->addStyleAttribute('font-family',$this->_name); + } +} +class TStyle extends TComponent +{ + private $_fields=array(); + private $_font=null; + private $_class=null; + private $_customStyle=null; + private $_displayStyle='Fixed'; + public function __construct($style=null) + { + if($style!==null) + $this->copyFrom($style); + } + public function __clone() + { + if($this->_font!==null) + $this->_font = clone($this->_font); + } + public function getBackColor() + { + return isset($this->_fields['background-color'])?$this->_fields['background-color']:''; + } + public function setBackColor($value) + { + if(trim($value)==='') + unset($this->_fields['background-color']); + else + $this->_fields['background-color']=$value; + } + public function getBorderColor() + { + return isset($this->_fields['border-color'])?$this->_fields['border-color']:''; + } + public function setBorderColor($value) + { + if(trim($value)==='') + unset($this->_fields['border-color']); + else + $this->_fields['border-color']=$value; + } + public function getBorderStyle() + { + return isset($this->_fields['border-style'])?$this->_fields['border-style']:''; + } + public function setBorderStyle($value) + { + if(trim($value)==='') + unset($this->_fields['border-style']); + else + $this->_fields['border-style']=$value; + } + public function getBorderWidth() + { + return isset($this->_fields['border-width'])?$this->_fields['border-width']:''; + } + public function setBorderWidth($value) + { + if(trim($value)==='') + unset($this->_fields['border-width']); + else + $this->_fields['border-width']=$value; + } + public function getCssClass() + { + return $this->_class===null?'':$this->_class; + } + public function hasCssClass() + { + return ($this->_class!==null); + } + public function setCssClass($value) + { + $this->_class=$value; + } + public function getFont() + { + if($this->_font===null) + $this->_font=new TFont; + return $this->_font; + } + public function hasFont() + { + return $this->_font !== null; + } + public function setDisplayStyle($value) + { + $this->_displayStyle = TPropertyValue::ensureEnum($value, 'TDisplayStyle'); + switch($this->_displayStyle) + { + case TDisplayStyle::None: + $this->_fields['display'] = 'none'; + break; + case TDisplayStyle::Dynamic: + $this->_fields['display'] = ''; break; + case TDisplayStyle::Fixed: + $this->_fields['visibility'] = 'visible'; + break; + case TDisplayStyle::Hidden: + $this->_fields['visibility'] = 'hidden'; + break; + } + } + public function getDisplayStyle() + { + return $this->_displayStyle; + } + public function getForeColor() + { + return isset($this->_fields['color'])?$this->_fields['color']:''; + } + public function setForeColor($value) + { + if(trim($value)==='') + unset($this->_fields['color']); + else + $this->_fields['color']=$value; + } + public function getHeight() + { + return isset($this->_fields['height'])?$this->_fields['height']:''; + } + public function setHeight($value) + { + if(trim($value)==='') + unset($this->_fields['height']); + else + $this->_fields['height']=$value; + } + public function getCustomStyle() + { + return $this->_customStyle===null?'':$this->_customStyle; + } + public function setCustomStyle($value) + { + $this->_customStyle=$value; + } + public function getStyleField($name) + { + return isset($this->_fields[$name])?$this->_fields[$name]:''; + } + public function setStyleField($name,$value) + { + $this->_fields[$name]=$value; + } + public function clearStyleField($name) + { + unset($this->_fields[$name]); + } + public function hasStyleField($name) + { + return isset($this->_fields[$name]); + } + public function getWidth() + { + return isset($this->_fields['width'])?$this->_fields['width']:''; + } + public function setWidth($value) + { + $this->_fields['width']=$value; + } + public function reset() + { + $this->_fields=array(); + $this->_font=null; + $this->_class=null; + $this->_customStyle=null; + } + public function copyFrom($style) + { + if($style instanceof TStyle) + { + $this->_fields=array_merge($this->_fields,$style->_fields); + if($style->_class!==null) + $this->_class=$style->_class; + if($style->_customStyle!==null) + $this->_customStyle=$style->_customStyle; + if($style->_font!==null) + $this->getFont()->copyFrom($style->_font); + } + } + public function mergeWith($style) + { + if($style instanceof TStyle) + { + $this->_fields=array_merge($style->_fields,$this->_fields); + if($this->_class===null) + $this->_class=$style->_class; + if($this->_customStyle===null) + $this->_customStyle=$style->_customStyle; + if($style->_font!==null) + $this->getFont()->mergeWith($style->_font); + } + } + public function addAttributesToRender($writer) + { + if($this->_customStyle!==null) + { + foreach(explode(';',$this->_customStyle) as $style) + { + $arr=explode(':',$style,2); + if(isset($arr[1]) && trim($arr[0])!=='') + $writer->addStyleAttribute(trim($arr[0]),trim($arr[1])); + } + } + $writer->addStyleAttributes($this->_fields); + if($this->_font!==null) + $this->_font->addAttributesToRender($writer); + if($this->_class!==null) + $writer->addAttribute('class',$this->_class); + } + public function getStyleFields() + { + return $this->_fields; + } +} +class TDisplayStyle extends TEnumerable +{ + const None='None'; + const Dynamic='Dynamic'; + const Fixed='Fixed'; + const Hidden='Hidden'; +} +class TTableStyle extends TStyle +{ + private $_backImageUrl=null; + private $_horizontalAlign=null; + private $_cellPadding=null; + private $_cellSpacing=null; + private $_gridLines=null; + private $_borderCollapse=null; + public function reset() + { + $this->_backImageUrl=null; + $this->_horizontalAlign=null; + $this->_cellPadding=null; + $this->_cellSpacing=null; + $this->_gridLines=null; + $this->_borderCollapse=null; + } + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TTableStyle) + { + if($style->_backImageUrl!==null) + $this->_backImageUrl=$style->_backImageUrl; + if($style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($style->_cellPadding!==null) + $this->_cellPadding=$style->_cellPadding; + if($style->_cellSpacing!==null) + $this->_cellSpacing=$style->_cellSpacing; + if($style->_gridLines!==null) + $this->_gridLines=$style->_gridLines; + if($style->_borderCollapse!==null) + $this->_borderCollapse=$style->_borderCollapse; + } + } + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TTableStyle) + { + if($this->_backImageUrl===null && $style->_backImageUrl!==null) + $this->_backImageUrl=$style->_backImageUrl; + if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($this->_cellPadding===null && $style->_cellPadding!==null) + $this->_cellPadding=$style->_cellPadding; + if($this->_cellSpacing===null && $style->_cellSpacing!==null) + $this->_cellSpacing=$style->_cellSpacing; + if($this->_gridLines===null && $style->_gridLines!==null) + $this->_gridLines=$style->_gridLines; + if($this->_borderCollapse===null && $style->_borderCollapse!==null) + $this->_borderCollapse=$style->_borderCollapse; + } + } + public function addAttributesToRender($writer) + { + if(($url=trim($this->getBackImageUrl()))!=='') + $writer->addStyleAttribute('background-image','url('.$url.')'); + if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) + $writer->addStyleAttribute('text-align',strtolower($horizontalAlign)); + if(($cellPadding=$this->getCellPadding())>=0) + $writer->addAttribute('cellpadding',"$cellPadding"); + if(($cellSpacing=$this->getCellSpacing())>=0) + $writer->addAttribute('cellspacing',"$cellSpacing"); + if($this->getBorderCollapse()) + $writer->addStyleAttribute('border-collapse','collapse'); + switch($this->getGridLines()) + { + case TTableGridLines::Horizontal : $writer->addAttribute('rules','rows'); break; + case TTableGridLines::Vertical : $writer->addAttribute('rules','cols'); break; + case TTableGridLines::Both : $writer->addAttribute('rules','all'); break; + } + parent::addAttributesToRender($writer); + } + public function getBackImageUrl() + { + return $this->_backImageUrl===null?'':$this->_backImageUrl; + } + public function setBackImageUrl($value) + { + $this->_backImageUrl=$value; + } + public function getHorizontalAlign() + { + return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; + } + public function setHorizontalAlign($value) + { + $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); + } + public function getCellPadding() + { + return $this->_cellPadding===null?-1:$this->_cellPadding; + } + public function setCellPadding($value) + { + if(($this->_cellPadding=TPropertyValue::ensureInteger($value))<-1) + throw new TInvalidDataValueException('tablestyle_cellpadding_invalid'); + } + public function getCellSpacing() + { + return $this->_cellSpacing===null?-1:$this->_cellSpacing; + } + public function setCellSpacing($value) + { + if(($this->_cellSpacing=TPropertyValue::ensureInteger($value))<-1) + throw new TInvalidDataValueException('tablestyle_cellspacing_invalid'); + } + public function getGridLines() + { + return $this->_gridLines===null?TTableGridLines::None:$this->_gridLines; + } + public function setGridLines($value) + { + $this->_gridLines=TPropertyValue::ensureEnum($value,'TTableGridLines'); + } + public function getBorderCollapse() + { + return $this->_borderCollapse===null?false:$this->_borderCollapse; + } + public function setBorderCollapse($value) + { + $this->_borderCollapse=TPropertyValue::ensureBoolean($value); + } +} +class TTableItemStyle extends TStyle +{ + private $_horizontalAlign=null; + private $_verticalAlign=null; + private $_wrap=null; + public function reset() + { + parent::reset(); + $this->_verticalAlign=null; + $this->_horizontalAlign=null; + $this->_wrap=null; + } + public function copyFrom($style) + { + parent::copyFrom($style); + if($style instanceof TTableItemStyle) + { + if($this->_verticalAlign===null && $style->_verticalAlign!==null) + $this->_verticalAlign=$style->_verticalAlign; + if($this->_horizontalAlign===null && $style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($this->_wrap===null && $style->_wrap!==null) + $this->_wrap=$style->_wrap; + } + } + public function mergeWith($style) + { + parent::mergeWith($style); + if($style instanceof TTableItemStyle) + { + if($style->_verticalAlign!==null) + $this->_verticalAlign=$style->_verticalAlign; + if($style->_horizontalAlign!==null) + $this->_horizontalAlign=$style->_horizontalAlign; + if($style->_wrap!==null) + $this->_wrap=$style->_wrap; + } + } + public function addAttributesToRender($writer) + { + if(!$this->getWrap()) + $writer->addStyleAttribute('white-space','nowrap'); + if(($horizontalAlign=$this->getHorizontalAlign())!==THorizontalAlign::NotSet) + $writer->addAttribute('align',strtolower($horizontalAlign)); + if(($verticalAlign=$this->getVerticalAlign())!==TVerticalAlign::NotSet) + $writer->addAttribute('valign',strtolower($verticalAlign)); + parent::addAttributesToRender($writer); + } + public function getHorizontalAlign() + { + return $this->_horizontalAlign===null?THorizontalAlign::NotSet:$this->_horizontalAlign; + } + public function setHorizontalAlign($value) + { + $this->_horizontalAlign=TPropertyValue::ensureEnum($value,'THorizontalAlign'); + } + public function getVerticalAlign() + { + return $this->_verticalAlign===null?TVerticalAlign::NotSet:$this->_verticalAlign; + } + public function setVerticalAlign($value) + { + $this->_verticalAlign=TPropertyValue::ensureEnum($value,'TVerticalAlign'); + } + public function getWrap() + { + return $this->_wrap===null?true:$this->_wrap; + } + public function setWrap($value) + { + $this->_wrap=TPropertyValue::ensureBoolean($value); + } +} +class THorizontalAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Left='Left'; + const Right='Right'; + const Center='Center'; + const Justify='Justify'; +} +class TVerticalAlign extends TEnumerable +{ + const NotSet='NotSet'; + const Top='Top'; + const Bottom='Bottom'; + const Middle='Middle'; +} +class TTableGridLines extends TEnumerable +{ + const None='None'; + const Horizontal='Horizontal'; + const Vertical='Vertical'; + const Both='Both'; +} +class TWebControlAdapter extends TControlAdapter +{ + public function render($writer) + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + public function renderBeginTag($writer) + { + $this->getControl()->renderBeginTag($writer); + } + public function renderContents($writer) + { + $this->getControl()->renderContents($writer); + } + public function renderEndTag($writer) + { + $this->getControl()->renderEndTag($writer); + } +} +class TWebControlDecorator extends TComponent { + private $_internalonly; + private $_usestate = false; + private $_control; + private $_outercontrol; + private $_addedTemplateDecoration=false; + private $_pretagtext = ''; + private $_precontentstext = ''; + private $_postcontentstext = ''; + private $_posttagtext = ''; + private $_pretagtemplate; + private $_precontentstemplate; + private $_postcontentstemplate; + private $_posttagtemplate; + public function __construct($control, $onlyinternal = false) { + $this->_control = $control; + $this->_internalonly = $onlyinternal; + } + public function getUseState() + { + return $this->_usestate; + } + public function setUseState($value) + { + $this->_usestate = TPropertyValue::ensureBoolean($value); + } + public function getPreTagText() { + return $this->_pretagtext; + } + public function setPreTagText($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_pretagtext = TPropertyValue::ensureString($value); + } + public function getPreContentsText() { + return $this->_precontentstext; + } + public function setPreContentsText($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_precontentstext = TPropertyValue::ensureString($value); + } + public function getPostContentsText() { + return $this->_postcontentstext; + } + public function setPostContentsText($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_postcontentstext = TPropertyValue::ensureString($value); + } + public function getPostTagText() { + return $this->_posttagtext; + } + public function setPostTagText($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_posttagtext = TPropertyValue::ensureString($value); + } + public function getPreTagTemplate() { + return $this->_pretagtemplate; + } + public function setPreTagTemplate($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_pretagtemplate = $value; + } + public function getPreContentsTemplate() { + return $this->_precontentstemplate; + } + public function setPreContentsTemplate($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_precontentstemplate = $value; + } + public function getPostContentsTemplate() { + return $this->_postcontentstemplate; + } + public function setPostContentsTemplate($value) { + if(!$this->_control->getIsSkinApplied()) + $this->_postcontentstemplate = $value; + } + public function getPostTagTemplate() { + return $this->_posttagtemplate; + } + public function setPostTagTemplate($value) { + if(!$this->_internalonly && !$this->_control->getIsSkinApplied()) + $this->_posttagtemplate = $value; + } + public function instantiate($outercontrol = null) { + if($this->getPreTagTemplate() || $this->getPreContentsTemplate() || + $this->getPostContentsTemplate() || $this->getPostTagTemplate()) { + $this->_outercontrol = $outercontrol; + if($this->getUseState()) + $this->ensureTemplateDecoration(); + else + $this->_control->getPage()->onSaveStateComplete[] = array($this, 'ensureTemplateDecoration'); + } + } + public function ensureTemplateDecoration($sender=null, $param=null) { + $control = $this->_control; + $outercontrol = $this->_outercontrol; + if($outercontrol === null) + $outercontrol = $control; + if($this->_addedTemplateDecoration) + return $this->_addedTemplateDecoration; + $this->_addedTemplateDecoration = true; + if($this->getPreContentsTemplate()) + { + $precontents = Prado::createComponent('TCompositeControl'); + $this->getPreContentsTemplate()->instantiateIn($precontents); + $control->getControls()->insertAt(0, $precontents); + } + if($this->getPostContentsTemplate()) + { + $postcontents = Prado::createComponent('TCompositeControl'); + $this->getPostContentsTemplate()->instantiateIn($postcontents); + $control->getControls()->add($postcontents); + } + if(!$outercontrol->getParent()) + return $this->_addedTemplateDecoration; + if($this->getPreTagTemplate()) + { + $pretag = Prado::createComponent('TCompositeControl'); + $this->getPreTagTemplate()->instantiateIn($pretag); + $outercontrol->getParent()->getControls()->insertBefore($outercontrol, $pretag); + } + if($this->getPostTagTemplate()) + { + $posttag = Prado::createComponent('TCompositeControl'); + $this->getPostTagTemplate()->instantiateIn($posttag); + $outercontrol->getParent()->getControls()->insertAfter($outercontrol, $posttag); + } + return true; + } + public function renderPreTagText($writer) { + $writer->write($this->getPreTagText()); + } + public function renderPreContentsText($writer) { + $writer->write($this->getPreContentsText()); + } + public function renderPostContentsText($writer) { + $writer->write($this->getPostContentsText()); + } + public function renderPostTagText($writer) { + $writer->write($this->getPostTagText()); + } +} +class TWebControl extends TControl implements IStyleable +{ + private $_ensureid=false; + protected $_decorator; + public function setEnsureId($value) + { + $this->_ensureid |= TPropertyValue::ensureBoolean($value); + } + public function getEnsureId() + { + return $this->_ensureid; + } + public function getDecorator($create=true) + { + if($create && !$this->_decorator) + $this->_decorator = Prado::createComponent('TWebControlDecorator', $this); + return $this->_decorator; + } + public function copyBaseAttributes(TWebControl $control) + { + $this->setAccessKey($control->getAccessKey()); + $this->setToolTip($control->getToolTip()); + $this->setTabIndex($control->getTabIndex()); + if(!$control->getEnabled()) + $this->setEnabled(false); + if($control->getHasAttributes()) + $this->getAttributes()->copyFrom($control->getAttributes()); + } + public function getAccessKey() + { + return $this->getViewState('AccessKey',''); + } + public function setAccessKey($value) + { + if(strlen($value)>1) + throw new TInvalidDataValueException('webcontrol_accesskey_invalid',get_class($this),$value); + $this->setViewState('AccessKey',$value,''); + } + public function getBackColor() + { + if($style=$this->getViewState('Style',null)) + return $style->getBackColor(); + else + return ''; + } + public function setBackColor($value) + { + $this->getStyle()->setBackColor($value); + } + public function getBorderColor() + { + if($style=$this->getViewState('Style',null)) + return $style->getBorderColor(); + else + return ''; + } + public function setBorderColor($value) + { + $this->getStyle()->setBorderColor($value); + } + public function getBorderStyle() + { + if($style=$this->getViewState('Style',null)) + return $style->getBorderStyle(); + else + return ''; + } + public function setBorderStyle($value) + { + $this->getStyle()->setBorderStyle($value); + } + public function getBorderWidth() + { + if($style=$this->getViewState('Style',null)) + return $style->getBorderWidth(); + else + return ''; + } + public function setBorderWidth($value) + { + $this->getStyle()->setBorderWidth($value); + } + public function getFont() + { + return $this->getStyle()->getFont(); + } + public function getForeColor() + { + if($style=$this->getViewState('Style',null)) + return $style->getForeColor(); + else + return ''; + } + public function setForeColor($value) + { + $this->getStyle()->setForeColor($value); + } + public function getHeight() + { + if($style=$this->getViewState('Style',null)) + return $style->getHeight(); + else + return ''; + } + public function setDisplay($value) + { + $this->getStyle()->setDisplayStyle($value); + } + public function getDisplay() + { + return $this->getStyle()->getDisplayStyle(); + } + public function setCssClass($value) + { + $this->getStyle()->setCssClass($value); + } + public function getCssClass() + { + if($style=$this->getViewState('Style',null)) + return $style->getCssClass(); + else + return ''; + } + public function setHeight($value) + { + $this->getStyle()->setHeight($value); + } + public function getHasStyle() + { + return $this->getViewState('Style',null)!==null; + } + protected function createStyle() + { + return new TStyle; + } + public function getStyle() + { + if($style=$this->getViewState('Style',null)) + return $style; + else + { + $style=$this->createStyle(); + $this->setViewState('Style',$style,null); + return $style; + } + } + public function setStyle($value) + { + if(is_string($value)) + $this->getStyle()->setCustomStyle($value); + else + throw new TInvalidDataValueException('webcontrol_style_invalid',get_class($this)); + } + public function clearStyle() + { + $this->clearViewState('Style'); + } + public function getTabIndex() + { + return $this->getViewState('TabIndex',0); + } + public function setTabIndex($value) + { + $this->setViewState('TabIndex',TPropertyValue::ensureInteger($value),0); + } + protected function getTagName() + { + return 'span'; + } + public function getToolTip() + { + return $this->getViewState('ToolTip',''); + } + public function setToolTip($value) + { + $this->setViewState('ToolTip',$value,''); + } + public function getWidth() + { + if($style=$this->getViewState('Style',null)) + return $style->getWidth(); + else + return ''; + } + public function setWidth($value) + { + $this->getStyle()->setWidth($value); + } + public function onPreRender($param) { + if($decorator = $this->getDecorator(false)) + $decorator->instantiate(); + parent::onPreRender($param); + } + protected function addAttributesToRender($writer) + { + if($this->getID()!=='' || $this->getEnsureId()) + $writer->addAttribute('id',$this->getClientID()); + if(($accessKey=$this->getAccessKey())!=='') + $writer->addAttribute('accesskey',$accessKey); + if(!$this->getEnabled()) + $writer->addAttribute('disabled','disabled'); + if(($tabIndex=$this->getTabIndex())>0) + $writer->addAttribute('tabindex',"$tabIndex"); + if(($toolTip=$this->getToolTip())!=='') + $writer->addAttribute('title',$toolTip); + if($style=$this->getViewState('Style',null)) + $style->addAttributesToRender($writer); + if($this->getHasAttributes()) + { + foreach($this->getAttributes() as $name=>$value) + $writer->addAttribute($name,$value); + } + } + public function render($writer) + { + $this->renderBeginTag($writer); + $this->renderContents($writer); + $this->renderEndTag($writer); + } + public function renderBeginTag($writer) + { + if($decorator = $this->getDecorator(false)) { + $decorator->renderPreTagText($writer); + $this->addAttributesToRender($writer); + $writer->renderBeginTag($this->getTagName()); + $decorator->renderPreContentsText($writer); + } else { + $this->addAttributesToRender($writer); + $writer->renderBeginTag($this->getTagName()); + } + } + public function renderContents($writer) + { + parent::renderChildren($writer); + } + public function renderEndTag($writer) + { + if($decorator = $this->getDecorator(false)) { + $decorator->renderPostContentsText($writer); + $writer->renderEndTag(); + $decorator->renderPostTagText($writer); + } else + $writer->renderEndTag($writer); + } +} +class TCompositeControl extends TControl implements INamingContainer +{ + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + parent::initRecursive($namingContainer); + } +} +class TTemplateControl extends TCompositeControl +{ + const EXT_TEMPLATE='.tpl'; + private static $_template=array(); + private $_localTemplate=null; + private $_master=null; + private $_masterClass=''; + private $_contents=array(); + private $_placeholders=array(); + public function getTemplate() + { + if($this->_localTemplate===null) + { + $class=get_class($this); + if(!isset(self::$_template[$class])) + self::$_template[$class]=$this->loadTemplate(); + return self::$_template[$class]; + } + else + return $this->_localTemplate; + } + public function setTemplate($value) + { + $this->_localTemplate=$value; + } + public function getIsSourceTemplateControl() + { + if(($template=$this->getTemplate())!==null) + return $template->getIsSourceTemplate(); + else + return false; + } + public function getTemplateDirectory() + { + if(($template=$this->getTemplate())!==null) + return $template->getContextPath(); + else + return ''; + } + protected function loadTemplate() + { + $template=$this->getService()->getTemplateManager()->getTemplateByClassName(get_class($this)); + return $template; + } + public function createChildControls() + { + if($tpl=$this->getTemplate()) + { + foreach($tpl->getDirective() as $name=>$value) + { + if(is_string($value)) + $this->setSubProperty($name,$value); + else + throw new TConfigurationException('templatecontrol_directive_invalid',get_class($this),$name); + } + $tpl->instantiateIn($this); + } + } + public function registerContent($id,TContent $object) + { + if(isset($this->_contents[$id])) + throw new TConfigurationException('templatecontrol_contentid_duplicated',$id); + else + $this->_contents[$id]=$object; + } + public function registerContentPlaceHolder($id,TContentPlaceHolder $object) + { + if(isset($this->_placeholders[$id])) + throw new TConfigurationException('templatecontrol_placeholderid_duplicated',$id); + else + $this->_placeholders[$id]=$object; + } + public function getMasterClass() + { + return $this->_masterClass; + } + public function setMasterClass($value) + { + $this->_masterClass=$value; + } + public function getMaster() + { + return $this->_master; + } + public function injectContent($id,$content) + { + if(isset($this->_placeholders[$id])) + { + $placeholder=$this->_placeholders[$id]; + $controls=$placeholder->getParent()->getControls(); + $loc=$controls->remove($placeholder); + $controls->insertAt($loc,$content); + } + else + throw new TConfigurationException('templatecontrol_placeholder_inexistent',$id); + } + protected function initRecursive($namingContainer=null) + { + $this->ensureChildControls(); + if($this->_masterClass!=='') + { + $master=Prado::createComponent($this->_masterClass); + if(!($master instanceof TTemplateControl)) + throw new TInvalidDataValueException('templatecontrol_mastercontrol_invalid'); + $this->_master=$master; + $this->getControls()->clear(); + $this->getControls()->add($master); + $master->ensureChildControls(); + foreach($this->_contents as $id=>$content) + $master->injectContent($id,$content); + } + else if(!empty($this->_contents)) + throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this)); + parent::initRecursive($namingContainer); + } +} +class TForm extends TControl +{ + public function onInit($param) + { + parent::onInit($param); + $this->getPage()->setForm($this); + } + protected function addAttributesToRender($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $writer->addAttribute('method',$this->getMethod()); + $uri=$this->getRequest()->getRequestURI(); + $writer->addAttribute('action',str_replace('&','&',str_replace('&','&',$uri))); + if(($enctype=$this->getEnctype())!=='') + $writer->addAttribute('enctype',$enctype); + $attributes=$this->getAttributes(); + $attributes->remove('action'); + $writer->addAttributes($attributes); + if(($butt=$this->getDefaultButton())!=='') + { + if(($button=$this->findControl($butt))!==null) + $this->getPage()->getClientScript()->registerDefaultButton($this, $button); + else + throw new TInvalidDataValueException('form_defaultbutton_invalid',$butt); + } + } + public function render($writer) + { + $page=$this->getPage(); + $this->addAttributesToRender($writer); + $writer->renderBeginTag('form'); + $cs=$page->getClientScript(); + if($page->getClientSupportsJavaScript()) + { + $cs->renderHiddenFieldsBegin($writer); + $cs->renderScriptFilesBegin($writer); + $cs->renderBeginScripts($writer); + $page->beginFormRender($writer); + $this->renderChildren($writer); + $cs->renderHiddenFieldsEnd($writer); + $page->endFormRender($writer); + $cs->renderScriptFilesEnd($writer); + $cs->renderEndScripts($writer); + } + else + { + $cs->renderHiddenFieldsBegin($writer); + $page->beginFormRender($writer); + $this->renderChildren($writer); + $page->endFormRender($writer); + $cs->renderHiddenFieldsEnd($writer); + } + $writer->renderEndTag(); + } + public function getDefaultButton() + { + return $this->getViewState('DefaultButton',''); + } + public function setDefaultButton($value) + { + $this->setViewState('DefaultButton',$value,''); + } + public function getMethod() + { + return $this->getViewState('Method','post'); + } + public function setMethod($value) + { + $this->setViewState('Method',TPropertyValue::ensureEnum($value,'post','get'),'post'); + } + public function getEnctype() + { + return $this->getViewState('Enctype',''); + } + public function setEnctype($value) + { + $this->setViewState('Enctype',$value,''); + } + public function getName() + { + return $this->getUniqueID(); + } +} +class TClientScriptManager extends TApplicationComponent +{ + const SCRIPT_PATH='Web/Javascripts/source'; + const PACKAGES_FILE='Web/Javascripts/packages.php'; + private $_page; + private $_hiddenFields=array(); + private $_beginScripts=array(); + private $_endScripts=array(); + private $_scriptFiles=array(); + private $_headScriptFiles=array(); + private $_headScripts=array(); + private $_styleSheetFiles=array(); + private $_styleSheets=array(); + private $_registeredPradoScripts=array(); + private static $_pradoScripts; + private static $_pradoPackages; + private $_renderedHiddenFields; + private $_renderedScriptFiles=array(); + private $_expandedPradoScripts; + public function __construct(TPage $owner) + { + $this->_page=$owner; + } + public function getRequiresHead() + { + return count($this->_styleSheetFiles) || count($this->_styleSheets) + || count($this->_headScriptFiles) || count($this->_headScripts); + } + public static function getPradoPackages() + { + return self::$_pradoPackages; + } + public static function getPradoScripts() + { + return self::$_pradoScripts; + } + public function registerPradoScript($name) + { + $this->registerPradoScriptInternal($name); + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerPradoScript',$params); + } + protected function registerPradoScriptInternal($name) + { + if(!isset($this->_registeredPradoScripts[$name])) + { + if(self::$_pradoScripts === null) + { + $packageFile = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::PACKAGES_FILE; + list($packages,$deps)= include($packageFile); + self::$_pradoScripts = $deps; + self::$_pradoPackages = $packages; + } + if (isset(self::$_pradoScripts[$name])) + $this->_registeredPradoScripts[$name]=true; + else + throw new TInvalidOperationException('csmanager_pradoscript_invalid',$name); + if(($packages=array_keys($this->_registeredPradoScripts))!==array()) + { + $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; + list($path,$baseUrl)=$this->getPackagePathUrl($base); + $packagesUrl=array(); + $isDebug=$this->getApplication()->getMode()===TApplicationMode::Debug; + foreach ($packages as $p) + { + foreach (self::$_pradoScripts[$p] as $dep) + { + foreach (self::$_pradoPackages[$dep] as $script) + if (!isset($this->_expandedPradoScripts[$script])) + { + $this->_expandedPradoScripts[$script] = true; + if($isDebug) + { + if (!in_array($url=$baseUrl.'/'.$script,$packagesUrl)) + $packagesUrl[]=$url; + } else { + if (!in_array($url=$baseUrl.'/min/'.$script,$packagesUrl)) + { + if(!is_file($filePath=$path.'/min/'.$script)) + { + $dirPath=dirname($filePath); + if(!is_dir($dirPath)) + mkdir($dirPath, PRADO_CHMOD, true); + file_put_contents($filePath, TJavaScript::JSMin(file_get_contents($base.'/'.$script))); + chmod($filePath, PRADO_CHMOD); + } + $packagesUrl[]=$url; + } + } + } + } + } + foreach($packagesUrl as $url) + $this->registerScriptFile($url,$url); + } + } + } + public function getPradoScriptAssetUrl() + { + $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH; + $assets = Prado::getApplication()->getAssetManager(); + return $assets->getPublishedUrl($base); + } + public function getScriptUrls() + { + $scripts = array_values($this->_headScriptFiles); + $scripts = array_merge($scripts, array_values($this->_scriptFiles)); + $scripts = array_unique($scripts); + return $scripts; + } + protected function getPackagePathUrl($base) + { + $assets = Prado::getApplication()->getAssetManager(); + if(strpos($base, $assets->getBaseUrl())===false) + { + if(($dir = Prado::getPathOfNameSpace($base)) !== null) { + $base = $dir; + } + return array($assets->getPublishedPath($base), $assets->publishFilePath($base)); + } + else + { + return array($assets->getBasePath().str_replace($assets->getBaseUrl(),'',$base), $base); + } + } + public function getCallbackReference(ICallbackEventHandler $callbackHandler, $options=null) + { + $options = !is_array($options) ? array() : $options; + $class = new ReflectionClass($callbackHandler); + $clientSide = $callbackHandler->getActiveControl()->getClientSide(); + $options = array_merge($options, $clientSide->getOptions()->toArray()); + $optionString = TJavaScript::encode($options); + $this->registerPradoScriptInternal('ajax'); + $id = $callbackHandler->getUniqueID(); + return "new Prado.CallbackRequest('{$id}',{$optionString})"; + } + public function registerCallbackControl($class, $options) + { + $optionString=TJavaScript::encode($options); + $code="new {$class}({$optionString});"; + $this->_endScripts[sprintf('%08X', crc32($code))]=$code; + $this->registerPradoScriptInternal('ajax'); + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerCallbackControl',$params); + } + public function registerPostBackControl($class,$options) + { + if($class === null) { + return; + } + if(!isset($options['FormID']) && ($form=$this->_page->getForm())!==null) + $options['FormID']=$form->getClientID(); + $optionString=TJavaScript::encode($options); + $code="new {$class}({$optionString});"; + $this->_endScripts[sprintf('%08X', crc32($code))]=$code; + $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]=''; + $this->_hiddenFields[TPage::FIELD_POSTBACK_PARAMETER]=''; + $this->registerPradoScriptInternal('prado'); + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerPostBackControl',$params); + } + public function registerDefaultButton($panel, $button) + { + $panelID=is_string($panel)?$panel:$panel->getUniqueID(); + if(is_string($button)) + $buttonID=$button; + else + { + $button->setIsDefaultButton(true); + $buttonID=$button->getUniqueID(); + } + $options = TJavaScript::encode($this->getDefaultButtonOptions($panelID, $buttonID)); + $code = "new Prado.WebUI.DefaultButton($options);"; + $this->_endScripts['prado:'.$panelID]=$code; + $this->_hiddenFields[TPage::FIELD_POSTBACK_TARGET]=''; + $this->registerPradoScriptInternal('prado'); + $params=array($panelID,$buttonID); + $this->_page->registerCachingAction('Page.ClientScript','registerDefaultButton',$params); + } + protected function getDefaultButtonOptions($panelID, $buttonID) + { + $options['ID'] = TControl::convertUniqueIdToClientId($panelID); + $options['Panel'] = TControl::convertUniqueIdToClientId($panelID); + $options['Target'] = TControl::convertUniqueIdToClientId($buttonID); + $options['EventTarget'] = $buttonID; + $options['Event'] = 'click'; + return $options; + } + public function registerFocusControl($target) + { + $this->registerPradoScriptInternal('effects'); + if($target instanceof TControl) + $target=$target->getClientID(); + $id = TJavaScript::quoteString($target); + $this->_endScripts['prado:focus'] = 'new Effect.ScrollTo('.$id.'); Prado.Element.focus('.$id.');'; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerFocusControl',$params); + } + public function registerStyleSheetFile($key,$url,$media='') + { + if($media==='') + $this->_styleSheetFiles[$key]=$url; + else + $this->_styleSheetFiles[$key]=array($url,$media); + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheetFile',$params); + } + public function registerStyleSheet($key,$css,$media='') + { + $this->_styleSheets[$key]=$css; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerStyleSheet',$params); + } + public function getStyleSheetUrls() + { + $stylesheets = array_values( + array_map( + create_function('$e', 'return is_array($e) ? $e[0] : $e;'), + $this->_styleSheetFiles) + ); + foreach(Prado::getApplication()->getAssetManager()->getPublished() as $path=>$url) + if (substr($url,strlen($url)-4)=='.css') + $stylesheets[] = $url; + $stylesheets = array_unique($stylesheets); + return $stylesheets; + } + public function getStyleSheetCodes() + { + return array_unique(array_values($this->_styleSheets)); + } + public function registerHeadScriptFile($key,$url) + { + $this->checkIfNotInRender(); + $this->_headScriptFiles[$key]=$url; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerHeadScriptFile',$params); + } + public function registerHeadScript($key,$script) + { + $this->checkIfNotInRender(); + $this->_headScripts[$key]=$script; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerHeadScript',$params); + } + public function registerScriptFile($key, $url) + { + $this->_scriptFiles[$key]=$url; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerScriptFile',$params); + } + public function registerBeginScript($key,$script) + { + $this->checkIfNotInRender(); + $this->_beginScripts[$key]=$script; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerBeginScript',$params); + } + public function registerEndScript($key,$script) + { + $this->_endScripts[$key]=$script; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerEndScript',$params); + } + public function registerHiddenField($name,$value) + { + $this->_hiddenFields[$name]=$value; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','registerHiddenField',$params); + } + public function isStyleSheetFileRegistered($key) + { + return isset($this->_styleSheetFiles[$key]); + } + public function isStyleSheetRegistered($key) + { + return isset($this->_styleSheets[$key]); + } + public function isHeadScriptFileRegistered($key) + { + return isset($this->_headScriptFiles[$key]); + } + public function isHeadScriptRegistered($key) + { + return isset($this->_headScripts[$key]); + } + public function isScriptFileRegistered($key) + { + return isset($this->_scriptFiles[$key]); + } + public function isBeginScriptRegistered($key) + { + return isset($this->_beginScripts[$key]); + } + public function isEndScriptRegistered($key) + { + return isset($this->_endScripts[$key]); + } + public function hasEndScripts() + { + return count($this->_endScripts) > 0; + } + public function hasBeginScripts() + { + return count($this->_beginScripts) > 0; + } + public function isHiddenFieldRegistered($key) + { + return isset($this->_hiddenFields[$key]); + } + public function renderStyleSheetFiles($writer) + { + $str=''; + foreach($this->_styleSheetFiles as $url) + { + if(is_array($url)) + $str.="\n"; + else + $str.="\n"; + } + $writer->write($str); + } + public function renderStyleSheets($writer) + { + if(count($this->_styleSheets)) + $writer->write("\n"); + } + public function renderHeadScriptFiles($writer) + { + $this->renderScriptFiles($writer,$this->_headScriptFiles); + } + public function renderHeadScripts($writer) + { + $writer->write(TJavaScript::renderScriptBlocks($this->_headScripts)); + } + public function renderScriptFilesBegin($writer) + { + $this->renderAllPendingScriptFiles($writer); + } + public function renderScriptFilesEnd($writer) + { + $this->renderAllPendingScriptFiles($writer); + } + public function markScriptFileAsRendered($url) + { + $this->_renderedScriptFiles[$url] = $url; + $params=func_get_args(); + $this->_page->registerCachingAction('Page.ClientScript','markScriptFileAsRendered',$params); + } + protected function renderScriptFiles($writer, Array $scripts) + { + foreach($scripts as $script) + { + $writer->write(TJavaScript::renderScriptFile($script)); + $this->markScriptFileAsRendered($script); + } + } + protected function getRenderedScriptFiles() + { + return $this->_renderedScriptFiles; + } + public function renderAllPendingScriptFiles($writer) + { + if(!empty($this->_scriptFiles)) + { + $addedScripts = array_diff($this->_scriptFiles,$this->getRenderedScriptFiles()); + $this->renderScriptFiles($writer,$addedScripts); + } + } + public function renderBeginScripts($writer) + { + $writer->write(TJavaScript::renderScriptBlocks($this->_beginScripts)); + } + public function renderEndScripts($writer) + { + $writer->write(TJavaScript::renderScriptBlocks($this->_endScripts)); + } + public function renderHiddenFieldsBegin($writer) + { + $this->renderHiddenFieldsInt($writer,true); + } + public function renderHiddenFieldsEnd($writer) + { + $this->renderHiddenFieldsInt($writer,false); + } + public function flushScriptFiles($writer, $control=null) + { + $this->_page->ensureRenderInForm($control); + $this->renderAllPendingScriptFiles($writer); + } + protected function renderHiddenFieldsInt($writer, $initial) + { + if ($initial) $this->_renderedHiddenFields = array(); + $str=''; + foreach($this->_hiddenFields as $name=>$value) + { + if (in_array($name,$this->_renderedHiddenFields)) continue; + $id=strtr($name,':','_'); + if(is_array($value)) + { + foreach($value as $v) + $str.='\n"; + } + else + { + $str.='\n"; + } + $this->_renderedHiddenFields[] = $name; + } + if($str!=='') + $writer->write("
      \n".$str."
      \n"); + } + public function getHiddenFields() + { + return $this->_hiddenFields; + } + protected function checkIfNotInRender() + { + if ($form = $this->_page->InFormRender) + throw new Exception('Operation invalid when page is already rendering'); + } +} +abstract class TClientSideOptions extends TComponent +{ + private $_options; + protected function setFunction($name, $code) + { + if(!TJavaScript::isJsLiteral($code)) + $code = TJavaScript::quoteJsLiteral($this->ensureFunction($code)); + $this->setOption($name, $code); + } + protected function getOption($name) + { + if ($this->_options) + return $this->_options->itemAt($name); + else + return null; + } + protected function setOption($name, $value) + { + $this->getOptions()->add($name, $value); + } + public function getOptions() + { + if (!$this->_options) + $this->_options = Prado::createComponent('System.Collections.TMap'); + return $this->_options; + } + protected function ensureFunction($javascript) + { + return "function(sender, parameter){ {$javascript} }"; + } +} +class TPage extends TTemplateControl +{ + const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET'; + const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER'; + const FIELD_LASTFOCUS='PRADO_LASTFOCUS'; + const FIELD_PAGESTATE='PRADO_PAGESTATE'; + const FIELD_CALLBACK_TARGET='PRADO_CALLBACK_TARGET'; + const FIELD_CALLBACK_PARAMETER='PRADO_CALLBACK_PARAMETER'; + private static $_systemPostFields=array( + 'PRADO_POSTBACK_TARGET'=>true, + 'PRADO_POSTBACK_PARAMETER'=>true, + 'PRADO_LASTFOCUS'=>true, + 'PRADO_PAGESTATE'=>true, + 'PRADO_CALLBACK_TARGET'=>true, + 'PRADO_CALLBACK_PARAMETER'=>true + ); + private $_form; + private $_head; + private $_validators=array(); + private $_validated=false; + private $_theme; + private $_title; + private $_styleSheet; + private $_clientScript; + protected $_postData; + protected $_restPostData; + protected $_controlsPostDataChanged=array(); + protected $_controlsRequiringPostData=array(); + protected $_controlsRegisteredForPostData=array(); + private $_postBackEventTarget; + private $_postBackEventParameter; + protected $_formRendered=false; + protected $_inFormRender=false; + private $_focus; + private $_pagePath=''; + private $_enableStateValidation=true; + private $_enableStateEncryption=false; + private $_enableStateCompression=true; + private $_statePersisterClass='System.Web.UI.TPageStatePersister'; + private $_statePersister; + private $_cachingStack; + private $_clientState=''; + protected $_postDataLoaders=array(); + protected $_isLoadingPostData=false; + private $_enableJavaScript=true; + private $_writer; + public function __construct() + { + $this->setPage($this); + } + public function run($writer) + { + $this->_writer = $writer; + $this->determinePostBackMode(); + if($this->getIsPostBack()) + { + if($this->getIsCallback()) + $this->processCallbackRequest($writer); + else + $this->processPostBackRequest($writer); + } + else + $this->processNormalRequest($writer); + $this->_writer = null; + } + protected function processNormalRequest($writer) + { + $this->onPreInit(null); + $this->initRecursive(); + $this->onInitComplete(null); + $this->onPreLoad(null); + $this->loadRecursive(); + $this->onLoadComplete(null); + $this->preRenderRecursive(); + $this->onPreRenderComplete(null); + $this->savePageState(); + $this->onSaveStateComplete(null); + $this->renderControl($writer); + $this->unloadRecursive(); + } + protected function processPostBackRequest($writer) + { + $this->onPreInit(null); + $this->initRecursive(); + $this->onInitComplete(null); + $this->_restPostData=new TMap; + $this->loadPageState(); + $this->processPostData($this->_postData,true); + $this->onPreLoad(null); + $this->loadRecursive(); + $this->processPostData($this->_restPostData,false); + $this->raiseChangedEvents(); + $this->raisePostBackEvent(); + $this->onLoadComplete(null); + $this->preRenderRecursive(); + $this->onPreRenderComplete(null); + $this->savePageState(); + $this->onSaveStateComplete(null); + $this->renderControl($writer); + $this->unloadRecursive(); + } + protected static function decodeUTF8($data, $enc) + { + if(is_array($data)) + { + foreach($data as $k=>$v) + $data[$k]=self::decodeUTF8($v, $enc); + return $data; + } elseif(is_string($data)) { + return iconv('UTF-8',$enc.'//IGNORE',$data); + } else { + return $data; + } + } + protected function processCallbackRequest($writer) + { + Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); + $this->setAdapter(new TActivePageAdapter($this)); + if (($g=$this->getApplication()->getGlobalization(false))!==null && + strtoupper($enc=$g->getCharset())!='UTF-8') + foreach ($this->_postData as $k=>$v) + $this->_postData[$k]=self::decodeUTF8($v, $enc); + $this->onPreInit(null); + $this->initRecursive(); + $this->onInitComplete(null); + $this->_restPostData=new TMap; + $this->loadPageState(); + $this->processPostData($this->_postData,true); + $this->onPreLoad(null); + $this->loadRecursive(); + $this->processPostData($this->_restPostData,false); + $this->raiseChangedEvents(); + $this->getAdapter()->processCallbackEvent($writer); + $this->onLoadComplete(null); + $this->preRenderRecursive(); + $this->onPreRenderComplete(null); + $this->savePageState(); + $this->onSaveStateComplete(null); + $this->getAdapter()->renderCallbackResponse($writer); + $this->unloadRecursive(); + } + public function getCallbackClient() + { + if($this->getAdapter() !== null) + return $this->getAdapter()->getCallbackClientHandler(); + else + return new TCallbackClientScript(); + } + public function setCallbackClient($client) + { + $this->getAdapter()->setCallbackClientHandler($client); + } + public function getCallbackEventTarget() + { + return $this->getAdapter()->getCallbackEventTarget(); + } + public function setCallbackEventTarget(TControl $control) + { + $this->getAdapter()->setCallbackEventTarget($control); + } + public function getCallbackEventParameter() + { + return $this->getAdapter()->getCallbackEventParameter(); + } + public function setCallbackEventParameter($value) + { + $this->getAdapter()->setCallbackEventParameter($value); + } + public function registerPostDataLoader($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_postDataLoaders[$id] = true; + } + public function getPostDataLoaders() + { + return array_keys($this->_postDataLoaders); + } + public function getForm() + { + return $this->_form; + } + public function setForm(TForm $form) + { + if($this->_form===null) + $this->_form=$form; + else + throw new TInvalidOperationException('page_form_duplicated'); + } + public function getValidators($validationGroup=null) + { + if(!$this->_validators) + $this->_validators=new TList; + if(empty($validationGroup) === true) + return $this->_validators; + else + { + $list=new TList; + foreach($this->_validators as $validator) + if($validator->getValidationGroup()===$validationGroup) + $list->add($validator); + return $list; + } + } + public function validate($validationGroup=null) + { + $this->_validated=true; + if($this->_validators && $this->_validators->getCount()) + { + if($validationGroup===null) + { + foreach($this->_validators as $validator) + $validator->validate(); + } + else + { + foreach($this->_validators as $validator) + { + if($validator->getValidationGroup()===$validationGroup) + $validator->validate(); + } + } + } + } + public function getIsValid() + { + if($this->_validated) + { + if($this->_validators && $this->_validators->getCount()) + { + foreach($this->_validators as $validator) + if(!$validator->getIsValid()) + return false; + } + return true; + } + else + throw new TInvalidOperationException('page_isvalid_unknown'); + } + public function getTheme() + { + if(is_string($this->_theme)) + $this->_theme=$this->getService()->getThemeManager()->getTheme($this->_theme); + return $this->_theme; + } + public function setTheme($value) + { + $this->_theme=empty($value)?null:$value; + } + public function getStyleSheetTheme() + { + if(is_string($this->_styleSheet)) + $this->_styleSheet=$this->getService()->getThemeManager()->getTheme($this->_styleSheet); + return $this->_styleSheet; + } + public function setStyleSheetTheme($value) + { + $this->_styleSheet=empty($value)?null:$value; + } + public function applyControlSkin($control) + { + if(($theme=$this->getTheme())!==null) + $theme->applySkin($control); + } + public function applyControlStyleSheet($control) + { + if(($theme=$this->getStyleSheetTheme())!==null) + $theme->applySkin($control); + } + public function getClientScript() + { + if(!$this->_clientScript) { + $className = $classPath = $this->getService()->getClientScriptManagerClass(); + Prado::using($className); + if(($pos=strrpos($className,'.'))!==false) + $className=substr($className,$pos+1); + if(!class_exists($className,false) || ($className!=='TClientScriptManager' && !is_subclass_of($className,'TClientScriptManager'))) + throw new THttpException(404,'page_csmanagerclass_invalid',$classPath); + $this->_clientScript=new $className($this); + } + return $this->_clientScript; + } + public function onPreInit($param) + { + $this->raiseEvent('OnPreInit',$this,$param); + } + public function onInitComplete($param) + { + $this->raiseEvent('OnInitComplete',$this,$param); + } + public function onPreLoad($param) + { + $this->raiseEvent('OnPreLoad',$this,$param); + } + public function onLoadComplete($param) + { + $this->raiseEvent('OnLoadComplete',$this,$param); + } + public function onPreRenderComplete($param) + { + $this->raiseEvent('OnPreRenderComplete',$this,$param); + $cs=$this->getClientScript(); + $theme=$this->getTheme(); + if($theme instanceof ITheme) + { + foreach($theme->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($theme->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + $styleSheet=$this->getStyleSheetTheme(); + if($styleSheet instanceof ITheme) + { + foreach($styleSheet->getStyleSheetFiles() as $url) + $cs->registerStyleSheetFile($url,$url,$this->getCssMediaType($url)); + foreach($styleSheet->getJavaScriptFiles() as $url) + $cs->registerHeadScriptFile($url,$url); + } + if($cs->getRequiresHead() && $this->getHead()===null) + throw new TConfigurationException('page_head_required'); + } + private function getCssMediaType($url) + { + $segs=explode('.',basename($url)); + if(isset($segs[2])) + return $segs[count($segs)-2]; + else + return ''; + } + public function onSaveStateComplete($param) + { + $this->raiseEvent('OnSaveStateComplete',$this,$param); + } + private function determinePostBackMode() + { + $postData=$this->getRequest(); + if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET)) + $this->_postData=$postData; + } + public function getIsPostBack() + { + return $this->_postData!==null; + } + public function getIsCallback() + { + return $this->getIsPostBack() && $this->getRequest()->contains(self::FIELD_CALLBACK_TARGET); + } + public function saveState() + { + parent::saveState(); + $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostData,array()); + } + public function loadState() + { + parent::loadState(); + $this->_controlsRequiringPostData=$this->getViewState('ControlsRequiringPostBack',array()); + } + protected function loadPageState() + { + $state=$this->getStatePersister()->load(); + $this->loadStateRecursive($state,$this->getEnableViewState()); + } + protected function savePageState() + { + $state=&$this->saveStateRecursive($this->getEnableViewState()); + $this->getStatePersister()->save($state); + } + protected function isSystemPostField($field) + { + return isset(self::$_systemPostFields[$field]); + } + public function registerRequiresPostData($control) + { + $id=is_string($control)?$control:$control->getUniqueID(); + $this->_controlsRegisteredForPostData[$id]=true; + $this->registerPostDataLoader($id); + $params=func_get_args(); + foreach($this->getCachingStack() as $item) + $item->registerAction('Page','registerRequiresPostData',array($id)); + } + public function getPostBackEventTarget() + { + if($this->_postBackEventTarget===null && $this->_postData!==null) + { + $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET); + if(!empty($eventTarget)) + $this->_postBackEventTarget=$this->findControl($eventTarget); + } + return $this->_postBackEventTarget; + } + public function setPostBackEventTarget(TControl $control) + { + $this->_postBackEventTarget=$control; + } + public function getPostBackEventParameter() + { + if($this->_postBackEventParameter===null && $this->_postData!==null) + { + if(($this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER))===null) + $this->_postBackEventParameter=''; + } + return $this->_postBackEventParameter; + } + public function setPostBackEventParameter($value) + { + $this->_postBackEventParameter=$value; + } + protected function processPostData($postData,$beforeLoad) + { + $this->_isLoadingPostData=true; + if($beforeLoad) + $this->_restPostData=new TMap; + foreach($postData as $key=>$value) + { + if($this->isSystemPostField($key)) + continue; + else if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$postData)) + $this->_controlsPostDataChanged[]=$control; + } + else if($control instanceof IPostBackEventHandler && + empty($this->_postData[self::FIELD_POSTBACK_TARGET])) + { + $this->_postData->add(self::FIELD_POSTBACK_TARGET,$key); } + unset($this->_controlsRequiringPostData[$key]); + } + else if($beforeLoad) + $this->_restPostData->add($key,$value); + } + foreach($this->_controlsRequiringPostData as $key=>$value) + { + if($control=$this->findControl($key)) + { + if($control instanceof IPostBackDataHandler) + { + if($control->loadPostData($key,$this->_postData)) + $this->_controlsPostDataChanged[]=$control; + } + else + throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key); + unset($this->_controlsRequiringPostData[$key]); + } + } + $this->_isLoadingPostData=false; + } + public function getIsLoadingPostData() + { + return $this->_isLoadingPostData; + } + protected function raiseChangedEvents() + { + foreach($this->_controlsPostDataChanged as $control) + $control->raisePostDataChangedEvent(); + } + protected function raisePostBackEvent() + { + if(($postBackHandler=$this->getPostBackEventTarget())===null) + $this->validate(); + else if($postBackHandler instanceof IPostBackEventHandler) + $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter()); + } + public function getInFormRender() + { + return $this->_inFormRender; + } + public function ensureRenderInForm($control) + { + if(!$this->getIsCallback() && !$this->_inFormRender) + throw new TConfigurationException('page_control_outofform',get_class($control), $control ? $control->getUniqueID() : null); + } + public function beginFormRender($writer) + { + if($this->_formRendered) + throw new TConfigurationException('page_form_duplicated'); + $this->_formRendered=true; + $this->getClientScript()->registerHiddenField(self::FIELD_PAGESTATE,$this->getClientState()); + $this->_inFormRender=true; + } + public function endFormRender($writer) + { + if($this->_focus) + { + if(($this->_focus instanceof TControl) && $this->_focus->getVisible(true)) + $focus=$this->_focus->getClientID(); + else + $focus=$this->_focus; + $this->getClientScript()->registerFocusControl($focus); + } + else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null) + $this->getClientScript()->registerFocusControl($lastFocus); + $this->_inFormRender=false; + } + public function setFocus($value) + { + $this->_focus=$value; + } + public function getClientSupportsJavaScript() + { + return $this->_enableJavaScript; + } + public function setClientSupportsJavaScript($value) + { + $this->_enableJavaScript=TPropertyValue::ensureBoolean($value); + } + public function getHead() + { + return $this->_head; + } + public function setHead(THead $value) + { + if($this->_head) + throw new TInvalidOperationException('page_head_duplicated'); + $this->_head=$value; + if($this->_title!==null) + { + $this->_head->setTitle($this->_title); + $this->_title=null; + } + } + public function getTitle() + { + if($this->_head) + return $this->_head->getTitle(); + else + return $this->_title===null ? '' : $this->_title; + } + public function setTitle($value) + { + if($this->_head) + $this->_head->setTitle($value); + else + $this->_title=$value; + } + public function getClientState() + { + return $this->_clientState; + } + public function setClientState($state) + { + $this->_clientState=$state; + } + public function getRequestClientState() + { + return $this->getRequest()->itemAt(self::FIELD_PAGESTATE); + } + public function getStatePersisterClass() + { + return $this->_statePersisterClass; + } + public function setStatePersisterClass($value) + { + $this->_statePersisterClass=$value; + } + public function getStatePersister() + { + if($this->_statePersister===null) + { + $this->_statePersister=Prado::createComponent($this->_statePersisterClass); + if(!($this->_statePersister instanceof IPageStatePersister)) + throw new TInvalidDataTypeException('page_statepersister_invalid'); + $this->_statePersister->setPage($this); + } + return $this->_statePersister; + } + public function getEnableStateValidation() + { + return $this->_enableStateValidation; + } + public function setEnableStateValidation($value) + { + $this->_enableStateValidation=TPropertyValue::ensureBoolean($value); + } + public function getEnableStateEncryption() + { + return $this->_enableStateEncryption; + } + public function setEnableStateEncryption($value) + { + $this->_enableStateEncryption=TPropertyValue::ensureBoolean($value); + } + public function getEnableStateCompression() + { + return $this->_enableStateCompression; + } + public function setEnableStateCompression($value) + { + $this->_enableStateCompression=TPropertyValue::ensureBoolean($value); + } + public function getPagePath() + { + return $this->_pagePath; + } + public function setPagePath($value) + { + $this->_pagePath=$value; + } + public function registerCachingAction($context,$funcName,$funcParams) + { + if($this->_cachingStack) + { + foreach($this->_cachingStack as $cache) + $cache->registerAction($context,$funcName,$funcParams); + } + } + public function getCachingStack() + { + if(!$this->_cachingStack) + $this->_cachingStack=new TStack; + return $this->_cachingStack; + } + public function flushWriter() + { + if ($this->_writer) + $this->Response->write($this->_writer->flush()); + } +} +interface IPageStatePersister +{ + public function getPage(); + public function setPage(TPage $page); + public function save($state); + public function load(); +} +class TPageStateFormatter +{ + public static function serialize($page,$data) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateValidation()) + $str=$sm->hashData(Prado::serialize($data)); + else + $str=Prado::serialize($data); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=gzcompress($str); + if($page->getEnableStateEncryption()) + $str=$sm->encrypt($str); + return base64_encode($str); + } + public static function unserialize($page,$data) + { + $str=base64_decode($data); + if($str==='') + return null; + if($str!==false) + { + $sm=$page->getApplication()->getSecurityManager(); + if($page->getEnableStateEncryption()) + $str=$sm->decrypt($str); + if($page->getEnableStateCompression() && extension_loaded('zlib')) + $str=@gzuncompress($str); + if($page->getEnableStateValidation()) + { + if(($str=$sm->validateData($str))!==false) + return Prado::unserialize($str); + } + else + return Prado::unserialize($str); + } + return null; + } +} +class TOutputCache extends TControl implements INamingContainer +{ + const CACHE_ID_PREFIX='prado:outputcache'; + private $_cacheModuleID=''; + private $_dataCached=false; + private $_cacheAvailable=false; + private $_cacheChecked=false; + private $_cacheKey=null; + private $_duration=60; + private $_cache=null; + private $_contents; + private $_state; + private $_actions=array(); + private $_varyByParam=''; + private $_keyPrefix=''; + private $_varyBySession=false; + private $_cachePostBack=false; + private $_cacheTime=0; + public function getAllowChildControls() + { + $this->determineCacheability(); + return !$this->_dataCached; + } + private function determineCacheability() + { + if(!$this->_cacheChecked) + { + $this->_cacheChecked=true; + if($this->_duration>0 && ($this->_cachePostBack || !$this->getPage()->getIsPostBack())) + { + if($this->_cacheModuleID!=='') + { + $this->_cache=$this->getApplication()->getModule($this->_cacheModuleID); + if(!($this->_cache instanceof ICache)) + throw new TConfigurationException('outputcache_cachemoduleid_invalid',$this->_cacheModuleID); + } + else + $this->_cache=$this->getApplication()->getCache(); + if($this->_cache!==null) + { + $this->_cacheAvailable=true; + $data=$this->_cache->get($this->getCacheKey()); + if(is_array($data)) + { + $param=new TOutputCacheCheckDependencyEventParameter; + $param->setCacheTime(isset($data[3])?$data[3]:0); + $this->onCheckDependency($param); + $this->_dataCached=$param->getIsValid(); + } + else + $this->_dataCached=false; + if($this->_dataCached) + list($this->_contents,$this->_state,$this->_actions,$this->_cacheTime)=$data; + } + } + } + } + protected function initRecursive($namingContainer=null) + { + if($this->_cacheAvailable && !$this->_dataCached) + { + $stack=$this->getPage()->getCachingStack(); + $stack->push($this); + parent::initRecursive($namingContainer); + $stack->pop(); + } + else + parent::initRecursive($namingContainer); + } + protected function loadRecursive() + { + if($this->_cacheAvailable && !$this->_dataCached) + { + $stack=$this->getPage()->getCachingStack(); + $stack->push($this); + parent::loadRecursive(); + $stack->pop(); + } + else + { + if($this->_dataCached) + $this->performActions(); + parent::loadRecursive(); + } + } + private function performActions() + { + $page=$this->getPage(); + $cs=$page->getClientScript(); + foreach($this->_actions as $action) + { + if($action[0]==='Page.ClientScript') + call_user_func_array(array($cs,$action[1]),$action[2]); + else if($action[0]==='Page') + call_user_func_array(array($page,$action[1]),$action[2]); + else + call_user_func_array(array($this->getSubProperty($action[0]),$action[1]),$action[2]); + } + } + protected function preRenderRecursive() + { + if($this->_cacheAvailable && !$this->_dataCached) + { + $stack=$this->getPage()->getCachingStack(); + $stack->push($this); + parent::preRenderRecursive(); + $stack->pop(); + } + else + parent::preRenderRecursive(); + } + protected function loadStateRecursive(&$state,$needViewState=true) + { + $st=unserialize($state); + parent::loadStateRecursive($st,$needViewState); + } + protected function &saveStateRecursive($needViewState=true) + { + if($this->_dataCached) + return $this->_state; + else + { + $st=parent::saveStateRecursive($needViewState); + $this->_state=serialize($st); + return $this->_state; + } + } + public function registerAction($context,$funcName,$funcParams) + { + $this->_actions[]=array($context,$funcName,$funcParams); + } + public function getCacheKey() + { + if($this->_cacheKey===null) + $this->_cacheKey=$this->calculateCacheKey(); + return $this->_cacheKey; + } + protected function calculateCacheKey() + { + $key=$this->getBaseCacheKey(); + if($this->_varyBySession) + $key.=$this->getSession()->getSessionID(); + if($this->_varyByParam!=='') + { + $params=array(); + $request=$this->getRequest(); + foreach(explode(',',$this->_varyByParam) as $name) + { + $name=trim($name); + $params[$name]=$request->itemAt($name); + } + $key.=serialize($params); + } + $param=new TOutputCacheCalculateKeyEventParameter; + $this->onCalculateKey($param); + $key.=$param->getCacheKey(); + return $key; + } + protected function getBaseCacheKey() + { + return self::CACHE_ID_PREFIX.$this->_keyPrefix.$this->getPage()->getPagePath().$this->getUniqueID(); + } + public function getCacheModuleID() + { + return $this->_cacheModuleID; + } + public function setCacheModuleID($value) + { + $this->_cacheModuleID=$value; + } + public function setCacheKeyPrefix($value) + { + $this->_keyPrefix=$value; + } + public function getCacheTime() + { + return $this->_cacheTime; + } + protected function getCacheDependency() + { + return null; + } + public function getContentCached() + { + return $this->_dataCached; + } + public function getDuration() + { + return $this->_duration; + } + public function setDuration($value) + { + if(($value=TPropertyValue::ensureInteger($value))<0) + throw new TInvalidDataValueException('outputcache_duration_invalid',get_class($this)); + $this->_duration=$value; + } + public function getVaryByParam() + { + return $this->_varyByParam; + } + public function setVaryByParam($value) + { + $this->_varyByParam=trim($value); + } + public function getVaryBySession() + { + return $this->_varyBySession; + } + public function setVaryBySession($value) + { + $this->_varyBySession=TPropertyValue::ensureBoolean($value); + } + public function getCachingPostBack() + { + return $this->_cachePostBack; + } + public function setCachingPostBack($value) + { + $this->_cachePostBack=TPropertyValue::ensureBoolean($value); + } + public function onCheckDependency($param) + { + $this->raiseEvent('OnCheckDependency',$this,$param); + } + public function onCalculateKey($param) + { + $this->raiseEvent('OnCalculateKey',$this,$param); + } + public function render($writer) + { + if($this->_dataCached) + $writer->write($this->_contents); + else if($this->_cacheAvailable) + { + $textwriter = new TTextWriter(); + $multiwriter = new TOutputCacheTextWriterMulti(array($writer->getWriter(),$textwriter)); + $htmlWriter = Prado::createComponent($this->GetResponse()->getHtmlWriterType(), $multiwriter); + $stack=$this->getPage()->getCachingStack(); + $stack->push($this); + parent::render($htmlWriter); + $stack->pop(); + $content=$textwriter->flush(); + $data=array($content,$this->_state,$this->_actions,time()); + $this->_cache->set($this->getCacheKey(),$data,$this->getDuration(),$this->getCacheDependency()); + } + else + parent::render($writer); + } +} +class TOutputCacheCheckDependencyEventParameter extends TEventParameter +{ + private $_isValid=true; + private $_cacheTime=0; + public function getIsValid() + { + return $this->_isValid; + } + public function setIsValid($value) + { + $this->_isValid=TPropertyValue::ensureBoolean($value); + } + public function getCacheTime() + { + return $this->_cacheTime; + } + public function setCacheTime($value) + { + $this->_cacheTime=TPropertyValue::ensureInteger($value); + } +} +class TOutputCacheCalculateKeyEventParameter extends TEventParameter +{ + private $_cacheKey=''; + public function getCacheKey() + { + return $this->_cacheKey; + } + public function setCacheKey($value) + { + $this->_cacheKey=TPropertyValue::ensureString($value); + } +} +class TOutputCacheTextWriterMulti extends TTextWriter +{ + protected $_writers; + public function __construct(Array $writers) + { + $this->_writers = $writers; + } + public function write($s) + { + foreach($this->_writers as $writer) + $writer->write($s); + } + public function flush() + { + foreach($this->_writers as $writer) + $s = $writer->flush(); + return $s; + } +} +class TTemplateManager extends TModule +{ + const TEMPLATE_FILE_EXT='.tpl'; + const TEMPLATE_CACHE_PREFIX='prado:template:'; + public function init($config) + { + $this->getService()->setTemplateManager($this); + } + public function getTemplateByClassName($className) + { + $class=new ReflectionClass($className); + $tplFile=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$className.self::TEMPLATE_FILE_EXT; + return $this->getTemplateByFileName($tplFile); + } + public function getTemplateByFileName($fileName) + { + if(($fileName=$this->getLocalizedTemplate($fileName))!==null) + { + if(($cache=$this->getApplication()->getCache())===null) + return new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); + else + { + $array=$cache->get(self::TEMPLATE_CACHE_PREFIX.$fileName); + if(is_array($array)) + { + list($template,$timestamps)=$array; + if($this->getApplication()->getMode()===TApplicationMode::Performance) + return $template; + $cacheValid=true; + foreach($timestamps as $tplFile=>$timestamp) + { + if(!is_file($tplFile) || filemtime($tplFile)>$timestamp) + { + $cacheValid=false; + break; + } + } + if($cacheValid) + return $template; + } + $template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName); + $includedFiles=$template->getIncludedFiles(); + $timestamps=array(); + $timestamps[$fileName]=filemtime($fileName); + foreach($includedFiles as $includedFile) + $timestamps[$includedFile]=filemtime($includedFile); + $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,$timestamps)); + return $template; + } + } + else + return null; + } + protected function getLocalizedTemplate($filename) + { + if(($app=$this->getApplication()->getGlobalization(false))===null) + return is_file($filename)?$filename:null; + foreach($app->getLocalizedResource($filename) as $file) + { + if(($file=realpath($file))!==false && is_file($file)) + return $file; + } + return null; + } +} +class TTemplate extends TApplicationComponent implements ITemplate +{ + const REGEX_RULES='/||<\/?com:([\w\.]+)((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?"|\s*[\w\.]+\s*=\s*<%.*?%>)*)\s*\/?>|<\/?prop:([\w\.]+)\s*>|<%@\s*((?:\s*[\w\.]+\s*=\s*\'.*?\'|\s*[\w\.]+\s*=\s*".*?")*)\s*%>|<%[%#~\/\\$=\\[](.*?)%>|)*)\s*\/>/msS'; + const CONFIG_DATABIND=0; + const CONFIG_EXPRESSION=1; + const CONFIG_ASSET=2; + const CONFIG_PARAMETER=3; + const CONFIG_LOCALIZATION=4; + const CONFIG_TEMPLATE=5; + private $_tpl=array(); + private $_directive=array(); + private $_contextPath; + private $_tplFile=null; + private $_startingLine=0; + private $_content; + private $_sourceTemplate=true; + private $_hashCode=''; + private $_tplControl=null; + private $_includedFiles=array(); + private $_includeAtLine=array(); + private $_includeLines=array(); + public function __construct($template,$contextPath,$tplFile=null,$startingLine=0,$sourceTemplate=true) + { + $this->_sourceTemplate=$sourceTemplate; + $this->_contextPath=$contextPath; + $this->_tplFile=$tplFile; + $this->_startingLine=$startingLine; + $this->_content=$template; + $this->_hashCode=md5($template); + $this->parse($template); + $this->_content=null; } + public function getTemplateFile() + { + return $this->_tplFile; + } + public function getIsSourceTemplate() + { + return $this->_sourceTemplate; + } + public function getContextPath() + { + return $this->_contextPath; + } + public function getDirective() + { + return $this->_directive; + } + public function getHashCode() + { + return $this->_hashCode; + } + public function &getItems() + { + return $this->_tpl; + } + public function instantiateIn($tplControl,$parentControl=null) + { + $this->_tplControl=$tplControl; + if($parentControl===null) + $parentControl=$tplControl; + if(($page=$tplControl->getPage())===null) + $page=$this->getService()->getRequestedPage(); + $controls=array(); + $directChildren=array(); + foreach($this->_tpl as $key=>$object) + { + if($object[0]===-1) + $parent=$parentControl; + else if(isset($controls[$object[0]])) + $parent=$controls[$object[0]]; + else + continue; + if(isset($object[2])) { + $component=Prado::createComponent($object[1]); + $properties=&$object[2]; + if($component instanceof TControl) + { + if($component instanceof TOutputCache) + $component->setCacheKeyPrefix($this->_hashCode.$key); + $component->setTemplateControl($tplControl); + if(isset($properties['id'])) + { + if(is_array($properties['id'])) + $properties['id']=$component->evaluateExpression($properties['id'][1]); + $tplControl->registerObject($properties['id'],$component); + } + if(isset($properties['skinid'])) + { + if(is_array($properties['skinid'])) + $component->setSkinID($component->evaluateExpression($properties['skinid'][1])); + else + $component->setSkinID($properties['skinid']); + unset($properties['skinid']); + } + $component->trackViewState(false); + $component->applyStyleSheetSkin($page); + foreach($properties as $name=>$value) + $this->configureControl($component,$name,$value); + $component->trackViewState(true); + if($parent===$parentControl) + $directChildren[]=$component; + else + $component->createdOnTemplate($parent); + if($component->getAllowChildControls()) + $controls[$key]=$component; + } + else if($component instanceof TComponent) + { + $controls[$key]=$component; + if(isset($properties['id'])) + { + if(is_array($properties['id'])) + $properties['id']=$component->evaluateExpression($properties['id'][1]); + $tplControl->registerObject($properties['id'],$component); + if(!$component->hasProperty('id')) + unset($properties['id']); + } + foreach($properties as $name=>$value) + $this->configureComponent($component,$name,$value); + if($parent===$parentControl) + $directChildren[]=$component; + else + $component->createdOnTemplate($parent); + } + } + else + { + if($object[1] instanceof TCompositeLiteral) + { + $o=clone $object[1]; + $o->setContainer($tplControl); + if($parent===$parentControl) + $directChildren[]=$o; + else + $parent->addParsedObject($o); + } + else + { + if($parent===$parentControl) + $directChildren[]=$object[1]; + else + $parent->addParsedObject($object[1]); + } + } + } + foreach($directChildren as $control) + { + if($control instanceof TComponent) + $control->createdOnTemplate($parentControl); + else + $parentControl->addParsedObject($control); + } + } + protected function configureControl($control,$name,$value) + { + if(strncasecmp($name,'on',2)===0) $this->configureEvent($control,$name,$value,$control); + else if(($pos=strrpos($name,'.'))===false) $this->configureProperty($control,$name,$value); + else $this->configureSubProperty($control,$name,$value); + } + protected function configureComponent($component,$name,$value) + { + if(strpos($name,'.')===false) $this->configureProperty($component,$name,$value); + else $this->configureSubProperty($component,$name,$value); + } + protected function configureEvent($control,$name,$value,$contextControl) + { + if(strpos($value,'.')===false) + $control->attachEventHandler($name,array($contextControl,'TemplateControl.'.$value)); + else + $control->attachEventHandler($name,array($contextControl,$value)); + } + protected function configureProperty($component,$name,$value) + { + if(is_array($value)) + { + switch($value[0]) + { + case self::CONFIG_DATABIND: + $component->bindProperty($name,$value[1]); + break; + case self::CONFIG_EXPRESSION: + if($component instanceof TControl) + $component->autoBindProperty($name,$value[1]); + else + { + $setter='set'.$name; + $component->$setter($this->_tplControl->evaluateExpression($value[1])); + } + break; + case self::CONFIG_TEMPLATE: + $setter='set'.$name; + $component->$setter($value[1]); + break; + case self::CONFIG_ASSET: $setter='set'.$name; + $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); + $component->$setter($url); + break; + case self::CONFIG_PARAMETER: $setter='set'.$name; + $component->$setter($this->getApplication()->getParameters()->itemAt($value[1])); + break; + case self::CONFIG_LOCALIZATION: + $setter='set'.$name; + $component->$setter(Prado::localize($value[1])); + break; + default: throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); + break; + } + } + else + { + if (substr($name,0,2)=='js') + if ($value and !($value instanceof TJavaScriptLiteral)) + $value = new TJavaScriptLiteral($value); + $setter='set'.$name; + $component->$setter($value); + } + } + protected function configureSubProperty($component,$name,$value) + { + if(is_array($value)) + { + switch($value[0]) + { + case self::CONFIG_DATABIND: $component->bindProperty($name,$value[1]); + break; + case self::CONFIG_EXPRESSION: if($component instanceof TControl) + $component->autoBindProperty($name,$value[1]); + else + $component->setSubProperty($name,$this->_tplControl->evaluateExpression($value[1])); + break; + case self::CONFIG_TEMPLATE: + $component->setSubProperty($name,$value[1]); + break; + case self::CONFIG_ASSET: $url=$this->publishFilePath($this->_contextPath.DIRECTORY_SEPARATOR.$value[1]); + $component->setSubProperty($name,$url); + break; + case self::CONFIG_PARAMETER: $component->setSubProperty($name,$this->getApplication()->getParameters()->itemAt($value[1])); + break; + case self::CONFIG_LOCALIZATION: + $component->setSubProperty($name,Prado::localize($value[1])); + break; + default: throw new TConfigurationException('template_tag_unexpected',$name,$value[1]); + break; + } + } + else + $component->setSubProperty($name,$value); + } + protected function parse($input) + { + $input=$this->preprocess($input); + $tpl=&$this->_tpl; + $n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + $expectPropEnd=false; + $textStart=0; + $stack=array(); + $container=-1; + $matchEnd=0; + $c=0; + $this->_directive=null; + try + { + for($i=0;$i<$n;++$i) + { + $match=&$matches[$i]; + $str=$match[0][0]; + $matchStart=$match[0][1]; + $matchEnd=$matchStart+strlen($str)-1; + if(strpos($str,'$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $type=$match[1][0]; + $attributes=$this->parseAttributes($match[2][0],$match[2][1]); + $this->validateAttributes($type,$attributes); + $tpl[$c++]=array($container,$type,$attributes); + if($str[strlen($str)-2]!=='/') { + $stack[] = $type; + $container=$c-1; + } + } + else if(strpos($str,'$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $type=$match[1][0]; + if(empty($stack)) + throw new TConfigurationException('template_closingtag_unexpected',""); + $name=array_pop($stack); + if($name!==$type) + { + $tag=$name[0]==='@' ? '' : ""; + throw new TConfigurationException('template_closingtag_expected',$tag); + } + $container=$tpl[$container][0]; + } + else if(strpos($str,'<%@')===0) { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + if(isset($tpl[0]) || $this->_directive!==null) + throw new TConfigurationException('template_directive_nonunique'); + $this->_directive=$this->parseAttributes($match[4][0],$match[4][1]); + } + else if(strpos($str,'<%')===0) { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $literal=trim($match[5][0]); + if($str[2]==='=') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,$literal)); + else if($str[2]==='%') $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_STATEMENTS,$literal)); + else if($str[2]==='#') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_DATABINDING,$literal)); + else if($str[2]==='$') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->getApplication()->getParameters()->itemAt('$literal')")); + else if($str[2]==='~') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"\$this->publishFilePath('$this->_contextPath/$literal')")); + else if($str[2]==='/') + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"rtrim(dirname(\$this->getApplication()->getRequest()->getApplicationUrl()), '/').'/$literal'")); + else if($str[2]==='[') + { + $literal=strtr(trim(substr($literal,0,strlen($literal)-1)),array("'"=>"\'","\\"=>"\\\\")); + $tpl[$c++]=array($container,array(TCompositeLiteral::TYPE_EXPRESSION,"Prado::localize('$literal')")); + } + } + else if(strpos($str,'')===strlen($str)-2) { + if($expectPropEnd) + continue; + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $prop=strtolower($match[6][0]); + $attrs=$this->parseAttributes($match[7][0],$match[7][1]); + $attributes=array(); + foreach($attrs as $name=>$value) + $attributes[$prop.'.'.$name]=$value; + $type=$tpl[$container][1]; + $this->validateAttributes($type,$attributes); + foreach($attributes as $name=>$value) + { + if(isset($tpl[$container][2][$name])) + throw new TConfigurationException('template_property_duplicated',$name); + $tpl[$container][2][$name]=$value; + } + } + else { + $prop=strtolower($match[3][0]); + $stack[] = '@'.$prop; + if(!$expectPropEnd) + { + if($matchStart>$textStart) + $tpl[$c++]=array($container,substr($input,$textStart,$matchStart-$textStart)); + $textStart=$matchEnd+1; + $expectPropEnd=true; + } + } + } + else if(strpos($str,'"); + $name=array_pop($stack); + if($name!=='@'.$prop) + { + $tag=$name[0]==='@' ? '' : ""; + throw new TConfigurationException('template_closingtag_expected',$tag); + } + if(($last=count($stack))<1 || $stack[$last-1][0]!=='@') + { + if($matchStart>$textStart) + { + $value=substr($input,$textStart,$matchStart-$textStart); + if(substr($prop,-8,8)==='template') + $value=$this->parseTemplateProperty($value,$textStart); + else + $value=$this->parseAttribute($value); + if($container>=0) + { + $type=$tpl[$container][1]; + $this->validateAttributes($type,array($prop=>$value)); + if(isset($tpl[$container][2][$prop])) + throw new TConfigurationException('template_property_duplicated',$prop); + $tpl[$container][2][$prop]=$value; + } + else $this->_directive[$prop]=$value; + $textStart=$matchEnd+1; + } + $expectPropEnd=false; + } + } + else if(strpos($str,' + + +
      +
      + + +
      +
      + +
      + +
      +
      + + +
      +
      +
      + + + + + + + +
      +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      + +
      +
      + + +
      +
      +
      +
      +
      + + + + + +
      + + + Element.hide('<%=$this->DbTestResultOk->ClientID%>'); + Element.hide('<%=$this->DbTestResultErr->ClientID%>'); + Element.show('<%=$this->DbTestLoader->ClientID%>'); + + + Element.hide('<%=$this->DbTestLoader->ClientID%>'); + + + + <%[ Loading... ]%> + Validate <%[ OK ]%> + Invalidate <%[ Connection error ]%> +
      +
      +
      + +
      +
      + + +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + + + + + +
      + + + Element.hide('<%=$this->BconsoleTestResultOk->ClientID%>'); + Element.hide('<%=$this->BconsoleTestResultErr->ClientID%>'); + Element.show('<%=$this->BconsoleTestLoader->ClientID%>'); + + + Element.hide('<%=$this->BconsoleTestLoader->ClientID%>'); + + + + <%[ Loading... ]%> + Validate <%[ OK ]%> + Invalidate <%[ Connection error ]%> +
      +
      +
      +
      +

      <%[ NOTE! ]%>
      <%[ Baculum needs access to bconsole by the web server. ]%>

      +
      +
      +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      + + + + +
      +
      +
      +

      <%[ NOTE! ]%>
      <%[ Above administration login and administration password should be the same as login params defined in Web Server authorization file. They are HTTP Basic authorization params by using which you have logged in to this wizard. ]%>

      +
      +
      + +
      + <%[ Database parameters ]%> +
      +
      <%[ Database type: ]%>
      +
      <%=$this->getModule('configuration')->getDbNameByType($this->DBType->SelectedValue)%>
      +
      +
      +
      <%[ Database name: ]%>
      +
      <%=$this->DBName->Text%>
      +
      +
      +
      <%[ Login: ]%>
      +
      <%=$this->getModule('configuration')->isSQLiteType($this->DBType->SelectedValue) === false ? $this->Login->Text : '-'%>
      +
      +
      +
      <%[ Password: ]%>
      +
      <%=$this->getModule('configuration')->isSQLiteType($this->DBType->SelectedValue) === false ? preg_replace('/.{1}/', '*', $this->Password->Text) : '-'%>
      +
      +
      +
      <%[ IP address (or hostname): ]%>
      +
      <%=$this->getModule('configuration')->isSQLiteType($this->DBType->SelectedValue) === false ? $this->IP->Text : '-'%>
      +
      +
      +
      <%[ Database port: ]%>
      +
      <%=$this->getModule('configuration')->isSQLiteType($this->DBType->SelectedValue) === false ? $this->Port->Text : '-'%>
      +
      +
      +
      <%[ Database file path (SQLite only): ]%>
      +
      <%=$this->getModule('configuration')->isSQLiteType($this->DBType->SelectedValue) === true ? $this->DBPath->Text : '-'%>
      +
      +
      +
      + <%[ Bconsole access ]%> +
      +
      <%[ Bconsole binary file path: ]%>
      +
      <%=$this->BconsolePath->Text%>
      +
      +
      +
      <%[ Bconsole config file path: ]%>
      +
      <%=$this->BconsoleConfigPath->Text%>
      +
      +
      +
      <%[ Use sudo for bconsole requests: ]%>
      +
      <%=($this->UseSudo->Checked === true) ? 'yes' : 'no'%>
      +
      +
      +
      + <%[ Authorization to Baculum panel ]%> +
      +
      <%[ Administration login: ]%>
      +
      <%=$this->PanelLogin->Text%>
      +
      +
      +
      <%[ Administration password: ]%>
      +
      <%=preg_replace('/.{1}/', '*', $this->PanelPassword->Text)%>
      +
      +
      +
      + + \ No newline at end of file diff --git a/gui/baculum/protected/Pages/ConfigurationWizard.php b/gui/baculum/protected/Pages/ConfigurationWizard.php new file mode 100644 index 0000000000..2a6214872f --- /dev/null +++ b/gui/baculum/protected/Pages/ConfigurationWizard.php @@ -0,0 +1,211 @@ +Lang->SelectedValue = $this->Session['language']; + $this->firstRun = !$this->getModule('configuration')->isApplicationConfig(); + $this->applicationConfig = $this->getModule('configuration')->getApplicationConfig(); + } + + public function onLoad($param) { + parent::onLoad($param); + $this->Licence->Text = $this->getModule('misc')->getLicence(); + $this->Port->setViewState('port', $this->Port->Text); + if(!$this->IsPostBack && !$this->IsCallBack) { + if($this->firstRun === true) { + $this->DBName->Text = self::DEFAULT_DB_NAME; + $this->Login->Text = self::DEFAULT_DB_LOGIN; + $this->BconsolePath->Text = self::DEFAULT_BCONSOLE_BIN; + $this->BconsoleConfigPath->Text = self::DEFAULT_BCONSOLE_CONF; + } else { + $this->DBType->SelectedValue = $this->getPage()->applicationConfig['db']['type']; + $this->DBName->Text = $this->applicationConfig['db']['name']; + $this->Login->Text = $this->applicationConfig['db']['login']; + $this->Password->Text = $this->applicationConfig['db']['password']; + $this->IP->Text = $this->applicationConfig['db']['ip_addr']; + $this->Port->Text = $this->applicationConfig['db']['port']; + $this->Port->setViewState('port', $this->applicationConfig['db']['port']); + $this->DBPath->Text = $this->applicationConfig['db']['path']; + $this->BconsolePath->Text = $this->applicationConfig['bconsole']['bin_path']; + $this->BconsoleConfigPath->Text = $this->applicationConfig['bconsole']['cfg_path']; + $this->UseSudo->Checked = $this->getPage()->applicationConfig['bconsole']['use_sudo'] == 1; + $this->PanelLogin->Text = $this->applicationConfig['baculum']['login']; + $this->PanelPassword->Text = $this->applicationConfig['baculum']['password']; + $this->RetypePanelPassword->Text = $this->applicationConfig['baculum']['password']; + } + } + } + + public function NextStep($sender, $param) { + } + + public function PreviousStep($sender, $param) { + } + + public function wizardStop($sender, $param) { + $this->goToDefaultPage(); + } + + public function wizardCompleted() { + $cfgData = array('db' => array(), 'bconsole' => array(), 'baculum' => array()); + $cfgData['db']['type'] = $this->DBType->SelectedValue; + $cfgData['db']['name'] = $this->DBName->Text; + $cfgData['db']['login'] = $this->Login->Text; + $cfgData['db']['password'] = $this->Password->Text; + $cfgData['db']['ip_addr'] = $this->IP->Text; + $cfgData['db']['port'] = $this->Port->Text; + $cfgData['db']['path'] = $this->Application->getModule('configuration')->isSQLiteType($cfgData['db']['type']) ? $this->DBPath->Text : ''; + $cfgData['bconsole']['bin_path'] = $this->BconsolePath->Text; + $cfgData['bconsole']['cfg_path'] = $this->BconsoleConfigPath->Text; + $cfgData['bconsole']['use_sudo'] = (integer)($this->UseSudo->Checked === true); + $cfgData['baculum']['login'] = $this->PanelLogin->Text; + $cfgData['baculum']['password'] = $this->PanelPassword->Text; + $cfgData['baculum']['debug'] = isset($this->applicationConfig['baculum']['debug']) ? $this->applicationConfig['baculum']['debug'] : "0"; + $ret = $this->getModule('configuration')->setApplicationConfig($cfgData); + if($ret === true) { + $this->goToDefaultPage(); + } + } + + public function setDBType($sender, $param) { + $db = $this->DBType->SelectedValue; + $this->setLogin($db); + $this->setPassword($db); + $this->setIP($db); + $this->setDefaultPort($db); + $this->setDBPath($db); + } + + public function setLogin($db) { + $this->Login->Enabled = ($this->getModule('configuration')->isSQLiteType($db) === false); + } + + public function setPassword($db) { + $this->Password->Enabled = ($this->getModule('configuration')->isSQLiteType($db) === false); + } + + public function setIP($db) { + $this->IP->Enabled = ($this->getModule('configuration')->isSQLiteType($db) === false); + } + + public function setDefaultPort($db) { + $configuration = $this->Application->getModule('configuration'); + $port = null; + if($configuration->isPostgreSQLType($db) === true) { + $port = $configuration::PGSQL_PORT; + } elseif($configuration->isMySQLType($db) === true) { + $port = $configuration::MYSQL_PORT; + } elseif($configuration->isSQLiteType($db) === true) { + $port = $configuration::SQLITE_PORT; + } + + $prevPort = $this->Port->getViewState('port'); + + if(is_null($port)) { + $this->Port->Text = ''; + $this->Port->Enabled = false; + } else { + $this->Port->Enabled = true; + $this->Port->Text = (empty($prevPort)) ? $port : $prevPort; + } + $this->Port->setViewState('port', ''); + } + + public function setDBPath($db) { + if($this->getModule('configuration')->isSQLiteType($db) === true) { + $this->DBPath->Enabled = true; + $this->DBPathField->Display = 'Fixed'; + } else { + $this->DBPath->Enabled = false; + $this->DBPathField->Display = 'Hidden'; + } + } + + public function setLang($sender, $param) { + $this->Session['language'] = $sender->SelectedValue; + $this->goToPage('ConfigurationWizard'); + } + + public function validateAdministratorPassword($sender, $param) { + $sender->Display = ($this->RetypePasswordRequireValidator->IsValid === true && $this->RetypePasswordRegexpValidator->IsValid === true) ? 'Dynamic' : 'None'; + $param->IsValid = ($param->Value === $this->PanelPassword->Text); + } + + public function renderPanel($sender, $param) { + $this->LoginValidator->Display = ($this->Login->Enabled === true) ? 'Dynamic' : 'None'; + $this->PortValidator->Display = ($this->Port->Enabled === true) ? 'Dynamic' : 'None'; + $this->IPValidator->Display = ($this->IP->Enabled === true) ? 'Dynamic' : 'None'; + $this->DBPathValidator->Display = ($this->DBPath->Enabled === true) ? 'Dynamic' : 'None'; + $this->DbTestResultOk->Display = 'None'; + $this->DbTestResultErr->Display = 'None'; + $this->Step2Content->render($param->NewWriter); + } + + public function connectionDBTest($sender, $param) { + $validation = false; + $configuration = $this->getModule('configuration'); + $dbParams = array(); + $dbParams['type'] = $this->DBType->SelectedValue; + if($configuration->isMySQLType($dbParams['type']) === true || $configuration->isPostgreSQLType($dbParams['type']) === true) { + $dbParams['name'] = $this->DBName->Text; + $dbParams['login'] = $this->Login->Text; + $dbParams['password'] = $this->Password->Text; + $dbParams['host'] = $this->IP->Text; + $dbParams['port'] = $this->Port->Text; + $validation = true; + } elseif($configuration->isSQLiteType($dbParams['type']) === true && !empty($this->DBPath->Text)) { + $dbParams['path'] = $this->DBPath->Text; + $validation = true; + } + + $isValidate = ($validation === true) ? $this->getModule('db')->testDbConnection($dbParams) : false; + $this->DbTestResultOk->Display = ($isValidate === true) ? 'Dynamic' : 'None'; + $this->DbTestResultErr->Display = ($isValidate === false) ? 'Dynamic' : 'None'; + $this->Step2Content->render($param->NewWriter); + + } + + public function connectionBconsoleTest($sender, $param) { + $result = $this->getModule('bconsole')->testBconsoleCommand(array('version'), $this->BconsolePath->Text, $this->BconsoleConfigPath->Text, $this->UseSudo->Checked)->exitcode; + $isValidate = ($result === 0); + $this->BconsoleTestResultOk->Display = ($isValidate === true) ? 'Dynamic' : 'None'; + $this->BconsoleTestResultErr->Display = ($isValidate === false) ? 'Dynamic' : 'None'; + $this->Step4Content->render($param->NewWriter); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Pages/Home.page b/gui/baculum/protected/Pages/Home.page new file mode 100644 index 0000000000..178095a8f3 --- /dev/null +++ b/gui/baculum/protected/Pages/Home.page @@ -0,0 +1,46 @@ +<%@ MasterClass="Application.Layouts.Main" Theme="Baculum-v1"%> + +
      +
      + Baculum +
      +
      + + +
      + + + + + + +
      + + + + + + +
      +
      +
      + +
      +
      +
      diff --git a/gui/baculum/protected/Pages/Home.php b/gui/baculum/protected/Pages/Home.php new file mode 100644 index 0000000000..29c21eae22 --- /dev/null +++ b/gui/baculum/protected/Pages/Home.php @@ -0,0 +1,70 @@ +getModule('configuration')->isApplicationConfig(); + if($isConfigExists === false) { + $this->goToPage('ConfigurationWizard'); + } + + if(!$this->IsPostBack && !$this->IsCallBack) { + $this->Logging->Checked = $this->getModule('logging')->isDebugOn(); + } + + $directors = $this->getModule('api')->get(array('directors'))->output; + + if(!$this->IsPostBack && !$this->IsCallBack) { + if(!array_key_exists('director', $_SESSION)) { + $_SESSION['director'] = $directors[0]; + } + $this->Director->dataSource = array_combine($directors, $directors); + $this->Director->SelectedValue = $_SESSION['director']; + $this->Director->dataBind(); + } + } + + public function restore($sender, $param) { + $this->goToPage('RestoreWizard'); + } + + public function configuration($sender, $param) { + $this->goToPage('ConfigurationWizard'); + } + + public function director($sender, $param) { + $_SESSION['director'] = $this->Director->SelectedValue; + } + + public function setDebug($sender, $param) { + $this->getModule('logging')->enableDebug($this->Logging->Checked); + } + + public function clearBvfsCache($sender, $param) { + $this->getModule('api')->set(array('bvfs', 'clear'), array()); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Pages/Requirements.php b/gui/baculum/protected/Pages/Requirements.php new file mode 100644 index 0000000000..9b7d446334 --- /dev/null +++ b/gui/baculum/protected/Pages/Requirements.php @@ -0,0 +1,72 @@ +validateEnvironment($baseDir); + } + + private function validateEnvironment($baseDir) { + $requirements = array(); + if(!is_writable(self::ASSETS_DIR)) { + $requirements[] = 'Please make writable by the web server next directory: ' . $baseDir . '/' . self::ASSETS_DIR . ''; + } + + if(!is_writable(self::DATA_DIR)) { + $requirements[] = 'Please make writable by the web server next directory: ' . $baseDir . '/' . self::DATA_DIR . ''; + } + + if(!is_writable(self::RUNTIME_DIR)) { + $requirements[] = 'Please make writable by the web server next directory: ' . $baseDir . '/' . self::RUNTIME_DIR . ''; + } + + if(!function_exists('curl_init') || !function_exists('curl_setopt') || !function_exists('curl_exec') || !function_exists('curl_close')) { + $requirements[] = 'Please install cURL PHP module.'; + } + + if(!function_exists('bcmul') || !function_exists('bcpow')) { + $requirements[] = 'Please install BCMath PHP module.'; + } + + if(!extension_loaded('pdo_pgsql') && !extension_loaded('pdo_mysql')) { + $requirements[] = 'Please install PDO (PHP Data Objects) PHP module for PostgreSQL or MySQL depending on what database type you are using with Bacula.'; + } + + if(!function_exists('mb_strlen')) { + $requirements[] = 'Please install MB String PHP module for support for multi-byte string handling to PHP.'; + } + + if(count($requirements) > 0) { + echo '

      Baculum - Missing dependencies

        '; + for($i = 0; $i < count($requirements); $i++) { + echo '
      • ' . $requirements[$i] . '
      • '; + + } + echo '
      '; + echo 'For run Baculum please correct above requirements and refresh this page in web browser.'; + echo ''; + exit(); + } + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Pages/RestoreWizard.page b/gui/baculum/protected/Pages/RestoreWizard.page new file mode 100644 index 0000000000..0546e88d01 --- /dev/null +++ b/gui/baculum/protected/Pages/RestoreWizard.page @@ -0,0 +1,330 @@ +<%@ MasterClass="Application.Layouts.Wizard" Theme="Baculum-v1"%> + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      <%=$this->Parent->ActiveStep->Title%>
      +
      + + +
      +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + +
      +
      +
      + + +
      +
      +
      + + +
      +
      +
      + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      + + + + + + + + + +
      +
      + + +
      + <%[ It seems that there is no files for choosing or file records in database for this job has been purged (file retention period expired) ]%> +
      +
      + + + + <%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file' %> <%=($this->getParent()->Data['name'] != '/') ? preg_replace('/\/$/', '', $this->getParent()->Data['name']) : '/'%> + + + + <%=$this->getPage()->UniqueID%>_lock = false; + + + + + +
      +
      +
      +
      + + +
      + <%[ For see a file versions please double click file on the left files browser. ]%> +
      +
      + + + + <%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file'%> + <%=is_array($this->getParent()->Data['lstat']) && array_key_exists('mtime', $this->getParent()->Data['lstat']) ? date("Y-m-d H:i:s", $this->getParent()->Data['lstat']['mtime']) : ''%> + + + + +
      +
      +
      +
      + + + +
      + <%[ For add a file to restore please drag a file from frame on left or from above frame and drop it here ]%> +
      +
      + + + + + <%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file'%> + <%=is_array($this->getParent()->Data['lstat']) && array_key_exists('mtime', $this->getParent()->Data['lstat']) ? date("Y-m-d H:i:s", $this->getParent()->Data['lstat']['mtime']) : ''%> + + + +
      +
      +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      + + + + + + +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + <%[ Source parameters ]%> +
      +
      <%[ Backup data from client: ]%>
      +
      <%=$this->getModule('api')->get(array('clients', $this->BackupClientName->SelectedValue))->output->name%>
      +
      +
      +
      <%[ Backup selection method: ]%>
      +
      + + +
      +
      +
      +
      <%[ Backup for restore: ]%>
      +
      + + +
      +
      + +
      +
      <%[ FileSet for restore: ]%>
      +
      <%=$this->GroupBackupFileSet->SelectedValue%>
      +
      +
      +
      +
      + <%[ Files for restore ]%> +
      +
      <%[ Selected directories count: ]%>
      +
      <%=isset($this->getRestoreElements(true)->dirid) ? count($this->getRestoreElements(true)->dirid) : '0'%>
      +
      +
      +
      <%[ Selected files count: ]%>
      +
      <%=isset($this->getRestoreElements(true)->fileid) ? count($this->getRestoreElements(true)->fileid) : '0'%>
      +
      +
      +
      + <%[ Destination parameters ]%> +
      +
      <%[ Restore to client: ]%>
      +
      <%=$this->getModule('api')->get(array('clients', $this->RestoreClient->SelectedValue))->output->name%>
      +
      +
      +
      <%[ Restore to path: ]%>
      +
      <%=$this->RestorePath->Text%>
      +
      +
      +
      + <%[ Restore job options ]%> +
      +
      <%[ Replace files: ]%>
      +
      + + + + +
      +
      +
      +
      <%[ Restore job priority: ]%>
      +
      <%=$this->RestoreJobPriority->Text%>
      +
      +
      +
      + + \ No newline at end of file diff --git a/gui/baculum/protected/Pages/RestoreWizard.php b/gui/baculum/protected/Pages/RestoreWizard.php new file mode 100644 index 0000000000..29f7ef9419 --- /dev/null +++ b/gui/baculum/protected/Pages/RestoreWizard.php @@ -0,0 +1,416 @@ + 'Full', 'I' => 'Incremental', 'D'=> 'Differential'); + private $jobs = null; + private $filesets = null; + private $storages = null; + private $clients = null; + private $browserRootDir = array('name' => '.', 'type' => 'dir'); + private $browserUpDir = array('name' => '..', 'type' => 'dir'); + + const BVFS_PATH_PREFIX = 'b2'; + + public function onInit($param) { + parent::onInit($param); + if(!$this->IsPostBack && !$this->IsCallBack) { + $this->setBrowserFiles(array()); + $this->setFileVersions(array()); + $this->setFilesToRestore(array()); + $this->markFileToRestore(null, null); + $_SESSION['restore_path'] = array(); + } + } + + public function onLoad($param) { + parent::onLoad($param); + if($this->RestoreWizard->ActiveStepIndex == 0) { + $this->jobs = $this->getModule('api')->get(array('jobs'))->output; + $this->filesets = $this->getModule('api')->get(array('filesets'))->output; + } + $this->clients = $this->getModule('api')->get(array('clients'))->output; + + if(!$this->IsCallBack && (($this->RestoreWizard->ActiveStepIndex == 1 && $this->getPage()->BackupToRestore->ItemCount > 0) || $this->RestoreWizard->ActiveStepIndex == 3)) { + $this->setFileVersions(array()); + $this->setBrowserPath(''); + $this->prepareFilesToRestore(); + $this->prepareVersionsToRestore(); + } + } + + public function addFileToRestore($sender, $param) { + $control=$param->getDroppedControl(); + $item=$control->getNamingContainer(); + list(, , , $dragElementID, , ) = explode('_', $param->getDragElementID(), 6); // I know that it is ugly. + if($dragElementID == $this->VersionsDataGrid->ID) { + $fileid = $this->VersionsDataGrid->getDataKeys()->itemAt($item->getItemIndex()); + $fileProperties = $this->getFileVersions($fileid); + } else { + $fileid = $this->DataGridFiles->getDataKeys()->itemAt($item->getItemIndex()); + $fileProperties = $this->getBrowserFile($fileid); + } + if($fileProperties['name'] != $this->browserRootDir['name'] && $fileProperties['name'] != $this->browserUpDir['name']) { + $this->markFileToRestore($fileid, $fileProperties); + $this->prepareFilesToRestore(); + } + } + + public function removeSelectedFile($sender, $param) { + $fileid = $param->CallbackParameter; + $this->unmarkFileToRestore($fileid); + $this->prepareFilesToRestore(); + } + + public function getVersions($sender, $param) { + list($filename, $pathid, $filenameid, $jobid) = explode('|', $param->CallbackParameter, 4); + if($filenameid == 0) { + $this->setBrowserPath($filename); + return; + } + $clientname = null; + foreach($this->clients as $client) { + if($client->clientid == $this->BackupClientName->SelectedValue) { + $clientname = $client->name; + break; + } + } + $versions = $this->getModule('api')->get(array('bvfs', 'versions', $clientname, $jobid, $pathid, $filenameid))->output; + $fileVersions = $this->getModule('misc')->parseFileVersions($filename, $versions); + $this->setFileVersions($fileVersions); + $this->VersionsDataGrid->dataSource = $fileVersions; + $this->VersionsDataGrid->dataBind(); + $this->prepareFilesToRestore(); + } + + public function refreshSelectedFiles($sender, $param) { + $this->prepareFilesToRestore(); + $this->SelectedVersionsDropper->render($param->NewWriter); + } + + public function NextStep($sender, $param) { + } + + public function PreviousStep($sender, $param) { + } + + public function wizardStop($sender, $param) { + $this->goToDefaultPage(); + } + + public function setJobs($sender, $param) { + $jobsList = $jobsGroupList = $fileSetsList = array(); + if(is_array($this->jobs)) { + foreach($this->jobs as $job) { + if(array_key_exists($job->level, $this->jobLevelsToRestore) && $job->type == 'B' && $job->jobstatus == 'T' && $job->clientid == $this->BackupClientName->SelectedValue) { + $jobsList[$job->jobid] = sprintf('[%s] %s, %s, %s', $job->jobid, $job->name, $this->jobLevelsToRestore[$job->level], $job->endtime); + $jobsGroupList[$job->name] = $job->name; + //$fileSetsList[$job->filesetid] = $this->getFileSet($job->filesetid)->fileset; + } + } + } + + foreach($this->filesets as $director => $filesets) { + $fileSetsList = array_merge($filesets, $fileSetsList); + } + + $this->BackupToRestore->dataSource = $jobsList; + $this->BackupToRestore->dataBind(); + $this->GroupBackupToRestore->dataSource = $jobsGroupList; + $this->GroupBackupToRestore->dataBind(); + $this->GroupBackupFileSet->dataSource = array_combine($fileSetsList, $fileSetsList); + $this->GroupBackupFileSet->dataBind(); + $this->setRestoreClients($sender, $param); + } + + public function setBackupClients($sender, $param) { + if(!$this->IsPostBack) { + $clientsList = array(); + foreach($this->clients as $client) { + $clientsList[$client->clientid] = $client->name; + } + $this->BackupClientName->dataSource = $clientsList; + $this->BackupClientName->dataBind(); + } + } + + public function setRestoreClients($sender, $param) { + if(!$this->IsPostBack || $sender->ID == $this->BackupClientName->ID) { + $clientsList = array(); + foreach($this->clients as $client) { + $clientsList[$client->clientid] = $client->name; + } + $this->RestoreClient->SelectedValue = $this->BackupClientName->SelectedValue; + $this->RestoreClient->dataSource = $clientsList; + $this->RestoreClient->dataBind(); + } + } + + public function getFileSet($filesetId) { + $filesetObj = null; + foreach($this->filesets as $fileset) { + if($fileset->filesetid == $filesetId) { + $filesetObj = $fileset; + break; + } + } + return $filesetObj; + } + + public function setStorage($sender, $param) { + if(!$this->IsPostBack) { + $storagesList = array(); + $storages = $this->getModule('api')->get(array('storages'))->output; + foreach($storages as $storage) { + $storagesList[$storage->storageid] = $storage->name; + } + $this->GroupBackupStorage->dataSource = $storagesList; + $this->GroupBackupStorage->dataBind(); + } + } + + public function setPool($sender, $param) { + if(!$this->IsPostBack) { + $poolsList = array(); + $pools = $this->getModule('api')->get(array('pools'))->output; + foreach($pools as $pool) { + $poolsList[$pool->poolid] = $pool->name; + } + $this->GroupBackupPool->dataSource = $poolsList; + $this->GroupBackupPool->dataBind(); + } + } + + public function setBackupClient($sender, $param) { + $this->ClientToRestore->SelectedValue = $param->SelectedValue; + } + + public function setBackupSelection($sender, $param) { + $this->GroupBackupToRestoreField->Display = ($sender->ID == $this->GroupBackupSelection->ID) ? 'Dynamic' : 'None'; + $this->BackupToRestoreField->Display = ($sender->ID == $this->OnlySelectedBackupSelection->ID) ? 'Dynamic' : 'None'; + $this->setBrowserFiles(array()); + $this->setFileVersions(array()); + $this->setFilesToRestore(array()); + $this->markFileToRestore(null, null); + $_SESSION['restore_path'] = array(); + } + + /*public function setGroupBackup($sender, $param) { + foreach($this->jobs as $job) { + if($job->name == $sender->SelectedValue) { + $this->GroupBackupFileSet->SelectedValue = $job->filesetid; + } + } + }*/ + + public function resetFileBrowser($sender, $param) { + $this->markFileToRestore(null, null); + $this->setBrowserPath($this->browserRootDir['name']); + } + + private function prepareBrowserFiles($files) { + $this->setBrowserFiles($files); + $this->DataGridFiles->dataSource = $files; + @$this->DataGridFiles->dataBind(); + } + + private function prepareVersionsToRestore() { + $this->VersionsDataGrid->dataSource = $_SESSION['files_versions']; + $this->VersionsDataGrid->dataBind(); + } + + private function prepareFilesToRestore() { + $this->SelectedVersionsDataGrid->dataSource = $_SESSION['restore']; + $this->SelectedVersionsDataGrid->dataBind(); + } + + private function setBrowserPath($path) { + if(!empty($path)) { + if($path == $this->browserUpDir['name']) { + array_pop($_SESSION['restore_path']); + } elseif($path == $this->browserRootDir['name']) { + $_SESSION['restore_path'] = array(); + } else { + array_push($_SESSION['restore_path'], $path); + } + } + $this->setBrowserLocalizator(); + $this->prepareBrowserContent(); + } + + private function getBrowserPath($stringFormat = false) { + return ($stringFormat === true) ? implode($_SESSION['restore_path']) : $_SESSION['restore_path']; + } + + private function setBrowserLocalizator() { + $localization = $this->getBrowserPath(true); + $this->PathField->HeaderText = mb_strlen($localization) > 56 ? '...' . mb_substr($localization, -56) : $localization; + } + + private function prepareBrowserContent() { + $jobids = $this->getElementaryBackup(); + + // generating Bvfs may takes a moment + $this->generateBvfsCacheByJobids($jobids); + $bvfsDirsList = $this->getModule('api')->get(array('bvfs', 'lsdirs', $jobids, '?path=' . urlencode($this->getBrowserPath(true)) . '')); + $bvfsDirs = is_object($bvfsDirsList) ? $bvfsDirsList->output : array(); + $dirs = $this->getModule('misc')->parseBvfsList($bvfsDirs); + $bvfsFilesList = $this->getModule('api')->get(array('bvfs', 'lsfiles', $jobids, '?path=' . urlencode($this->getBrowserPath(true)) . '')); + $bvfsFiles = is_object($bvfsFilesList) ? $bvfsFilesList->output : array(); + $files = $this->getModule('misc')->parseBvfsList($bvfsFiles); + $elements = array_merge($dirs, $files); + if(count($this->getBrowserPath()) > 0) { + array_unshift($elements, $this->browserRootDir); + } + $this->prepareBrowserFiles($elements); + } + + private function getElementaryBackup() { + $jobids = null; + if($this->OnlySelectedBackupSelection->Checked === true) { + $jobs = $this->getModule('api')->get(array('bvfs', 'getjobids', $this->BackupToRestore->SelectedValue)); + $ids = is_object($jobs) ? $jobs->output : array(); + foreach($ids as $jobid) { + if(preg_match('/^([\d\,]+)$/', $jobid, $match) == 1) { + $jobids = $match[1]; + break; + } + } + } else { + $jobsRecent = $this->getModule('api')->get(array('jobs', 'recent', $this->GroupBackupToRestore->SelectedValue, $this->BackupClientName->SelectedValue)); + if(isset($jobsRecent->output)) { + $ids = $jobsRecent->output; + $jobids = implode(',', $ids); + } + } + + $completeJobids = (!is_null($jobids)) ? $jobids : $this->BackupToRestore->SelectedValue; + return $completeJobids; + } + + private function generateBvfsCacheByJobids($jobids) { + $this->getModule('api')->set(array('bvfs', 'update', $jobids), array()); + } + + private function setFileVersions($versions) { + $_SESSION['files_versions'] = $versions; + } + + private function getFileVersions($fileid) { + $versions = array(); + foreach($_SESSION['files_versions'] as $file) { + if(array_key_exists('fileid', $file) && $file['fileid'] == $fileid) { + $versions = $file; + break; + } + } + return $versions; + } + + private function setBrowserFiles($files) { + $_SESSION['files_browser'] = $files; + } + + private function getBrowserFile($fileid) { + $element = array(); + foreach($_SESSION['files_browser'] as $file) { + if(array_key_exists('fileid', $file) && $file['fileid'] == $fileid) { + $element = $file; + break; + } + } + return $element; + } + + private function markFileToRestore($fileid, $file) { + if($fileid === null) { + $_SESSION['restore'] = array(); + } elseif($file['name'] != $this->browserRootDir['name'] && $file['name'] != $this->browserUpDir['name']) { + $_SESSION['restore'][$fileid] = $file; + } + } + + private function unmarkFileToRestore($fileid) { + if(array_key_exists($fileid, $_SESSION['restore'])) { + unset($_SESSION['restore'][$fileid]); + } + } + + public function getFilesToRestore() { + return $_SESSION['restore']; + } + + public function setFilesToRestore($files) { + $_SESSION['restore'] = $files; + } + + public function getRestoreElements($asObject = false) { + $fileids = array(); + $dirids = array(); + foreach($this->getFilesToRestore() as $fileid => $properties) { + if($properties['type'] == 'dir') { + $dirids[] = $properties['pathid']; + } elseif($properties['type'] == 'file') { + $fileids[] = $fileid; + } + } + $ret = array('fileid' => $fileids, 'dirid' => $dirids); + if($asObject === true) { + $ret = (object)$ret; + } + return $ret; + } + + public function wizardCompleted() { + $jobids = $this->getElementaryBackup(); + $path = self::BVFS_PATH_PREFIX . str_replace(',', '', $jobids); + $restoreElements = $this->getRestoreElements(); + $cmdProps = array('jobids' => $jobids, 'path' => $path); + if(count($restoreElements['fileid']) > 0) { + $cmdProps['fileid'] = implode(',', $restoreElements['fileid']); + } + if(count($restoreElements['dirid']) > 0) { + $cmdProps['dirid'] = implode(',', $restoreElements['dirid']); + } + + $this->getModule('api')->create(array('bvfs', 'restore'), $cmdProps); + $restoreProps = array(); + $restoreProps['rpath'] = $path; + $restoreProps['clientid'] = intval($this->RestoreClient->SelectedValue); + $restoreProps['fileset'] = $this->GroupBackupFileSet->SelectedValue; + $restoreProps['priority'] = intval($this->RestoreJobPriority->Text); + $restoreProps['where'] = $this->RestorePath->Text; + $restoreProps['replace'] = $this->ReplaceFiles->SelectedValue; + + $ret = $this->getModule('api')->create(array('jobs', 'restore'), $restoreProps); + $this->goToDefaultPage(); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/BActiveButton.php b/gui/baculum/protected/Portlets/BActiveButton.php new file mode 100644 index 0000000000..13bb4970b5 --- /dev/null +++ b/gui/baculum/protected/Portlets/BActiveButton.php @@ -0,0 +1,57 @@ +actionClass = $param; + } + + public function save($sender, $param) { + $this->actionClass->save($sender, $param); + } + + public function setCommandName($param) { + $this->commandName = $param; + } + + public function setText($param) { + $this->text = $param; + } + + public function setCausesValidation($param) { + $this->causesValidation = $param; + } + + public function setValidationGroup($param) { + $this->validationGroup = $param; + } +} + +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/BActiveButton.tpl b/gui/baculum/protected/Portlets/BActiveButton.tpl new file mode 100644 index 0000000000..05884e68a2 --- /dev/null +++ b/gui/baculum/protected/Portlets/BActiveButton.tpl @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/BButton.php b/gui/baculum/protected/Portlets/BButton.php new file mode 100644 index 0000000000..7631a99579 --- /dev/null +++ b/gui/baculum/protected/Portlets/BButton.php @@ -0,0 +1,53 @@ +commandName = $param; + } + + public function setText($param) { + $this->text = $param; + } + + public function setCausesValidation($param) { + $this->causesValidation = $param; + } + + public function setVisible($param) { + $this->Visible = $param; + } + + public function getVisible() { + return $this->Visible; + } +} + +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/BButton.tpl b/gui/baculum/protected/Portlets/BButton.tpl new file mode 100644 index 0000000000..df2c37ddc3 --- /dev/null +++ b/gui/baculum/protected/Portlets/BButton.tpl @@ -0,0 +1,3 @@ +Visible === false ? 'style="display: none"' : ''%>> + + \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/ClientConfiguration.php b/gui/baculum/protected/Portlets/ClientConfiguration.php new file mode 100644 index 0000000000..e2a6c44beb --- /dev/null +++ b/gui/baculum/protected/Portlets/ClientConfiguration.php @@ -0,0 +1,75 @@ +Status->setActionClass($this); + $this->Apply->setActionClass($this); + } + + public function configure($clientId) { + $clientdata = $this->Application->getModule('api')->get(array('clients', 'show', $clientId))->output; + $this->ShowClient->Text = implode(PHP_EOL, $clientdata); + $client = $this->Application->getModule('api')->get(array('clients', $clientId))->output; + $this->ClientName->Text = $client->name; + $this->ClientIdentifier->Text = $client->clientid; + $this->ClientDescription->Text = $client->uname; + $this->FileRetention->Text = intval($client->fileretention / 86400); // conversion to days + $this->JobRetention->Text = intval($client->jobretention / 86400); // conversion to days + $this->AutoPrune->Checked = $client->autoprune == 1; + } + + public function save($sender, $param) { + switch($sender->getParent()->ID) { + case $this->Status->ID: { + $status = $this->Application->getModule('api')->get(array('clients', 'status', $this->ClientIdentifier->Text))->output; + $this->ShowClient->Text = implode(PHP_EOL, $status); + break; + } + case $this->Apply->ID: { + if($this->JobRetentionValidator->IsValid === false || $this->FileRetentionValidator->IsValid === false) { + return false; + } + $client = array(); + $client['clientid'] = $this->ClientIdentifier->Text; + $client['fileretention'] = $this->FileRetention->Text * 86400; // conversion to seconds + $client['jobretention'] = $this->JobRetention->Text * 86400; // conversion to seconds + $client['autoprune'] = (integer)$this->AutoPrune->Checked; + $this->Application->getModule('api')->set(array('clients', $client['clientid']), $client); + break; + } + } + } + + public function fileRetentionValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->FileRetention->Text) && $this->FileRetention->Text >= 0; + $param->setIsValid($isValid); + } + + public function jobRetentionValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->JobRetention->Text) && $this->JobRetention->Text >= 0; + $param->setIsValid($isValid); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/ClientConfiguration.tpl b/gui/baculum/protected/Portlets/ClientConfiguration.tpl new file mode 100644 index 0000000000..5f7ce4439b --- /dev/null +++ b/gui/baculum/protected/Portlets/ClientConfiguration.tpl @@ -0,0 +1,51 @@ +<%@ MasterClass="Application.Portlets.ConfigurationPanel"%> + + <%[ Client name: ]%>
      + +
      + +
      <%[ Console status ]%>
      +
      + +
      +
      + +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      +
      + + +
      + +
      +
      diff --git a/gui/baculum/protected/Portlets/ClientList.php b/gui/baculum/protected/Portlets/ClientList.php new file mode 100644 index 0000000000..5d3d5dadda --- /dev/null +++ b/gui/baculum/protected/Portlets/ClientList.php @@ -0,0 +1,78 @@ +prepareData(); + } + + public function setWindowTitle($param) { + $this->windowTitle = $param; + } + + public function prepareData($forceReload = false) { + $allowedButtons = array('ClientBtn', 'ReloadClients'); + if($this->Page->IsPostBack || $this->Page->IsCallBack || $forceReload) { + if(in_array($this->getPage()->CallBackEventTarget->ID, $allowedButtons) || $forceReload) { + $params = $this->getUrlParams('clients', $this->getPage()->ClientWindow->ID); + $clients = $this->Application->getModule('api')->get($params); + $isDetailView = $this->Session['view' . $this->getPage()->ClientWindow->ID] == 'details'; + $clientsList = $this->Application->getModule('misc')->objectToArray($clients->output); + $this->RepeaterShow->Visible = !$isDetailView; + $this->Repeater->DataSource = $isDetailView === false ? $clientsList : array(); + $this->Repeater->dataBind(); + $this->DataGridShow->Visible = $isDetailView; + $this->DataGrid->DataSource = $isDetailView === true ? $clientsList : array(); + $this->DataGrid->dataBind(); + } + } + } + + public function sortDataGrid($sender, $param) { + $params = $this->getUrlParams('clients', $this->getPage()->ClientWindow->ID); + $data = $this->Application->getModule('api')->get($params)->output; + $data = $this->Application->getModule('misc')->objectToArray($data); + $this->DataGrid->DataSource = $this->sortData($data, $param->SortExpression, $sender->UniqueID); + $this->DataGrid->dataBind(); + } + + + public function setShowID($ShowID) { + $this->ShowID = $this->getMaster()->ShowID = $ShowID; + } + + public function getShowID() { + return $this->ShowID; + } + + public function configure($sender, $param) { + if($this->Page->IsCallBack) { + $this->getPage()->ClientConfiguration->configure($param->CallbackParameter); + } + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/ClientList.tpl b/gui/baculum/protected/Portlets/ClientList.tpl new file mode 100644 index 0000000000..8de08df9d4 --- /dev/null +++ b/gui/baculum/protected/Portlets/ClientList.tpl @@ -0,0 +1,81 @@ +<%@ MasterClass="Application.Portlets.SlideWindow"%> + + + + + + <%=@$this->DataItem['name']%> + + + + ConfigurationWindow<%=$this->getPage()->ClientConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->ClientConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->ClientWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->ClientWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + + + + <%=$this->getParent()->Data['name']%> + + + ConfigurationWindow<%=$this->getPage()->ClientConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->ClientConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->ClientWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->ClientWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + <%=$this->getParent()->Data['autoprune'] == 1 ? 'Yes' : 'No'%> + + + + + <%=(integer)($this->getParent()->Data['fileretention'] / 3600 / 24)%> <%=$this->getParent()->Data['fileretention'] < 172800 ? 'day' : 'days'%> + + + + + <%=(integer)($this->getParent()->Data['jobretention'] / 3600 / 24)%> <%=$this->getParent()->Data['jobretention'] < 172800 ? 'day' : 'days'%> + + + + + diff --git a/gui/baculum/protected/Portlets/ConfigurationPanel.php b/gui/baculum/protected/Portlets/ConfigurationPanel.php new file mode 100644 index 0000000000..eeab6628ea --- /dev/null +++ b/gui/baculum/protected/Portlets/ConfigurationPanel.php @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/ConfigurationPanel.tpl b/gui/baculum/protected/Portlets/ConfigurationPanel.tpl new file mode 100644 index 0000000000..caafffa8c1 --- /dev/null +++ b/gui/baculum/protected/Portlets/ConfigurationPanel.tpl @@ -0,0 +1,12 @@ +
      +
      +
      + + + + +
      +
      + \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/Console.php b/gui/baculum/protected/Portlets/Console.php new file mode 100644 index 0000000000..3eea94b1be --- /dev/null +++ b/gui/baculum/protected/Portlets/Console.php @@ -0,0 +1,51 @@ +CommandLine->Text); + if(!empty($cmd)) { + $command = explode(' ', $cmd); + $out = $this->Application->getModule('api')->set(array('console'), $command)->output; + if(is_array($out)) { + $out = array_slice($out, self::MAX_CONSOLE_OUTPUT_BATCH); + $output = $this->OutputListing->Text . PHP_EOL . implode(PHP_EOL, $out); + } else { + $output = $this->OutputListing->Text . PHP_EOL . $out; + } + $this->OutputListing->Text = $output; + $this->CommandLine->Text = ''; + } + } + + public function clearConsole($sender, $param) { + $this->OutputListing->Text = ''; + $this->CommandLine->Text = ''; + } +} + +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/Console.tpl b/gui/baculum/protected/Portlets/Console.tpl new file mode 100644 index 0000000000..3250000771 --- /dev/null +++ b/gui/baculum/protected/Portlets/Console.tpl @@ -0,0 +1,24 @@ + + diff --git a/gui/baculum/protected/Portlets/JobConfiguration.php b/gui/baculum/protected/Portlets/JobConfiguration.php new file mode 100644 index 0000000000..b2bcd404e3 --- /dev/null +++ b/gui/baculum/protected/Portlets/JobConfiguration.php @@ -0,0 +1,143 @@ +Run->setActionClass($this); + $this->Status->setActionClass($this); + $this->Cancel->setActionClass($this); + $this->Delete->setActionClass($this); + $this->Estimate->setActionClass($this); + } + + public function configure($jobId) { + $jobdata = $this->Application->getModule('api')->get(array('jobs', $jobId))->output; + $this->JobName->Text = $jobdata->name; + $this->JobID->Text = $jobdata->jobid; + $joblog = $this->Application->getModule('api')->get(array('joblog', $jobdata->jobid))->output; + $this->Estimation->Text = is_array($joblog) ? implode(PHP_EOL, $joblog) : Prado::localize("Output for selected job is not available yet or you do not have enabled logging job logs to catalog database." . PHP_EOL . PHP_EOL . "For watching job log there is need to add to the job Messages resource next directive:" . PHP_EOL . PHP_EOL . "console = all, !skipped, !saved" . PHP_EOL); + + $this->Level->dataSource = $this->Application->getModule('misc')->getJobLevels(); + $this->Level->SelectedValue = $jobdata->level; + $this->Level->dataBind(); + + $clients = $this->Application->getModule('api')->get(array('clients'))->output; + $clientsList = array(); + foreach($clients as $client) { + $clientsList[$client->clientid] = $client->name; + } + $this->Client->dataSource = $clientsList; + $this->Client->SelectedValue = $jobdata->clientid; + $this->Client->dataBind(); + + $filesetsAll = $this->Application->getModule('api')->get(array('filesets'))->output; + $filesetsList = array(); + foreach($filesetsAll as $director => $filesets) { + $filesetsList = array_merge($filesets, $filesetsList); + } + if($jobdata->filesetid != 0) { + $selectedFileset = $this->Application->getModule('api')->get(array('filesets', $jobdata->filesetid))->output; + } + $this->FileSet->dataSource = array_combine($filesetsList, $filesetsList); + $this->FileSet->SelectedValue = @$selectedFileset->fileset; + $this->FileSet->dataBind(); + + $pools = $this->Application->getModule('api')->get(array('pools'))->output; + $poolList = array(); + foreach($pools as $pool) { + $poolList[$pool->poolid] = $pool->name; + } + $this->Pool->dataSource = $poolList; + $this->Pool->SelectedValue = $jobdata->poolid; + $this->Pool->dataBind(); + + $storages = $this->Application->getModule('api')->get(array('storages'))->output; + $storagesList = array(); + foreach($storages as $storage) { + $storagesList[$storage->storageid] = $storage->name; + } + $this->Storage->dataSource = $storagesList; + $this->Storage->dataBind(); + + $this->Priority->Text = ($jobdata->priorjobid == 0) ? self::DEFAULT_JOB_PRIORITY : $jobdata->priorjobid; + $this->DeleteButton->Visible = true; + $this->CancelButton->Visible = in_array($jobdata->jobstatus, $this->runningJobStates); + } + + public function save($sender, $param) { + switch($sender->getParent()->ID) { + case $this->Estimate->ID: { + $params = array(); + $params['id'] = $this->JobID->Text; + $params['level'] = $this->Level->SelectedValue; + $params['fileset'] = $this->FileSet->SelectedValue; + $params['clientid'] = $this->Client->SelectedValue; + $params['accurate'] = (integer)$this->Accurate->Checked; + $result = $this->Application->getModule('api')->create(array('jobs', 'estimate'), $params)->output; + $this->Estimation->Text = implode(PHP_EOL, $result); + break; + } + case $this->Run->ID: { + if($this->PriorityValidator->IsValid === false) { + return false; + } + $params = array(); + $params['id'] = $this->JobID->Text; + $params['level'] = $this->Level->SelectedValue; + $params['fileset'] = $this->FileSet->SelectedValue; + $params['clientid'] = $this->Client->SelectedValue; + $params['storageid'] = $this->Storage->SelectedValue; + $params['poolid'] = $this->Pool->SelectedValue; + $params['priority'] = $this->Priority->Text; + $result = $this->Application->getModule('api')->create(array('jobs', 'run'), $params)->output; + $this->Estimation->Text = implode(PHP_EOL, $result); + break; + } + case $this->Delete->ID: { + $this->Application->getModule('api')->remove(array('jobs', $this->JobID->Text)); + $this->DeleteButton->Visible = false; + break; + } + case $this->Cancel->ID: { + $this->Application->getModule('api')->set(array('jobs', 'cancel', $this->JobID->Text), array('a' => 'b')); + $this->CancelButton->Visible = false; + break; + } + case $this->Status->ID: { + $joblog = $this->Application->getModule('api')->get(array('joblog', $this->JobID->Text))->output; + $this->Estimation->Text = is_array($joblog) ? implode(PHP_EOL, $joblog) : Prado::localize("Output for selected job is not available yet or you do not have enabled logging job logs to catalog database." . PHP_EOL . PHP_EOL . "For watching job log there is need to add to the job Messages resource next directive:" . PHP_EOL . PHP_EOL . "console = all, !skipped, !saved" . PHP_EOL); + break; + } + } + } + + public function priorityValidator($sender, $param) { + $isValid = preg_match('/^[0-9]+$/',$this->Priority->Text) === 1 && $this->Priority->Text > 0; + $param->setIsValid($isValid); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/JobConfiguration.tpl b/gui/baculum/protected/Portlets/JobConfiguration.tpl new file mode 100644 index 0000000000..8e57bbdd5d --- /dev/null +++ b/gui/baculum/protected/Portlets/JobConfiguration.tpl @@ -0,0 +1,76 @@ +<%@ MasterClass="Application.Portlets.ConfigurationPanel"%> + + <%[ Job name: ]%> +
      + +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + + +
      +
      + + +
      + + + + +
      +
      <%[ Console status ]%>
      +
      + +
      +
      +
      +
      +
      +
      + +
      +
      diff --git a/gui/baculum/protected/Portlets/JobList.php b/gui/baculum/protected/Portlets/JobList.php new file mode 100644 index 0000000000..d84bda5b1d --- /dev/null +++ b/gui/baculum/protected/Portlets/JobList.php @@ -0,0 +1,121 @@ + 'Backup', 'M' => 'Migrated', 'V' => 'Verify', 'R' => 'Restore', 'I' => 'Internal', 'D' => 'Admin', 'A' => 'Archive', 'C' => 'Copy', 'g' => 'Migration'); + + private $jobStates; + + public $jobLevels; + + public function onLoad($param) { + parent::onLoad($param); + $this->prepareData(); + $this->jobLevels = $this->Application->getModule('misc')->getJobLevels(); + } + + public function setWindowTitle($param) { + $this->windowTitle = $param; + } + + public function getJobType($jobLetter) { + return array_key_exists($jobLetter, $this->jobTypes) ? $this->jobTypes[$jobLetter] : null; + } + + public function getJobState($jobStateLetter) { + $jobstates = array( + 'C' => (object)array('value' => 'Created', 'description' =>'Created but not yet running'), + 'R' => (object)array('value' => 'Running', 'description' => 'Running'), + 'B' => (object)array('value' => 'Blocked', 'description' => 'Blocked'), + 'T' => (object)array('value' => 'Terminated', 'description' =>'Terminated normally'), + 'W' => (object)array('value' => 'Terminated with warnings', 'description' =>'Terminated normally with warnings'), + 'E' => (object)array('value' => 'Error', 'description' =>'Terminated in Error'), + 'e' => (object)array('value' => 'Non-fatal error', 'description' =>'Non-fatal error'), + 'f' => (object)array('value' => 'Fatal error', 'description' =>'Fatal error'), + 'D' => (object)array('value' => 'Verify', 'description' =>'Verify Differences'), + 'A' => (object)array('value' => 'Canceled by user', 'description' =>'Canceled by the user'), + 'I' => (object)array('value' => 'Incomplete', 'description' =>'Incomplete Job'), + 'F' => (object)array('value' => 'Waiting on FD', 'description' =>'Waiting on the File daemon'), + 'S' => (object)array('value' => 'Waiting on SD', 'description' =>'Waiting on the Storage daemon'), + 'm' => (object)array('value' => 'Waiting for new vol.', 'description' =>'Waiting for a new Volume to be mounted'), + 'M' => (object)array('value' => 'Waiting for mount', 'description' =>'Waiting for a Mount'), + 's' => (object)array('value' => 'Waiting for storage', 'description' =>'Waiting for Storage resource'), + 'j' => (object)array('value' => 'Waiting for job', 'description' =>'Waiting for Job resource'), + 'c' => (object)array('value' => 'Waiting for client', 'description' =>'Waiting for Client resource'), + 'd' => (object)array('value' => 'Waiting for Max. jobs', 'description' =>'Wating for Maximum jobs'), + 't' => (object)array('value' => 'Waiting for start', 'description' =>'Waiting for Start Time'), + 'p' => (object)array('value' => 'Waiting for higher priority', 'description' =>'Waiting for higher priority job to finish'), + 'i' => (object)array('value' => 'Batch insert', 'description' =>'Doing batch insert file records'), + 'a' => (object)array('value' => 'Despooling attributes', 'description' =>'SD despooling attributes'), + 'l' => (object)array('value' => 'Data despooling', 'description' =>'Doing data despooling'), + 'L' => (object)array('value' => 'Commiting data', 'description' =>'Committing data (last despool)') + ); + return array_key_exists($jobStateLetter, $jobstates) ? $jobstates[$jobStateLetter] : null; + } + + public function prepareData($forceReload = false) { + $allowedButtons = array('JobBtn', 'ReloadJobs', 'Run'); + if($this->Page->IsPostBack || $this->Page->IsCallBack || $forceReload) { + if(in_array($this->getPage()->CallBackEventTarget->ID, $allowedButtons) || $forceReload) { + $params = $this->getUrlParams('jobs', $this->getPage()->JobWindow->ID); + $jobs = $this->Application->getModule('api')->get($params); + $isDetailView = $this->Session['view' . $this->getPage()->JobWindow->ID] == 'details'; + $this->RepeaterShow->Visible = !$isDetailView; + $this->Repeater->DataSource = $isDetailView == false ? $jobs->output : array(); + $this->Repeater->dataBind(); + $this->DataGridShow->Visible = $isDetailView; + $this->DataGrid->DataSource = $isDetailView === true ? $this->Application->getModule('misc')->objectToArray($jobs->output) : array(); + $this->DataGrid->dataBind(); + } + } + } + + public function sortDataGrid($sender, $param) { + $params = $this->getUrlParams('jobs', $this->getPage()->JobWindow->ID); + $data = $this->Application->getModule('api')->get($params)->output; + $data = $this->Application->getModule('misc')->objectToArray($data); + $this->DataGrid->DataSource = $this->sortData($data, $param->SortExpression, $sender->UniqueID); + $this->DataGrid->dataBind(); + } + + public function setShowID($ShowID) { + $this->ShowID = $this->getMaster()->ShowID = $ShowID; + } + + public function getShowID() { + return $this->ShowID; + } + + public function configure($sender, $param) { + if($this->Page->IsCallBack) { + $this->getPage()->JobConfiguration->configure($param->CallbackParameter); + } + } +} +?> diff --git a/gui/baculum/protected/Portlets/JobList.tpl b/gui/baculum/protected/Portlets/JobList.tpl new file mode 100644 index 0000000000..0a9e4f3cb1 --- /dev/null +++ b/gui/baculum/protected/Portlets/JobList.tpl @@ -0,0 +1,92 @@ +<%@ MasterClass="Application.Portlets.SlideWindow" %> + + + + + + [<%=@$this->DataItem->jobid%>] <%=@$this->DataItem->name%> + + + + ConfigurationWindow<%=$this->getPage()->JobConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->JobConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->JobWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->JobWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + + + + + <%=$this->getParent()->Data['name']%> + + + ConfigurationWindow<%=$this->getPage()->JobConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->JobConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->JobWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->JobWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + T + + + + + L + + + + +
      <%=isset($this->getPage()->JobWindow->getJobState($this->getParent()->Data['jobstatus'])->value) ? $this->getPage()->JobWindow->getJobState($this->getParent()->Data['jobstatus'])->value : ''%>
      +
      +
      + +
      +
      +
      diff --git a/gui/baculum/protected/Portlets/JobRunConfiguration.php b/gui/baculum/protected/Portlets/JobRunConfiguration.php new file mode 100644 index 0000000000..ff8ef6f929 --- /dev/null +++ b/gui/baculum/protected/Portlets/JobRunConfiguration.php @@ -0,0 +1,112 @@ +Run->setActionClass($this); + $this->Estimate->setActionClass($this); + } + + public function configure($jobname) { + $this->JobName->Text = $jobname; + $this->Estimation->Text = ''; + + $this->Level->dataSource = $this->Application->getModule('misc')->getJobLevels(); + $this->Level->dataBind(); + + $clients = $this->Application->getModule('api')->get(array('clients'))->output; + $clientsList = array(); + foreach($clients as $client) { + $clientsList[$client->clientid] = $client->name; + } + $this->Client->dataSource = $clientsList; + $this->Client->dataBind(); + + $filesetsAll = $this->Application->getModule('api')->get(array('filesets'))->output; + $filesetsList = array(); + foreach($filesetsAll as $director => $filesets) { + $filesetsList = array_merge($filesets, $filesetsList); + } + $this->FileSet->dataSource = array_combine($filesetsList, $filesetsList); + $this->FileSet->dataBind(); + + $pools = $this->Application->getModule('api')->get(array('pools'))->output; + $poolList = array(); + foreach($pools as $pool) { + $poolList[$pool->poolid] = $pool->name; + } + $this->Pool->dataSource = $poolList; + $this->Pool->dataBind(); + + $storages = $this->Application->getModule('api')->get(array('storages'))->output; + $storagesList = array(); + foreach($storages as $storage) { + $storagesList[$storage->storageid] = $storage->name; + } + $this->Storage->dataSource = $storagesList; + $this->Storage->dataBind(); + + $this->Priority->Text = self::DEFAULT_JOB_PRIORITY; + } + + public function save($sender, $param) { + switch($sender->getParent()->ID) { + case $this->Estimate->ID: { + $params = array(); + $params['name'] = $this->JobName->Text; + $params['level'] = $this->Level->SelectedValue; + $params['fileset'] = $this->FileSet->SelectedValue; + $params['clientid'] = $this->Client->SelectedValue; + $params['accurate'] = (integer)$this->Accurate->Checked; + var_dump($params); + $result = $this->Application->getModule('api')->create(array('jobs', 'estimate'), $params)->output; + $this->Estimation->Text = implode(PHP_EOL, $result); + break; + } + case $this->Run->ID: { + if($this->PriorityValidator->IsValid === false) { + return false; + } + $params = array(); + $params['name'] = $this->JobName->Text; + $params['level'] = $this->Level->SelectedValue; + $params['fileset'] = $this->FileSet->SelectedValue; + $params['clientid'] = $this->Client->SelectedValue; + $params['storageid'] = $this->Storage->SelectedValue; + $params['poolid'] = $this->Pool->SelectedValue; + $params['priority'] = $this->Priority->Text; + $result = $this->Application->getModule('api')->create(array('jobs', 'run'), $params)->output; + $this->Estimation->Text = implode(PHP_EOL, $result); + break; + } + } + } + + public function priorityValidator($sender, $param) { + $isValid = preg_match('/^[0-9]+$/', $this->Priority->Text) === 1 && $this->Priority->Text > 0; + $param->setIsValid($isValid); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/JobRunConfiguration.tpl b/gui/baculum/protected/Portlets/JobRunConfiguration.tpl new file mode 100644 index 0000000000..649b87417d --- /dev/null +++ b/gui/baculum/protected/Portlets/JobRunConfiguration.tpl @@ -0,0 +1,73 @@ +<%@ MasterClass="Application.Portlets.ConfigurationPanel"%> + + <%[ Job name: ]%> +
      + +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + + +
      +
      + + +
      + +
      +
      <%[ Console status ]%>
      +
      + +
      +
      +
      +
      +
      +
      + +
      +
      diff --git a/gui/baculum/protected/Portlets/JobRunList.php b/gui/baculum/protected/Portlets/JobRunList.php new file mode 100644 index 0000000000..0ad8c41711 --- /dev/null +++ b/gui/baculum/protected/Portlets/JobRunList.php @@ -0,0 +1,94 @@ + 'Backup', 'M' => 'Migrated', 'V' => 'Verify', 'R' => 'Restore', 'I' => 'Internal', 'D' => 'Admin', 'A' => 'Archive', 'C' => 'Copy', 'g' => 'Migration'); + + private $jobStates; + + public function onLoad($param) { + parent::onLoad($param); + $this->prepareData(); + } + + public function setWindowTitle($param) { + $this->windowTitle = $param; + } + + public function prepareData($forceReload = false) { + $allowedButtons = array('JobRunBtn'); + if($this->Page->IsPostBack || $this->Page->IsCallBack || $forceReload) { + if(in_array($this->getPage()->CallBackEventTarget->ID, $allowedButtons) || $forceReload) { + $params = $this->getUrlParams(array('jobs', 'tasks'), $this->getPage()->JobRunWindow->ID); + $jobTasks = $this->Application->getModule('api')->get($params)->output; + $jobs = $this->prepareJobs($jobTasks); + $isDetailView = $this->Session['view' . $this->getPage()->JobRunWindow->ID] == 'details'; + $this->RepeaterShow->Visible = !$isDetailView; + $this->Repeater->DataSource = $isDetailView === false ? $jobs : array(); + $this->Repeater->dataBind(); + $this->DataGridShow->Visible = $isDetailView; + $this->DataGrid->DataSource = $isDetailView === true ? $jobs : array(); + $this->DataGrid->dataBind(); + } + } + } + + private function prepareJobs($jobTasks) { + $jobs = array(); + foreach($jobTasks as $director => $tasks) { + for($i = 0; $i < count($tasks); $i++) { + $jobs[] = array('name' => $tasks[$i], 'director' => $director); + } + } + return $jobs; + } + + public function sortDataGrid($sender, $param) { + $params = $this->getUrlParams(array('jobs', 'tasks'), $this->getPage()->JobRunWindow->ID); + $data = $this->Application->getModule('api')->get($params)->output; + $data = $this->prepareJobs($data); + $this->DataGrid->DataSource = $this->sortData($data, $param->SortExpression, $sender->UniqueID); + $this->DataGrid->dataBind(); + } + + public function setShowID($ShowID) { + $this->ShowID = $this->getMaster()->ShowID = $ShowID; + } + + public function getShowID() { + return $this->ShowID; + } + + public function configure($sender, $param) { + if($this->Page->IsCallBack) { + $this->getPage()->JobRunConfiguration->configure($param->CallbackParameter); + } + } +} +?> diff --git a/gui/baculum/protected/Portlets/JobRunList.tpl b/gui/baculum/protected/Portlets/JobRunList.tpl new file mode 100644 index 0000000000..525a03a491 --- /dev/null +++ b/gui/baculum/protected/Portlets/JobRunList.tpl @@ -0,0 +1,74 @@ +<%@ MasterClass="Application.Portlets.SlideWindow" %> + + + + + <%=($this->getPage()->JobRunWindow->oldDirector != $this->DataItem['director']) ? '
      Director: ' . $this->DataItem['director'] . '
      ': ''%> + + <%=@$this->DataItem['name']%> + + + + ConfigurationWindow<%=$this->getPage()->JobRunConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->JobRunConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->JobRunWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->JobRunWindow->ShowID%>SlideWindow.resetSize(); + } + + + + <%=!($this->getPage()->JobRunWindow->oldDirector = $this->DataItem['director'])%> +
      +
      +
      + + + + + <%=$this->getParent()->DataItem['name']%> + + + ConfigurationWindow<%=$this->getPage()->JobRunConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->JobRunConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->JobRunWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->JobRunWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + +
      diff --git a/gui/baculum/protected/Portlets/PoolConfiguration.php b/gui/baculum/protected/Portlets/PoolConfiguration.php new file mode 100644 index 0000000000..ecf608cf26 --- /dev/null +++ b/gui/baculum/protected/Portlets/PoolConfiguration.php @@ -0,0 +1,120 @@ +Apply->setActionClass($this); + $this->RestoreConfiguration->setActionClass($this); + $this->UpdateVolumes->setActionClass($this); + } + + public function configure($poolId) { + $pooldata = $this->Application->getModule('api')->get(array('pools', $poolId))->output; + $this->PoolName->Text = $pooldata->name; + $this->PoolID->Text = $pooldata->poolid; + $this->Enabled->Checked = $pooldata->enabled == 1; + $this->MaxVolumes->Text = $pooldata->maxvols; + $this->MaxVolJobs->Text = $pooldata->maxvoljobs; + $this->MaxVolBytes->Text = $pooldata->maxvolbytes; + $this->UseDuration->Text = intval($pooldata->voluseduration / 3600); // conversion to hours; + $this->RetentionPeriod->Text = intval($pooldata->volretention / 3600); // conversion to hours; + $this->LabelFormat->Text = $pooldata->labelformat; + $pools = $this->Application->getModule('api')->get(array('pools'))->output; + $poolList = array('none' => Prado::localize('select pool')); + foreach($pools as $pool) { + $poolList[$pool->poolid] = $pool->name; + } + $this->ScratchPool->dataSource = $poolList; + $this->ScratchPool->SelectedValue = $pooldata->scratchpoolid; + $this->ScratchPool->dataBind(); + $this->RecyclePool->dataSource = $poolList; + $this->RecyclePool->SelectedValue = $pooldata->recyclepoolid; + $this->RecyclePool->dataBind(); + $this->Recycle->Checked = $pooldata->recycle == 1; + $this->AutoPrune->Checked = $pooldata->autoprune == 1; + $this->ActionOnPurge->Checked = $pooldata->actiononpurge == 1; + } + + public function save($sender, $param) { + if(($sender->getParent()->ID == $this->Apply->ID) || ($sender->getParent()->ID == $this->UpdateVolumes->ID)) { + if($this->MaxVolumesValidator->IsValid === false || $this->MaxVolJobsValidator->IsValid === false || $this->MaxVolBytesValidator->IsValid === false || $this->UseDurationValidator->IsValid === false || $this->RetentionPeriodValidator->IsValid === false || $this->LabelFormatValidator->IsValid === false) { + return false; + } + $pooldata = array(); + $pooldata['poolid'] = $this->PoolID->Text; + $pooldata['enabled'] = (integer)$this->Enabled->Checked; + $pooldata['maxvols'] = $this->MaxVolumes->Text; + $pooldata['maxvoljobs'] = $this->MaxVolJobs->Text; + $pooldata['maxvolbytes'] = $this->MaxVolBytes->Text; + $pooldata['voluseduration'] = $this->UseDuration->Text * 3600; // conversion to seconds + $pooldata['volretention'] = $this->RetentionPeriod->Text * 3600; // conversion to seconds + $pooldata['labelformat'] = $this->LabelFormat->Text; + $pooldata['scratchpoolid'] = (integer)$this->ScratchPool->SelectedValue; + $pooldata['recyclepoolid'] = (integer)$this->RecyclePool->SelectedValue; + $pooldata['recycle'] = (integer)$this->Recycle->Checked; + $pooldata['autoprune'] = (integer)$this->AutoPrune->Checked; + $pooldata['actiononpurge'] = (integer)$this->ActionOnPurge->Checked; + $this->Application->getModule('api')->set(array('pools', $this->PoolID->Text), $pooldata); + if($sender->getParent()->ID == $this->UpdateVolumes->ID) { + $this->Application->getModule('api')->set(array('pools', 'update', 'volumes', $this->PoolID->Text), array('')); + } + } elseif($sender->getParent()->ID == $this->RestoreConfiguration->ID) { + $this->Application->getModule('api')->set(array('pools', 'update', $this->PoolID->Text), array('')); + //@TOFIX $this->configure($this->PoolID->Text); + } + } + + public function maxVolumesValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->MaxVolumes->Text) && $this->MaxVolumes->Text >= 0; + $param->setIsValid($isValid); + } + + public function maxVolJobsValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->MaxVolJobs->Text) && $this->MaxVolJobs->Text >= 0; + $param->setIsValid($isValid); + } + + public function maxVolBytesValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->MaxVolBytes->Text) && $this->MaxVolBytes->Text >= 0; + $param->setIsValid($isValid); + } + + public function useDurationValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->UseDuration->Text) && $this->UseDuration->Text >= 0; + $param->setIsValid($isValid); + } + + public function retentionPeriodValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->RetentionPeriod->Text) && $this->RetentionPeriod->Text >= 0; + $param->setIsValid($isValid); + } + + public function labelFormatValidator($sender, $param) { + $value = trim($this->LabelFormat->Text); + $isValid = !empty($value); + $param->setIsValid($isValid); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/PoolConfiguration.tpl b/gui/baculum/protected/Portlets/PoolConfiguration.tpl new file mode 100644 index 0000000000..f350ba9c23 --- /dev/null +++ b/gui/baculum/protected/Portlets/PoolConfiguration.tpl @@ -0,0 +1,97 @@ +<%@ MasterClass="Application.Portlets.ConfigurationPanel"%> + + <%[ Pool name: ]%>
      +
      + +
      +
      +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + +
      +   +   + +
      +
      diff --git a/gui/baculum/protected/Portlets/PoolList.php b/gui/baculum/protected/Portlets/PoolList.php new file mode 100644 index 0000000000..4e4944b962 --- /dev/null +++ b/gui/baculum/protected/Portlets/PoolList.php @@ -0,0 +1,75 @@ +prepareData(); + } + + public function setWindowTitle($param) { + $this->windowTitle = $param; + } + + public function prepareData($forceReload = false) { + $allowedButtons = array('PoolBtn', 'ReloadPools'); + if($this->Page->IsPostBack || $this->Page->IsCallBack || $forceReload) { + if(in_array($this->getPage()->CallBackEventTarget->ID, $allowedButtons) || $forceReload) { + $params = $this->getUrlParams('pools', $this->getPage()->PoolWindow->ID); + $pools = $this->Application->getModule('api')->get($params); + $isDetailView = $this->Session['view' . $this->getPage()->PoolWindow->ID] == 'details'; + $this->RepeaterShow->Visible = !$isDetailView; + $this->Repeater->DataSource = $isDetailView === false ? $pools->output : array(); + $this->Repeater->dataBind(); + $this->DataGridShow->Visible = $isDetailView; + $this->DataGrid->DataSource = $isDetailView === true ? $this->Application->getModule('misc')->objectToArray($pools->output) : array(); + $this->DataGrid->dataBind(); + } + } + } + + public function sortDataGrid($sender, $param) { + $params = $this->getUrlParams('pools', $this->getPage()->PoolWindow->ID); + $data = $this->Application->getModule('api')->get($params)->output; + $data = $this->Application->getModule('misc')->objectToArray($data); + $this->DataGrid->DataSource = $this->sortData($data, $param->SortExpression, $sender->UniqueID); + $this->DataGrid->dataBind(); + } + + public function setShowID($ShowID) { + $this->ShowID = $this->getMaster()->ShowID = $ShowID; + } + + public function getShowID() { + return $this->ShowID; + } + + public function configure($sender, $param) { + if($this->Page->IsCallBack) { + $this->getPage()->PoolConfiguration->configure($param->CallbackParameter); + } + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/PoolList.tpl b/gui/baculum/protected/Portlets/PoolList.tpl new file mode 100644 index 0000000000..add1ca8b61 --- /dev/null +++ b/gui/baculum/protected/Portlets/PoolList.tpl @@ -0,0 +1,88 @@ +<%@ MasterClass="Application.Portlets.SlideWindow"%> + + + + + + <%=@$this->DataItem->name%> + + + + ConfigurationWindow<%=$this->getPage()->PoolConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->PoolConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->PoolWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->PoolWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + + + + + <%=$this->getParent()->Data['name']%> + + + ConfigurationWindow<%=$this->getPage()->PoolConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->PoolConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->PoolWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->PoolWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + + <%=(integer)($this->getParent()->Data['volretention'] / 3600 / 24)%> <%=$this->getParent()->Data['volretention'] < 172800 ? 'day' : 'days'%> + + + + + <%=$this->getParent()->Data['autoprune'] == 1 ? 'Yes' : 'No'%> + + + + + <%=$this->getParent()->Data['recycle'] == 1 ? 'Yes' : 'No'%> + + + + + diff --git a/gui/baculum/protected/Portlets/Portlets.php b/gui/baculum/protected/Portlets/Portlets.php new file mode 100644 index 0000000000..db24c93479 --- /dev/null +++ b/gui/baculum/protected/Portlets/Portlets.php @@ -0,0 +1,62 @@ +Session['limit' . $id]; + if(is_numeric($limit)) { + if(is_array($section)) { + array_push($section, 'limit', $limit); + $params = $section; + } else { + $params = array($section, 'limit', $limit); + } + } else { + $params = (is_array($section)) ? $section : array($section); + } + return $params; + } + + protected function getSortOrder($id) { + $order = self::SORT_ASC; + if(@$this->Request->Cookies['sort']->Value == $id) { + $this->Response->Cookies[] = new THttpCookie('sort', null); + $order = self::SORT_DESC; + } else { + $this->Response->Cookies[] = new THttpCookie('sort', $id); + $order = self::SORT_ASC; + } + return $order; + } + + protected function sortData($data, $key, $id) { + if($this->getSortOrder($id) == self::SORT_DESC) { + $compare = create_function('$a,$b','if ($a["'.$key.'"] == $b["'.$key.'"]) {return 0;}else {return ($a["'.$key.'"] < $b["'.$key.'"]) ? 1 : -1;}'); + } else { + $compare = create_function('$a,$b','if ($a["'.$key.'"] == $b["'.$key.'"]) {return 0;}else {return ($a["'.$key.'"] > $b["'.$key.'"]) ? 1 : -1;}'); + } + usort($data,$compare); + return $data; + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/SlideWindow.php b/gui/baculum/protected/Portlets/SlideWindow.php new file mode 100644 index 0000000000..56a8c59768 --- /dev/null +++ b/gui/baculum/protected/Portlets/SlideWindow.php @@ -0,0 +1,65 @@ +Session['view' . $this->getParent()->ID]) && empty($this->Session['limit' . $this->getParent()->ID])) { + $this->Session['view' . $this->getParent()->ID] = self::NORMAL_VIEW; + $this->Session['limit' . $this->getParent()->ID] = 'unlimited'; + } + } + + public function onLoad($param) { + parent::onLoad($param); + if(!$this->getPage()->IsPostBack) { + $this->Limit->dataSource = array_combine($this->elementsLimits, $this->elementsLimits); + $this->Limit->SelectedValue = $this->Session['limit' . $this->getParent()->ID]; + $this->Limit->dataBind(); + $this->Simple->Checked = ($this->Session['view' . $this->getParent()->ID] == self::NORMAL_VIEW); + $this->Details->Checked = ($this->Session['view' . $this->getParent()->ID] == self::DETAIL_VIEW); + } + } + + public function switchView($sender, $param) { + $this->Session['view' . $this->getParent()->ID] = ($this->Simple->Checked === true) ? self::NORMAL_VIEW : self::DETAIL_VIEW; + $this->Session['limit' . $this->getParent()->ID] = $this->Limit->SelectedValue; + $this->getParent()->prepareData(true); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/SlideWindow.tpl b/gui/baculum/protected/Portlets/SlideWindow.tpl new file mode 100644 index 0000000000..faec0e304e --- /dev/null +++ b/gui/baculum/protected/Portlets/SlideWindow.tpl @@ -0,0 +1,45 @@ + + \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/StorageConfiguration.php b/gui/baculum/protected/Portlets/StorageConfiguration.php new file mode 100644 index 0000000000..ae9eb7b6da --- /dev/null +++ b/gui/baculum/protected/Portlets/StorageConfiguration.php @@ -0,0 +1,87 @@ +Mount->setActionClass($this); + $this->Release->setActionClass($this); + $this->Umount->setActionClass($this); + $this->Status->setActionClass($this); + } + + public function configure($storageId) { + $storagedata = $this->Application->getModule('api')->get(array('storages', 'show', $storageId))->output; + $this->ShowStorage->Text = implode(PHP_EOL, $storagedata); + $storage = $this->Application->getModule('api')->get(array('storages', $storageId))->output; + $this->StorageName->Text = $storage->name; + $this->StorageID->Text = $storage->storageid; + $this->AutoChanger->Visible = (boolean)$storage->autochanger; + } + + public function save($sender, $param) { + $isValid = $this->DriveValidator->IsValid === true && $this->SlotValidator->IsValid === true; + switch($sender->getParent()->ID) { + case $this->Status->ID: { + $status = $this->Application->getModule('api')->get(array('storages', 'status', $this->StorageID->Text))->output; + $this->ShowStorage->Text = implode(PHP_EOL, $status); + break; + } + case $this->Mount->ID: { + if($isValid === false) { + return; + } + $drive = ($this->AutoChanger->Visible === true) ? intval($this->Drive->Text) : 0; + $slot = ($this->AutoChanger->Visible === true) ? intval($this->Slot->Text) : 0; + $mount = $this->Application->getModule('api')->get(array('storages', 'mount', $this->StorageID->Text, $drive, $slot))->output; + $this->ShowStorage->Text = implode(PHP_EOL, $mount); + break; + } + case $this->Umount->ID: { + if($isValid === false) { + return; + } + $drive = ($this->AutoChanger->Visible === true) ? intval($this->Drive->Text) : 0; + $umount = $this->Application->getModule('api')->get(array('storages', 'umount', $this->StorageID->Text, $drive))->output; + $this->ShowStorage->Text = implode(PHP_EOL, $umount); + break; + } + case $this->Release->ID: { + $release = $this->Application->getModule('api')->get(array('storages', 'release', $this->StorageID->Text))->output; + $this->ShowStorage->Text = implode(PHP_EOL, $release); + break; + } + } + } + + public function driveValidator($sender, $param) { + $isValid = is_numeric($this->Drive->Text); + $param->setIsValid($isValid); + } + + public function slotValidator($sender, $param) { + $isValid = is_numeric($this->Slot->Text); + $param->setIsValid($isValid); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/StorageConfiguration.tpl b/gui/baculum/protected/Portlets/StorageConfiguration.tpl new file mode 100644 index 0000000000..94cd635521 --- /dev/null +++ b/gui/baculum/protected/Portlets/StorageConfiguration.tpl @@ -0,0 +1,35 @@ +<%@ MasterClass="Application.Portlets.ConfigurationPanel"%> + + <%[ Storage name: ]%> +
      + +
      <%[ Console status ]%>
      +
      + +
      + +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +        +
      +
      diff --git a/gui/baculum/protected/Portlets/StorageList.php b/gui/baculum/protected/Portlets/StorageList.php new file mode 100644 index 0000000000..20154272f8 --- /dev/null +++ b/gui/baculum/protected/Portlets/StorageList.php @@ -0,0 +1,75 @@ +prepareData(); + } + + public function setWindowTitle($param) { + $this->windowTitle = $param; + } + + public function prepareData($forceReload = false) { + $allowedButtons = array('StorageBtn'); + if($this->Page->IsPostBack || $this->Page->IsCallBack || $forceReload) { + if(in_array($this->getPage()->CallBackEventTarget->ID, $allowedButtons) || $forceReload) { + $params = $this->getUrlParams('storages', $this->getPage()->StorageWindow->ID); + $storages = $this->Application->getModule('api')->get($params); + $isDetailView = $this->Session['view' . $this->getPage()->StorageWindow->ID] == 'details'; + $this->RepeaterShow->Visible = !$isDetailView; + $this->Repeater->DataSource = $isDetailView === false ? $storages->output : array(); + $this->Repeater->dataBind(); + $this->DataGridShow->Visible = $isDetailView; + $this->DataGrid->DataSource = $isDetailView === true ? $this->Application->getModule('misc')->objectToArray($storages->output) : array(); + $this->DataGrid->dataBind(); + } + } + } + + public function sortDataGrid($sender, $param) { + $params = $this->getUrlParams('storages', $this->getPage()->StorageWindow->ID); + $data = $this->Application->getModule('api')->get($params)->output; + $data = $this->Application->getModule('misc')->objectToArray($data); + $this->DataGrid->DataSource = $this->sortData($data, $param->SortExpression, $sender->UniqueID); + $this->DataGrid->dataBind(); + } + + public function setShowID($ShowID) { + $this->ShowID = $this->getMaster()->ShowID = $ShowID; + } + + public function getShowID() { + return $this->ShowID; + } + + public function configure($sender, $param) { + if($this->Page->IsCallBack) { + $this->getPage()->StorageConfiguration->configure($param->CallbackParameter); + } + } +} +?> diff --git a/gui/baculum/protected/Portlets/StorageList.tpl b/gui/baculum/protected/Portlets/StorageList.tpl new file mode 100644 index 0000000000..5cd4485490 --- /dev/null +++ b/gui/baculum/protected/Portlets/StorageList.tpl @@ -0,0 +1,71 @@ +<%@ MasterClass="Application.Portlets.SlideWindow" %> + + + + + + <%=@$this->DataItem->name%> + + + + ConfigurationWindow<%=$this->getPage()->StorageConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->StorageConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->StorageWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->StorageWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + + + + <%=$this->getParent()->Data['name']%> + + + ConfigurationWindow<%=$this->getPage()->StorageConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->StorageConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->StorageWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->StorageWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + <%=$this->getParent()->Data['autochanger'] == 1 ? 'Yes' : 'No'%> + + + + + diff --git a/gui/baculum/protected/Portlets/VolumeConfiguration.php b/gui/baculum/protected/Portlets/VolumeConfiguration.php new file mode 100644 index 0000000000..0cb702a562 --- /dev/null +++ b/gui/baculum/protected/Portlets/VolumeConfiguration.php @@ -0,0 +1,143 @@ +Apply->setActionClass($this); + $this->Prune->setActionClass($this); + $this->Purge->setActionClass($this); + } + + public function configure($mediaId) { + $voldata = $this->Application->getModule('api')->get(array('volumes', $mediaId))->output; + $this->VolumeID->Text = $voldata->mediaid; + $this->VolumeName->Text = $voldata->volumename; + $volstates = $this->getVolumeStates(true); + if (!in_array($voldata->volstatus, $volstates)) { + array_push($volstates, $voldata->volstatus); + } + $volstatesSelect = array(); + for($i = 0; $i < count($volstates); $i++) { + $volstatesSelect[$volstates[$i]] = $volstates[$i]; + } + $this->VolumeStatus->DataSource = $volstatesSelect; + $this->VolumeStatus->SelectedValue = $voldata->volstatus; + $this->VolumeStatus->dataBind(); + $this->RetentionPeriod->Text = intval($voldata->volretention / 3600); // conversion to hours + $this->UseDuration->Text = intval($voldata->voluseduration / 3600); // conversion to hours + $this->MaxVolJobs->Text = $voldata->maxvoljobs; + $this->MaxVolFiles->Text = $voldata->maxvolfiles; + $this->MaxVolBytes->Text = $voldata->maxvolbytes; + $this->Slot->Text = $voldata->slot; + $this->Recycle->Checked = ($voldata->recycle == 1); + $this->Enabled->Checked = ($voldata->enabled == 1); + $this->InChanger->Checked = ($voldata->inchanger == 1); + $pools = $this->Application->getModule('api')->get(array('pools'))->output; + $poolList = array(); + foreach($pools as $pool) { + $poolList[$pool->poolid] = $pool->name; + } + $this->Pool->dataSource = $poolList; + $this->Pool->SelectedValue = $voldata->poolid; + $this->Pool->dataBind(); + } + + public function save($sender, $param) { + switch($sender->getParent()->ID) { + case $this->Apply->ID: { + $isInvalid = $this->RetentionPeriodValidator->IsValid === false || $this->UseDurationValidator->IsValid === false || $this->MaxVolJobsValidator->IsValid === false || $this->MaxVolFilesValidator->IsValid === false || $this->MaxVolBytesValidator->IsValid === false || $this->SlotValidator->IsValid === false; + if($isInvalid) { + return false; + } + $voldata = array(); + $voldata['mediaid'] = $this->VolumeID->Text; + $voldata['volstatus'] = $this->VolumeStatus->SelectedValue; + $voldata['poolid'] = $this->Pool->SelectedValue; + $voldata['volretention'] = $this->RetentionPeriod->Text * 3600; // conversion to seconds + $voldata['voluseduration'] = $this->UseDuration->Text * 3600; // conversion to seconds + $voldata['maxvoljobs'] = $this->MaxVolJobs->Text; + $voldata['maxvolfiles'] = $this->MaxVolFiles->Text; + $voldata['maxvolbytes'] = $this->MaxVolBytes->Text; + $voldata['slot'] = $this->Slot->Text; + $voldata['recycle'] = (integer)$this->Recycle->Checked; + $voldata['enabled'] = (integer)$this->Enabled->Checked; + $voldata['inchanger'] = (integer)$this->InChanger->Checked; + $this->Application->getModule('api')->set(array('volumes', $voldata['mediaid']), $voldata); + break; + } + case $this->Prune->ID: { + $this->Application->getModule('api')->get(array('volumes', 'prune', $this->VolumeID->Text)); + break; + } + case $this->Purge->ID: { + $this->Application->getModule('api')->get(array('volumes', 'purge', $this->VolumeID->Text)); + break; + } + } + } + + public function getVolumeStates($forSetOnly = false) { + $states = ($forSetOnly === true ) ? $this->volumeStatesForSet : array_merge($this->volumeStatesByDirectorOnly, $this->volumeStatesForSet); + return $states; + } + + public function retentionPeriodValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->RetentionPeriod->Text) && $this->RetentionPeriod->Text >= 0; + $param->setIsValid($isValid); + } + + public function useDurationValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->UseDuration->Text) && $this->UseDuration->Text >= 0; + $param->setIsValid($isValid); + } + + public function maxVolJobsValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->MaxVolJobs->Text) && $this->MaxVolJobs->Text >= 0; + $param->setIsValid($isValid); + } + + public function maxVolFilesValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->MaxVolFiles->Text) && $this->MaxVolFiles->Text >= 0; + $param->setIsValid($isValid); + } + + public function maxVolBytesValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->MaxVolBytes->Text) && $this->MaxVolBytes->Text >= 0; + $param->setIsValid($isValid); + } + + public function slotValidator($sender, $param) { + $isValid = preg_match('/^\d+$/', $this->Slot->Text) && $this->Slot->Text >= 0; + $param->setIsValid($isValid); + } +} +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/VolumeConfiguration.tpl b/gui/baculum/protected/Portlets/VolumeConfiguration.tpl new file mode 100644 index 0000000000..f4d4f0436e --- /dev/null +++ b/gui/baculum/protected/Portlets/VolumeConfiguration.tpl @@ -0,0 +1,91 @@ +<%@ MasterClass="Application.Portlets.ConfigurationPanel"%> + + <%[ Volume name: ]%> +
      + +
      +
      +
      + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + +
      +    +
      +
      diff --git a/gui/baculum/protected/Portlets/VolumeList.php b/gui/baculum/protected/Portlets/VolumeList.php new file mode 100644 index 0000000000..7d6e2bc42f --- /dev/null +++ b/gui/baculum/protected/Portlets/VolumeList.php @@ -0,0 +1,101 @@ +prepareData(); + } + + public function setWindowTitle($param) { + $this->windowTitle = $param; + } + + public function prepareData($forceReload = false) { + $allowedButtons = array('MediaBtn', 'ReloadVolumes'); + if($this->Page->IsPostBack || $this->Page->IsCallBack || $forceReload) { + if(in_array($this->getPage()->CallBackEventTarget->ID, $allowedButtons) || $forceReload) { + $params = $this->getUrlParams('volumes', $this->getPage()->VolumeWindow->ID); + array_push($params, '?showpools=1'); + $volumes = $this->Application->getModule('api')->get($params); + $isDetailView = $this->Session['view' . $this->getPage()->VolumeWindow->ID] == 'details'; + $this->RepeaterShow->Visible = !$isDetailView; + $this->Repeater->DataSource = $volumes->output; + $this->Repeater->dataBind(); + $this->DataGridShow->Visible = $isDetailView; + $this->DataGrid->DataSource = $this->Application->getModule('misc')->objectToArray($volumes->output); + $this->DataGrid->dataBind(); + } + } + } + + protected function sortData($data, $key, $id) { + if($this->getSortOrder($id) == parent::SORT_DESC) { + if($key == 'pool') { + $key = 'name'; + $compare = create_function('$a,$b','if ($a["pool"]["'.$key.'"] == $b["pool"]["'.$key.'"]) {return 0;}else {return ($a["pool"]["'.$key.'"] < $b["pool"]["'.$key.'"]) ? 1 : -1;}'); + } else { + $compare = create_function('$a,$b','if ($a["'.$key.'"] == $b["'.$key.'"]) {return 0;}else {return ($a["'.$key.'"] < $b["'.$key.'"]) ? 1 : -1;}'); + } + } else { + if($key == 'pool') { + $key = 'name'; + $compare = create_function('$a,$b','if ($a["pool"]["'.$key.'"] == $b["pool"]["'.$key.'"]) {return 0;}else {return ($a["pool"]["'.$key.'"] > $b["pool"]["'.$key.'"]) ? 1 : -1;}'); + } else { + $compare = create_function('$a,$b','if ($a["'.$key.'"] == $b["'.$key.'"]) {return 0;}else {return ($a["'.$key.'"] > $b["'.$key.'"]) ? 1 : -1;}'); + } + } + usort($data,$compare); + return $data; + } + + public function sortDataGrid($sender, $param) { + $params = $this->getUrlParams('volumes', $this->getPage()->VolumeWindow->ID); + array_push($params, '?showpools=1'); + $data = $this->Application->getModule('api')->get($params)->output; + $data = $this->Application->getModule('misc')->objectToArray($data); + $this->DataGrid->DataSource = $this->sortData($data, $param->SortExpression, $sender->UniqueID); + $this->DataGrid->dataBind(); + } + + public function setShowID($ShowID) { + $this->ShowID = $this->getMaster()->ShowID = $ShowID; + } + + public function getShowID() { + return $this->ShowID; + } + + public function configure($sender, $param) { + if($this->Page->IsCallBack) { + $this->getPage()->VolumeConfiguration->configure($param->CallbackParameter); + } + } +} +?> diff --git a/gui/baculum/protected/Portlets/VolumeList.tpl b/gui/baculum/protected/Portlets/VolumeList.tpl new file mode 100644 index 0000000000..f85e7a9218 --- /dev/null +++ b/gui/baculum/protected/Portlets/VolumeList.tpl @@ -0,0 +1,90 @@ +<%@ MasterClass="Application.Portlets.SlideWindow" %> + + + + + <%=(isset($this->DataItem->pool->name) && $this->getPage()->VolumeWindow->oldPool != $this->DataItem->pool->name) ? '
      Pool: ' . $this->DataItem->pool->name . '
      ': ''%> + + <%=@$this->DataItem->volumename%> +
      <%=isset($this->DataItem->volstatus) ? $this->DataItem->volstatus : ''%>
      +
      + + + ConfigurationWindow<%=$this->getPage()->VolumeConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->VolumeConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->VolumeWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->VolumeWindow->ShowID%>SlideWindow.resetSize(); + } + + + + <%=!(isset($this->DataItem->pool->name) ? ($this->getPage()->VolumeWindow->oldPool = $this->DataItem->pool->name) : false)%> +
      +
      +
      + + + + + <%=$this->getParent()->Data['volumename']%> + + + ConfigurationWindow<%=$this->getPage()->VolumeConfiguration->getMaster()->ClientID%>.show(); + ConfigurationWindow<%=$this->getPage()->VolumeConfiguration->getMaster()->ClientID%>.progress(false); + if(<%=$this->getPage()->VolumeWindow->ShowID%>SlideWindow.isFullSize()) { + <%=$this->getPage()->VolumeWindow->ShowID%>SlideWindow.resetSize(); + } + + + + + + + + + <%=$this->getParent()->Data['pool']['name']%> + + + + +
      <%=$this->getParent()->Data['volstatus']%>
      +
      +
      + +
      +
      +
      diff --git a/gui/baculum/protected/Portlets/VolumesTools.php b/gui/baculum/protected/Portlets/VolumesTools.php new file mode 100644 index 0000000000..538f135cb5 --- /dev/null +++ b/gui/baculum/protected/Portlets/VolumesTools.php @@ -0,0 +1,186 @@ +LabelButton->setActionClass($this); + $this->UpdateSlotsButton->setActionClass($this); + $this->UpdateSlotsScanButton->setActionClass($this); + } + + public function onLoad($param) { + parent::onLoad($param); + if(!$this->getPage()->IsPostBack) { + $storages = $this->Application->getModule('api')->get(array('storages'))->output; + $storagesList = array(); + foreach($storages as $storage) { + $storagesList[$storage->storageid] = $storage->name; + } + $this->StorageLabel->dataSource = $storagesList; + $this->StorageLabel->dataBind(); + $this->StorageUpdateSlots->dataSource = $storagesList; + $this->StorageUpdateSlots->dataBind(); + $this->StorageUpdateSlotsScan->dataSource = $storagesList; + $this->StorageUpdateSlotsScan->dataBind(); + + $pools = $this->Application->getModule('api')->get(array('pools'))->output; + $poolsList = array(); + foreach($pools as $pool) { + $poolsList[$pool->poolid] = $pool->name; + } + $this->PoolLabel->dataSource = $poolsList; + $this->PoolLabel->dataBind(); + } + } + + public function save($sender, $param) { + switch($sender->getParent()->ID) { + case $this->LabelButton->ID: { + if($this->LabelNameValidator->isValid === false) { + return; + } + $cmd = array('label'); + if($this->Barcodes->Checked == true) { + $cmd[] = 'barcodes'; + $cmd[] = 'slots="' . $this->SlotsLabel->Text . '"'; + } else { + $cmd[] = 'volume="' . $this->LabelName->Text . '"'; + } + $cmd[] = 'drive="' . $this->DriveLabel->Text . '"'; + $cmd[] = 'storage="'. $this->StorageLabel->SelectedItem->Text . '"'; + $cmd[] = 'pool="'. $this->PoolLabel->SelectedItem->Text . '"'; + $this->getPage()->Console->CommandLine->Text = implode(' ', $cmd); + $this->getPage()->Console->sendCommand($sender, $param); + break; + } + case $this->UpdateSlotsButton->ID: { + $cmd = array('update'); + $cmd[] = 'slots="' . $this->SlotsUpdateSlots->Text . '"'; + $cmd[] = 'drive="' . $this->DriveUpdateSlots->Text . '"'; + $cmd[] = 'storage="'. $this->StorageUpdateSlots->SelectedItem->Text . '"'; + $this->getPage()->Console->CommandLine->Text = implode(' ', $cmd); + $this->getPage()->Console->sendCommand($sender, $param); + break; + } + case $this->UpdateSlotsScanButton->ID: { + $cmd = array('update'); + $cmd[] = 'slots="' . $this->SlotsUpdateSlotsScan->Text . '"'; + $cmd[] = 'scan'; + $cmd[] = 'drive="' . $this->DriveUpdateSlotsScan->Text . '"'; + $cmd[] = 'storage="'. $this->StorageUpdateSlotsScan->SelectedItem->Text . '"'; + $this->getPage()->Console->CommandLine->Text = implode(' ', $cmd); + $this->getPage()->Console->sendCommand($sender, $param); + break; + } + } + } + + public function setBarcodes($sender, $param) { + $this->LabelNameField->Display = $sender->Checked === false ? 'Dynamic' : 'None'; + $this->BarcodeSlotsField->Display = $sender->Checked === true ? 'Dynamic' : 'None'; + } + + public function setLabelVolume($sender, $param) { + $this->Labeling->Display = $sender->Checked === true ? 'Dynamic' : 'None'; + $this->UpdatingSlots->Display = 'None'; + $this->UpdatingSlotsScan->Display = 'None'; + } + + public function setUpdateSlots($sender, $param) { + $this->UpdatingSlots->Display = $sender->Checked === true ? 'Dynamic' : 'None'; + $this->Labeling->Display = 'None'; + $this->UpdatingSlotsScan->Display = 'None'; + } + + public function setUpdateSlotsScan($sender, $param) { + $this->UpdatingSlotsScan->Display = $sender->Checked === true ? 'Dynamic' : 'None'; + $this->UpdatingSlots->Display = 'None'; + $this->Labeling->Display = 'None'; + } + + public function labelNameValidator($sender, $param) { + $isValid = true; + if($this->LabelVolume->Checked === true && $this->Barcodes->Checked === false) { + $isValid = preg_match('/'. $this->labelVolumePattern . '/', $this->LabelName->Text) === 1; + } + $param->setIsValid($isValid); + } + + public function slotsLabelValidator($sender, $param) { + $isValid = true; + if($this->LabelVolume->Checked === true && $this->Barcodes->Checked === true) { + $isValid = preg_match('/' . $this->slotsPattern . '/', $this->SlotsLabel->Text) === 1; + } + $param->setIsValid($isValid); + } + + public function driveLabelValidator($sender, $param) { + $isValid = true; + if($this->LabelVolume->Checked === true) { + $isValid = preg_match('/' . $this->drivePattern . '/', $this->DriveLabel->Text) === 1; + } + $param->setIsValid($isValid); + } + + public function slotsUpdateSlotsValidator($sender, $param) { + $isValid = true; + if($this->UpdateSlots->Checked === true) { + $isValid = preg_match('/' . $this->slotsPattern . '/', $this->SlotsUpdateSlots->Text) === 1; + } + $param->setIsValid($isValid); + } + + public function driveUpdateSlotsValidator($sender, $param) { + $isValid = true; + if($this->UpdateSlots->Checked === true) { + $isValid = preg_match('/' . $this->drivePattern . '/', $this->DriveUpdateSlots->Text) === 1; + } + $param->setIsValid($isValid); + } + + public function slotsUpdateSlotsScanValidator($sender, $param) { + $isValid = true; + if($this->UpdateSlotsScan->Checked === true) { + $isValid = preg_match('/' . $this->slotsPattern . '/', $this->SlotsUpdateSlotsScan->Text) === 1; + } + $param->setIsValid($isValid); + } + + public function driveUpdateSlotsScanValidator($sender, $param) { + $isValid = true; + if($this->UpdateSlotsScan->Checked === true) { + $isValid = preg_match('/' . $this->drivePattern . '/', $this->DriveUpdateSlotsScan->Text) === 1; + } + $param->setIsValid($isValid); + } +} + +?> \ No newline at end of file diff --git a/gui/baculum/protected/Portlets/VolumesTools.tpl b/gui/baculum/protected/Portlets/VolumesTools.tpl new file mode 100644 index 0000000000..452c2398b9 --- /dev/null +++ b/gui/baculum/protected/Portlets/VolumesTools.tpl @@ -0,0 +1,135 @@ + + \ No newline at end of file diff --git a/gui/baculum/protected/application.xml b/gui/baculum/protected/application.xml new file mode 100644 index 0000000000..fa4232622e --- /dev/null +++ b/gui/baculum/protected/application.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/baculum/protected/runtime/.gitignore b/gui/baculum/protected/runtime/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gui/baculum/themes/Baculum-v1/ajax-loader.gif b/gui/baculum/themes/Baculum-v1/ajax-loader.gif new file mode 100644 index 0000000000..2fd8e0737e Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/ajax-loader.gif differ diff --git a/gui/baculum/themes/Baculum-v1/ajax-loader.orig.gif b/gui/baculum/themes/Baculum-v1/ajax-loader.orig.gif new file mode 100644 index 0000000000..61447df88d Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/ajax-loader.orig.gif differ diff --git a/gui/baculum/themes/Baculum-v1/background.png b/gui/baculum/themes/Baculum-v1/background.png new file mode 100644 index 0000000000..bcde42437c Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/background.png differ diff --git a/gui/baculum/themes/Baculum-v1/beleczka niebieska.png b/gui/baculum/themes/Baculum-v1/beleczka niebieska.png new file mode 100644 index 0000000000..5e9f0f2fcb Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/beleczka niebieska.png differ diff --git a/gui/baculum/themes/Baculum-v1/bls_bottom.png b/gui/baculum/themes/Baculum-v1/bls_bottom.png new file mode 100644 index 0000000000..15ccb9e3ae Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/bls_bottom.png differ diff --git a/gui/baculum/themes/Baculum-v1/bls_top.png b/gui/baculum/themes/Baculum-v1/bls_top.png new file mode 100644 index 0000000000..ec42c2f7c9 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/bls_top.png differ diff --git a/gui/baculum/themes/Baculum-v1/button-center.png b/gui/baculum/themes/Baculum-v1/button-center.png new file mode 100644 index 0000000000..3a7f6d4725 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/button-center.png differ diff --git a/gui/baculum/themes/Baculum-v1/button-left.png b/gui/baculum/themes/Baculum-v1/button-left.png new file mode 100644 index 0000000000..05fb405059 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/button-left.png differ diff --git a/gui/baculum/themes/Baculum-v1/button-right.png b/gui/baculum/themes/Baculum-v1/button-right.png new file mode 100644 index 0000000000..d9c228ffb0 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/button-right.png differ diff --git a/gui/baculum/themes/Baculum-v1/client-icon.png b/gui/baculum/themes/Baculum-v1/client-icon.png new file mode 100644 index 0000000000..81551fffff Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/client-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/client_icon.png b/gui/baculum/themes/Baculum-v1/client_icon.png new file mode 100644 index 0000000000..cbfc7d0239 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/client_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/client_icon_inactive.png b/gui/baculum/themes/Baculum-v1/client_icon_inactive.png new file mode 100644 index 0000000000..16e898050e Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/client_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/close.png b/gui/baculum/themes/Baculum-v1/close.png new file mode 100644 index 0000000000..5735484488 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/close.png differ diff --git a/gui/baculum/themes/Baculum-v1/console-icon.png b/gui/baculum/themes/Baculum-v1/console-icon.png new file mode 100644 index 0000000000..426a83f858 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/console-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/console_icon.png b/gui/baculum/themes/Baculum-v1/console_icon.png new file mode 100644 index 0000000000..14dec17915 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/console_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/directory-icon.png b/gui/baculum/themes/Baculum-v1/directory-icon.png new file mode 100644 index 0000000000..b488acba5c Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/directory-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/file-icon.png b/gui/baculum/themes/Baculum-v1/file-icon.png new file mode 100644 index 0000000000..e808ec91d2 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/file-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/icon_close.png b/gui/baculum/themes/Baculum-v1/icon_close.png new file mode 100644 index 0000000000..9facce8ff2 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/icon_close.png differ diff --git a/gui/baculum/themes/Baculum-v1/icon_err.png b/gui/baculum/themes/Baculum-v1/icon_err.png new file mode 100644 index 0000000000..447e7c5376 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/icon_err.png differ diff --git a/gui/baculum/themes/Baculum-v1/icon_ok.png b/gui/baculum/themes/Baculum-v1/icon_ok.png new file mode 100644 index 0000000000..dad2fa8b7b Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/icon_ok.png differ diff --git a/gui/baculum/themes/Baculum-v1/job-icon.png b/gui/baculum/themes/Baculum-v1/job-icon.png new file mode 100644 index 0000000000..6caed40465 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/job-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/job_icon.png b/gui/baculum/themes/Baculum-v1/job_icon.png new file mode 100644 index 0000000000..a8d00f55b9 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/job_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/job_icon_inactive.png b/gui/baculum/themes/Baculum-v1/job_icon_inactive.png new file mode 100644 index 0000000000..413e9a2c97 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/job_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/jobrun_icon.png b/gui/baculum/themes/Baculum-v1/jobrun_icon.png new file mode 100644 index 0000000000..d398b2f44e Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/jobrun_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/jobrun_icon_inactive.png b/gui/baculum/themes/Baculum-v1/jobrun_icon_inactive.png new file mode 100644 index 0000000000..b6a34ae2bc Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/jobrun_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/logo.png b/gui/baculum/themes/Baculum-v1/logo.png new file mode 100644 index 0000000000..f50c1ac025 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/logo.png differ diff --git a/gui/baculum/themes/Baculum-v1/media-icon.png b/gui/baculum/themes/Baculum-v1/media-icon.png new file mode 100644 index 0000000000..9dc0ff437d Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/media-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/media-tape-icon.png b/gui/baculum/themes/Baculum-v1/media-tape-icon.png new file mode 100644 index 0000000000..7af7db03a0 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/media-tape-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/media_icon.png b/gui/baculum/themes/Baculum-v1/media_icon.png new file mode 100644 index 0000000000..152c6b7b13 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/media_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/media_icon_inactive.png b/gui/baculum/themes/Baculum-v1/media_icon_inactive.png new file mode 100644 index 0000000000..0a40036516 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/media_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/opentip.css b/gui/baculum/themes/Baculum-v1/opentip.css new file mode 100644 index 0000000000..bfa4b3d655 --- /dev/null +++ b/gui/baculum/themes/Baculum-v1/opentip.css @@ -0,0 +1,459 @@ +/** + ** http://www.opentip.org + ** See opentip.js for the license. + ** + ** One word about the different states when showing / hiding opentips. + ** + ** An opentip starts with the .ot-completely-hidden class. + ** When shown, it changes to .ot-becoming-visible, then to .ot-visible (depending + ** on the length of the transition effect). + ** When hidden, it changes to .ot-hidden, and then to .ot-completely-hidden (again: + ** depending on the length of the transition effect). + **/ +.ot-container { + position: absolute; + max-width: 300px; + z-index: 100; +} + /** + * Only using the position transition on fixed opentips, so the tip only moves + * smoothly when it changes position due to browser viewport changes. + */ + .ot-container.ot-fixed { + -webkit-transition-property: left, top; + -webkit-transition-duration: 0.2s, 0.2s; + -moz-transition-property: left, top; + -moz-transition-duration: 0.2s, 0.2s; + } + .ot-container.ot-completely-hidden { + display: none; + } +.opentip { + position: relative; + font-size: 13px; + line-height: 120%; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); +} +.opentip .title { font-weight: bold; } +.opentip .content { width: auto !important;} + + +.opentip .loadingIndication { + display: none; + padding: 10px 15px; + background: url("loading.gif") center center no-repeat; + width: 25px; + height: 15px; +} +.opentip .loadingIndication span { display: none; } +.ot-loading .opentip .loadingIndication { display: block; } +.ot-loading .opentip .content { display: none; } + +.ot-buttons { + position: absolute; + right: 0; + top: 0; +} + +.ot-buttons .close { + display: block; + width: 15px; + height: 15px; + display: block; + text-decoration: none; +} +.ot-buttons .close .canvas { + position: relative; + display: block; + color: white; /* Read by JS and applied to canvas */ + background-color: rgba(0, 0, 0, 0.2); /* Read by JS and applied to canvas */ +} + +.ot-container .stem-container { + position: absolute; + width: 0; + height: 0; +} +.ot-container .stem { + position: absolute; + overflow: hidden; + color: #cccccc; /* JS will read this property to draw the stem in the right color. */ +/* background: rgba(255, 0, 0, 0.5);*/ +} + + +.ot-container .left { left: 0; } +.ot-container .center { left: 50%; } +.ot-container .right { right: 0; } +.ot-container .top { top: 0; } +.ot-container .middle { top: 50%; } +.ot-container .bottom { bottom: 0; } + + + +/** + +Styles +====== + +Those are the different styles available in opentip. + +**/ + +.style-standard .opentip { + border: 1px solid #f2e37b; + background-color: #fff18f; + color: black; + padding: 6px 10px; + box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 2px 2px 7px rgba(0, 0, 0, 0.1); +} +.style-standard .opentip .title { + margin-bottom: 1px; +} +.style-standard .stem { + color: #e3ca1b; /* JS will read this property to draw the stem in the right color. */ +} +.style-standard .ot-buttons { + top: -6px; + right: -6px; +} +.style-standard .ot-buttons .close { + width: 21px; + height: 21px; +} +.style-standard .ot-buttons .close .canvas { + background-color: rgba(255, 241, 143, 0.5); + color: #a2932B; +} + + + + +.style-rounded .opentip { + border: 7px solid #81b4da; + background-color: #f9fbfc; + color: #3f5d73; + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.4); + -moz-box-shadow: 0 0 5px rgba(0, 0, 0, 0.4); + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, 0.4); +} +.style-rounded .opentip .title { + background: #81b4da; + color: #f7fafd; + padding: 0px 10px 4px 10px; +} +.style-rounded .opentip .content { + padding: 6px 10px; +} +.style-rounded .stem { + color: #c3ddf0; +} +.style-rounded .ot-buttons { + top: -3px; + right: -3px; +} +.style-rounded .ot-buttons .close { + width: 21px; + height: 21px; +} +.style-rounded .ot-buttons .close .canvas { + background-color: #81B4DA; +} + +.style-slick .opentip { + border: 1px solid #eeeeee; + background: #f7f7f7; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} +.style-slick .opentip .title { + color: #49677e; + padding: 5px 10px 3px 10px; + border-bottom: 1px solid #eeeeee; +} +.style-slick .opentip .content { + padding: 6px 10px; + border-top: 1px solid #fefefe; +} +.style-slick .stem { + color: #cccccc; +} +.style-slick .ot-buttons { + right: -7px; + top: -5px; +} +.style-slick .ot-buttons .close { + height: 19px; + width: 19px; +} +.style-slick .ot-buttons .close .canvas { + background-color: rgba(0, 0, 0, 0.2); +} + + +.style-glass .opentip { + background: white; + background: rgba(255, 255, 255, 0.9); + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + box-shadow: 0 0 15px rgba(51, 113, 136, 0.5); + -moz-box-shadow: 0 0 15px rgba(51, 113, 136, 0.5); + -webkit-box-shadow: 0 0 15px rgba(51, 113, 136, 0.5); + padding: 20px 30px; +} +.style-glass .opentip .title { + color: #316F89; + font-family: serif; + font-style: italic; + margin: 0 0 20px 0; + font-size: 16px; +} +.style-glass .opentip .content { + color: #333333; +} +.style-glass .stem { + color: #ffffff; +} +.style-glass .ot-buttons { + right: -3px; + top: -3px; +} +.style-glass .ot-buttons .close { + width: 25px; + height: 25px; +} +.style-glass .ot-buttons .close .canvas { + background-color: #F7BA00; +} + +/** + +IE 6 HACKS +========== + +**/ + +/*\*/ +.ot-buttons { + z-index: 110; +} +.opentip { + z-index: 110; +} +.opentipIFrame { + position: absolute; + top: 0; + left: 0; + border: none; + background: none; + margin: 0; + padding: 0; + display:none; + /*sorry for IE5*/ display/**/:block;/*sorry for IE5*/ + z-index:-1;/*must have*/ + filter:mask();/*must have*/ +} + +.style-glass .opentip { + border: 1px solid #bbbbbb; +} +/**/ + + + + +/** + +CSS3 Transitions +================ + +The definitions that follow here, are only meant for browsers that support css3 +transitions. + +So the syntax I'm going to use, is really meant for more modern browsers than +IE6. + + + +To define a show effect, you have to define the position you want to come from +as the .ot-completely-hidden property (in combination with your +.ot-show-effectName). + +To define a hide effect, you do the same, but for .ot-hidden (in combination +with your .ot-show-effectName). + +**/ + + +.ot-container.ot-css3 { + -webkit-transition-duration: 1s; /* Well be reset by JS */ + -webkit-transition-property: opacity, -webkit-transform; + -moz-transition-duration: 1s; /* Well be reset by JS */ + -moz-transition-property: opacity, -moz-transform; + -o-transition-duration: 1s; /* Well be reset by JS */ + -o-transition-property: opacity, -o-transform; + + opacity: 1; +} + +.ot-container.ot-css3.ot-completely-hidden { + display: none; +} + + + + + +/** Appear / Fade**/ +.ot-container.ot-css3.ot-becoming-visible.ot-show-appear, +.ot-container.ot-css3.ot-hidden.ot-hide-fade { + opacity: 0; +} + + + +/** Grow / Shrink **/ +.ot-container.ot-css3.ot-becoming-visible.ot-show-grow, +.ot-container.ot-css3.ot-hidden.ot-hide-shrink { + -webkit-transform: scale(0); + -moz-transform: scale(0); + -o-transform: scale(0); +} + + + +/** Blind/Slide down / Blind up**/ +.ot-container.ot-css3.ot-becoming-visible.ot-show-blindDown, +.ot-container.ot-css3.ot-becoming-visible.ot-show-slideDown, +.ot-container.ot-css3.ot-hidden.ot-hide-blindUp { + -webkit-transform: scaleY(0); + -moz-transform: scaleY(0); + -o-transform: scaleY(0); + opacity: 0; +} + + +/** Condense / Puff **/ +.ot-container.ot-css3.ot-becoming-visible.ot-show-condense, +.ot-container.ot-css3.ot-hidden.ot-hide-puff { + -webkit-transform: scale(5); + -moz-transform: scale(5); + -o-transform: scale(5); + opacity: 0; +} + +/** Rotate **/ +.ot-container.ot-css3.ot-becoming-visible.ot-show-rotate { + -webkit-transform: scale(3) rotate(-500deg); + -moz-transform: scale(3) rotate(-500deg); + -o-transform: scale(3) rotate(-500deg); + opacity: 0; +} +.ot-container.ot-css3.ot-hidden.ot-hide-rotate { + -webkit-transform: scale(3) rotate(500deg); + -moz-transform: scale(3) rotate(500deg); + -o-transform: scale(3) rotate(500deg); + opacity: 0; +} + + + + + + + + + + + + +/** + +Content design +============== + +Nicer input fields, etc... + +*/ + + +.opentip label { + margin-bottom: 3px; + margin-top: 10px; + display: block; +} +.opentip input, .opentip textarea { + padding: 5px 6px; + border: 1px solid rgba(100, 100, 100, 0.2); + background: rgba(255,255,255,0.5); + display: block; + width: 100%; + margin: 3px 0 10px 0; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} +.opentip input:focus, .opentip textarea:focus { + border-color: rgba(100, 100, 100, 0.2); + -moz-box-shadow: 0 0 10px rgba(0,0,0,0.2); + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2); +} + +.opentip button { + margin-top: 20px; + display: block; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + + + border: 1px solid rgba(247, 186, 0, 0.8); + background: rgba(247, 186, 0, 0.9); + font-size: 14px; + line-height: 14px; + padding: 10px 10px; + position: relative; + color: rgba(255,255,255, 1); + text-shadow: 0 0 10px rgba(255,255,255,0.3); + text-align: center; + font-weight: bold; + font-family: serif; + font-style: italic; + text-decoration: none; + margin: 20px 0 0 0; + cursor: pointer; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -moz-box-shadow: 0 0 4px rgba(0,0,0,0.2), + 0 -2px 10px rgba(255, 255, 255, 0.4) inset; + -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.2), + 0 -2px 10px rgba(255, 255, 255, 0.4) inset; +} +.opentip button { + padding: 8px 6px; +} +.button:hover, button:hover { + -moz-box-shadow: 0 0 6px rgba(247, 192, 25, 1), + 0 -2px 10px rgba(255, 255, 255, 0.6) inset; + text-shadow: 0 0 4px rgba(255,255,255,1); +} + + + +/** IE 6 HACKS **/ +/*\*/ +.opentip input, .opentip textarea, .opentip button { + width: 200px; +} +/**/ \ No newline at end of file diff --git a/gui/baculum/themes/Baculum-v1/panel-border-bg.png b/gui/baculum/themes/Baculum-v1/panel-border-bg.png new file mode 100644 index 0000000000..b682bd4311 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/panel-border-bg.png differ diff --git a/gui/baculum/themes/Baculum-v1/panel-border-btn.png b/gui/baculum/themes/Baculum-v1/panel-border-btn.png new file mode 100644 index 0000000000..88ca81ce5d Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/panel-border-btn.png differ diff --git a/gui/baculum/themes/Baculum-v1/panel-border-btns.png b/gui/baculum/themes/Baculum-v1/panel-border-btns.png new file mode 100644 index 0000000000..d10d067255 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/panel-border-btns.png differ diff --git a/gui/baculum/themes/Baculum-v1/panel-border-middle-btn.png b/gui/baculum/themes/Baculum-v1/panel-border-middle-btn.png new file mode 100644 index 0000000000..018d47a596 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/panel-border-middle-btn.png differ diff --git a/gui/baculum/themes/Baculum-v1/pool.png b/gui/baculum/themes/Baculum-v1/pool.png new file mode 100644 index 0000000000..8229642a02 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/pool.png differ diff --git a/gui/baculum/themes/Baculum-v1/pool_icon.png b/gui/baculum/themes/Baculum-v1/pool_icon.png new file mode 100644 index 0000000000..540d0e31ea Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/pool_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/pool_icon_inactive.png b/gui/baculum/themes/Baculum-v1/pool_icon_inactive.png new file mode 100644 index 0000000000..30f0243db4 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/pool_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/progress.gif b/gui/baculum/themes/Baculum-v1/progress.gif new file mode 100644 index 0000000000..ffb3dd1ad8 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/progress.gif differ diff --git a/gui/baculum/themes/Baculum-v1/restore_icon.png b/gui/baculum/themes/Baculum-v1/restore_icon.png new file mode 100644 index 0000000000..bc3ae70bcb Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/restore_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/restore_icon_inactive.png b/gui/baculum/themes/Baculum-v1/restore_icon_inactive.png new file mode 100644 index 0000000000..63d7fcff7c Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/restore_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/server-storage-icon.png b/gui/baculum/themes/Baculum-v1/server-storage-icon.png new file mode 100644 index 0000000000..5c953dfcc8 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/server-storage-icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/setting_icon.png b/gui/baculum/themes/Baculum-v1/setting_icon.png new file mode 100644 index 0000000000..c189ba64ef Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/setting_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/setting_icon_inactive.png b/gui/baculum/themes/Baculum-v1/setting_icon_inactive.png new file mode 100644 index 0000000000..8e3fadc3ff Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/setting_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-active.png b/gui/baculum/themes/Baculum-v1/step-active.png new file mode 100644 index 0000000000..3956ce2014 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-active.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-content.png b/gui/baculum/themes/Baculum-v1/step-content.png new file mode 100644 index 0000000000..ee1d768241 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-content.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-first-active.png b/gui/baculum/themes/Baculum-v1/step-first-active.png new file mode 100644 index 0000000000..3c13aee6dd Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-first-active.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-first-next-active.png b/gui/baculum/themes/Baculum-v1/step-first-next-active.png new file mode 100644 index 0000000000..31cc1e4846 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-first-next-active.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-first.png b/gui/baculum/themes/Baculum-v1/step-first.png new file mode 100644 index 0000000000..d36d7342c6 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-first.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-last-active.png b/gui/baculum/themes/Baculum-v1/step-last-active.png new file mode 100644 index 0000000000..b6f2882cb0 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-last-active.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-last.png b/gui/baculum/themes/Baculum-v1/step-last.png new file mode 100644 index 0000000000..9fbc6b65a1 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-last.png differ diff --git a/gui/baculum/themes/Baculum-v1/step-prev-active.png b/gui/baculum/themes/Baculum-v1/step-prev-active.png new file mode 100644 index 0000000000..cb5634d273 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step-prev-active.png differ diff --git a/gui/baculum/themes/Baculum-v1/step.png b/gui/baculum/themes/Baculum-v1/step.png new file mode 100644 index 0000000000..fa82e900c8 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/step.png differ diff --git a/gui/baculum/themes/Baculum-v1/storage_icon.png b/gui/baculum/themes/Baculum-v1/storage_icon.png new file mode 100644 index 0000000000..7d096cffab Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/storage_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/storage_icon_inactive.png b/gui/baculum/themes/Baculum-v1/storage_icon_inactive.png new file mode 100644 index 0000000000..032498b4fa Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/storage_icon_inactive.png differ diff --git a/gui/baculum/themes/Baculum-v1/style.css b/gui/baculum/themes/Baculum-v1/style.css new file mode 100644 index 0000000000..ad7d23adc6 --- /dev/null +++ b/gui/baculum/themes/Baculum-v1/style.css @@ -0,0 +1,776 @@ +body { + background-color: #b5c9d3; + background-image: url('/themes/Baculum-v1/background.png'); + background-repeat: repeat-x; + font-family: Arial, Helvetica, sans-serif; + color: white; +} + +#message-body { + background-color: #832933; + background-image: none; + background-repeat: repeat-x; + font-family: Arial, Helvetica, sans-serif; + color: black; +} + +input.invalidate, select.invalidate { + border: 1px solid red; +} + +input[type=text], input[type=password], select, textarea { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + padding: 5px; +} + +input[type=checkbox] { + margin: 6px; +} + +input.textbox, select.textbox { + width: 260px; + border: 1px solid black; + font-size: 11pt; + display: table-cell; +} + +input.textbox-short, select.textbox-short { + width: 80px; + border: 1px solid black; + font-size: 11pt; + display: table-cell; +} + +input.textbox-auto, select.textbox-auto, textarea.textbox-auto { + width: 100%; + border: 1px solid black; + font-size: 14px; + display: table-cell; +} + +textarea.textbox-auto { + resize: vertical; + width: 406px; + font-size: 11px; +} + +a { + font-size: 12px; + color: white; + padding: 3px 4px; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} +#container { + margin: 0 auto; + width: 975px; +} + +#top { + width: 975px; + height: 51px; + background-image: url('/themes/Baculum-v1/bls_top.png'); + background-repeat: no-repeat; + clear: both; +} + +#top img { + padding: 11px 0 11px 35px; + float: left; +} + +#menu-left { + float: left; + width: 74px; + /*height: 498px;*/ + height: 600px; + background-color: #546e83; + border-left: 1px solid black; +} + +#content { + float: left; + width: 899px; + /*height: 498px;*/ + height: 600px; + background-color: #585758; + border-right: 1px solid black; +} + +#directors { + float: right; + font-size: 12px; +} + +#directors select { + margin: 15px 20px 0 0; + padding: 0; + font-size: 12px +} + +div.configuration { + width: 443px; + /*height: 473px;*/ + height: 575px; + margin: 12px 0; + z-index: 5; + position: relative; + left: 447px; + top: 0; + background-color: #898889; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + display: none; +} + +#configuration div.field select { + width: 178px; +} + +#configuration div.field input[type=text] { + width: 166px; +} + +div.configuration-progress { + background: rgba(182,182,182,0.7) url('/themes/Baculum-v1/progress.gif') no-repeat center center; + width: 443px; + height: 575px; + z-index: 10; + position: absolute; + top: 0; + left: 0; + display: none; + border-radius: 10px; +} + +div.slide-window-progress { + background: rgba(182,182,182,0.7) url('/themes/Baculum-v1/progress.gif') no-repeat center center; + width: 100%; + height: 100%; + z-index: 10; + position: absolute; + top: 0; + left: 0; + display: none; +} + +#console { + clear: left; + width: 973px; + min-height: 20px; + padding: 5px 0; + text-align: right; + background-color: #686a6c; + border-left: 1px solid black; + border-right: 1px solid black; +} + +#console a.left { + float: left; + padding: 0 5px; + font-weight: bold; +} + +#console input[type="text"], #console select { + width: 255px; +} + +#console select { + width: 267px; + padding-bottom: 4px; +} + +#console input[type="submit"] { + margin: 6px 0; +} + +#console div.button { + text-align: right; + margin: 3px 9px 5px 0; +} + +#bottom { + width: 975px; + height: 11px; + background: transparent url('/themes/Baculum-v1/bls_bottom.png') no-repeat top left; +} + +#message-box { + border: 2px solid #a6ac00; + width: 90%; + padding: 8px; + min-height: 200px; + margin: 80px auto; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + border-radius: 8px; + background-color: #faff74; +} + +#message-box h3, #message-box h2 { + margin: 20px 10px; +} + +#message-box li { + margin: 10px; +} + +#message-box a { + font-size: 16px; + color: red; + font-weight: bold; + text-decoration: underline; +} + +.storage-btn, .client-btn, .media-btn, .pool-btn, .setting-btn, .job-btn, .jobrun-btn, .restore-btn { + width: 74px; + height: 74px; + display: block; + cursor: pointer; + border: 0; +} + +.storage-btn { + background: transparent url('/themes/Baculum-v1/storage_icon_inactive.png') no-repeat top left; +} + +.storage-btn:hover { + background: transparent url('/themes/Baculum-v1/storage_icon.png') no-repeat top left; +} + +.client-btn { + background: transparent url('/themes/Baculum-v1/client_icon_inactive.png') no-repeat top left; +} + +.client-btn:hover { + background: transparent url('/themes/Baculum-v1/client_icon.png') no-repeat top left; +} + +.media-btn { + background: transparent url('/themes/Baculum-v1/media_icon_inactive.png') no-repeat top left; +} + +.media-btn:hover { + background: transparent url('/themes/Baculum-v1/media_icon.png') no-repeat top left; +} + +.pool-btn { + background: transparent url('/themes/Baculum-v1/pool_icon_inactive.png') no-repeat top left; +} + +.pool-btn:hover { + background: transparent url('/themes/Baculum-v1/pool_icon.png') no-repeat top left; +} + +.job-btn { + background: transparent url('/themes/Baculum-v1/job_icon_inactive.png') no-repeat top left; +} + +.job-btn:hover { + background: transparent url('/themes/Baculum-v1/job_icon.png') no-repeat top left; +} + +.jobrun-btn { + background: transparent url('/themes/Baculum-v1/jobrun_icon_inactive.png') no-repeat top left; +} + +.jobrun-btn:hover { + background: transparent url('/themes/Baculum-v1/jobrun_icon.png') no-repeat top left; +} + +.restore-btn { + background: transparent url('/themes/Baculum-v1/restore_icon_inactive.png') no-repeat top left; +} + +.restore-btn:hover { + background: transparent url('/themes/Baculum-v1/restore_icon.png') no-repeat top left; +} + +.setting-btn { + background: transparent url('/themes/Baculum-v1/setting_icon_inactive.png') no-repeat top left; +} + +.setting-btn:hover { + background: transparent url('/themes/Baculum-v1/setting_icon.png') no-repeat top left; +} + +.line { + height: 35px; + clear: both; +} + +.text { + width: 240px; + display: table-cell; + vertical-align: middle; + font-size: 15px; +} + +.field { + display: table-cell; +} + +.field-full { + display: block; + min-height: 200px; +} + +.validator, .validate { + color: #e3454b !important; + font-size: 10pt; + /*margin-left: 5px;*/ +} + +.validate { + color: #96c600 !important; +} + +.bbutton input.button-left, .bbutton input.button-center, .bbutton input.button-right { + font-size: 12px; + height: 25px; + color: black; + cursor: pointer; + padding: 0; + margin: 0; + border: 0; +} + +.bbutton input.button-left, .bbutton input.button-right { + width: 2px; +} + +.bbutton input.button-left { + background: transparent url('/themes/Baculum-v1/button-left.png') no-repeat left 0; +} + +.bbutton input.button-center { + padding: 0 7px; + min-width: 65px; + background: transparent url('/themes/Baculum-v1/button-center.png') repeat-x left 0; +} + +.bbutton input.button-right { + background: transparent url('/themes/Baculum-v1/button-right.png') no-repeat left 0; +} + +.bbutton:hover input.button-left { + background: transparent url('/themes/Baculum-v1/button-left.png') no-repeat left -25px; +} + +.bbutton:hover input.button-center { + background: transparent url('/themes/Baculum-v1/button-center.png') repeat-x left -25px; +} + +.bbutton:hover input.button-right { + background: transparent url('/themes/Baculum-v1/button-right.png') no-repeat left -25px; +} + +div.slide-window-container { + width: 400px; + height: 200px; + font-size: 11px; + font-weight: bold; + background-color: rgba(255,255,255,0.3); + z-index: 10; + position: absolute; +} + +div.slide-window-content { + height: 100%; + width: 100%; + border-right: 1px solid black; + overflow: auto; + overflow-y: auto; +} + +div.slide-window-element, div.slide-window-element:hover { + float: left; + width: 210px; + color: white; +} + +div.slide-window-element:hover { + text-decoration: underline; +} + +div.slide-window-element:hover, div.slide-window-element-detail:hover { + float: left; + width: 210px; + background-color: #686A6C; + cursor: pointer; +} + +tr.slide-window-element { + background-color: #7a7a7a; +} + +tr.slide-window-element-alternating { + background-color: #686A6C; +} + +tr.slide-window-element td, tr.slide-window-element-alternating td { + font-style: italic; +} +tr.slide-window-element:hover, tr.slide-window-element-alternating:hover { + float: none; + cursor: pointer; +} + +tr.slide-window-element:hover td, tr.slide-window-element-alternating:hover td{ + background-color: #aeb2b6; +} + +table.window-section-detail, table.file-browser-detail { + background-color: rgba(0,0,0,0.5); + width: 100%; + font-size: 11px; + font-weight: normal; +} + +table.window-section-detail th, table.file-browser-detail th { + background-color: black; + color: white; +} + +table.file-browser-detail { + border-spacing: 0; +} + +tr.file-browser-element { + background-color: white; + color: black; +} + +tr.file-browser-element td { + font-style: italic; + padding: 3px 0; + vertical-align: middle; +} + +tr.file-browser-element:hover { + cursor: move; +} + +tr.file-browser-element:hover td{ + background-color: #aeb2b6; +} + +tr.file-browser-header { + height: 25px; + text-align: left; + font-family: monospace; + font-size: 9px +} + +tr.file-browser-header th { + padding-left: 9px; +} + +.draggable { + cursor: move; +} + +div.slide-window-bar { + background: transparent url('/themes/Baculum-v1/panel-border-btns.png') no-repeat top right; + text-align: center; + font-size: 10px; + height: 14px; + width: 100%; + padding-right: 1px; + clear: both; +} + +div.slide-window-bar-title { + background: transparent url('/themes/Baculum-v1/panel-border-bg.png') repeat-x top left; + height: 14px; + margin-right: 51px; + padding: 0; +} + +div.slide-window-close, div.slide-window-fullsize, div.slide-window-sort { + width: 12px; + height: 14px; + position: relative; + top: -14px; + float: right; +} + +div.slide-window-close { + right: 9px; +} + +div.slide-window-fullsize { + right: 10px; +} + +div.slide-window-sort { + right: 14px; +} + +div.slide-window-toolbar { + height: 90px; + width: 240px; + z-index: 25; + position: absolute; + margin: 0; + padding: 0; + bottom: 0; + right: 0; + border-top-left-radius: 5px; + border-left: 1px solid black; + border-top: 1px solid black; + background-color: rgba(0,0,0,0.7); +} + +div.slide-window-toolbar table { + width: 220px; + border-spacing: 6px; + font-size: 11px; +} + +div.slide-window-toolbar table td input[type="radio"] { + vertical-align: text-bottom; +} + +div.slide-window-toolbar table select,div.slide-window-toolbar table input { + font-size: 11px; + padding: 0; +} + +div.slide-window-toolbar table input[type="text"] { + width: 123px; +} + +div.configuration-window-content { + padding: 10px 12px;; +} + + +div.status-bar-append, div.status-bar-full, div.status-bar-used, div.status-bar-error, div.status-bar-purged, div.status-bar-recycle, div.status-bar-busy, div.status-bar-disabled, div.status-bar-archive, div.status-bar-cleaning, div.status-bar-read-only, div.status-bar-detail-append, div.status-bar-detail-full, div.status-bar-detail-used, div.status-bar-detail-error, div.status-bar-detail-purged, div.status-bar-detail-recycle, div.status-bar-detail-busy, div.status-bar-detail-disabled, div.status-bar-detail-archive, div.status-bar-detail-cleaning, div.status-bar-detail-read-only { + position: relative; + border: 1px solid black; + width: 40px; + height: 20px; + line-height: 20px; + float: right; + font-size: 7px; + font-weight: bold; + margin: 4px 6px 4px 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + text-align: center; +} + +div.status-bar-append, div.status-bar-detail-append { + background-color: green; +} + +div.status-bar-full, div.status-bar-used, div.status-bar-detail-full, div.status-bar-detail-used { + background-color: orange; +} + +div.status-bar-purged, div.status-bar-recycle, div.status-bar-detail-purged, div.status-bar-detail-recycle { + background-color: yellow; + color: black; +} + +div.status-bar-error, div.status-bar-detail-error { + background-color: red; +} + +div.status-bar-busy, div.status-bar-detail-busy { + background-color: pink; +} + +div.status-bar-archive, div.status-bar-disabled, div.status-bar-detail-archive, div.status-bar-detail-disabled { + background-color: gray; + color: black; +} + +div.status-bar-cleaning, div.status-bar-detail-cleaning { + background-color: blue; +} + +div.status-bar-read-only, div.status-bar-detail-read-only { + background-color: white; + color: black; +} + +div.status-bar-detail-append, div.status-bar-detail-full, div.status-bar-detail-used, div.status-bar-detail-error, div.status-bar-detail-purged, div.status-bar-detail-recycle, div.status-bar-detail-busy, div.status-bar-detail-disabled, div.status-bar-detail-archive, div.status-bar-detail-cleaning, div.status-bar-detail-read-only { + float: none; + margin: 0 auto; +} + +/* JOB STATES */ + +div.job-status-C, div.job-status-R, div.job-status-B, div.job-status-T, div.job-status-W, div.job-status-E, div.job-status-e, div.job-status-f, div.job-status-D, div.job-status-A, div.job-status-I, div.job-status-F, div.job-status-S, div.job-status-m, div.job-status-M, div.job-status-s, div.job-status-j, div.job-status-c, div.job-status-d, div.job-status-t, div.job-status-p, div.job-status-i, div.job-status-a, div.job-status-l, div.job-status-L { + border: 1px solid black; + min-width: 80px; + height: 20px; + line-height: 20px; + font-size: 9px; + font-weight: bold; + margin: 4px auto; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + text-align: center; + background-color: black; + color: white; +} + +div.job-status-C { + background-color: #7A5DC7; +} + +div.job-status-A { + background-color: yellow; + color: black; +} + +div.job-status-R { + background-color: #E9AB17; + color: black; +} + +div.job-status-B, div.job-status-E, div.job-status-e, div.job-status-f, div.job-status-I { + background-color: red; +} + +div.job-status-T { + background-color: #347235; +} + +div.job-status-W { + background-color: #C68E17; +} + +div.job-status-D { + background-color: blue; +} + +/* +div.job-status-F, div.job-status-S, div.job-status-m, div.job-status-M, div.job-status-s, div.job-status-j, div.job-status-c, div.job-status-d, div.job-status-t, div.job-status-p { + background-color: #657383; +} + +div.job-status-i, div.job-status-a, div.job-status-l, div.job-status-L { + background-color: #151B54; +}*/ + + +/* END JOB STATES */ + +div.window-section { + border-bottom: 1px solid white; + border-top: 1px solid white; + background-color: rgba(0,0,0,0.5); + width: 100%; + height: 25px; + clear: both; + padding-top: 6px; + font-size: 13px; +} + +div.window-section span { + padding-left: 12px; +} + +div.button { + text-align: right; + margin: 3px 2px 5px 0; +} + +div.button-center { + text-align: center; + margin: 5px 0; +} + +input.validation-error { + border-color: red !important; +} + +span.validation-error-summary { + font-size: 12px; + line-height: 12px; + color: rgb(255, 187, 187) !important; +} + +textarea.console { + width: 952px; + height: 200px; + display: block; + margin: 5px auto; + font-size: 10px; +} + +.bold { + font-weight: bold; +} + +.center { + text-align: center; +} + +#console_launcher, #volumes_tools_launcher, #logging, #clear_bvfs_cache { + padding: 0 5px; +} + +#console_launcher span, #volumes_tools_launcher span, #logging label, #clear_bvfs_cache label { + padding: 0 5px; + font-family: monospace; + font-size: 11px; +} + +#logging input[type="checkbox"], #logging label, #clear_bvfs_cache label{ + cursor: pointer; +} + +#logging input[type="checkbox"], #clear_bvfs_cache input[type="image"] { + vertical-align: middle; + border: 0; +} + +fieldset { + margin-bottom: 15px; + font-size: 14px; +} + +fieldset .line { + height: 25px; +} + +fieldset .text { + width: 250px; +} + +legend { + font-size: 12px; + font-weight: bold; + color: white; +} + +img { + vertical-align: middle; + border: none; +} + +div.small { + font-size: 12px; + text-align: center; + width: 130px; + background-color: rgb(115, 143, 172); + border-top-left-radius: 5px; + border-top-right-radius: 5px; + border-left: 1px solid black; + border-right: 1px solid black; + border-top: 1px solid black; + margin: 3px 0; +} \ No newline at end of file diff --git a/gui/baculum/themes/Baculum-v1/tape_tools_icon.png b/gui/baculum/themes/Baculum-v1/tape_tools_icon.png new file mode 100644 index 0000000000..623402a34d Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/tape_tools_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/trash_icon.png b/gui/baculum/themes/Baculum-v1/trash_icon.png new file mode 100644 index 0000000000..e2eb2d4e40 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/trash_icon.png differ diff --git a/gui/baculum/themes/Baculum-v1/wizard-bottom.png b/gui/baculum/themes/Baculum-v1/wizard-bottom.png new file mode 100644 index 0000000000..e3b991fab0 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/wizard-bottom.png differ diff --git a/gui/baculum/themes/Baculum-v1/wizard-content.png b/gui/baculum/themes/Baculum-v1/wizard-content.png new file mode 100644 index 0000000000..01bdfb58ef Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/wizard-content.png differ diff --git a/gui/baculum/themes/Baculum-v1/wizard-header.png b/gui/baculum/themes/Baculum-v1/wizard-header.png new file mode 100644 index 0000000000..0bfda5fc90 Binary files /dev/null and b/gui/baculum/themes/Baculum-v1/wizard-header.png differ diff --git a/gui/baculum/themes/Baculum-v1/wizard.css b/gui/baculum/themes/Baculum-v1/wizard.css new file mode 100644 index 0000000000..a9ed56186f --- /dev/null +++ b/gui/baculum/themes/Baculum-v1/wizard.css @@ -0,0 +1,154 @@ +.wizard { + margin: 50px auto; + height: 547px; + width: 752px; + font-family: Arial; + color: white; + } + +.steps { + padding: 20px; + height: 435px; + background-image: url('/themes/Baculum-v1/wizard-content.png'); + background-repeat: repeat-y; + } + +.navigation { + text-align: center; + padding: 11px 0; + background-image: url('/themes/Baculum-v1/wizard-bottom.png'); + background-repeat: no-repeat; + } + +.step { + float: left; + height: 45px; + width: 124px; + font-size: 13pt; + text-align: center; + } + +.step div{ + margin: 12px 15px 12px 0; + } + +.step-normal, .step-active, .step-prev-active, .step-first-active, .step-first-next-active { + background-repeat: no-repeat; + background-position: right center; + } + + .step-first { + width: 126px; + background-image: url('/themes/Baculum-v1/step-first.png'); + } + + .step-first-active { + width: 126px; + background-image: url('/themes/Baculum-v1/step-first-active.png'); + } + + .step-first-next-active{ + width: 126px; + background-image: url('/themes/Baculum-v1/step-first-next-active.png'); + } + +.step-last { + width: 130px; + background-image: url('/themes/Baculum-v1/step-last.png'); + } + + .step-last-active { + width: 130px; + background-image: url('/themes/Baculum-v1/step-last-active.png'); + } + + .step-last-next-active{ + width: 130px; + background-image: url('/themes/Baculum-v1/step-last-next-active.png'); + } + +.step-normal, .step-prev-active { + background-image: url('/themes/Baculum-v1/step.png'); + } + +.step-active{ + background-image: url('/themes/Baculum-v1/step-active.png'); + } + +.step-prev-active { + background-image: url('/themes/Baculum-v1/step-prev-active.png'); + } + +#licence { + width: 650px; + height: 300px; + overflow: auto; + border: 1px solid gray; + margin: auto; + padding: 5px; +} + +#title { + clear: left; + font-size: 14px; + font-weight: bold; + background-image: url('/themes/Baculum-v1/wizard-header.png'); + background-repeat: no-repeat; + padding: 4px; +} + +#licence-agree { + width: 670px; + margin: 10px auto; +} + +#restore-browser { + width: 100%; + height: 100%; +} + +#restore-browser-files { + width: 315px; + height: 434px; +} + +#restore-browser-files, #restore-browser-versions, #restore-browser-selected { + overflow: auto; + width: 339px; + background-color: white; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + padding: 4px; + -moz-user-select: -moz-none; + -webkit-user-select: none; + -ms-user-select: none; +} + +#restore-browser-files { + height: 430px; +} + +#restore-browser-versions { + height: 206px; +} + +#restore-browser-selected { + height: 206px; +} + +.file-browser-watermark { + padding: 64px 10px; + text-align: center; + font-size: 21px; + color: rgb(185, 184, 184); +} + +div.button-cancel { + float: right; + margin-right: 28px +} + +div.button-prev-next { + margin-left: 84px +}