version 0.2

This commit is contained in:
Stan Ozier 2010-05-21 03:00:57 +02:00
commit 486c0770ca
109 changed files with 11980 additions and 0 deletions

3
.htaccess Normal file
View File

@ -0,0 +1,3 @@
RewriteEngine on
RewriteCond $1 !^(index\.php|phpdoc|skin|asset|plugin|favicon\.ico|robots\.txt)
RewriteRule ^(.*)$ /index.php/$1 [L]

674
README/gpl-3.0.txt Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

165
README/lgpl-3.0.txt Normal file
View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser 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
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

36
README/readme.txt Normal file
View File

@ -0,0 +1,36 @@
TaskFreak! Time Tracking
------------------------
version 0.2
released on 2010-05-21
------------------------
This is the first official release
status : beta
Requirements
------------
- apache with mod_rewrite
- PHP 5.3.x
- mySQL 4.1 or later
- a virtual host
v0.2 can not run under yourdomain.com/taskfreak, it has to be yourdomain.com
Setup
-----
1) Set up your virtual host
2) create a mySQL database
3) create tables and data with taskfreak_time.sql
4) open app/config/db.php and change database settings
You can stop here and have a look.
login is "admin" with no password
If you don't want multi user support, open app/config/core.php
Search for APP_SETUP_USER_MODEL and set it up to false :
[code]
define('APP_SETUP_USER_MODEL',false);
[/code]
If you feel like playing so more, open app/config/app.php and look into it

146
README/taskfreak_timer.sql Normal file
View File

@ -0,0 +1,146 @@
-- phpMyAdmin SQL Dump
-- version 3.3.1
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: May 21, 2010 at 02:12 AM
-- Server version: 5.0.67
-- PHP Version: 5.3.0
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
--
-- Database: `taskfreak_timer`
--
-- --------------------------------------------------------
--
-- Table structure for table `acl`
--
DROP TABLE IF EXISTS `acl`;
CREATE TABLE `acl` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`name` varchar(127) NOT NULL,
`section` varchar(63) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `acl`
--
INSERT INTO `acl` VALUES(1, 'task_see_all', 'general');
INSERT INTO `acl` VALUES(2, 'admin_user', 'general');
-- --------------------------------------------------------
--
-- Table structure for table `acl_user`
--
DROP TABLE IF EXISTS `acl_user`;
CREATE TABLE `acl_user` (
`user_id` mediumint(8) unsigned NOT NULL,
`acl_id` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`acl_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `acl_user`
--
INSERT INTO `acl_user` VALUES(1, 1);
INSERT INTO `acl_user` VALUES(1, 2);
-- --------------------------------------------------------
--
-- Table structure for table `member`
--
DROP TABLE IF EXISTS `member`;
CREATE TABLE `member` (
`id` int(10) unsigned NOT NULL auto_increment,
`nickname` varchar(30) NOT NULL,
`email` varchar(255) NOT NULL,
`username` varchar(255) NOT NULL,
`password` varchar(40) NOT NULL,
`salt` varchar(40) NOT NULL,
`auto_login` tinyint(1) unsigned NOT NULL,
`time_zone` varchar(63) NOT NULL,
`date_format_us` tinyint(1) NOT NULL default '0',
`creation_date` datetime NOT NULL,
`expiration_date` date NOT NULL,
`last_login_date` datetime NOT NULL,
`last_login_address` varchar(60) NOT NULL,
`last_change_date` datetime NOT NULL,
`visits` int(10) unsigned NOT NULL,
`bad_access` smallint(5) unsigned NOT NULL,
`activation` varchar(16) NOT NULL,
`enabled` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `member`
--
INSERT INTO `member` VALUES(1, 'Administrator', '', 'admin', '', '12345678', 0, 'Asia/Bangkok', 0, '2010-05-21 01:43:21', '0000-00-00', '0000-00-00 00:00:00', '127.0.0.1', '0000-00-00 00:00:00', 0, 0, '', 1);
INSERT INTO `member` VALUES(2, 'Emilie', '', 'emilie', '', '12345678', 0, 'Europe/Paris', 0, '2010-05-21 01:44:17', '0000-00-00', '0000-00-00 00:00:00', '127.0.0.1', '0000-00-00 00:00:00', 0, 0, '', 1);
-- --------------------------------------------------------
--
-- Table structure for table `task`
--
DROP TABLE IF EXISTS `task`;
CREATE TABLE `task` (
`id` int(10) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`note` text NOT NULL,
`priority` tinyint(3) unsigned NOT NULL default '5',
`begin` date NOT NULL,
`deadline` date NOT NULL,
`status` tinyint(3) unsigned NOT NULL,
`archived` tinyint(3) unsigned NOT NULL,
`member_id` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `member_id` (`member_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `task`
--
INSERT INTO `task` VALUES(1, 'taskfreak : README first about this beta version', 'Please keep in mind that this is an early beta version. There are quite a few known bugs, a handful of unknown bugs.\r\n\r\nHere''s what you can do so far :\r\n- create, edit and archive tasks\r\n- start and stop task timers\r\n- report time spent on a task\r\n- edit your own profile\r\n- see other''s tasks (task manager only)\r\n- create and edit users (user admin only)', 5, '9999-00-00', '2010-05-22', 0, 0, 1);
INSERT INTO `task` VALUES(2, 'taskfreak : another quick notes about v0.2', 'There''s still some work to be done before TaskFreak Time Tracking goes stable.\r\n\r\nThis includes :\r\n- fix found bugs (obviously)\r\n- finish table prefix testing\r\n- add multi language support\r\n- Internet Explorer support (only tested on FireFox, Safari & Chrome)', 4, '9999-00-00', '9999-00-00', 0, 0, 1);
-- --------------------------------------------------------
--
-- Table structure for table `timer`
--
DROP TABLE IF EXISTS `timer`;
CREATE TABLE `timer` (
`task_id` int(10) unsigned NOT NULL,
`start` datetime NOT NULL,
`stop` datetime NOT NULL,
`spent` int(10) unsigned NOT NULL,
`manual` tinyint(3) unsigned NOT NULL,
KEY `task_id` (`task_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `timer`
--

90
app/config/app.php Normal file
View File

@ -0,0 +1,90 @@
<?php
// ---- LOG and DEBUGGING -----------------------------------------------------
$GLOBALS['config']['log_front'] = 0;
$GLOBALS['config']['log_debug'] = 0;
$GLOBALS['config']['log_message'] = 0;
$GLOBALS['config']['log_warn'] = 0;
$GLOBALS['config']['log_error'] = 1;
$GLOBALS['config']['log_core'] = 0;
$GLOBALS['config']['log_signature'] = '[TF]';
// --- APPLICATION CONTOLLER, ACTION and PAGES---------------------------------
$GLOBALS['config']['app'] = array(
'default_controller' => 'task',
// default controller to call (home page)
'default_action' => 'main'
// default action to call (home page)
);
$GLOBALS['config']['pages'] = array(
'Todo' => 'task/main',
'Report' => 'task/report',
'Archives' => 'task/archives'
);
// ---- LANGUAGE DEFAULTS -----------------------------------------------------
$GLOBALS['config']['lang'] = array(
'default' => 'en',
'user' => 'en',
'specialchars' => 2
);
// ---- DATE / TIME FORMATS ---------------------------------------------------
// date/time timezone and formats defaults
$GLOBALS['config']['datetime'] = array(
'timezone_server' => new DateTimeZone(APP_TIMEZONE_SERVER),
'timezone_user' => new DateTimeZone(APP_TIMEZONE_SERVER),
'us_format' => false,
);
$GLOBALS['config']['datetime']['now'] = new DateTime('now', $GLOBALS['config']['datetime']['timezone_server']);
// --- Specific DATE FORMATS -------------------------------------------------
define("APP_DATE","%d/%m");
define("APP_DATETIME","%d/%m <small>%H:%M</small>");
// --- TASKFREAK DEFAULTS ---------------------------------------------------------
$GLOBALS['config']['task'] = array(
'date' => APP_SQL_TODAY, // default date is today
'validate' => true // add validation button
);
$GLOBALS['config']['task']['priority'] = array(
'options' => array(
1 => '1) urgent',
2 => '2) important',
3 => '3) quickly',
4 => '4) pretty soon',
5 => '5) normal',
6 => '6) after',
7 => '7) later',
8 => '8) anytime',
9 => '9) whatever'
),
'default' => 5
);
// ---- DEFAULT Javascript ----------------------------------------------------
$GLOBALS['config']['header']['js'] = array(
'jquery-1.4.2.min.js',
// 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js',
);
// ---- SKINS and Templates ---------------------------------------------------
$GLOBALS['config']['skin'] = 'default';
// ---- LANGUAGE --------------------------------------------------------------
$GLOBALS['config']['lang']['default'] = 'en';
$GLOBALS['config']['lang']['user'] = 'en';
$GLOBALS['config']['lang']['specialchars'] = 2;

151
app/config/core.php Normal file
View File

@ -0,0 +1,151 @@
<?php
error_reporting(E_ALL);
// --- APPLICATION SETUP ------------------------------------------------------
$GLOBALS['config'] = array();
// connect to database (see /app/config/db.php for settings)
define('APP_SETUP_DATABASE', true);
// load global settings on startup (requires database)
define('APP_SETUP_GLOBAL_SETTINGS', true);
// load user settings on startup (requires database and auth)
define('APP_SETUP_USER_SETTINGS', false);
// use translation helper
define('APP_SETUP_TRANSLATOR', false);
// use (trans-sessions) messaging helper
define('APP_SETUP_MESSAGING', true);
// use navi helper
define('APP_SETUP_NAVI', true);
// --- USER AUTHENTICATION SETUP ----------------------------------------------
// model class used for app users
// (will load logged in user if not false)
// see APP_AUTH_* below for other settings
define('APP_SETUP_USER_MODEL','member');
// field used for authentication (usually username or email)
define('APP_AUTH_FIELD', 'username');
// password encryption
// 1: php crypt, 2:mySQL ENCRYPT, 3: mysql ENCODE, 4: MD5, 5: MD5 with challenge
define('APP_AUTH_PASSWORD_MODE', 4);
// USER AUTHENTICATION DEFAULTS
// following settings can be modified by global settings
$GLOBALS['config']['auth'] = array(
'remember' => true,
// allow auto login (true or false)
'password_recover' => true,
// allow password recovery (true or false)
'register' => 2
// register mode
// 0: no registration, 1: account created directly (no validation)
// 2: user validation (from email), 3: admin validate account
);
// ---- FILE PATHs ------------------------------------------------------------
define('APP_ROOT_PATH',substr(dirname(__FILE__),0,-10));
define('APP_WWW_PATH',APP_ROOT_PATH);
define('APP_CORE_PATH',APP_ROOT_PATH.'app/');
define('APP_CONFIG_PATH',APP_CORE_PATH.'config/');
define('APP_MODEL_PATH',APP_CORE_PATH.'model/');
define('APP_CONTROLLER_PATH',APP_CORE_PATH.'controller/');
define('APP_VIEW_PATH',APP_CORE_PATH.'view/');
define('APP_INCLUDE_PATH', APP_ROOT_PATH);
define('APP_CACHE_PATH',APP_INCLUDE_PATH.'cache/');
define('APP_LIB_PATH',APP_INCLUDE_PATH.'lib/');
define('APP_CLASS_PATH',APP_LIB_PATH.'class/');
define('APP_HELPER_PATH',APP_LIB_PATH.'helper/');
define('APP_ASSET_PATH',APP_ROOT_PATH.'asset/');
define('APP_LANGUAGE_PATH',APP_ASSET_PATH.'lang/');
define('APP_PLUGIN_PATH',APP_ROOT_PATH.'plugin/');
define('APP_SKIN_PATH',APP_ROOT_PATH.'skin/');
// ---- APPLICATION URLs ------------------------------------------------------
define('APP_WWW_URI', '/');
if (!defined('APP_WWW_URI')) {
define('APP_WWW_URI',preg_replace('/^\/admin/','',dirname($_SERVER['PHP_SELF']))
.(preg_match('/\/$/',dirname($_SERVER['PHP_SELF']))?'':'/'));
}
define('APP_WWW_URL','http://'.$_SERVER['SERVER_NAME'].APP_WWW_URI);
// ---- USER and PASSWORD SETTINGS --------------------------------------------
define('APP_USER_ID_LENGTH',8); // length of user ID
define('APP_USER_SANITIZE','/^[a-z0-9_-]+$/i');
define('APP_USER_NAME_MIN',4); // minimum length for username
define('APP_USER_NAME_MAX',15); // maximum length for username
define('APP_USER_PASS_MIN',4); // minimum length for password
define('APP_USER_PASS_MAX',16); // maximum length for password
define('APP_PASSWORD_SANITIZE','/^[a-z0-9%!@#&*+:;,<>\.\?\|\{\}\(\)\[\]\$_-]+$/i');
// ---- FILE UPLOAD Settings --------------------------------------------------
define('APP_FILE_SLASH','/');
define('APP_FILE_RANDOM',false);
define('APP_FILE_FOLDER_SIZE',300); // number of files per folder
define('APP_FILE_GD_VERSION',2);
define('APP_FILE_GD_QUALITY',80);
// ---- Default DATE/TIME FORMATs ---------------------------------------------
define("APP_DATE_SQL","%Y-%m-%d");
define('APP_DATE_EUR', '%d/%m/%y');
define('APP_DATE_USA', '%m/%d/%y');
define("APP_DATE_SHT","%d %b %y"); // -TODO- %e bug
define("APP_DATE_SHX","%a %e %b %y");
define("APP_DATE_LNG","%d %B %Y");
define("APP_DATE_LNX","%A %d %B %Y");
define("APP_DATETIME_SQL","%Y-%m-%d %H:%M:%S");
define("APP_DATETIME_EUR","%d/%m/%y %H:%M");
define("APP_DATETIME_USA","%m/%d/%y %I:%M%p");
define("APP_DATETIME_SHT","%d %b %y, %H:%M");
define("APP_DATETIME_SHX","%a %d %b %y, %H:%M");
define("APP_DATETIME_LNG","%d %B %Y, %H:%M");
define("APP_DATETIME_LNX","%A %d %B %Y, %H:%M");
define("APP_DATETIME_HRS","%H:%M");
// date_default_timezone_set('Europe/Paris');
define('APP_TIMEZONE_SERVER',date_default_timezone_get());
define('APP_TZSERVER',intval(date('Z')));
// define('APP_TZSERVER',timezone_offset_get(new DateTimeZone(APP_TIMEZONE_SERVER), new DateTime()));
define('APP_SQL_NOW', gmdate('Y-m-d H:i:s'));
define('APP_SQL_TODAY',substr(APP_SQL_NOW,0,10));
define('APP_NOW',strtotime(APP_SQL_NOW));
define('APP_TODAY',strtotime(APP_SQL_TODAY));
define('APP_YEAR',substr(APP_SQL_TODAY,0,4));
// ---- MISC MODEL PROPERTIES Settings ----------------------------------------
define('APP_KEY_LENGTH',8);
define('APP_KEY_STRING','ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
define('APP_SANITIZE_SIMPLE','/^[a-z0-9_-]+$/i');
// ----- PHP SESSION Settings -------------------------------------------------
define('TZN_TRANS_ID',0);
define('TZN_TRANS_STATUS',1);
if (@constant('APP_TRANS_ID')) {
ini_set("session.use_trans_sid",1);
} else {
ini_set('session.use_trans_sid', 0);
ini_set('session.use_only_cookies', 1);
}

21
app/config/db.php Normal file
View File

@ -0,0 +1,21 @@
<?php
define('APP_DB_HOST','localhost');
define('APP_DB_USER','root'); // edit here
define('APP_DB_PASS',''); // edit here
define('APP_DB_BASE','taskfreak_timer'); // edit here
define('APP_DB_PREFIX','');
define('APP_DB_PERMANENT', true);
define('APP_DB_CONNECTOR','mysql');
define('APP_DB_CRITICAL', true); // exit on DB error
// DEBUG level :
// 0 : nothing at all
// 1 : use FC::log_error (on error only)
// 2 : show error code in browser, the rest using FC::log_error (on error only)
// 3 : show error and SQL query in browser (on error only) - useful in development
// 4 : verbose : use FC::log_message (everything) - for debugging only
// 5 : verbose : sends to browser (everything) - for heavy debugging only
define('APP_DB_DEBUG', 2);

36
app/config/path.php Normal file
View File

@ -0,0 +1,36 @@
<?php
// === PHP CLASS and SCRIPTS paths ============================================
$autoPath['class'] = array(
APP_CLASS_PATH
);
$autoPath['helper'] = array(
APP_HELPER_PATH
);
$autoPath['model'] = array(
APP_CORE_PATH.'model/',
APP_LIB_PATH.'model/'
);
$autoPath['controller'] = array(
APP_CORE_PATH.'controller/',
APP_CLASS_PATH
);
$autoPath['view'] = array(
APP_CORE_PATH.'view/'
);
// -TODO-
// browse plugin directory to add other classes into the searched path
// === JS and CSS paths =======================================================
$GLOBALS['config']['path']['css'] = array(
'skin/'.$GLOBALS['config']['skin'].'/css/',
'asset/css/'
);
$GLOBALS['config']['path']['js'] = array(
$GLOBALS['config']['skin'].'/js/',
'asset/js/'
);

205
app/controller/admin.php Normal file
View File

@ -0,0 +1,205 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.2
* @copyright GNU General Public License (GPL) version 3
*/
/**
* Admin
*
* Administration pages
* @since 0.2
*/
class Admin extends AppController {
public function __construct() {
parent::__construct(true);
$this->fc->setSessionDefault('userlimit',10);
if ($this->fc->getReqVar('ajax')) {
$this->page->clean('css');
$this->page->clean('js');
} else {
$this->page->add('css',array('form.css','freak.css','list.css','tracker.css','colorbox.css'));
// $this->page->add('js',array('jquery.form.min.js','jquery.colorbox-min.js'));
$this->page->add('js','freak.js');
}
}
public function mainAction() {
// check access rights
if (!$this->fc->user->checkAcl('admin_user')) {
$this->fc->redirect(APP_WWW_URI,'access_denied');
}
$this->filter = $this->fc->sessionVariable('userfilter');
$this->limit = $this->fc->sessionVariable('userlimit');
$this->search = $this->fc->sessionVariable('search');
$title = 'All users';
$filter = '';
switch ($this->filter) {
case '1':
$title = 'Task managers';
$filter = "actags LIKE '%task_see_all%'";
$this->limit = 0;
break;
case '2':
$title = 'User managers';
$filter = "actags LIKE '%admin_user%'";
$this->limit = 0;
break;
default:
break;
}
$this->data = new MemberModel();
$this->data->enableAuthentication();
$this->data->connectDb();
$this->data->orderBy('nickname ASC');
if ($fs = DbQueryHelper::parseSearch($this->search)) {
$this->data->where("nickname LIKE '$fs'");
}
if ($filter) {
$this->data->having($filter);
}
if ($this->limit) {
$this->data->limit(0, $this->limit);
}
$this->data->loadList();
$this->page->set('title',$title.' | TaskFreak! Time Tracker Administration');
$this->setView('admin/user-list');
$this->view();
}
public function editAction() {
$this->_loadUser();
$this->data->addHelper('html_form');
$this->setView('admin/user-edit');
$this->view();
}
public function editReaction() {
$this->_loadUser();
$this->data->fields('nickname,username,time_zone,email');
$this->data->set($this->fc->request);
if ($this->fc->chkReqVar('pass1') && $this->fc->chkReqVar('pass2')) {
if ($this->data->setPassword($this->fc->getReqVar('pass1'), $this->fc->getReqVar('pass2'))) {
// save it all
$this->data->ignore();
} else {
// do not save password
$this->data->ignore('password,salt');
}
}
if ($this->data->check()) {
$this->data->set('enabled',1); // always enable for now
$myself = ($this->data->getUid() == $this->fc->user->getUid());
if ($this->data->save(!$myself)) {
if ($myself) {
// editing own profile need to relogin
$this->data->updateSessionVariables();
}
$this->fc->autoRedirect('user saved');
}
}
return true;
}
public function deleteAction() {
$this->_loadUser();
if ($this->canDeleteThisUser) {
if ($this->data->delete()) {
$db = DbConnector::getConnection();
// delete this user's rights
$db->query('DELETE FROM '.$this->data->dbTable('acl_user').' WHERE user_id='.$this->data->getUid());
// delete this user's tasks
$db->query('DELETE FROM '.$this->data->dbTable('task').' WHERE member_id='.$this->data->getUid());
$this->fc->redirect(APP_WWW_URI.'admin','user deleted');
}
}
$this->fc->redirect(APP_WWW_URI.'admin','user deleted');
}
public function switchAction() {
if (!$this->fc->user->checkAcl('task_see_all')) {
$this->fc->redirect(APP_WWW_URI,'access_denied');
}
if ($id = $this->fc->getReqVar('id')) {
// switch has been requested
$obj = new MemberModel();
$obj->connectDb();
$obj->setUid($id);
if ($obj->load()) {
$this->fc->setSessionVariable('switch_id', $id);
$this->fc->setSessionVariable('switch_name', $obj->get('nickname'));
}
$this->fc->redirect(APP_WWW_URI,'switched to '.$obj->get('nickname'));
}
$this->switch_id = $this->fc->getSessionVariable('switch_id');
$this->data = new MemberModel();
$this->data->connectDb();
$this->data->orderBy('nickname ASC');
$this->data->loadList();
$this->setView('admin/switch');
$this->view();
}
protected function _loadUser() {
if (isset($this->data)) {
// already loaded
return false;
}
$this->canDeleteThisUser = false;
$this->data = new MemberModel();
$this->data->enableAuthentication();
$this->data->connectDb();
// check access rights
if ($this->fc->user->checkAcl('admin_user')) {
if ($id = $this->fc->getReqVar('id')) {
$this->data->setUid($id);
$this->data->load();
if ($id != $this->fc->user->getUid()) {
$this->canDeleteThisUser = true;
}
} else {
// create a ne user : set defaults
$this->data->set('time_zone', $GLOBALS['config']['datetime']['timezone_server']->getName());
}
} else {
// not an admin : can only load itself
$this->data = $this->fc->user;
}
return true;
}
}

56
app/controller/login.php Normal file
View File

@ -0,0 +1,56 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.2
* @copyright GNU General Public License (GPL) version 3
*/
/**
* Login
*
* List of current tasks
* @since 0.2
*/
class Login extends AppController {
public function __construct() {
parent::__construct(false);
}
/**
* Login form
*/
public function mainAction() {
$this->fc->user->addHelper('html_form');
$this->page->set('title','TaskFreak! Login');
$this->page->add('css',array('form.css','freak.css','login.css'));
$this->page->add('js','form.js');
$this->setView('login/login');
$this->view();
}
/**
* Logs user in
* @todo redirect to requested page
*/
public function mainReaction() {
$this->fc->user->fields('username,password');
$this->fc->user->set($this->fc->request);
if ($this->fc->user->login($this->fc->user->get('username'), $this->fc->user->get('password'))) {
NaviHelper::redirect(APP_WWW_URI);
}
return true; // show action again
}
/**
* Logs user out
* @todo show logout summary page
*/
public function outAction() {
$this->fc->user->logout();
NaviHelper::redirect(APP_WWW_URI);
}
}

328
app/controller/task.php Normal file
View File

@ -0,0 +1,328 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.2
* @copyright GNU General Public License (GPL) version 3
*/
/**
* Task
*
* List of current tasks
* @since 0.1
*/
class Task extends AppController {
public function __construct() {
parent::__construct(true);
$this->fc->setSessionDefault('limit',10);
if (APP_SETUP_USER_MODEL) {
$this->fc->setSessionDefault('switch_id',$this->fc->user->getUid());
$this->fc->setSessionDefault('switch_name',$this->fc->user->get('nickname'));
$this->switch_id = $this->fc->getSessionVariable('switch_id');
$this->user_id = $this->fc->user->getUid();
} else {
$this->switch_id = $this->user_id = 0;
}
$this->fc->loadModel('TaskModel');
$this->current = TaskSummary::loadCurrent();
if ($this->fc->getReqVar('ajax')) {
$this->page->clean('css');
$this->page->clean('js');
} else {
$this->page->add('css',array('form.css','freak.css','list.css','tracker.css','colorbox.css'));
// $this->page->add('js',array('jquery.form.min.js','jquery.colorbox-min.js'));
$this->page->add('js','freak.js');
}
}
public function mainAction() {
$this->filter = $this->fc->sessionVariable('filter');
$title = 'Todo';
$filter = 'status=0 AND archived=0';
$this->actions = array();
switch ($this->filter) {
case '1':
$title = 'Completed';
$filter = 'status=1 AND archived=0';
$this->actions['open'] = 'Re-open';
$this->actions['valid'] = 'Validate';
$this->actions['archive'] = 'Archive';
break;
case '2':
$title = 'Valid'; // -TODO- check english term
$filter = 'status=2 AND archived=0';
$this->actions['open'] = 'Re-open';
$this->actions['archive'] = 'Archive';
break;
case '3':
$title = 'Archives';
$filter = 'archived=1';
$this->actions['unarchive'] = 'Unarchive';
break;
default:
$this->actions['report'] = 'Report 1 day';
$this->actions['close'] = 'Mark as done';
$this->actions['valid'] = 'Validate';
$this->actions['archive'] = 'Archive';
break;
}
$this->_taskList($filter);
$this->page->set('title',$title.' | TaskFreak! Time Tracker');
if ($this->fc->getReqVar('ajax')) {
$this->setView('include/list-'.($this->expand?'expand':'compact'));
} else {
$this->setView('main');
}
$this->view();
}
public function mainReaction() {
// drop if no checkbox have been checked
if (!isset($_POST['chk'])) {
// -TODO- show error
$this->fc->redirect(APP_WWW_URI.'task/main/','please check at least one task');
}
// check action request
$action = $this->fc->chkReqVar('report,open,close,valid,archive,unarchive');
// do it then
TaskModel::updateManyAtOnce($action, $_POST['chk']);
// reload entire page
$this->fc->redirect(APP_WWW_URI.'task/main/');
}
/**
* called to start a task (from the task list)
* will only update the timer panel and change the task in the list
* (method called by ajax request only)
*/
public function timerAction() {
if ($id = $this->fc->getReqVar('id')) {
$obj = new TaskSummary();
$obj->connectDb();
$obj->setUid($id);
$obj->load();
if ($obj->getUid()) {
// -TODO- check rights
// first stop any running task (TODO for current user only)
if ($this->current) {
$cid = $this->current->getUid();
TimerModel::stop($cid);
$this->jsCode = "clockreport('$cid');";
}
// now start selected task
TimerModel::start($id);
$this->current = TaskSummary::loadCurrent();
$this->jsCode .= "clockstart('$id')";
}
$this->setView('include/timer');
$this->view();
return false;
}
}
/**
* change a task status (start, stop, close) or save a new one
* will only update the timer panel and reload the task list
* (method called by ajax request only)
*/
public function timerReaction() {
$this->jsCode = '';
if ($id = $this->fc->getReqVar('id')) {
// start / stop timer
$obj = new TaskSummary();
$obj->connectDb();
$obj->setUid($id);
$obj->load();
// what action then ?
$action = $this->fc->chkReqVar('pause,resume,start,stop,close');
FC::log_debug('loaded task ID='.$obj->getUid());
switch($action) {
case 'pause':
// try to pause
if ($this->current && $this->current->getUid() == $id) {
// ok, task is actually running
TimerModel::stop($id);
$this->current->set('stop',APP_SQL_NOW);
$this->jsCode .= "clockreport('$cid');clockstatus('paused');";
} else {
// nope, requested task is not running, show error
$this->jsCode = "alert('error trying to pause')";
FC::log_debug('error trying to pause non running task');
}
break;
case 'resume':
case 'start':
TimerModel::start($id);
$this->current = TaskSummary::loadCurrent();
$this->jsCode = "clockstart();";
break;
case 'stop':
if (TimerModel::stop($id)) {
$this->jsCode = "clockstatus();";
} else {
$this->jsCode = "alert('task already stopped');";
}
$this->current = false;
break;
case 'close':
if (TimerModel::stop($id)) {
$this->jsCode = "clockstatus();";
$this->current->updateStatus(1); // mark as done
$this->current = false;
}
break;
}
} else if ($title = $this->fc->getReqVar('title')) {
// creating a new task ?
$obj = TaskModel::parse($title, $def, $dte);
$obj->connectDb();
if ($this->fc->getReqVar('start')) {
$obj->set('deadline', APP_SQL_TODAY);
}
if ($obj->check($this->switch_id)) {
$obj->insert();
}
if ($this->fc->chkReqVar('start')) {
TimerModel::start($obj->getUid());
$this->current = TaskSummary::loadCurrent();
$this->jsCode = "clockstart();";
} else {
$this->current = false;
$this->jsCode = "clockstatus();";
}
}
$this->jsCode .= 'reloadList();';
$this->setView('include/timer');
$this->view();
return false;
}
protected function _taskList($filter) {
$this->expand = $this->fc->sessionVariable('expand');
$this->search = $this->fc->sessionVariable('search');
$this->limit = $this->fc->sessionVariable('limit');
$this->data = new TaskSummary();
$this->data->connectDb();
$this->data->where($filter);
if ($this->search) {
$this->data->where("title LIKE '%".$this->search."%'");
}
if ($this->switch_id) {
// user login enabled, filter by users
$this->data->where('member_id='.$this->switch_id);
}
$this->data->orderBy('deadline ASC, priority ASC, title ASC, start ASC');
if ($this->expand) {
$this->limit = 0;
$this->data->loadExpandList();
} else {
$this->data->limit(0,$this->limit);
$this->data->loadCompactList();
}
}
public function viewAction() {
$this->data = new TaskSummary();
$this->data->connectDb();
$ok = false;
if ($id = $this->fc->getReqVar('id')) {
$this->data->where('id='.$id);
$this->data->orderBy('start ASC');
if ($this->data->loadExpandList()) {
$ok = $this->data->next();
}
}
if (!$ok) {
echo 'Todo not found';
exit;
}
$this->data->chkDeadline();
$this->timer = new TimerModel();
$this->timer->addHelper('html_form');
$this->page->set('title','Todo details | TaskFreak! Time Tracker');
// $this->page->add('js',array('jquery.dateentry.pack.js','jquery.timeentry.pack.js'));
$this->setView('view');
$this->view();
}
public function createAction() {
$this->_loadTask();
$this->data->addHelper('html_form');
$this->page->set('title','Create Multiple Todos | TaskFreak! Time Tracker');
$this->setView('create');
$this->view();
}
public function createReaction() {
$this->result = array();
$data = explode("\n",$this->fc->getReqVar('data'));
ArrayHelper::arrayTrim($data);
$t = count($data); // total to parse
$i = 0; // successfully parsed counter
$defDate = ''; // date of previous created task
$defValue = ''; // default value for multiple tasks creation (set by *)
foreach ($data as $val) {
if ($objTask = TaskModel::parse($val, $defValue, $defDate)) {
// really creating a task
$objTask->connectDb();
if ($objTask->check($this->switch_id)) {
$objTask->insert();
$i++;
}
}
}
$this->fc->redirect('/task/main',$i.' task(s) created !');
}
public function editAction() {
$this->_loadTask();
$this->data->addHelper('html_form');
$this->page->set('title',($this->data->getUid()?'Edit':'Create').' Todo | TaskFreak! Time Tracker');
$this->setView('edit');
$this->view();
}
public function editReaction() {
$this->_loadTask();
$this->data->ignore('creation_date'); // do not submit or change creation date
$this->data->set($this->fc->request);
if ($this->data->check($this->switch_id)) {
$this->data->save();
$this->fc->redirect(APP_WWW_URI,'task_created');
}
return true; // show action again
}
protected function _loadTask() {
if (empty($this->data)) {
$this->data = new TaskModel();
$this->data->connectDb();
$uid = $this->data->dbUid();
if ($id = $this->fc->getReqVar($uid)) {
$this->data->setUid($id);
$this->data->load();
}
}
return $this->data->getUid();
}
}

132
app/controller/timer.php Normal file
View File

@ -0,0 +1,132 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.2
* @copyright GNU General Public License (GPL) version 3
*/
/**
* Task
*
* List of current tasks
* @since 0.1
*/
class Timer extends AppController {
public function __construct() {
parent::__construct(true);
$this->fc->loadModel('TaskModel');
$this->page->clean('css');
$this->page->clean('js');
}
/**
* show timer creation form
*/
public function mainAction() {
$this->setView('include/timer-form.php');
$this->view();
}
/**
* create timer
*/
public function mainReaction() {
$date = VarDte::sanitize($_POST['date'],$err);
$start = VarTim::sanitize($_POST['start_time'],$err);
$stop = VarTim::sanitize($_POST['stop_time'],$err);
$spent = VarDur::sanitize($_POST['spent'],$err);
$this->data = new TimerModel();
$this->data->set('task_id', $_POST['id']);
if ($start) {
$this->data->set('start',$_POST['date'].' '.$_POST['start_time']);
}
if ($stop) {
$this->data->set('stop',$_POST['date'].' '.$_POST['stop_time']);
}
if ($spent) {
$this->data->set('spent', $_POST['spent']);
}
/*
echo '<pre>';
print_r($_POST);
echo "\n\n";
echo "date : $date\n";
echo "start : $start : ".$this->data->get('start')."\n";
echo "stop : $stop : ".$this->data->get('stop')."\n";
echo "spent: $spent\n";
echo '</pre>';
*/
$this->data->setCheck();
if ($this->data->check()) {
$this->data->connectDb();
$this->data->set('manual',1);
$this->data->insert();
}
/*
echo $this->data;
exit;
*/
echo '<script type="text/javascript">';
echo "reloadList(); window.setTimeout('$.fn.colorbox.close()',1000);";
echo '</script>';
echo '<p class="empty">time added</p>';
return false;
}
public function deleteAction() {
// check stuff
$id = $this->fc->getReqVar('id');
$start = $this->fc->getReqVar('start');
if (empty($id) || empty($start)) {
$this->fc->redirect(CMS_WWW_URI,'ERROR:missing parameters');
}
// delete timer
$this->data = new TimerModel();
$this->data->connectDb();
if ($this->data->delete('task_id='.$id." AND start='".$start."'"))
{
// successfully deleted
if ($this->fc->getReqVar('ajax')) {
$this->data = new TaskSummary();
$this->data->connectDb();
$ok = false;
if ($id = $this->fc->getReqVar('id')) {
$this->data->where('id='.$id);
if ($this->data->loadExpandList()) {
$ok = $this->data->next();
}
}
$this->setView('include/timer-list');
$this->view();
echo '<script type="text/javascript">';
echo "reloadList();";
echo '</script>';
} else {
$this->fc->redirect(CMS_WWW_URI,'Timer deleted');
}
} else {
// error deleting
if ($this->fc->getReqVar('ajax')) {
echo '<script type="text/javascript">';
echo 'alert("can not delete timer");';
echo '</script>';
} else {
$this->fc->redirect(CMS_WWW_URI,'Timer NOT deleted');
}
}
}
}

44
app/model/member.php Normal file
View File

@ -0,0 +1,44 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.2
* @copyright GNU General Public License (GPL) version 3
*/
/**
* MemberModel
* @since 0.2
*/
class MemberModel extends UserAclModel {
public function __construct() {
parent::__construct('member');
$this->addProperties(array(
'nickname' => 'STR',
'email' => 'EML'
));
}
public function htmlRights() {
if ($this->isEmpty('actags')) {
return '';
}
$arr = explode(',', $this->get('actags'));
$arrTrans = array();
if (in_array('task_see_all', $arr)) {
$arrTrans[] = 'task manager';
}
if (in_array('admin_user', $arr)) {
$arrTrans[] = 'user admin';
}
return implode(', ',$arrTrans);
}
public function check() {
return parent::check('nickname,username');
}
}

391
app/model/task.php Normal file
View File

@ -0,0 +1,391 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.2
* @copyright GNU General Public License (GPL) version 3
*/
/**
* Task
*
* Class representing a task
* @since 0.1
*/
class TaskModel extends Model {
public function __construct() {
parent::__construct('task');
$this->addProperties(array(
'id' => 'UID',
'title' => 'STR',
'note' => 'BBS',
'priority' => 'NUM,'.json_encode($GLOBALS['config']['task']['priority']),
'begin' => 'DTE',
'deadline' => 'DTE',
'status' => 'NUM,{"options":["todo","done","valid"],"default":0}',
'archived' => 'BOL',
'member_id' => 'NUM'
));
}
/**
* check submitted data before saving task
*/
public function check($usrId) {
if ($this->isEmpty('begin')) {
$this->set('begin','9999-00-00');
}
if ($this->isEmpty('deadline')) {
$this->set('deadline','9999-00-00');
}
if ($this->isEmpty('member_id') && APP_SETUP_USER_MODEL) {
$this->set('member_id', $usrId);
}
return parent::check('title');
}
/**
* parse a single line string for task params
* @return a task object
*/
public static function parse($str, &$def, &$dte) {
if (!preg_match('/^(\* |\*{2,3})?([+|-][0-9]{0,2}|[0-9]{2}\/[0-9]{2})?( ?[0-9]+\))?(.+)?$/',$str, $arr)) {
return false;
}
ArrayHelper::arrayTrim($arr);
$obj = new TaskModel();
$tst = substr($arr[1],0,1);
switch ($tst) {
case '*': // multiple
if ($arr[1] == '**') {
// reset default date (**)
$dte = '';
} else if ($arr[1] == '***') {
// reset default title (***)
$def = '';
} else {
if ($arr[2]) {
// remember date
if ($tst == '+') {
if ($n = substr($arr[2], 1)) {
// number of days later
$obj->set('deadline',$arr[2].' days');
} else {
// no number, means today
$obj->set('deadline', APP_SQL_TODAY);
}
} else {
$dte = $arr[2];
}
}
if ($arr[4]) {
// remember title
$def = $arr[4];
}
}
return false;
break;
case '-': // no deadline
$obj->set('deadline','9999-00-00');
break;
case '+': // specify deadline
if ($n = substr($arr[2], 1)) {
// number of days later
$obj->set('deadline',$arr[2].' days');
} else {
// no number, means today
$obj->set('deadline', APP_SQL_TODAY);
}
break;
default:
if ($arr[2]) {
// specific date set
$obj->set('deadline', $arr[2]);
$dte = $obj->get('deadline');
} else if (!empty($dte)) {
// use default date (from batch)
$obj->set('deadline', $dte);
} else if ($GLOBALS['config']['task']['date']) {
// use default date (config)
$obj->set('deadline', $GLOBALS['config']['task']['date']);
// $dte = $obj->get('deadline');
} else {
// no date by default (config)
$dte = '';
}
}
$prio = $GLOBALS['config']['task']['priority']['default']; // default priority
if ($arr[3]) {
// priority ?
$prio = intval(substr($arr[3],0,-1));
}
$obj->set('priority',$prio);
$title = $arr[4];
if ($def) {
$title = $def.' : '.$title;
}
$obj->set('title',$title);
return $obj;
}
/*
* set archive status for multiple tasks
*/
public static function updateManyAtOnce($action, $arr) {
if (!count($arr)) {
return false;
}
$sta = '';
switch ($action) {
case 'report':
$sta = 'deadline = DATE_ADD(deadline, INTERVAL 1 DAY)';
break;
case 'open':
$sta = 'status=0';
break;
case 'close':
$sta = 'status=1';
break;
case 'valid':
$sta = 'status=2';
break;
case 'archive':
$sta = 'archived=1';
break;
case 'unarchive':
$sta = 'archived=0';
break;
}
if ($sta) {
$filter = 'id IN ('.implode(',',$arr).')';
DbConnector::query('UPDATE task SET '.$sta.' WHERE '.$filter);
return true;
}
return false;
}
}
class TaskSummary extends TaskModel {
public function __construct() {
parent::__construct();
$this->addProperties(array(
'start' => 'DTM',
'stop' => 'DTM',
'spent' => 'NUM',
'timers' => 'NUM'
));
}
public function htmlPriority() {
$arr = $this->getPropertyOptions('priority');
$st = $this->get('priority');
return $arr['options'][$st];
}
public function htmlStatus() {
$arr = $this->getPropertyOptions('status');
$str = $this->get('status');
$str = $arr['options'][$str]; // -TODO- TRANSLATE
if ($this->get('archived')) {
$str .= ' (archived)';
}
return $str;
}
public function htmlBegin() {
if ($this->isEmpty('start')) {
if ($this->isEmpty('begin')) {
return '-';
} else {
// -TODO- show in grey
return $this->html('begin',APP_DATE);
}
} else {
return $this->html('start',APP_DATETIME);
}
}
public function getEnd() {
if ($this->isEmpty('stop')) {
if ($this->isEmpty('deadline')) {
return '';
} else {
return $this->get('deadline');
}
} else {
return $this->get('stop');
}
}
public function htmlEnd($expanded=false) {
if ($this->isEmpty('stop')) {
return '-';
} else {
return $this->html('stop',APP_DATETIME);
}
}
public function htmlDeadline() {
if ($this->isEmpty('deadline')) {
return '-';
} else {
switch ($this->_diff) {
case 9999:
return '-';
case -1:
return 'yesterday';
case 0:
return 'today';
case 1:
return 'tomorrow';
default:
return $this->html('deadline',APP_DATE);
}
}
}
public function getTimeSpent() {
return $this->htmlTime($this->get('spent'), $this->isEmpty('start'));
}
public function getRealSpentSecs() {
return APP_NOW - strtotime($this->get('start'));
}
public function getRealSpent() {
$spent = $this->getRealSpentSecs();
$h = floor($spent / 3600);
$m = floor($spent / 60) - ($h*60);
$s = $spent - ($h*3600 + $m*60);
return str_pad($h, 2, '0',STR_PAD_LEFT)
.':'.str_pad($m, 2, '0',STR_PAD_LEFT)
.':'.str_pad($s, 2, '0',STR_PAD_LEFT);
}
public function chkDeadline() {
if ($this->isEmpty('deadline')) {
$this->_diff = 9999;
} else {
$dead = strtotime($this->get('deadline'));
$this->_diff = ($dead - intval(APP_TODAY)) / 3600 / 24;
}
}
public function isOpened($user_id) {
// -TODO- if no "validate" option, do not allow on closed tasks
return ($this->get('status') < 2 && (!$this->get('archived')) && ($this->get('member_id') == $user_id));
}
public function curCss($default='') {
$arr = array();
if ($this->_diff < 0) {
$arr[] = 'overdue';
} else if ($this->_diff == 0) {
$arr[] = 'today';
} else {
$arr[] = 'future';
}
if ($default) {
$arr[] = $default;
}
if (count($arr)) {
return ' class="'.implode(' ',$arr).'"';
} else {
return '';
}
}
public function htmlDate() {
$str = $this->html('end_date',APP_DATE_FRM,'no_date');
if ($css = $this->curCss()) {
return '<span'.$css.'>'.$str.'</span>';
} else {
return $str;
}
}
public static function htmlTime($spent, $stopped=true) {
if (empty($spent)) {
if ($stopped) {
return '--:--';
} else {
return 'running';
}
}
$h = floor($spent / 60);
$m = $spent - ($h*60);
return str_pad($h, 2, '0',STR_PAD_LEFT).':'.str_pad($m, 2, '0',STR_PAD_LEFT);
}
/**
* update current task status
*/
public function updateStatus($status) {
$this->connectDb();
$this->set('status',$status);
$this->fields('status');
return parent::update();
}
/**
* override load function
*/
public function load($filter='') {
$this->select('id, title, begin, deadline, start, stop, status, archived, '
.'CEIL(spent/60) as spent');
$this->from('task');
$this->leftJoin('timer','task.id=timer.task_id');
return parent::load($filter, false);
}
/**
* load current running timer
*/
public static function loadCurrent($id=0) {
$obj = new TaskSummary();
$obj->connectDb();
if ($id) {
$obj->setUid($id);
if ($obj->load()) {
return $obj;
}
} else {
$ftr = "stop='0000-00-00 00:00:00'";
if (!empty($_SESSION['appUserId'])) {
$ftr .= " AND member_id='".$_SESSION['appUserId']."'";
}
if ($obj->load($ftr)) {
return $obj;
}
}
return false;
}
public function loadCompactList() {
/*
SELECT task.*, MIN(start) as start, MAX(stop) as stop, SUM(CEIL(spent/60)) as spent
FROM `task`
LEFT JOIN timer ON task.id = timer.task_id
WHERE status < 2
GROUP BY id
*/
$this->select('task.*, MIN(start) as start, MAX(stop) as stop, '
.'SUM(CEIL(spent/60)) as spent, COUNT(timer.task_id) AS timers');
$this->from('task');
$this->leftJoin('timer','task.id=timer.task_id');
$this->groupBy('id');
return parent::loadList(false);
}
public function loadExpandList() {
$this->select('task.*, start, stop, CEIL(spent/60) as spent');
$this->from('task');
$this->leftJoin('timer','task.id=timer.task_id');
return parent::loadList(false);
}
}

93
app/model/timer.php Normal file
View File

@ -0,0 +1,93 @@
<?php
/**
* TaskFreak! Time Tracker
*
* @package taskfreak_tt
* @author Stan Ozier <taskfreak@gmail.com>
* @version 0.1
* @copyright GNU General Public License (GPL) version 3
*/
/**
* Timer
*
* Class representing a task time log
* @since 0.1
*/
class TimerModel extends Model {
public function __construct() {
parent::__construct('timer');
$this->addProperties(array(
'task_id' => 'NUM',
'start' => 'DTM',
'stop' => 'DTM',
'spent' => 'DUR',
'manual' => 'BOL', // 0:auto, 1:manual
));
}
/**
* check submitted data before saving task
*/
public function check() {
return parent::check('task_id,start,stop,spent');
}
/**
* set data and compute missing fields
* @param integer $start number of seconds since midnight
* @param integer $stop number of seconds since midnight
* @param integer $spent number of seconds spent on task
*/
public function setCheck() {
$start = strtotime($this->get('start'));
$stop = strtotime($this->get('stop'));
$spent = intval($this->get('spent'));
if (!$this->isEmpty('start')) {
if (!$this->isEmpty('stop')) {
// calc diff between start and stop
$this->set('spent',$stop-$start);
} else if ($this->get('spent')) {
$this->set('stop',strftime(APP_DATETIME_SQL,$start+$spent));
}
} else if ($spent) {
if (!$stop) {
$this->set('stop',APP_SQL_NOW);
$stop = APP_NOW;
}
$this->set('start',strftime(APP_DATETIME_SQL,$stop-$spent));
}
}
/**
* starts a timer
*/
public static function start($tid) {
$obj = new TimerModel();
$obj->connectDb();
$obj->set('task_id',$tid);
$obj->set('start',APP_SQL_NOW);
return $obj->insert();
}
/**
* stops a timer
*/
public static function stop($tid) {
if (!($tid = VarUid::sanitize($tid, $err))) {
return false;
}
$filter = "task_id='$tid' AND stop='0000-00-00 00:00:00'";
$obj = new TimerModel();
$obj->connectDb();
if (!$obj->load($filter)) {
return false;
}
$obj->set('stop',APP_SQL_NOW);
$obj->set('spent', strtotime($obj->get('stop')) - strtotime($obj->get('start')));
$obj->fields('stop,spent');
return $obj->update($filter);
}
}

20
app/view/admin/switch.php Normal file
View File

@ -0,0 +1,20 @@
<?php
$this->incView('include/page-top', false);
?>
<h1>Select user whose tasks you want to see</h1>
<ul>
<?php
while ($this->data->next()) {
echo '<li>';
if ($this->data->getUid() == $this->switch_id) {
echo $this->data->html('nickname');
} else {
echo '<a href="/admin/switch/id/'.$this->data->getUid().'">'.$this->data->html('nickname').'</a>';
}
echo '</li>';
}
?>
</ul>
<?php
$this->incView('include/page-bot', false);

View File

@ -0,0 +1,74 @@
<?php
$this->incView('include/page-top', false);
// echo $this->data->iAutoForm('user_edit','post','','top');
echo '<h1>';
if ($id = $this->data->getUid()) {
if ($id == $this->fc->user->getUid()) {
echo 'My preferences';
} else {
echo 'Edit a user';
}
} else {
echo 'Create new user';
}
echo '</h1>';
echo $this->data->iForm('user_edit','post');
echo $this->data->iHidden('id');
?>
<ol class="fields side">
<?php
echo $this->data->iFieldLabelled('nickname','','','li class="compulsory"');
echo $this->data->iFieldLabelled('email');
?>
<li>
<label for="i_time_zone">time zone</label>
<?php echo $this->data->iTimeZone('time_zone'); ?>
</li>
<?php
echo $this->data->iFieldLabelled('username','','','li class="compulsory"');
?>
<li>
<label for="i_pass1">password</label>
<?php echo $this->data->iPass('pass1', false); ?>
</li>
<li>
<label for="i_pass2">(repeat)</label>
<?php echo $this->data->iPass('pass2', false); ?>
</li>
<?php
if ($this->data->getUid() != $this->fc->user->getUid()) {
// can only change rights for other users
?>
<li>
<label>user rights</label>
<ul>
<li><input type="checkbox" id="i_acl_task_see_all" name="acl_task_see_all" value="1"<?php
if ($this->data->checkAcl('task_see_all')) echo ' checked="checked"';
?> /> <label for="i_acl_task_see_all">task manager</label></li>
<li><input type="checkbox" id="i_acl_admin_user" name="acl_admin_user" value="1"<?php
if ($this->data->checkAcl('admin_user')) echo ' checked="checked"';
?> /> <label for="i_acl_admin_user">user admin</label></li>
</ul>
</li>
<?php
}
?>
<li class="buttons">
<button type="submit" name="save" value="1" class="save">Save</button>
<?php
if ($this->canDeleteThisUser) {
?>
<a href="<?php echo $this->fc->getUrl('admin','delete',array('id'=>$this->data->getUid())); ?>"
onclick="return confirm('really delete the dude ?');" class="button marge delete">Delete</a>
<?php
}
?>
</li>
</ol>
<?php
$this->incView('include/page-bot', false);

View File

@ -0,0 +1,83 @@
<?php
$this->incView('include/page-top', false);
?>
<div id="dmain" class="full">
<div id="dfilters">
<span>
<a href="<?php echo $this->fc->getUrl('admin/edit');?>" class="ajax box new inv" title="Create new user">Create user</a>
</span>
<ul class="links horiz">
<?php
$arr = array('all'=>0,'task managers'=>1,'user admins'=>2);
foreach($arr as $lbl => $val) {
echo '<li';
if ($this->filter == $val) {
echo ' class="active"';
}
echo '><a href='.$this->fc->thisUrl(array('userfilter'=>$val)).'>'.$lbl.'</a></li>';
}
?>
</ul>
<ul class="links horiz">
<?php
$arr = array('10'=>10,'25'=>25,'50'=>50,'all'=>0);
foreach($arr as $lbl => $val) {
echo '<li';
if ($this->limit == $val) {
echo ' class="active"';
}
echo '><a href='.$this->fc->thisUrl(array('userlimit'=>$val)).'>'.$lbl.'</a></li>';
}
?>
</ul>
<form id="search" action="<?php $this->fc->thisUrl(); ?>" method="get">
<p>
<input type="text" name="search" value="<?php echo $this->search; ?>" tabindex="4" />
<button type="submit" name="go" value="1">search</button>
<button type="button" onclick="this.form.elements[0].value='';this.form.submit()">x</button>
</p>
</form>
</div>
<div id="dlist" class="users">
<?php
if ($this->data->count()) {
?>
<table>
<thead>
<tr>
<th>name</th>
<th>last visit</th>
</tr>
</thead>
<tbody>
<?php
while ($this->data->next()) {
$id = $this->data->getUid();
echo '<tr>';
echo '<td>';
echo '<a href="'.$this->fc->getUrl('admin','edit',array('id'=>$id)).'" class="onhold ajax box" title="Edit user">edit</a>';
echo $this->data->html('nickname');
if ($tmp = $this->data->htmlRights()) {
echo ' <small>'.$tmp.'</small>';
}
echo '</td>';
echo '<td>'.$this->data->html('last_login_date').'</td>';
echo '</tr>';
}
?>
</tbody>
</table>
<?php
} else {
?>
<p class="empty">sorry, no user found</p>
<?php
}
?>
</div>
</div>
<?php
$this->incView('include/page-bot', false);

32
app/view/create.php Normal file
View File

@ -0,0 +1,32 @@
<?php
$this->incView('include/page-top', false);
?>
<div id="sidepanel">
<p>Enter one task per line.</p>
<h4>Task format</h4>
<p>Task format : [date] [priority] [label :] task title<br />
<small>fields within brackets are optional</small></p>
<p><em>date</em> can be :</p>
<ul>
<li>a date in format dd/mm[/yy]</li>
<li>a + followed by number of days in the future</li>
<li>a - means no deadline</li>
<li>nothing means deadline is today</li>
</ul>
<p><em>priority</em> is a number between 1 and 9, followed by a )<br />
<small>&laquo; 1) &raquo; for urgent tasks, &laquo; 5) &raquo; for normal priority</small></p>
<p><em>label</em> is optional</p>
<h4>Defaults for multiple tasks</h4>
<p>a line starting by a * sets up defaults</p>
<p>Format is : * [date] [label]<br />
<small>&laquo; * +1 taskfreak &raquo; or &laquo; * 12/04 &raquo;</small></p>
<p>reset defaults with a line with &laquo; ** &raquo</p>
</div>
<?php
$hh = new HtmlFormHelper();
echo $hh->iForm('task_batch','post',$this->fc->thisUrl());
echo '<p>'.$hh->iTextArea('data').'</p>';
echo '<p><button type="submit" name="save" value="1" class="save">Save</button></p>';
echo '</form>';
$this->incView('include/page-bot', false);

34
app/view/edit.php Normal file
View File

@ -0,0 +1,34 @@
<?php
$this->incView('include/page-top', false);
// echo $this->data->iAutoForm('task_edit','post','','top');
echo $this->data->iForm('task_edit','post');
echo $this->data->iHidden('id');
?>
<ol class="fields multicol top">
<?php
echo $this->data->iFieldLabelled('title','','','li class="nline"');
// $this->data->iFieldLabelled('begin'); -TODO- unused ATM
echo $this->data->iFieldLabelled('deadline','','','li class="nline"');
?>
<li>
<label for="i_priority">priority</label>
<?php echo $this->data->iSelect('priority'); ?>
</li>
<?php
echo $this->data->iFieldLabelled('note','','','li class="nline"');
?>
<li class="nline inline">
<label for="i_status">status</label> :
<?php echo $this->data->iSelect('status'); ?>
</li>
<li class="inline">
<?php echo $this->data->iCheckBox('archived'); ?>
<label for="i_archived">mark task as archived</label>
</li>
<li class="nline buttons">
<button type="submit" name="save" value="1" class="save">Save Task</button>
</li>
</ol>
<?php
$this->incView('include/page-bot', false);

View File

@ -0,0 +1,105 @@
<?php
if ($this->data->count()) {
echo HtmlFormHelper::iForm('tasks');
?>
<table>
<thead>
<tr>
<th>&nbsp;</th>
<th>deadline</th>
<th>
<small><?php echo $this->data->total(); ?> item(s) found</small>
task
</th>
<th>start</th>
<th>stop</th>
<th>spent</th>
</tr>
</thead>
<tbody>
<?php
$arr = array();
$i = $total = 0;
$cid = ($this->current)?$this->current->getUid():0;
while ($this->data->next()) {
$this->data->chkDeadline();
$id = $this->data->getUid();
echo '<tr id="tr_'.$id.'"'
.((!$this->filter)?$this->data->curCss(($cid == $id)?'current':''):'')
.'>';
echo '<td>';
if (!in_array($id, $arr)) {
echo '<input type="checkbox" id="chk_'.$i.'" name="chk[]" '
.'value="'.$id.'" />';
$i++;
$arr[] = $id;
} else {
echo '<input type="checkbox" disabled="disabled" />';
}
$total += $this->data->get('spent');
?></td>
<td><?php echo $this->data->htmlDeadline(); ?></td>
<td>
<a href="<?php echo $this->fc->getUrl('task','edit',array('id'=>$id)); ?>" class="onhold ajax box" title="Edit task">edit</a>
<?php
// priority
echo '<span class="prio pr'.$this->data->get('priority');
echo '">'.$this->data->get('priority').'</span> ';
// note
echo '<a href="'.$this->fc->getUrl('task','view',array('id'=>$id)).'" ';
if ($this->data->isEmpty('note')) {
echo 'class="ajax box clickme"';
} else {
echo 'class="note ajax box clickme" ';
echo 'title="'.$this->data->html('note',200).'"';
}
echo '>';
// title
echo $this->data->html('title');
echo '</a>';
?>
</td>
<td><?php echo $this->data->htmlBegin(); ?></td>
<td><?php echo $this->data->htmlEnd(); ?></td>
<td id="sts_<?php echo $id; ?>">
<?php
if ($this->data->isOpened($this->user_id)) {
echo '<a href="'.$this->fc->getUrl('task','timer',array('id'=>$id)).'" '
.'class="onhold clock ajax" title="start task" rel="drun">start</a>';
}
echo '<span>';
if (!$this->expand && $cid == $id) {
echo 'running';
} else {
echo $this->data->getTimeSpent();
}
echo '</span>';
?>
</td>
</tr>
<?php
}
?>
</tbody>
<tfoot>
<tr>
<td colspan="3">
<a href="javascript:checkAll('f_tasks')">select all</a> |
<?php
foreach ($this->actions as $key => $label) {
echo ' <button type="submit" name="'.$key.'" '
.'value="1">'.VarStr::html($label).'</button>';
}
?>
</td>
<td colspan="2">TOTAL</td>
<td><?php echo TaskSummary::htmlTime($total); ?></td>
</tr>
</tfoot>
</table>
</form>
<?php
} else {
echo '<p class="empty">No Task found</p>';
}

View File

@ -0,0 +1,163 @@
<?php
if ($this->data->count()) {
// prepare array of data
$arrData = array();
$c = $d = $cid = -1;
while ($this->data->next()) {
if ($cid != $this->data->getUid()) {
$c++;
$d=0;
$cid = $this->data->getUid();
}
$arrData[$c][$d] = clone($this->data);
$d++;
}
// start list
echo HtmlFormHelper::iForm('tasks');
?>
<table>
<thead>
<tr>
<th>&nbsp;</th>
<th>deadline</th>
<th>
<small><?php echo count($arrData); ?> item(s) found</small>
task
</th>
<th>start</th>
<th>stop</th>
<th>spent</th>
</tr>
</thead>
<tbody>
<?php
$arr = array();
$i = $total = 0;
$cid = ($this->current)?$this->current->getUid():0;
for($j=0;$j<=$c;$j++) {
$arrObj = $arrData[$j];
$d = count($arrObj);
$k=0;
$str = '';
$subtotal = 0;
$obj = $arrObj[0];
$id = $obj->getUid();
// first loop to get total time spent on task
foreach ($arrObj as $obj) {
$subtotal += $obj->get('spent');
}
$total += $subtotal;
$obj->chkDeadline();
// first row displays task information
echo '<tr id="tr_'.$id.'"'
.((!$this->filter)?$obj->curCss(($d>1)?'noline':''):($d>1?' class="noline"':''))
.'>';
// checkbox
echo '<td><input type="checkbox" id="chk_'.$i.'" name="chk[]" '
.'value="'.$id.'" /></td>';
// deadline
echo '<td>'.$obj->htmlDeadline().'</td>';
echo '<td>';
// edit link
echo '<a href="/task/edit/id/'.$id.'" class="onhold ajax box" title="Edit task">edit</a>';
// priority
echo '<span class="prio pr'.$obj->get('priority');
echo '">'.$obj->get('priority').'</span> ';
// note
echo '<a href="'.$this->fc->getUrl('task','view',array('id'=>$id)).'" ';
if ($obj->isEmpty('note')) {
echo 'class="ajax box clickme"';
} else {
echo 'class="note ajax box clickme" ';
echo 'title="'.$obj->html('note',200).'"';
}
echo '>';
// title
echo $obj->html('title');
echo '</a>';
echo '</td>';
// start and end
if ($d>1) {
// many timer, skip info here
echo '<td>&nbsp;</td>';
echo '<td>&nbsp;</td>';
} else {
echo '<td>'.$obj->htmlBegin().'</td>';
echo '<td>'.$obj->htmlEnd().'</td>';
}
// subtotal
echo '<td id="sts_'.$id,'">';
if ($obj->isOpened($this->user_id)) {
echo '<a href="/task/timer/id/'.$id.'" class="onhold clock ajax" title="start task" rel="drun">start</a>';
}
echo TaskSummary::htmlTime($subtotal);
echo '</td>';
echo '</tr>';
if ($d == 1) {
// no timers defined, skip to next task
continue;
}
// second loop to display timers
foreach ($arrObj as $obj) {
$id = $obj->getUid();
$k++;
echo '<tr id="tr_'.$id.'_'.$k.'" class="timer'
.(($k<$d)?' noline':'')
.'">';
// checkbox
echo '<td>&nbsp;</td>';
// deadline
echo '<td>&nbsp;</td>';
// title
echo '<td>&nbsp;</td>';
// start
echo '<td>'.$obj->htmlBegin().'</td>';
// stop
echo '<td>'.$obj->htmlEnd().'</td>';
// time spent
echo '<td>'.$obj->getTimeSpent().'</td>';
echo '</tr>';
}
}
?>
</tbody>
<tfoot>
<tr>
<td colspan="3">
<a href="javascript:checkAll('f_tasks')">select all</a> |
<?php
foreach ($this->actions as $key => $label) {
echo ' <button type="submit" name="'.$key.'" '
.'value="1">'.VarStr::html($label).'</button>';
}
?>
</td>
<td colspan="2">TOTAL</td>
<td><?php echo TaskSummary::htmlTime($total); ?></td>
</tr>
</tfoot>
</table>
</form>
<?php
} else {
echo '<p>No Task found</p>';
}

View File

@ -0,0 +1,18 @@
</div>
</div>
<div id="dfoot">
<div id="dcopy">
<a href="http://www.taskfreak.com" target="_blank">TaskFreak</a>
<span>TT v0.2</span>
</div>
<?php
$err = false;
$tmp = $this->fc->getHelper('messaging');
$tmp = $tmp->getMessages($err);
if ($tmp) {
echo $tmp;
} else {
echo '...';
}
?>
</div>

View File

@ -0,0 +1,43 @@
<?php
if (isset($this->current)) {
?>
<form id="drun" action="/task/timer"<?php
if ($this->current) {
echo ' class="running"';
}
?> method="post">
<?php
$this->incView('include/timer');
?>
</form>
<?php
}
?>
<div id="global">
<div id="dtop">
<h1><a href="/">TaskFreak</a></h1>
<div id="duser">
<?php
if (APP_SETUP_USER_MODEL && $this->fc->user->isLoggedIn()) {
echo $this->fc->user->html('nickname');
if ($this->fc->getSessionVariable('switch_id') != $this->fc->user->getUid()) {
echo ' as '.varStr::html($this->fc->getSessionVariable('switch_name'));
}
echo '<br /><small>';
if ($this->fc->user->checkAcl('task_see_all')) {
echo '<a href="'.APP_WWW_URI.'admin/switch" class="ajax box">switch</a> | ';
}
if ($this->fc->user->checkAcl('admin_user')) {
echo '<a href="'.APP_WWW_URI.'admin">admin</a> | ';
} else {
echo '<a href="'.APP_WWW_URI.'admin/edit" class="ajax box">profile</a> | ';
}
echo '<a href="'.APP_WWW_URI.'login/out">logout</a>';
echo '</small>';
} else {
echo 'TaskFreak!<br /><small>Time Tracking</small>';
}
?>
</div>
</div>
<div id="dwork">

View File

@ -0,0 +1,37 @@
<?php
echo $this->timer->iForm('task_timer','post',APP_WWW_URI.'timer/main');
?>
<input type="hidden" name="id" value="<?php echo $this->data->getUid(); ?>" />
<ol class="fields multicol side">
<li class="nline">
<label>date</label>
<input type="text" id="i_date" name="date" class="date" value="<?php echo VarDte::value(APP_SQL_TODAY); ?>" />
</li>
<li class="nline">
<label>start</label>
<input type="text" id="i_start_time" name="start_time" class="time" />
</li>
<li>
<label>stop</label>
<input type="text" id="i_stop_time" name="stop_time" class="time" />
</li>
<li class="nline">
<label>spent</label>
<input type="text" id="i_spent" name="spent" class="duration" value="00:00" />
</li>
<li class="nline buttons">
<button type="submit" name="save" value="1" class="save">Save report</button>
<a href="javascript:document.task_timer.reset()">reset</a>
</li>
</ol>
<hr class="clear" />
<div class="help">
<p>Enter either :</p>
<ul>
<li>A start time only (will calculate time from then until now)</li>
<li>A time spent only (will consider you've just finished the task)</li>
<li>A start time and an stop time</li>
<li>A start time and a time spent</li>
<li>A stop time and a time spent</li>
</ul>
</div>

View File

@ -0,0 +1,47 @@
<?php
$total = 0;
if ($this->data->get('spent')) {
?>
<table class="list">
<thead>
<tr>
<th>start</th>
<th>stop</th>
<th>spent</th>
</tr>
</thead>
<tbody>
<?php
do {
$params = array(
'id' => $this->data->getUid(),
'start' => $this->data->get('start')
);
$total += $this->data->get('spent');
echo '<tr id="tr_'.$this->data->get('id').'_'.VarDtm::strToUnix($this->data->get('start')).'">';
// start
echo '<td>'.$this->data->htmlBegin().'</td>';
// stop
echo '<td>'.$this->data->htmlEnd().'</td>';
// time spent
echo '<td>';
echo '<a href="'.$this->fc->getUrl('timer','delete',$params).'" class="onhold ajax confirm" rel="tab2">delete</a>';
echo $this->data->getTimeSpent();
echo '</td>';
echo '</tr>';
} while ($this->data->next());
?>
</tbody>
<tfoot>
<tr>
<td colspan="2">Total</td>
<td><?php echo TaskSummary::htmlTime($total); ?></td>
</tr>
</tfoot>
</table>
<?php
} else {
echo '<p class="empty">No timers yet</p>';
}
?>
<p class="empty"><a href="#tab3" onclick="tabber.show(3); return false;">Report more time spent</a></p>

View File

@ -0,0 +1,45 @@
<?php
if ($this->current) {
?>
<div id="timerstatus">
<?php
if ($this->current->isEmpty('stop')) {
// task is running
?>
<!-- button type="submit" name="pause" value="1" class="submit" tabindex="2">pause</button -->
<button type="submit" name="stop" value="1" class="submit" tabindex="2">stop</button>
<button type="submit" name="close" value="1" class="warn" tabindex="3" onclick="return confirm('mark this task as done ?')">done</button>
<?php
} else {
// task is paused (requested from ajax only)
?>
<button type="submit" name="resume" value="1" class="submit" tabindex="2">resume</button>
<button type="submit" name="stop" value="1" class="warn" tabindex="3">stop</button>
<?php
}
?>
</div>
<p>
<input type="hidden" name="id" value="<?php echo $this->current->getUid(); ?>" />
<input type="hidden" id="i_timer" name="timer" value="<?php echo $this->current->getRealSpentSecs(); ?>" />
<span id="dtimer"><?php echo $this->current->getRealSpent(); ?></span>
<?php echo $this->current->html('title'); ?>
</p>
<?php
} else {
?>
<div id="timerstatus">
<button type="submit" name="save" value="1" class="saveadd" tabindex="2">Save</button>
<button type="submit" name="start" value="1" class="save" tabindex="3">Start</button>
</div>
<p>
<input type="text" name="title" value="" tabindex="1" />
</p>
<?php
}
if (isset($this->jsCode)) {
echo '<script type="text/javascript">';
echo $this->jsCode;
echo '</script>';
}
?>

30
app/view/login/login.php Normal file
View File

@ -0,0 +1,30 @@
<?php
$this->incView('include/page-top', false);
?>
<form action="/login" method="post">
<?php
if ($str = $this->fc->user->getAuthError()) {
echo '<p>Can not login: '.VarStr::html($str).'</p>';
}
?>
<ol class="fields side">
<li>
<label for="i_username">Username</label>
<?php echo $this->fc->user->iText('username'); ?>
</li>
<li>
<label for="i_password">Password</label>
<?php echo $this->fc->user->iPass('password'); ?>
</li>
<li class="buttons">
<button type="submit" name="login" value="1" class="submit">Login</button>
</li>
</ol>
</form>
<?php
if ($GLOBALS['config']['log_debug'] == 2) {
$this->fc->user->htmlAllErrors();
echo $this->fc->user;
}
$this->incView('include/page-bot', false);

66
app/view/main.php Normal file
View File

@ -0,0 +1,66 @@
<?php
$this->incView('include/page-top', false);
?>
<div id="dmain" class="full">
<div id="dfilters">
<span>
<a href="<?php echo $this->fc->getUrl('task/edit');?>" class="ajax box new inv" title="Create single task">Create task</a>
<a href="<?php echo $this->fc->getUrl('task/create');?>" class="ajax bigbox new multi inv" title="Create multiple tasks">Create multiple</a>
<a href="javascript:reloadList()" class="new reload inv" title="Reload list">Reload</a>
</span>
<ul class="links horiz">
<?php
$arr = array('todo'=>0,'done'=>1,'valid'=>2,'archived'=>3);
foreach($arr as $lbl => $val) {
echo '<li';
if ($this->filter == $val) {
echo ' class="active"';
}
echo '><a href='.$this->fc->thisUrl(array('filter'=>$val)).'>'.$lbl.'</a></li>';
}
?>
</ul>
<ul class="links horiz">
<?php
$arr = array('compact'=>0,'expand'=>1);
foreach($arr as $lbl => $val) {
echo '<li';
if ($this->expand == $val) {
echo ' class="active"';
}
echo '><a href='.$this->fc->thisUrl(array('expand'=>$val)).'>'.$lbl.'</a></li>';
}
?>
</ul>
<ul class="links horiz">
<?php
$arr = array('10'=>10,'25'=>25,'50'=>50,'all'=>0);
foreach($arr as $lbl => $val) {
echo '<li';
if ($this->limit == $val) {
echo ' class="active"';
}
echo '><a href='.$this->fc->thisUrl(array('limit'=>$val)).'>'.$lbl.'</a></li>';
}
?>
</ul>
<form id="search" action="<?php $this->fc->thisUrl(); ?>" method="get">
<p>
<input type="text" name="search" value="<?php echo $this->search; ?>" tabindex="4" />
<button type="submit" name="go" value="1">search</button>
<button type="button" onclick="this.form.elements[0].value='';this.form.submit()">x</button>
</p>
</form>
</div>
<div id="dlist" class="tasks">
<?php
if ($this->expand) {
$this->incView('include/list-expand');
} else {
$this->incView('include/list-compact');
}
?>
</div>
</div>
<?php
$this->incView('include/page-bot', false);

64
app/view/view.php Normal file
View File

@ -0,0 +1,64 @@
<?php
$this->incView('include/page-top', false);
?>
<h1><?php echo $this->data->html('title'); ?></h1>
<div id="tabs" class="tabs">
<ul id="tabs-nav" class="nav">
<li><a href="#tab1">info</a></li>
<li><a href="#tab2">history</a></li>
<li><a href="#tab3">report time spent</a></li>
</ul>
<div id="tab1" class="tab">
<table class="info">
<tbody>
<tr>
<th>deadline</th>
<td><?php echo $this->data->htmlDeadline(); ?></td>
</tr>
<tr>
<th>priority</th>
<td><?php echo $this->data->htmlPriority(); ?></td>
</tr>
<tr>
<th>status</th>
<td><?php echo $this->data->htmlStatus(); ?></td>
</tr>
<?php
if (!$this->data->isEmpty('note')) {
?>
<tr>
<th>note</th>
<td><?php echo $this->data->html('note'); ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
<div id="tab2" class="tab">
<?php
$this->incView('include/timer-list');
?>
</div>
<div id="tab3" class="tab">
<?php
$this->incView('include/timer-form');
?>
</div>
</div>
<script type="text/javascript">
var tabber = new Yetii({id: 'tabs'});
$('a.ajax').makeajax();
$('#f_task_timer').ajaxForm({
target: '#f_task_timer',
data: {'ajax':'1'}
});
// $.getScript('/asset/js/jquery.dateentry.pack.js');
// $.getScript('/asset/js/jquery.timeentry.pack.js');
</script>
<script type="text/javascript" src="<?php echo APP_WWW_URI.'asset/js/jquery.dateentry.pack.js'; ?>"></script>
<script type="text/javascript" src="<?php echo APP_WWW_URI.'asset/js/jquery.timeentry.pack.js'; ?>"></script>
<?php
$this->incView('include/page-bot', false);

297
asset/css/form.css Normal file
View File

@ -0,0 +1,297 @@
/**
* fields should always be under a OL.fields
* OL.side will show labels on the left
* OL.multicol may have multi columns
* -> LI.newrow stars a new row
* SPAN.error for error messages
*/
/* layout */
ol.fields {
list-style: none;
padding: 0 0 5px 5px;
}
ol.lined li {
border-bottom: 1px solid #eee;
}
ol li.linefree {
border-bottom: 0px none;
}
ol.side li {
padding-top: 2px;
padding-bottom: 6px;
}
ol.side li label {
float: left;
width: 120px;
padding: 2px 0 0 4px;
color: #333;
}
ol.top li label {
display: block;
}
ol.fields li.compulsory {
}
ol.fields li.compulsory label {
/* color: #600; */
background: transparent url(../img/compulsory.png) no-repeat right top;
}
ol.fields li span, ol.fields li small.nline {
padding: 2px 0 0 4px;
}
ol.fields li span.ui-icon {
padding: 0;
}
ol.side li span, ol.side li small.nline {
display: block;
margin-left: 120px;
}
ol.fields li.buttons {
border-bottom: 0px none;
}
ol.side li.buttons {
padding-left: 122px;
}
ol.fields ul {
clear: left;
list-style: none;
}
ol.fields ul li {
border-bottom: 0 none;
}
ol.fields li.inline {
padding: 6px 4px;
}
ol.fields li.inline span {
display: inline;
}
ol.side ul {
margin: 0 0 -18px 100px;
position:relative;
top:-18px;
}
ol.side ul li label, ol li.inline label {
padding: 0;
width: auto;
float: none;
display: inline;
}
ol.multicol li {
float: left;
margin-left: 12px;
}
ol.multicol li.nline {
clear: left;
margin-left: 0;
}
ol.side li strong {
display: block;
padding-left: 124px;
}
ol.side li span.error {
display: block;
/* padding-left: 124px; */
font-size: .917em;
color: #900;
}
/* fields */
input, textarea, option {
font-style: Helvetica, Arial, sans-serif;
font-size: 1em;
}
ol.styled input, ol.styled select, ol.styled textarea {
padding: 2px;
border: 2px solid #666;
background-color: #fff;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
ol.styled input:focus, ol.styled select:focus, ol.styled textarea:focus {
border-color: #333;
outline: none;
}
.wxxs {
width: 30px;
}
.wxs {
width: 50px;
}
.ws {
width: 80px;
}
.wm {
width: 120px;
}
.wl {
width: 250px;
}
.wxl {
width: 400px;
}
.wxxl {
width: 99%;
}
textarea.hs {
height: 50px;
}
textarea.hm {
height: 100px;
}
textarea.hl {
height: 200px;
}
textarea.hxl {
height: 350px;
}
input.fake, textarea.fake {
border: 1px solid transparent;
}
input.file {
border: 0 none;
}
input.red, textarea.red {
border: 1px solid #933;
background-color: #fff3f3;
}
input.date {
width: 80px;
}
input.time, input.duration {
width: 60px;
}
ol.fields li span.dateEntry_wrap, ol.fields li span.timeEntry_wrap {
padding: 0;
margin-left: 3px;
display: inline;
}
ol.fields li span.dateEntry_control, ol.fields li span.timeEntry_control {
padding: 0;
margin: 0 0 0 3px;
display: inline;
}
/* captcha */
ol li.form_captcha {
}
ol li.form_captcha span.cms_captcha {
display:block;
margin: 0 0 12px 0;
width: 360px;
height: 40px;
border: 2px solid #369;
background-color: #fff;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
ol li.form_captcha span.cms_captcha img {
float: left;
}
ol li.form_captcha span.cms_captcha input {
float: left;
border: 0 none;
border-left: 2px dotted #69c;
width: 140px;
height: 24px;
padding: 8px 5px;
vertical-align: middle;
}
ol li.form_captcha span.tznError {
display: block;
padding: 3px 0;
font-size: 1em;
color: #900;
}
ol.fields li.form_captcha span.cms_captcha {
margin: 0 0 12px 122px;
}
/* buttons */
button, a.button {
color: #333;
font-size: 12px;
border: 1px solid #ccc;
padding: 1px 4px;
/* height: 22px; */
background-image: url(../img/bg-buttons.png);
background-position: 0 0;
background-repeat: repeat-x;
text-decoration: none;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
a.button {
padding: 2px 4px;
height: auto;
}
button:hover, a.button:hover, button:focus, a.button:focus {
color: #000;
border-color: #999;
cursor: pointer;
}
button.create, a.create {
background-position: -30px -22px;
}
button.submit, a.submit {
background-position: -30px -22px;
}
button.save {
padding-left: 30px;
background-position: 0 -22px;
}
button.saveclose {
padding-left: 30px;
background-position: 0 -44px;
}
button.saveadd {
padding-left: 30px;
background-position: 0 -66px;
}
button.delete, a.delete {
padding-left: 30px;
background-position: 0 -88px;
}
button.warn, a.warn {
background-position: -30px -88px;
}
.close {
margin-left: 10px;
padding: 0 4px 2px 10px;
background: transparent url(../img/cross.png) no-repeat left center;
}
a.close {
color: #333;
text-decoration: underline;
}
a.close:hover {
color: #000;
}
.faded {
opacity: 0.3;
-moz-opacity: 0.3;
filter: alpha(opacity=30);
}
.marge {
margin-left: 10px;
}
/*
#ajax-load {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.7;
-moz-opacity: 0.7;
filter: alpha(opacity=70);
background: #fff url(../img/loading.gif) no-repeat center center;
z-index: 200;
behavior: url(/assets/css/iedivfix.htc);
}
.loading {
opacity: 0.5;
-moz-opacity: 0.5;
filter: alpha(opacity=50);
background: url(../img/loading.gif) no-repeat center center;
}
*/

View File

@ -0,0 +1,3 @@
<?php $this->callHelper('html_asset','footerStuff'); ?>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title><?php echo $this->html('title'); ?></title>
<?php
if (!$this->isEmpty('description')) {
?>
<meta name="description" content="<?php echo $this->html('description'); ?>" />
<?php
}
if (!$this->isEmpty('keywords')) {
?>
<meta name="keywords" content="<?php echo $this->html('keywords'); ?>" />
<?php
}
?>
<link rel="SHORTCUT ICON" href="/favicon.ico" />
<?php $this->callHelper('html_asset','headerStuff'); ?>
</head>
<body>

BIN
asset/img/bg-buttons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
asset/img/close.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

BIN
asset/img/closebox.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

BIN
asset/img/closebox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
asset/img/compulsory.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

BIN
asset/img/cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

BIN
asset/img/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
asset/img/spinner.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
asset/img/spinnerOrange.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

BIN
asset/img/spinnerSquare.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
asset/img/vtip_arrow.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

270
asset/js/freak.js Normal file

File diff suppressed because one or more lines are too long

154
asset/js/jquery-1.4.2.min.js vendored Normal file
View File

@ -0,0 +1,154 @@
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.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|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);

2
asset/js/jquery.colorbox-min.js vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

675
asset/js/jquery.form.js Normal file
View File

@ -0,0 +1,675 @@
/*!
* jQuery Form Plugin
* version: 2.43 (12-MAR-2010)
* @requires jQuery v1.3.2 or later
*
* Examples and documentation at: http://malsup.com/jquery/form/
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
;(function($) {
/*
Usage Note:
-----------
Do not use both ajaxSubmit and ajaxForm on the same form. These
functions are intended to be exclusive. Use ajaxSubmit if you want
to bind your own submit handler to the form. For example,
$(document).ready(function() {
$('#myForm').bind('submit', function() {
$(this).ajaxSubmit({
target: '#output'
});
return false; // <-- important!
});
});
Use ajaxForm when you want the plugin to manage all the event binding
for you. For example,
$(document).ready(function() {
$('#myForm').ajaxForm({
target: '#output'
});
});
When using ajaxForm, the ajaxSubmit function will be invoked for you
at the appropriate time.
*/
/**
* ajaxSubmit() provides a mechanism for immediately submitting
* an HTML form using AJAX.
*/
$.fn.ajaxSubmit = function(options) {
// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
if (!this.length) {
log('ajaxSubmit: skipping submit process - no element selected');
return this;
}
if (typeof options == 'function')
options = { success: options };
var url = $.trim(this.attr('action'));
if (url) {
// clean url (don't include hash vaue)
url = (url.match(/^([^#]+)/)||[])[1];
}
url = url || window.location.href || '';
options = $.extend({
url: url,
type: this.attr('method') || 'GET',
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
}, options || {});
// hook for manipulating the form data before it is extracted;
// convenient for use with rich editors like tinyMCE or FCKEditor
var veto = {};
this.trigger('form-pre-serialize', [this, options, veto]);
if (veto.veto) {
log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
return this;
}
// provide opportunity to alter form data before it is serialized
if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
log('ajaxSubmit: submit aborted via beforeSerialize callback');
return this;
}
var a = this.formToArray(options.semantic);
if (options.data) {
options.extraData = options.data;
for (var n in options.data) {
if(options.data[n] instanceof Array) {
for (var k in options.data[n])
a.push( { name: n, value: options.data[n][k] } );
}
else
a.push( { name: n, value: options.data[n] } );
}
}
// give pre-submit callback an opportunity to abort the submit
if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
log('ajaxSubmit: submit aborted via beforeSubmit callback');
return this;
}
// fire vetoable 'validate' event
this.trigger('form-submit-validate', [a, this, options, veto]);
if (veto.veto) {
log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
return this;
}
var q = $.param(a);
if (options.type.toUpperCase() == 'GET') {
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
options.data = null; // data is null for 'get'
}
else
options.data = q; // data is the query string for 'post'
var $form = this, callbacks = [];
if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
// perform a load on the target only if dataType is not provided
if (!options.dataType && options.target) {
var oldSuccess = options.success || function(){};
callbacks.push(function(data) {
var fn = options.replaceTarget ? 'replaceWith' : 'html';
$(options.target)[fn](data).each(oldSuccess, arguments);
});
}
else if (options.success)
callbacks.push(options.success);
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
for (var i=0, max=callbacks.length; i < max; i++)
callbacks[i].apply(options, [data, status, xhr || $form, $form]);
};
// are there files to upload?
var files = $('input:file', this).fieldValue();
var found = false;
for (var j=0; j < files.length; j++)
if (files[j])
found = true;
var multipart = false;
// var mp = 'multipart/form-data';
// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
// options.iframe allows user to force iframe mode
// 06-NOV-09: now defaulting to iframe mode if file input is detected
if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
// hack to fix Safari hang (thanks to Tim Molendijk for this)
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
if (options.closeKeepAlive)
$.get(options.closeKeepAlive, fileUpload);
else
fileUpload();
}
else
$.ajax(options);
// fire 'notify' event
this.trigger('form-submit-notify', [this, options]);
return this;
// private function for handling file uploads (hat tip to YAHOO!)
function fileUpload() {
var form = $form[0];
if ($(':input[name=submit]', form).length) {
alert('Error: Form elements must not be named "submit".');
return;
}
var opts = $.extend({}, $.ajaxSettings, options);
var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
var id = 'jqFormIO' + (new Date().getTime());
var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" onload="(jQuery(this).data(\'form-plugin-onload\'))()" />');
var io = $io[0];
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
var xhr = { // mock object
aborted: 0,
responseText: null,
responseXML: null,
status: 0,
statusText: 'n/a',
getAllResponseHeaders: function() {},
getResponseHeader: function() {},
setRequestHeader: function() {},
abort: function() {
this.aborted = 1;
$io.attr('src', opts.iframeSrc); // abort op in progress
}
};
var g = opts.global;
// trigger ajax global events so that activity/block indicators work like normal
if (g && ! $.active++) $.event.trigger("ajaxStart");
if (g) $.event.trigger("ajaxSend", [xhr, opts]);
if (s.beforeSend && s.beforeSend(xhr, s) === false) {
s.global && $.active--;
return;
}
if (xhr.aborted)
return;
var cbInvoked = false;
var timedOut = 0;
// add submitting element to data if we know it
var sub = form.clk;
if (sub) {
var n = sub.name;
if (n && !sub.disabled) {
opts.extraData = opts.extraData || {};
opts.extraData[n] = sub.value;
if (sub.type == "image") {
opts.extraData[n+'.x'] = form.clk_x;
opts.extraData[n+'.y'] = form.clk_y;
}
}
}
// take a breath so that pending repaints get some cpu time before the upload starts
function doSubmit() {
// make sure form attrs are set
var t = $form.attr('target'), a = $form.attr('action');
// update form attrs in IE friendly way
form.setAttribute('target',id);
if (form.getAttribute('method') != 'POST')
form.setAttribute('method', 'POST');
if (form.getAttribute('action') != opts.url)
form.setAttribute('action', opts.url);
// ie borks in some cases when setting encoding
if (! opts.skipEncodingOverride) {
$form.attr({
encoding: 'multipart/form-data',
enctype: 'multipart/form-data'
});
}
// support timout
if (opts.timeout)
setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
// add "extra" data to form if provided in options
var extraInputs = [];
try {
if (opts.extraData)
for (var n in opts.extraData)
extraInputs.push(
$('<input type="hidden" name="'+n+'" value="'+opts.extraData[n]+'" />')
.appendTo(form)[0]);
// add iframe to doc and submit the form
$io.appendTo('body');
$io.data('form-plugin-onload', cb);
form.submit();
}
finally {
// reset attrs and remove "extra" input elements
form.setAttribute('action',a);
t ? form.setAttribute('target', t) : $form.removeAttr('target');
$(extraInputs).remove();
}
};
if (opts.forceSync)
doSubmit();
else
setTimeout(doSubmit, 10); // this lets dom updates render
var domCheckCount = 100;
function cb() {
if (cbInvoked)
return;
var ok = true;
try {
if (timedOut) throw 'timeout';
// extract the server response from the iframe
var data, doc;
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
log('isXml='+isXml);
if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
if (--domCheckCount) {
// in some browsers (Opera) the iframe DOM is not always traversable when
// the onload callback fires, so we loop a bit to accommodate
log('requeing onLoad callback, DOM not available');
setTimeout(cb, 250);
return;
}
log('Could not access iframe DOM after 100 tries.');
return;
}
log('response detected');
cbInvoked = true;
xhr.responseText = doc.body ? doc.body.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
xhr.getResponseHeader = function(header){
var headers = {'content-type': opts.dataType};
return headers[header];
};
if (opts.dataType == 'json' || opts.dataType == 'script') {
// see if user embedded response in textarea
var ta = doc.getElementsByTagName('textarea')[0];
if (ta)
xhr.responseText = ta.value;
else {
// account for browsers injecting pre around json response
var pre = doc.getElementsByTagName('pre')[0];
if (pre)
xhr.responseText = pre.innerHTML;
}
}
else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
xhr.responseXML = toXml(xhr.responseText);
}
data = $.httpData(xhr, opts.dataType);
}
catch(e){
log('error caught:',e);
ok = false;
xhr.error = e;
$.handleError(opts, xhr, 'error', e);
}
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
if (ok) {
opts.success(data, 'success');
if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
}
if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
if (g && ! --$.active) $.event.trigger("ajaxStop");
if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
// clean up
setTimeout(function() {
$io.removeData('form-plugin-onload');
$io.remove();
xhr.responseXML = null;
}, 100);
};
function toXml(s, doc) {
if (window.ActiveXObject) {
doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = 'false';
doc.loadXML(s);
}
else
doc = (new DOMParser()).parseFromString(s, 'text/xml');
return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
};
};
};
/**
* ajaxForm() provides a mechanism for fully automating form submission.
*
* The advantages of using this method instead of ajaxSubmit() are:
*
* 1: This method will include coordinates for <input type="image" /> elements (if the element
* is used to submit the form).
* 2. This method will include the submit element's name/value data (for the element that was
* used to submit the form).
* 3. This method binds the submit() method to the form for you.
*
* The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
* passes the options argument along after properly binding events for submit elements and
* the form itself.
*/
$.fn.ajaxForm = function(options) {
return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
e.preventDefault();
$(this).ajaxSubmit(options);
}).bind('click.form-plugin', function(e) {
var target = e.target;
var $el = $(target);
if (!($el.is(":submit,input:image"))) {
// is this a child element of the submit el? (ex: a span within a button)
var t = $el.closest(':submit');
if (t.length == 0)
return;
target = t[0];
}
var form = this;
form.clk = target;
if (target.type == 'image') {
if (e.offsetX != undefined) {
form.clk_x = e.offsetX;
form.clk_y = e.offsetY;
} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
var offset = $el.offset();
form.clk_x = e.pageX - offset.left;
form.clk_y = e.pageY - offset.top;
} else {
form.clk_x = e.pageX - target.offsetLeft;
form.clk_y = e.pageY - target.offsetTop;
}
}
// clear form vars
setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
});
};
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
$.fn.ajaxFormUnbind = function() {
return this.unbind('submit.form-plugin click.form-plugin');
};
/**
* formToArray() gathers form element data into an array of objects that can
* be passed to any of the following ajax functions: $.get, $.post, or load.
* Each object in the array has both a 'name' and 'value' property. An example of
* an array for a simple login form might be:
*
* [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
*
* It is this array that is passed to pre-submit callback functions provided to the
* ajaxSubmit() and ajaxForm() methods.
*/
$.fn.formToArray = function(semantic) {
var a = [];
if (this.length == 0) return a;
var form = this[0];
var els = semantic ? form.getElementsByTagName('*') : form.elements;
if (!els) return a;
for(var i=0, max=els.length; i < max; i++) {
var el = els[i];
var n = el.name;
if (!n) continue;
if (semantic && form.clk && el.type == "image") {
// handle image inputs on the fly when semantic == true
if(!el.disabled && form.clk == el) {
a.push({name: n, value: $(el).val()});
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
}
continue;
}
var v = $.fieldValue(el, true);
if (v && v.constructor == Array) {
for(var j=0, jmax=v.length; j < jmax; j++)
a.push({name: n, value: v[j]});
}
else if (v !== null && typeof v != 'undefined')
a.push({name: n, value: v});
}
if (!semantic && form.clk) {
// input type=='image' are not found in elements array! handle it here
var $input = $(form.clk), input = $input[0], n = input.name;
if (n && !input.disabled && input.type == 'image') {
a.push({name: n, value: $input.val()});
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
}
}
return a;
};
/**
* Serializes form data into a 'submittable' string. This method will return a string
* in the format: name1=value1&amp;name2=value2
*/
$.fn.formSerialize = function(semantic) {
//hand off to jQuery.param for proper encoding
return $.param(this.formToArray(semantic));
};
/**
* Serializes all field elements in the jQuery object into a query string.
* This method will return a string in the format: name1=value1&amp;name2=value2
*/
$.fn.fieldSerialize = function(successful) {
var a = [];
this.each(function() {
var n = this.name;
if (!n) return;
var v = $.fieldValue(this, successful);
if (v && v.constructor == Array) {
for (var i=0,max=v.length; i < max; i++)
a.push({name: n, value: v[i]});
}
else if (v !== null && typeof v != 'undefined')
a.push({name: this.name, value: v});
});
//hand off to jQuery.param for proper encoding
return $.param(a);
};
/**
* Returns the value(s) of the element in the matched set. For example, consider the following form:
*
* <form><fieldset>
* <input name="A" type="text" />
* <input name="A" type="text" />
* <input name="B" type="checkbox" value="B1" />
* <input name="B" type="checkbox" value="B2"/>
* <input name="C" type="radio" value="C1" />
* <input name="C" type="radio" value="C2" />
* </fieldset></form>
*
* var v = $(':text').fieldValue();
* // if no values are entered into the text inputs
* v == ['','']
* // if values entered into the text inputs are 'foo' and 'bar'
* v == ['foo','bar']
*
* var v = $(':checkbox').fieldValue();
* // if neither checkbox is checked
* v === undefined
* // if both checkboxes are checked
* v == ['B1', 'B2']
*
* var v = $(':radio').fieldValue();
* // if neither radio is checked
* v === undefined
* // if first radio is checked
* v == ['C1']
*
* The successful argument controls whether or not the field element must be 'successful'
* (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
* The default value of the successful argument is true. If this value is false the value(s)
* for each element is returned.
*
* Note: This method *always* returns an array. If no valid value can be determined the
* array will be empty, otherwise it will contain one or more values.
*/
$.fn.fieldValue = function(successful) {
for (var val=[], i=0, max=this.length; i < max; i++) {
var el = this[i];
var v = $.fieldValue(el, successful);
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
continue;
v.constructor == Array ? $.merge(val, v) : val.push(v);
}
return val;
};
/**
* Returns the value of the field element.
*/
$.fieldValue = function(el, successful) {
var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
if (typeof successful == 'undefined') successful = true;
if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
(t == 'checkbox' || t == 'radio') && !el.checked ||
(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
tag == 'select' && el.selectedIndex == -1))
return null;
if (tag == 'select') {
var index = el.selectedIndex;
if (index < 0) return null;
var a = [], ops = el.options;
var one = (t == 'select-one');
var max = (one ? index+1 : ops.length);
for(var i=(one ? index : 0); i < max; i++) {
var op = ops[i];
if (op.selected) {
var v = op.value;
if (!v) // extra pain for IE...
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
if (one) return v;
a.push(v);
}
}
return a;
}
return el.value;
};
/**
* Clears the form data. Takes the following actions on the form's input fields:
* - input text fields will have their 'value' property set to the empty string
* - select elements will have their 'selectedIndex' property set to -1
* - checkbox and radio inputs will have their 'checked' property set to false
* - inputs of type submit, button, reset, and hidden will *not* be effected
* - button elements will *not* be effected
*/
$.fn.clearForm = function() {
return this.each(function() {
$('input,select,textarea', this).clearFields();
});
};
/**
* Clears the selected form elements.
*/
$.fn.clearFields = $.fn.clearInputs = function() {
return this.each(function() {
var t = this.type, tag = this.tagName.toLowerCase();
if (t == 'text' || t == 'password' || tag == 'textarea')
this.value = '';
else if (t == 'checkbox' || t == 'radio')
this.checked = false;
else if (tag == 'select')
this.selectedIndex = -1;
});
};
/**
* Resets the form data. Causes all form elements to be reset to their original value.
*/
$.fn.resetForm = function() {
return this.each(function() {
// guard against an input with the name of 'reset'
// note that IE reports the reset function as an 'object'
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
this.reset();
});
};
/**
* Enables or disables any matching elements.
*/
$.fn.enable = function(b) {
if (b == undefined) b = true;
return this.each(function() {
this.disabled = !b;
});
};
/**
* Checks/unchecks any matching checkboxes or radio buttons and
* selects/deselects and matching option elements.
*/
$.fn.selected = function(select) {
if (select == undefined) select = true;
return this.each(function() {
var t = this.type;
if (t == 'checkbox' || t == 'radio')
this.checked = select;
else if (this.tagName.toLowerCase() == 'option') {
var $sel = $(this).parent('select');
if (select && $sel[0] && $sel[0].type == 'select-one') {
// deselect all other options
$sel.find('option').selected(false);
}
this.selected = select;
}
});
};
// helper fn for console logging
// set $.fn.ajaxSubmit.debug to true to enable debug logging
function log() {
if ($.fn.ajaxSubmit.debug) {
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
if (window.console && window.console.log)
window.console.log(msg);
else if (window.opera && window.opera.postError)
window.opera.postError(msg);
}
};
})(jQuery);

1
asset/js/jquery.form.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5
asset/js/vtip-min.js vendored Executable file
View File

@ -0,0 +1,5 @@
/**
Vertigo Tip by www.vertigo-project.com
Requires jQuery
*/
this.vtip=function(){this.xOffset=-10;this.yOffset=10;$("*[title]").unbind().hover(function(a){this.t=this.title;this.title="";this.top=(a.pageY+yOffset);this.left=(a.pageX+xOffset);$("body").append('<p id="vtip"><img id="vtipArrow" />'+this.t+"</p>");$("p#vtip #vtipArrow").attr("src","/asset/img/vtip_arrow.png");$("p#vtip").css("top",this.top+"px").css("left",this.left+"px").fadeIn("slow")},function(){this.title=this.t;$("p#vtip").fadeOut("slow").remove()}).mousemove(function(a){this.top=(a.pageY+yOffset);this.left=(a.pageX+xOffset);$("p#vtip").css("top",this.top+"px").css("left",this.left+"px")})};jQuery(document).ready(function(a){vtip()});

1
asset/js/yetii-min.js vendored Normal file
View File

@ -0,0 +1 @@
/*Yetii - Yet (E)Another Tab Interface Implementation,version 1.6,http://www.kminek.pl/lab/yetii/,Copyright (c) Grzegorz Wojcik,Code licensed under the BSD License: http://www.kminek.pl/bsdlicense.txt*/function Yetii(){this.defaults={id:null,active:1,interval:null,wait:null,persist:null,tabclass:'tab',activeclass:'active',callback:null,leavecallback:null};this.activebackup=null;for(var n in arguments[0]){this.defaults[n]=arguments[0][n]};this.getTabs=function(){var a=[];var b=document.getElementById(this.defaults.id).getElementsByTagName('*');var c=new RegExp("(^|\\s)"+this.defaults.tabclass.replace(/\-/g,"\\-")+"(\\s|$)");for(var i=0;i<b.length;i++){if(c.test(b[i].className))a.push(b[i])}return a};this.links=document.getElementById(this.defaults.id+'-nav').getElementsByTagName('a');this.listitems=document.getElementById(this.defaults.id+'-nav').getElementsByTagName('li');this.show=function(a){for(var i=0;i<this.tabs.length;i++){this.tabs[i].style.display=((i+1)==a)?'block':'none';if((i+1)==a){this.addClass(this.links[i],this.defaults.activeclass);this.addClass(this.listitems[i],this.defaults.activeclass+'li')}else{this.removeClass(this.links[i],this.defaults.activeclass);this.removeClass(this.listitems[i],this.defaults.activeclass+'li')}}if(this.defaults.leavecallback&&(a!=this.activebackup))this.defaults.leavecallback(this.defaults.active);this.activebackup=a;this.defaults.active=a;if(this.defaults.callback)this.defaults.callback(a)};this.rotate=function(a){this.show(this.defaults.active);this.defaults.active++;if(this.defaults.active>this.tabs.length)this.defaults.active=1;var b=this;if(this.defaults.wait)clearTimeout(this.timer2);this.timer1=setTimeout(function(){b.rotate(a)},a*1000)};this.next=function(){var a=(this.defaults.active+1>this.tabs.length)?1:this.defaults.active+1;this.show(a);this.defaults.active=a};this.previous=function(){var a=((this.defaults.active-1)==0)?this.tabs.length:this.defaults.active-1;this.show(a);this.defaults.active=a};this.previous=function(){this.defaults.active--;if(!this.defaults.active)this.defaults.active=this.tabs.length;this.show(this.defaults.active)};this.gup=function(a){a=a.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");var b="[\\?&]"+a+"=([^&#]*)";var c=new RegExp(b);var d=c.exec(window.location.href);if(d==null)return null;else return d[1]};this.parseurl=function(a){var b=this.gup(a);if(b==null)return null;if(parseInt(b))return parseInt(b);if(document.getElementById(b)){for(var i=0;i<this.tabs.length;i++){if(this.tabs[i].id==b)return(i+1)}}return null};this.createCookie=function(a,b,c){if(c){var d=new Date();d.setTime(d.getTime()+(c*24*60*60*1000));var e="; expires="+d.toGMTString()}else var e="";document.cookie=a+"="+b+e+"; path=/"};this.readCookie=function(a){var b=a+"=";var d=document.cookie.split(';');for(var i=0;i<d.length;i++){var c=d[i];while(c.charAt(0)==' ')c=c.substring(1,c.length);if(c.indexOf(b)==0)return c.substring(b.length,c.length)}return null};this.contains=function(a,b,c){return a.indexOf(b,c)!=-1};this.hasClass=function(a,b){return this.contains(a.className,b,' ')};this.addClass=function(a,b){if(!this.hasClass(a,b))a.className=(a.className+' '+b).replace(/\s{2,}/g,' ').replace(/^\s+|\s+$/g,'')};this.removeClass=function(a,b){a.className=a.className.replace(new RegExp('(^|\\s)'+b+'(?:\\s|$)'),'$1');a.className.replace(/\s{2,}/g,' ').replace(/^\s+|\s+$/g,'')};this.tabs=this.getTabs();this.defaults.active=(this.parseurl(this.defaults.id))?this.parseurl(this.defaults.id):this.defaults.active;if(this.defaults.persist&&this.readCookie(this.defaults.id))this.defaults.active=this.readCookie(this.defaults.id);this.activebackup=this.defaults.active;this.show(this.defaults.active);var f=this;for(var i=0;i<this.links.length;i++){this.links[i].customindex=i+1;this.links[i].onclick=function(){if(f.timer1)clearTimeout(f.timer1);if(f.timer2)clearTimeout(f.timer2);f.show(this.customindex);if(f.defaults.persist)f.createCookie(f.defaults.id,this.customindex,0);if(f.defaults.wait)f.timer2=setTimeout(function(){f.rotate(f.defaults.interval)},f.defaults.wait*1000);return false}}if(this.defaults.interval)this.rotate(this.defaults.interval)};

206
asset/js/yetii.js Normal file
View File

@ -0,0 +1,206 @@
/*
Yetii - Yet (E)Another Tab Interface Implementation
version 1.6
http://www.kminek.pl/lab/yetii/
Copyright (c) Grzegorz Wojcik
Code licensed under the BSD License:
http://www.kminek.pl/bsdlicense.txt
*/
function Yetii() {
this.defaults = {
id: null,
active: 1,
interval: null,
wait: null,
persist: null,
tabclass: 'tab',
activeclass: 'active',
callback: null,
leavecallback: null
};
this.activebackup = null;
for (var n in arguments[0]) { this.defaults[n]=arguments[0][n]; };
this.getTabs = function() {
var retnode = [];
var elem = document.getElementById(this.defaults.id).getElementsByTagName('*');
var regexp = new RegExp("(^|\\s)" + this.defaults.tabclass.replace(/\-/g, "\\-") + "(\\s|$)");
for (var i = 0; i < elem.length; i++) {
if (regexp.test(elem[i].className)) retnode.push(elem[i]);
}
return retnode;
};
this.links = document.getElementById(this.defaults.id + '-nav').getElementsByTagName('a');
this.listitems = document.getElementById(this.defaults.id + '-nav').getElementsByTagName('li');
this.show = function(number) {
for (var i = 0; i < this.tabs.length; i++) {
this.tabs[i].style.display = ((i+1)==number) ? 'block' : 'none';
if ((i+1)==number) {
this.addClass(this.links[i], this.defaults.activeclass);
this.addClass(this.listitems[i], this.defaults.activeclass + 'li');
} else {
this.removeClass(this.links[i], this.defaults.activeclass);
this.removeClass(this.listitems[i], this.defaults.activeclass + 'li');
}
}
if (this.defaults.leavecallback && (number != this.activebackup)) this.defaults.leavecallback(this.defaults.active);
this.activebackup = number;
this.defaults.active = number;
if (this.defaults.callback) this.defaults.callback(number);
};
this.rotate = function(interval) {
this.show(this.defaults.active);
this.defaults.active++;
if (this.defaults.active > this.tabs.length) this.defaults.active = 1;
var self = this;
if (this.defaults.wait) clearTimeout(this.timer2);
this.timer1 = setTimeout(function(){self.rotate(interval);}, interval*1000);
};
this.next = function() {
var _target = (this.defaults.active + 1 > this.tabs.length) ? 1 : this.defaults.active + 1;
this.show(_target);
this.defaults.active = _target;
};
this.previous = function() {
var _target = ((this.defaults.active - 1) == 0) ? this.tabs.length : this.defaults.active - 1;
this.show(_target);
this.defaults.active = _target;
};
this.previous = function() {
this.defaults.active--;
if(!this.defaults.active) this.defaults.active = this.tabs.length;
this.show(this.defaults.active);
};
this.gup = function(name) {
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if (results == null) return null;
else return results[1];
};
this.parseurl = function(tabinterfaceid) {
var result = this.gup(tabinterfaceid);
if (result==null) return null;
if (parseInt(result)) return parseInt(result);
if (document.getElementById(result)) {
for (var i=0;i<this.tabs.length;i++) {
if (this.tabs[i].id == result) return (i+1);
}
}
return null;
};
this.createCookie = function(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
};
this.readCookie = function(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
};
this.contains = function(el, item, from) {
return el.indexOf(item, from) != -1;
};
this.hasClass = function(el, className){
return this.contains(el.className, className, ' ');
};
this.addClass = function(el, className){
if (!this.hasClass(el, className)) el.className = (el.className + ' ' + className).replace(/\s{2,}/g, ' ').replace(/^\s+|\s+$/g, '');
};
this.removeClass = function(el, className){
el.className = el.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
el.className.replace(/\s{2,}/g, ' ').replace(/^\s+|\s+$/g, '');
};
this.tabs = this.getTabs();
this.defaults.active = (this.parseurl(this.defaults.id)) ? this.parseurl(this.defaults.id) : this.defaults.active;
if (this.defaults.persist && this.readCookie(this.defaults.id)) this.defaults.active = this.readCookie(this.defaults.id);
this.activebackup = this.defaults.active;
this.show(this.defaults.active);
var self = this;
for (var i = 0; i < this.links.length; i++) {
this.links[i].customindex = i+1;
this.links[i].onclick = function(){
if (self.timer1) clearTimeout(self.timer1);
if (self.timer2) clearTimeout(self.timer2);
self.show(this.customindex);
if (self.defaults.persist) self.createCookie(self.defaults.id, this.customindex, 0);
if (self.defaults.wait) self.timer2 = setTimeout(function(){self.rotate(self.defaults.interval);}, self.defaults.wait*1000);
return false;
};
}
if (this.defaults.interval) this.rotate(this.defaults.interval);
};

10
asset/lang/en/config.php Normal file
View File

@ -0,0 +1,10 @@
<?php
/**
* TaskFreak
*
* @author Stan Ozier <framework@tirzen.com>
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
$GLOBALS['config']['lang']['specialchars'] = 2;

0
asset/lang/en/core.php Normal file
View File

0
asset/lang/en/freak.php Normal file
View File

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

18
index.php Normal file
View File

@ -0,0 +1,18 @@
<?php
// load config file
include './app/config/core.php'; // load up core config (paths, etc..)
include APP_CONFIG_PATH.'app.php'; // load up application specific settings (customizable)
// minimum needed classes
include APP_CLASS_PATH.'helpable.php';
include APP_HELPER_PATH.'string.php';
include APP_CLASS_PATH.'front.php';
// start session
session_start();
// initialize front controller
$fc = FrontController::getInstance();
// launch up (app) controller
$fc->run();

86
lib/class/app.php Normal file
View File

@ -0,0 +1,86 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Controller super class (abstract)
*
* All controllers must extend this class
* @since 0.1
* @todo constuctor parameter login : could require specific level
*/
abstract class AppController extends Pluginable {
private $view;
protected $fc;
protected $page;
/**
* constructor
* @param mixed $login is user login required
*/
public function __construct($login=false) {
parent::__construct();
// get front controller instance
$this->fc = FrontController::getInstance();
// instantiate page
$this->page = new PageModel();
// check login ?
if ($login && APP_SETUP_USER_MODEL) {
if (!$this->fc->user->isLoggedIn()) {
NaviHelper::redirect('/login');
}
}
}
/**
* setting the file containing HTML which is sent to the browser
*/
protected function setView($view) {
$this->view = $view;
}
/**
* dispatch controller's action view
* @todo search in other folders, such as plugins
*/
protected function view() {
$this->page->dispatchHeader();
include APP_VIEW_PATH.$this->view.'.php';
$this->page->dispatchFooter();
}
/**
* include another view (called within a view)
* @todo search in other folders, such as plugins
*/
protected function incView($file, $evenOnAjax=true) {
if ($evenOnAjax || !$this->fc->getReqVar('ajax')) {
include APP_VIEW_PATH.$file.'.php';
}
}
/**
* reset Navi::referrers to the current page
* this should be called in any top level controller action
*/
protected function resetReferrer() {
$this->fc->setReferrer($this->fc->thisUrl());
}
/**
* any controller must inmplement at least the default action
*/
abstract function mainAction();
}

105
lib/class/collectable.php Normal file
View File

@ -0,0 +1,105 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Colectable
*
* A FIFO implementation
* @since 0.1
*/
class Collectable extends Pluginable {
protected $skip;
public function __construct() {
parent::__construct();
$this->skip = array();
foreach(get_object_vars($this) as $key => $val) {
if ($key == 'skip') {
continue;
}
$this->_init($key);
$this->skip[$key] = array();
}
}
/**
* initialize an empty stack
*/
protected function _init($key,$reset=false) {
if (!is_array($this->$key) || $reset) {
$this->$key = array();
}
}
/**
* reset a stack
*/
public function clean($key, $full=false) {
$this->_init($key, true);
if ($full) {
$this->skip[$key] = array();
}
}
/**
* initialize the stack with a value
*/
public function set($key, $value) {
$this->add($key,$value,true,true);
}
/**
* add a value in the stack
*/
public function add($key, $value, $force=false, $reset=false) {
if (!$reset && !$force) {
if ($this->checkSkip($key, $value)) {
return false;
}
}
if (is_array($value)) {
$this->$key = array_unique(array_merge($this->$key, $value));
} else if (!in_array($value, $this->$key)) {
$this->{$key}[] = $value;
}
return true;
}
/**
* remove value from the stack
*/
public function remove($key, $value) {
$this->addSkip($key, $value);
if (is_array($this->$key) && in_array($value, $this->$key)) {
$newArray = array();
foreach ($this->$key as $val) {
if ($val == $value) {
continue;
}
$newArray[] = $val;
}
$this->$key = $newArray;
}
}
/**
* skip a value : use remove is a safer option
*/
protected function addSkip($key, $value) {
if (!in_array($value,$this->skip[$key])) {
$this->skip[$key][] = $value;
}
}
/**
* check if a value must be skipped
* @return boolean true if value must be ignored
*/
protected function checkSkip($key, $value) {
if (!is_array($this->skip[$key])) {
return false;
}
return in_array($value, $this->skip[$key]);
}
}

209
lib/class/db_connector.php Normal file
View File

@ -0,0 +1,209 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Database Connector
*
* @since 0.1
*/
class DbConnector { // extends HelpableSingleton
protected $connector;
protected $critical;
protected $debug;
protected $sqlQueryCount;
public function __construct() {
$this->debug = APP_DB_DEBUG;
$this->critical = APP_DB_CRITICAL;
$this->sqlQueryCount = 0;
}
// ---- Managing CONNECTION --------------------------------------------------
/**
* get DB connection
*/
public static function getConnection($i=0) {
// -TODO-
// connections shouldn't be hold by the front controller,
// but by this class instantiated as a singleton
$fc = FrontController::getInstance();
if (empty($fc->db[$i])) {
throw new AppException('Connection #'.$i.' does not exists');
}
try {
if ($fc->db[$i]->isConnected()) {
return $fc->db[$i];
}
} catch (Exception $e) {
throw new AppException('Connection #'.$i.' not established : '.$e->getMessage());
}
}
protected function connectTheConnector(DbEngine $obj) {
$this->connector = $obj;
}
public function connect($host=APP_DB_HOST, $user=APP_DB_USER, $pass=APP_DB_PASS, $base=APP_DB_BASE) {
if ($this->connector) {
return true;
}
// not connected yet, just do it
$dbc = 'Db'.ucfirst(APP_DB_CONNECTOR);
$con = new $dbc($host, $user, $pass, $base);
if ($con->connect()) {
$this->connectTheConnector($con);
return true;
}
// reaching this point means an error as occured
// $this->reportError();
// return false;
echo 'Cannot connect to database "'.APP_DB_BASE.'" on host "'.APP_DB_HOST.'" with user "'.APP_DB_USER.'"';
exit;
}
public function isConnected() {
return is_a($this->connector, 'DbEngine');
}
public function setDbDebug($level) {
$this->debug = $level;
}
public function setDbCritical($mode=true) {
$this->critical = $mode;
}
// ---- QUERYING the Database ---------------------------------------------
public static function query($qry, $dbi=0) {
$db = self::getConnection($dbi);
return $db->_query($qry);
}
protected function _query($qry) {
$this->sqlQueryCount++;
switch ($this->debug) {
case 4:
FC::log_debug($qry);
break;
case 5:
echo "<code>".$qry."</code><br/>";
break;
}
if (preg_match("/^(SELECT|SHOW)/i",ltrim($qry))) {
$r = $this->connector->querySelect($qry);
} else {
$r = $this->connector->queryAffect($qry);
}
if ($r === false) {
// error !
$this->reportError($qry);
}
$this->setDbDebug(APP_DB_DEBUG); // back to normal debug level
return $r;
}
public function transactionQueries($arrSql) {
if (!is_array($arrSql)) {
if (empty($arrSql)) {
return false;
}
$arrSql = array($arrSql);
}
if (!$this->transactionBegin()) {
FC::log_warn('can not begin transaction');
}
$ok = true;
foreach($arrSql as $sql) {
if (!$this->query($sql)) {
$ok = false;
break;
}
}
if ($ok) {
return $this->transactionCommit();
} else {
$this->transactionRollBack();
return false;
}
}
// ---- MISCELLANEOUS --------------------------------------------------------
/**
* log or display DB errors in the browser
*/
public function reportError($qry='') {
if (empty($this->debug)) {
return false;
}
// if critical, should at least show an error code in browser
$debug = ($this->critical && $this->debug < 2)?2:$this->debug;
$html = '';
$str = 'DB Error #'.$this->connector->getErrorNo().' : '.$this->connector->getErrorMsg();
switch ($debug) {
case 2:
echo '<p>DB Error #'.$this->connector->getErrorNo().'</p>';
case 1:
if ($qry) {
FC::log_error($qry);
}
case 4:
FC::log_error($str);
break;
case 3:
if ($qry) {
$htm = '<br /><code>'.htmlentities($qry).'</code>';
}
case 5:
$htm = nl2br(htmlentities($str)).$htm;
break;
default:
echo '<p>DB DEBUG level undefined</p>';
break;
}
echo '<p>'.$htm.'</p>';
if ($this->critical) {
exit;
}
}
/**
* pass on undefined method to selected connector
*/
public function __call($name, $args) {
if ($this->connector) {
if (method_exists($this->connector, $name)) {
return call_user_func_array(array($this->connector, $name), $args);
}
}
throw new AppException('Unknown method '.$name.' in '.__CLASS__.' or in the Db Connector ('.APP_DB_CONNECTOR.')');
}
}
interface DbEngine {
public function connect();
public function escapeString($str);
public function querySelect($qry);
public function queryAffect($qry);
public function getTable($table);
public function getErrorNo();
public function getErrorMsg();
}

View File

@ -0,0 +1,203 @@
<?php
/**
* Tzn Framework
*
* @package tzn_deprecated
* @author Stan Ozier <framework@tirzen.com>
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Database Connector
*
* singleton database connector
*/
class DbConnector extends HelpableSingleton {
private static $instance;
protected $connector;
protected $critical;
protected $debug;
protected $sqlQueryCount;
private function __construct() {
$this->debug = APP_DB_DEBUG;
$this->critical = APP_DB_CRITICAL;
$this->sqlQueryCount = 0;
$this->connector = array();
}
/**
* the getInstance() method returns a single instance of the object
*/
public static function getInstance() {
if(!isset(self::$instance)){
$object= __CLASS__;
self::$instance=new $object;
}
return self::$instance;
}
// ---- Managing CONNECTION --------------------------------------------------
/**
* get DB connection
*/
public static function getConnection($i=0) {
$db = self::getInstance();
if (empty($db[$i])) {
throw new AppException('Connection #'.$i.' does not exists');
}
try {
if ($fc->db[$i]->isConnected()) {
return $obj->db[$i];
}
} catch (Exception $e) {
throw new AppException('Connection #'.$i.' not established : '.$e->getMessage());
}
}
protected function connectTheConnector(DbEngine $obj) {
$this->connector = $obj;
}
public function connect($host=APP_DB_HOST, $user=APP_DB_USER, $pass=APP_DB_PASS, $base=APP_DB_BASE) {
// -TODO-
if ($this->connector) {
return true;
}
// not connected yet, just do it
$dbc = 'Db'.ucfirst(APP_DB_CONNECTOR);
$con = new $dbc($host, $user, $pass, $base);
if ($con->connect()) {
$this->connectTheConnector($con);
return true;
}
// reaching this point means an error as occured
$this->reportError();
return false;
}
public function isConnected() {
return is_a($this->connector, 'DbEngine');
}
public function setDbDebug($level) {
$this->debug = $level;
}
// ---- QUERYING the Database ---------------------------------------------
public static function query($qry, $dbi=0) {
$db = self::getConnection($dbi);
return $db->_query($qry);
}
protected function _query($qry) {
$this->sqlQueryCount++;
switch ($this->debug) {
case 3:
FC::log_debug($qry);
break;
case 4:
echo "<code>".$qry."</code><br/>";
break;
}
if (preg_match("/^(SELECT|SHOW)/i",ltrim($qry))) {
$r = $this->connector->querySelect($qry);
} else {
$r = $this->connector->queryAffect($qry, $this->debug, $this->critical);
}
if ($r === false) {
// error !
$this->reportError();
}
$this->setDbDebug(APP_DB_DEBUG); // back to normal debug level
return $r;
}
public function transactionQueries($arrSql) {
if (!is_array($arrSql)) {
if (empty($arrSql)) {
return false;
}
$arrSql = array($arrSql);
}
if (!$this->transactionBegin()) {
FC::log_warn('can not begin transaction');
}
$ok = true;
foreach($arrSql as $sql) {
if (!$this->query($sql)) {
$ok = false;
break;
}
}
if ($ok) {
return $this->transactionCommit();
} else {
$this->transactionRollBack();
return false;
}
}
// ---- MISCELLANEOUS --------------------------------------------------------
/**
* log or display DB errors in the browser
*/
public function reportError($qry='') {
if (!$this->debug) {
return false;
}
$str = 'DB Error #'.$this->connector->getErrorNo().' : '.$this->connector->getErrorMsg();
switch ($this->debug) {
case 1:
case 3:
FC::log_error($str);
if ($qry) {
FC::log_error($str);
}
break;
case 2:
case 4:
echo '<p>'.nl2br(htmlentities($str));
echo '<br /><code>'.htmlentities($qry).'</code></p>';
break;
default:
echo '<p>DB DEBUG level undefined</p>';
break;
}
if ($this->critical) {
exit;
}
}
/**
* pass on undefined method to selected connector
*/
public function __call($name, $args) {
if ($this->connector) {
if (method_exists($this->connector, $name)) {
return call_user_func_array(array($this->connector, $name), $args);
}
}
throw new AppException('Unknown method '.$name.' in '.__CLASS__.' or in the Db Connector ('.APP_DB_CONNECTOR.')');
}
}
interface DbEngine {
public function connect();
public function querySelect($qry, $debug, $critical);
public function queryAffect($qry, $debug, $critical);
public function getTable($table);
public function getErrorNo();
public function getErrorMsg();
}

160
lib/class/db_mysql.php Normal file
View File

@ -0,0 +1,160 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* DbMysql
*
* MySQL connector
* @since 0.1
*/
class DbMysql implements DbEngine {
private $host, $user, $pass, $base;
protected $isConnected;
protected $dblink;
public function __construct($host, $user, $pass, $base) {
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->base = $base;
}
/**
* connect to mysql and select database
*/
public function connect() {
$this->isConnected = false;
if (@constant('APP_DB_PERMANENT')) {
$this->dblink = @mysql_pconnect($this->host,$this->user,$this->pass);
} else {
$this->dblink = @mysql_connect($this->host,$this->user,$this->pass);
}
if (!$this->dblink) {
return false;
}
if (!@mysql_select_db($this->base,$this->dblink)) {
return false;
}
return true;
}
/**
* escape string for mySQL
*/
public function escapeString($str) {
return mysql_real_escape_string($str,$this->dblink);
}
// ---- TRANSACTIONS ---------------------------------------------------------
public function transactionBegin() {
@mysql_query('SET AUTOCOMMIT=0',$this->dblink);
$r = @mysql_query('BEGIN',$this->dblink);
// error_log('BEGIN : '.$r);
return $r;
}
public function transactionCommit() {
$r = @mysql_query('COMMIT',$this->dblink);
@mysql_query('SET AUTOCOMMIT=1',$this->dblink);
// error_log('COMMIT : '.$r);
return $r;
}
public function transactionRollBack() {
$r = @mysql_query('ROLLBACK',$this->dblink);
@mysql_query('SET AUTOCOMMIT=1',$this->dblink);
//error_log('ROLLBACK : '.$r);
return $r;
}
// ---- QUERIES -----------------------------------------------------------
/**
* SELECT and SHOW queries
*/
public function querySelect($qry) {
$r = @mysql_query($qry, $this->dblink);
if (!$r) {
// there's obviously an error
return false;
}
$data = array();
$i = 0;
while ($row = mysql_fetch_assoc($r)) {
$data[$i++] = $row;
}
return $data;
}
/**
* INSERT, REPLACE, UPDATE and DELETE queries
*/
public function queryAffect($qry) {
if (!@mysql_query($qry,$this->dblink)) {
// error_log('queryAffect (mysql) fails : '.$qry);
return false;
}
$r = mysql_affected_rows($this->dblink);
// error_log('queryAffect (mysql) returns : '.$r);
if ($r == -1) {
return false;
} else {
if ($r == 1) {
if ($id = mysql_insert_id($this->dblink)) {
return $id;
}
}
return ($r)?$r:true;
}
}
// ---- TABLE operations --------------------------------------------------
public function getTable($table) {
if ($result = @mysql_query('SHOW TABLES LIKE \''.$table.'\'')) {
if ($row = mysql_fetch_row($result)) {
if (strtolower($row[0]) == strtolower($table)) {
return true;
}
}
}
return false;
}
public function getTables($table=null) {
$arrTables = array();
$sql = 'SHOW TABLES';
if ($table) {
$sql .= ' LIKE \''.$table.'\'';
}
if ($result = @mysql_query($sql))
{
while($row = mysql_fetch_row($result)) {
$arrTables[] = $row[0];
}
}
return $arrTables;
}
// ---- ERROR reporting ------------------------------------------------------
public function getErrorNo() {
return mysql_errno();
}
public function getErrorMsg() {
return mysql_error();
}
}

623
lib/class/front.php Normal file
View File

@ -0,0 +1,623 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Front Controller
*
* The is the root class of the application, the mother of initialization process
* It's a singleton, defining mostly static methods
* @since 0.1
*/
class FrontController extends HelpableSingleton {
private static $instance;
protected $autoPath;
public $controller;
public $action;
public $request;
public $settings;
public $user;
public $db;
/**
* private constructor (singleton)
*/
private function __construct() {
$this->autoPath = $authoPath = array();
include APP_CONFIG_PATH.'path.php';
$this->autoPath = $autoPath;
unset($autoPath);
$this->controller = $GLOBALS['config']['app']['default_controller'];
$this->action = $GLOBALS['config']['app']['default_action'];
$this->request = array();
$this->_helpers = array();
$this->db = array();
if (!isset($_SESSION['appVariables'])) {
$_SESSION['appVariables'] = array();
}
}
/**
* the getInstance() method returns a single instance of the object
*/
public static function getInstance() {
if(!isset(self::$instance)){
spl_autoload_register(array('FrontController','autoLoad'));
$object= __CLASS__;
self::$instance=new $object;
self::$instance->initApp();
}
return self::$instance;
}
// ---- APP INITIALIZATION ---------------------------------------------------
/**
* app initialization (from config)
*/
protected function initApp() {
if (APP_SETUP_DATABASE) {
// load up database settings (host, username, etc...)
include APP_CONFIG_PATH.'db.php';
// connect to database
$this->initDatabase();
}
if (APP_SETUP_MESSAGING) {
// initialize messaging (trans-pages messages)
$this->initMessaging();
}
if (APP_SETUP_DATABASE) {
// load general settings
if (APP_SETUP_GLOBAL_SETTINGS) {
$this->loadSettings();
}
// load user (if needed)
$this->loadUser(APP_SETUP_USER_MODEL);
// load user preferences
if (APP_SETUP_USER_SETTINGS) {
$this->loadUserSettings();
}
}
if (APP_SETUP_TRANSLATOR) {
// initialize translations system
$this->initTranslator();
}
if (APP_SETUP_NAVI) {
// initialize navigation (referers, redirects and URL manipulation)
$this->initNavi();
}
}
/**
* initialize database connection
*/
public function initDatabase() {
$i = count($this->db);
$this->db[$i] = new DbConnector();
$this->db[$i]->connect();
}
/**
* load application settings
* @todo load settings
* @todo set default controller and action
*/
public function loadSettings() {
$this->settings = new SettingModel();
// -TODO- load 'em up
}
/**
* load and authenticate user
*/
public function loadUser($class) {
if (empty($class)) {
return false;
}
$class = StringHelper::flatToCamel($class,true).'Model';
$this->user = new $class;
$this->user->enableAuthentication();
$this->user->connectDb();
$this->user->checkLogin();
}
/**
* load user settings
* @todo load user settings and override global settings
*/
public function loadUserSettings() {
// might overload default controller and action
// -TODO-
if ($this->user->isLoggedIn()) {
$this->setSessionDefault('usertask', $this->user->getUid());
}
}
/**
* initialize messaging system
*/
public function initMessaging() {
$this->addHelper('messaging');
}
/**
* initialize referrers system
*/
public function initNavi() {
$this->addHelper('navi');
}
/**
* initialize translator
*/
public function initTranslator() {
$this->addHelper('translator');
}
/**
* launch application controller
*/
public function run() {
// parse URL : look for controller, action, parameters...
$this->parseUrl();
// load up requested Application Controller
$con = self::loadController($this->controller);
if (!$con) {
// controller not found, throw exception
if (isset($GLOBALS['config']['error']['not_found'])) {
$this->_sendErrorCodeToRobots('404');
include $GLOBALS['config']['error']['not_found'];
exit;
} else {
try {
throw new AppException('Controller '.$this->controller.' not defined in '.implode(', ', $this->autoPath['controller']));
} catch(Exception $e) {
error_log('error loading controller '.$this->controller);
echo $e;
exit;
}
}
}
$obj = new $con;
$act = $this->action;
// if submitted, call requested action (mainReaction by default)
if (!empty($_POST)) {
if (method_exists($obj, $act.'Reaction')) {
if (!call_user_func(array($obj,$act.'Reaction'))) {
// stop here
return true;
}
} else {
self::log_error('method '.$act.'Reaction not defined in controller '.$this->controller);
}
}
// if still needed, call requested action (mainAction by default)
if (method_exists($obj, $act.'Action')) {
call_user_func(array($obj, $act.'Action'));
} else {
if (isset($GLOBALS['config']['error']['not_found'])) {
$this->_sendErrorCodeToRobots('404');
include $GLOBALS['config']['error']['not_found'];
exit;
}
self::log_error('method '.$act.'Action not defined in controller '.$this->controller);
return false;
}
return true;
}
// ---- HTTP QUERY PARSER ----------------------------------------------------
/**
* clean $_POST, $_GET and $_REQUEST if magic quotes are on
*/
protected static function cleanRequest() {
if (!get_magic_quotes_gpc()) {
return true;
}
$arrReq = array('_POST','_GET','_REQUEST');
foreach ($arrReq as $key) {
if (isset($$key)) {
array_walk($$key, 'stripslashes');
}
}
return true;
}
/**
* gets HTTP variable
* @return null if not found, value if set
*/
public function getReqVar($key) {
if (isset($this->request[$key])) {
return $this->request[$key];
} else {
return null;
}
}
/**
* checks if variable is submitted by HTTP request
* @param $mix an array or string containing key(s) to be checked
* @return first found in request
*/
public function chkReqVar($mix) {
$arr = StringHelper::mixedToArray($mix);
foreach($arr as $key) {
if (isset($this->request[$key])) {
return $key;
}
}
return false;
}
/**
* parse request for controller, action and other parameters
*/
public function parseUrl() {
// clean ugly magic quotes
self::cleanRequest();
// parse URL path
$req = trim($_SERVER['REQUEST_URI'],'/');
if ($req) {
if ($pos = strrpos($_SERVER['REQUEST_URI'],'.html')) {
$req = substr($req, 0, $pos-1);
} else if ($pos = strrpos($_SERVER['REQUEST_URI'],'?')) {
$req = substr($req, 0, $pos-1);
}
$arrReq = explode('/',$req);
if (count($arrReq)) {
$this->controller = array_shift($arrReq);
}
if (count($arrReq)) {
$this->action = array_shift($arrReq);
} else {
$this->action = 'main';
}
while(count($arrReq)) {
$key = array_shift($arrReq);
if (count($arrReq)) {
$val = array_shift($arrReq);
$this->request[$key] = urldecode($val);
} else {
$this->request[$key] = '';
}
}
}
// parse query string
if (count($_REQUEST)) {
foreach($_REQUEST as $key => $val) {
switch($key) {
case 'controller' :
$this->controller = $val;
break;
case 'action' :
$this->action = $val;
break;
default :
$this->request[$key] = $val;
break;
}
}
}
// get real names
$this->controller = StringHelper::flatToCamel($this->controller, true,'-');
$this->action = StringHelper::flatToCamel($this->action, false,'-');
}
/**
* generate URL
*/
public static function getUrl($controller='', $action = '', $params = '') {
if (!$controller) {
return APP_WWW_URI;
}
if (!$action) {
return APP_WWW_URI.$controller;
}
$url = APP_WWW_URI.StringHelper::camelToFlat($controller,'-')
.'/'.StringHelper::camelToFlat($action,'-');
if (is_array($params)) {
foreach ($params as $key => $val) {
$url .= '/'.urlencode($key).'/'.urlencode($val);
}
}
return $url;
}
/**
* returns current URL
* @param mixed params false if no parameter, true if all current parameters, array to submit other parameters
*/
public function thisUrl($params=false) {
if (is_array($params)) {
return self::getUrl($this->controller, $this->action, $params);
} else {
return self::getUrl($this->controller, $this->action, $params?$this->request:false);
}
}
/**
* return navigation menu
*/
public function menu($id='pages') {
$str = '<ul id="'.$id.'">';
foreach ($GLOBALS['config'][$id] as $key => $url) {
$arr = explode('/',$url);
$arr[0] = ucfirst($arr[0]);
if (!$arr[1]) {
$arr[1] = 'main';
}
$str .= '<li';
if ($this->controller == $arr[0] && $this->action == $arr[1]) {
$str .= ' class="active"';
}
$str .= '><a href="'.FrontController::getUrl($url).'">'.$key.'</a></li>';
}
$str .= '</ul>';
return $str;
}
/**
* send error code (http header) to robots
*/
protected function _sendErrorCodeToRobots($code) {
if (preg_match('/'.APP_ROBOT_AGENT.'/', $_SERVER['HTTP_USER_AGENT'])) {
header("Status : 404 Not Found");
header("HTTP/1.1 404 Not Found");
}
}
// ---- APPLICATION VARIABLES ------------------------------------------------
/**
* get a persistent variable
* try first from $_REQUEST, then look into session variables
* if found in $_REQUEST, save it in session for later use
*/
public function sessionVariable($key) {
if (isset($this->request[$key])) {
$this->setSessionVariable($key, $this->request[$key]);
}
return $this->getSessionVariable($key);
}
/**
* set a session variable
*/
public function setSessionVariable($key, $value) {
$_SESSION['appVariables'][$key] = $value;
}
/**
* set a session variable default's value
*/
public function setSessionDefault($key, $default) {
if (!isset($_SESSION['appVariables'][$key])) {
$_SESSION['appVariables'][$key] = $default;
}
}
/**
* get a session variable
*/
public function getSessionVariable($key) {
if (!isset($_SESSION['appVariables'][$key])) {
return NULL;
} else {
return $_SESSION['appVariables'][$key];
}
}
/**
* reset a session variable
*/
public function cleanSessionVariable($keys) {
$keys = StringHelper::mixedToArray($keys);
foreach($keys as $key) {
if (isset($_SESSION['appVariables'][$key])) {
unset($_SESSION['appVariables'][$key]);
}
}
}
// ---- AUTOLOAD setup -------------------------------------------------------
/**
* generic method loading classes
*/
public function _load($file, $type) {
self::log_front("loading $file [$type]");
foreach($this->autoPath[$type] as $path) {
if (file_exists($path.$file)) {
self::log_front("-> Yes in $path");
include_once($path.$file);
return true;
} else {
self::log_front("!> No $file in $path");
}
}
return false;
}
/**
* static method loading any type of class definition
* check all accessible folder that may contain the class definition
* and include it if necessary
*/
protected static function load($class, $type) {
$file = StringHelper::camelToFlat($class);
if ($idx = strrpos($file, '_'.$type)) {
$file = substr($file, 0, $idx);
}
$file .= '.php';
if (!class_exists($class)) {
$obj = self::getInstance();
if (!$obj->_load($file, $type)) {
return false;
}
}
return $class;
}
/**
* load core class
*/
public static function loadClass($class) {
return self::load($class, 'class');
}
/**
* load helper class
*/
public static function loadHelper($class) {
return self::load($class, 'helper');
}
/**
* load a model class
*/
public static function loadModel($class) {
return self::load($class, 'model');
}
/**
* load a controller class
*/
public static function loadController($class) {
return self::load($class, 'controller');
}
/**
* load a view class
*/
public static function loadView($class) {
return self::load($class, 'view');
}
/**
* autoload class
* @parameter would require the type of class to be said in there
*/
private static function autoLoad($name) {
$str = StringHelper::camelToFlat($name);
$type = 'class';
$sep = strrpos($str,'_');
if ($sep) {
$class = substr($str, 0, $sep);
$type = substr($str, $sep+1);
switch ($type){
case 'helper':
case 'class':
case 'controller':
case 'model':
case 'view':
// valid type
break;
default:
// invalid type, must be a core class
$type = 'class';
break;
}
}
return self::load($name, $type);
}
// ---- LOGGING and DEBUGGING ------------------------------------------------
public static function log_front($str) {
self::log_any($GLOBALS['config']['log_front'], 'front', $str);
}
public static function log_debug($str) {
self::log_any($GLOBALS['config']['log_debug'], 'debug', $str);
}
public static function log_message($str) {
self::log_any($GLOBALS['config']['log_message'], 'message', $str);
}
public static function log_warn($str) {
self::log_any($GLOBALS['config']['log_warn'], 'warning', $str);
}
public static function log_error($str) {
self::log_any($GLOBALS['config']['log_error'], 'error', $str);
}
public static function log_core_debug($str) {
self::log_any($GLOBALS['config']['log_core'], 'error', $str);
}
public static function log_any($mode, $head, $str) {
switch ($mode) {
case 1:
$arr = explode("\n", trim($str));
foreach ($arr as $s) {
error_log($GLOBALS['config']['log_signature']." $head : $s");
}
break;
case 2:
echo VarStr::html($GLOBALS['config']['log_signature']." $head : ".nl2br($str));
break;
}
}
}
class FC extends FrontController
{
}
class AppException extends Exception {
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Exception $previous = null) {
// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}
// custom string representation of object
public function __toString() {
echo "<pre>".parent::__toString()."</pre>";
return __CLASS__;
}
}

247
lib/class/helpable.php Normal file
View File

@ -0,0 +1,247 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Helpable
*
* add possibility to have helpers, and call helper method if it doesn't exist
* @since 0.1
*/
abstract class Helpable {
protected $_helpers;
public function __construct() {
$this->_helpers = array();
}
/**
* add helper to the class
* @param string $helper name of helper (flat format)
* @param object $obj object to be passed on
*/
public function addHelper() {
$args = func_get_args();
// helper's name
$helper = array_shift($args);
$class = StringHelper::flatToCamel($helper, true).'Helper';
// reference to this object
$obj = array_shift($args);
if (is_null($obj)) {
$obj = $this;
}
// add helper (didn't find any better way to deal with variable constructor parameters)
switch(count($args)) {
case 4:
$this->_helpers[$helper] = new $class($obj, $args[0], $args[1], $args[2], $args[3]);
break;
case 3:
$this->_helpers[$helper] = new $class($obj, $args[0], $args[1], $args[2]);
break;
case 2:
$this->_helpers[$helper] = new $class($obj, $args[0], $args[1]);
break;
case 1:
$this->_helpers[$helper] = new $class($obj, $args[0]);
break;
default:
$this->_helpers[$helper] = new $class($obj);
break;
}
}
/**
* call helper method
* @param string $key the helper key
* @param string $method the method to be called
*/
public function callHelper() {
$args = func_get_args();
$helper = array_shift($args);
$method = array_shift($args);
return call_user_func_array(array($this->_helpers[$helper], $method), $args);
}
/**
* call helper's method (arguments are passed as an array
*/
public function callHelperArray() {
$args = func_get_args();
$helper = array_shift($args);
$method = array_shift($args);
if (!empty($args)) {
return call_user_func_array(array($this->_helpers[$helper], $method), $args[0]);
} else {
return $this->_helpers[$helper]->$method();
}
}
/**
* call all available helpers method
* @param string $method the method to be called
*/
protected function callHelpers($method) {
if (!count($this->_helpers)) {
return false;
}
$arr = array();
$args = func_get_args();
$method = array_shift($args);
foreach ($this->_helpers as $key => $obj) {
if (!method_exists($obj, $method)) {
continue;
}
$r = call_user_func_array(array($obj,$method),$args);
$arr[$key] = $r;
}
return $arr;
}
/**
* check if helper is available
*/
public function hasHelper($helper) {
return array_key_exists($helper, $this->_helpers);
}
/**
* get helper
*/
public function &getHelper($helper) {
if ($this->hasHelper($helper)) {
return $this->_helpers[$helper];
} else {
throw new AppException('Unknown helper '.$helper.' in '.get_class($this));
}
}
/**
* call helper method if not defined here
*/
public function __call($name, $args) {
if (!count($this->_helpers)) {
throw new AppException('Unknown method '.$name.' in '.get_class($this).' (and no helper available)');
}
// check if helper's name has been added to method's name eg. methodHelper
/* TOO UGLY, TOO COSTY
$subname = StringHelper::camelToFlat($name);
if ($idx = strrpos($subname, '__')) {
$subhelp = substr($subname, $idx+1);
if (array_key_exists($subhelp, $this->_helpers)) {
$subname = StringHelper::flatToCamel(substr($subname, 0, $idx));
$obj = $this->_helpers[$subhelp];
if (method_exists($obj, $name)) {
return call_user_func_array(array($obj, $name), $args);
}
}
}
*/
// check if method exists in one of the helper
foreach ($this->_helpers as $key => $obj) {
if (method_exists($obj, $name)) {
return call_user_func_array(array($obj, $name), $args);
}
}
// try again within helper if __call is available
foreach ($this->_helpers as $key => $obj) {
if (method_exists($obj, '__call')) {
return call_user_func_array(array($obj, $name), $args);
}
}
// nope, forget it
throw new AppException('Unknown method '.$name.' in '.get_class($this).' or any helper ('.implode(', ',array_keys($this->_helpers)).')');
}
}
/**
* Singleton helpable super class
*
* sucks duplicating code, but needed for singletons as constructor must be private
* @since 0.1
*/
abstract class HelpableSingleton {
private $_helpers;
private function __construct() {
$this->_helpers = array();
}
/**
* add helper to the class
*/
protected function addHelper($helper, $obj=null) {
$class = StringHelper::flatToCamel($helper, true).'Helper';
$this->_helpers[$helper] = new $class($obj);
}
/**
* check if helper is available
*/
public function hasHelper($helper) {
return array_key_exists($helper, $this->_helpers);
}
/**
* get helper
*/
public function &getHelper($helper) {
if ($this->hasHelper($helper)) {
return $this->_helpers[$helper];
} else {
throw new AppException('Unknown helper '.$helper.' in '.get_class($this));
}
}
/**
* call helper method if not defined here
*/
public function __call($name, $args) {
if (!count($this->_helpers)) {
throw new AppException('Unknown method '.$name.' in '.__CLASS__.' (and no helper available)');
}
foreach ($this->_helpers as $key => $obj) {
foreach ($this->_helpers as $key => $obj) {
if (method_exists($obj, $name)) {
return call_user_func_array(array($obj, $name), $args);
}
}
if (is_a($obj, 'Callable')) {
return call_user_func_array(array($obj, $name), $args);
}
}
throw new AppException('Unknown method '.$name.' in '.__CLASS__.' or any helper ('.implode(', ',array_keys($this->_helpers)).')');
}
}
/**
* Callable interface
*
* class implementing this will be able to send calls to other nested classes
*/
interface Callable {
public function __call($name, $args);
/*
public function __call($name, $args) {
$arr = array('class1','class2');
foreach ($arr as $class) {
if (method_exists($this->$class, $name)) {
return call_user_func_array(array($this->$class, $name), $args);
}
}
throw new AppException('Unknown method '.$name.' in '.__CLASS__.', '.implode(', ',$arr));
}
*/
}

25
lib/class/helper.php Normal file
View File

@ -0,0 +1,25 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Helper
*
* @since 0.1
*/
abstract class Helper {
protected $obj;
public function __construct($obj=null) {
if (is_object($obj)) {
$this->obj = $obj;
}
}
}

1298
lib/class/model.php Normal file

File diff suppressed because it is too large Load Diff

99
lib/class/pluginable.php Normal file
View File

@ -0,0 +1,99 @@
<?php
/**
* Tzn Framework
*
* @package tzn_core_classes
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Pluginable
*
* Abstract class implementing all plugin related methods
* All extendable / customizable class should implement Pluginable
*/
abstract class Pluginable extends Helpable {
private $_plugins;
public function __construct() {
parent::__construct();
$this->_plugins = array();
$class = get_class($this); // get class name of implemented object (not the super class name)
/*
if (is_array($GLOBALS['config']['plugin']) && count($GLOBALS['config']['plugin'][$class])) {
foreach ($GLOBALS['config']['plugin'][$class] as $key) {
// error_log("INIT plugin for $class : $key");
$this->addPlugin($key);
}
}
*/
}
protected function addPlugin($key, $obj=null) {
if (is_null($obj)) {
$class = StringHelper::flatToCamel($key, true);
$this->_plugins[$key] = new $class;
} else {
$this->_plugins[$key] = $obj;
}
}
protected function getPlugin($key) {
if (!is_array($this->_plugins)) {
return false;
}
if (array_key_exists($key, $this->_plugins)) {
return $this->_plugins[$key];
} else {
return false;
}
}
protected function setPlugin($key, $obj) {
if (!is_array($this->_plugins)) {
return false;
}
if (array_key_exists($key, $this->_plugins)) {
$this->_plugins[$key] = $obj;
return true;
} else {
return false;
}
}
protected function callPlugin() {
$args = func_get_args();
$key = array_shift($args);
$method = array_shift($args);
$obj = $this->getPlugin($key);
if (!$obj) {
return false;
}
$obj = $this->_plugins[$key];
call_user_func_array(array($obj,$method),$args);
}
protected function callPlugins($method) {
if (!count($this->_plugins)) {
return false;
}
$arr = array();
$args = func_get_args();
$method = array_shift($args);
foreach ($this->_plugins as $key => $obj) {
if (!method_exists($obj, $method)) {
continue;
}
if ($this->id) {
$obj->id = $this->id;
}
$r = call_user_func_array(array($obj,$method),$args);
$arr[$key] = $r;
}
return $arr;
}
}

32
lib/helper/array.php Normal file
View File

@ -0,0 +1,32 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Array Helper
*
* common functions to manipulate arrays
*/
class ArrayHelper {
public function __construct() {
}
public static function arrayTrim(&$arr)
{
array_walk($arr,"_trim");
}
}
function _trim(&$value)
{
$value = trim($value);
}

555
lib/helper/auth.php Normal file
View File

@ -0,0 +1,555 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* AuthHelper
*
* authentication system
*/
class AuthHelper extends Helper {
protected $isLoggedIn;
protected $isLoggingOut;
protected $fc;
protected $error;
public function __construct($obj) {
parent::__construct($obj);
$this->fc = FrontController::getInstance();
$this->error = '';
$this->isLoggedIn = $this->isLoggingOut = false;
}
public function checkPassword($password) {
$pass = $this->obj->get('password');
$salt = $this->obj->get('salt');
switch (APP_AUTH_PASSWORD_MODE) {
case 1:
if ($pass == "") {
$pass = crypt("", $salt);
}
if (crypt($password, $salt) != $pass) {
// password invalid
$this->error = 'user_pass_invalid';
$this->badAccess();
return false;
}
break;
case 2:
$sql = "ENCRYPT('$password','$salt')";
if (!self::checkDbPass($sql, $pass)) {
// password not OK
$this->error = 'user_pass_invalid';
$this->badAccess();
return false; // error or password mismatch
}
break;
case 3:
$sql = "ENCODE('$password','$pass')";
if (!self::checkDbPass($sql, $pass)) {
// password not OK
$this->error = 'user_pass_invalid';
$this->badAccess();
return false; // error or password mismatch
}
break;
case 4:
if (!$pass && !$password) {
break;
}
$sql = "MD5('$password')";
if (!self::checkDbPass($sql, $pass)) {
// password not OK
$this->error = 'user_pass_invalid';
$this->badAccess();
return false; // error or password mismatch
}
break;
case 5:
if (!$pass && !$password) {
break;
}
if ($_SESSION['challenge']) {
$value = md5($pass.$_SESSION['challenge']);
unset($_SESSION['challenge']);
if ($value == $password) {
break;
}
}
$this->error = 'user_pass_invalid';
$this->badAccess();
return false;
break;
default:
for ($i = 0; $i < strlen($pass); $i += 2) {
$passBin .= chr(hexdec(substr($s,$i,2)));
}
$iv = mcrypt_create_iv (mcrypt_get_iv_size (MCRYPT_3DES,
MCRYPT_MODE_ECB), MCRYPT_RAND);
if (mcrypt_decrypt (MCRYPT_3DES, $pass, $passBin,
MCRYPT_MODE_ECB, $iv) == $password)
{
break;
}
$this->error = 'user_pass_invalid';
$this->badAccess();
return false;
break;
}
return true;
}
/**
*
*/
public function activateAccount($byUser=false) {
// update DB
$this->obj->set('activation','');
$this->obj->set('enabled',1);
if ($this->obj->isLoaded()) {
$this->obj->fields('enabled,activation');
$this->obj->update();
}
// send Emails
/* -TODO-
include CMS_INCLUDE_PATH.'language/'.$GLOBALS['objCms']->settings->get('default_language').'/system.php';
include_once(CMS_CLASS_PATH."pkg_com.php");
$objMessage = new EmailMessage();
//email admin (if account activated by user)
if ($byUser) {
if ($objMessage->loadByKey('description','sign_up_new')) {
$bodyMessage = "\r\n\t"
.$this->obj->getName()."\r\n\r\n"
.CMS_WWW_URL.'admin/member.php?id='.$this->obj->getUid()."\r\n";
if ($objMessage->send($bodyMessage, $this->obj->email)) {
// well, ok, fine
} else {
$pErrorMessage = $GLOBALS['langSystemEmailStuff']['send_error'];
}
} else {
$pErrorMessage = $GLOBALS['langSystemEmailStuff']['send_not_found'];
}
}
// email user
if ($this->obj->email) {
if ($objMessage->loadByKey('description','sign_up_confirmation')) {
$bodyMessage = CMS_WWW_URL.'login.php?username='.$this->obj->username;
if ($_POST['password1']) {
$bodyMessage .= "\r\n\t"
.$GLOBALS['langTznUser']['login_username'].': '
.$this->obj->username."\r\n\t"
.$GLOBALS['langTznUser']['login_password'].': '
.$_POST['password1']."\r\n";
}
if ($objMessage->send($bodyMessage, $this->obj->email)) {
// well, ok, fine
} else {
$this->_error['activation'] = $GLOBALS['langSystemEmailStuff']['send_error'];
}
} else {
$this->_error['activation'] = $GLOBALS['langSystemEmailStuff']['send_not_found'];
}
} else {
$this->_error['activation'] = $GLOBALS['langSystemEmailStuff']['send_no_address'];
}
*/
return (empty($this->error));
}
/**
* login and update DB
*/
protected function _activateLogin() {
$updTz = '';
if (isset($_REQUEST['appUserTimeZone'])) {
if ($this->obj->get('time_zone') != $_REQUEST['appUserTimeZone']) {
$this->obj->set('time_zone',$_REQUEST['appUserTimeZone']);
$updTz = ',time_zone';
}
}
// register session
$_SESSION["appUserId"] = $this->obj->getUid();
$_SESSION["appUserLastLogin"] = $this->obj->get('last_login_date');
$_SESSION["appUserLastAddress"] = $this->obj->get('last_login_address');
$this->updateSessionVariables();
// update last login
$this->obj->set('last_login_date',APP_SQL_NOW);
$this->obj->set('last_login_address', $_SERVER['REMOTE_ADDR']);
$_SESSION['appUserCurrentAddress'] = $this->obj->get('last_login_address');
$this->obj->_isLoggedIn = true;
$this->obj->set('bad_access',0);
$this->obj->set('visits',$this->obj->get('visits')+1);
$this->obj->fields('last_login_date,last_login_address,bad_access,visits'.$updTz);
$this->obj->update();
}
/**
* update sessions variables (on login and when updating own profile)
*/
public function updateSessionVariables() {
$_SESSION["appUserName"] = $this->obj->get('username');
$_SESSION["appUserTimeZone"] = $this->obj->get('time_zone');
$this->obj->setupTimeZone();
}
/**
* Verify username and password
*/
public function login($username, $password, $activation=false) {
$this->error = '';
if ($username == '') {
$this->error = 'user_name_empty';
return false;
}
if (APP_AUTH_FIELD == 'username' && (!VarUsr::sanitize($username, $error))) {
$this->error = $error;
return false;
}
$this->obj->set(APP_AUTH_FIELD,$username);
if ($this->obj->load(APP_AUTH_FIELD)) {
$activ = $this->obj->get('activation');
if (!$this->obj->get('enabled')) {
if (!$activation || $activation != $activ) {
//Account Disabled
$this->error = 'user_disabled';
}
}
if (!$this->checkPassword($password)) {
$this->error = 'user_password_invalid';
} else if ($activation && $activation == $activ) {
// activate account
$this->activateAccount(true);
}
if (!empty($this->error)) {
$this->badAccess();
return false;
}
} else {
$this->error = 'user_name_not_found';
return false;
}
$this->_activateLogin();
return true;
}
/**
*coming soon
*/
public function silentLogin($username, $password) {
$this->error = '';
if ($username == '') {
return false;
}
$this->obj->set(APP_AUTH_FIELD, $username);
if ($this->obj->load(APP_AUTH_FIELD)) {
if (!$this->obj->get('enabled')) {
//Account Disabled
$this->error = 'user_disabled';
}
if (!$this->checkPassword($password)) {
$this->error = 'user_password_invalid';
}
} else {
$this->error = 'user_name_not_found';
return false;
}
return (empty($this->error));
}
/**
* Is there a cookie for AutoLogin ??
*/
public function checkAutoLogin($forReal=true) {
if (empty($_COOKIE['auto_login'])) {
return false;
}
$arrVal = explode(":",$_COOKIE['auto_login']);
$id = VarUid::sanitize($arrVal[0]);
$salt = VarStr::sanitize($arrVal[1]);
if (!$id || !$salt) {
return false;
}
if ($this->obj->load($this->dbUid(true)."='".$id
."' AND ".$this->dbField('salt', $salt)." AND "
.$this->dbField('auto_login',1)." AND ".$this->dbField('enabled',1))
) {
if (!$forReal) {
return true;
}
setCookie('auto_login',$this->obj->getUid().":".$this->obj->get('salt')
,time()+(3600*24*30));
$this->activateLogin();
return true;
} else {
return false;
}
}
/**
* Activate AutoLogin
*/
public function setAutoLogin() {
if (($this->obj->getUid()) && ($this->obj->get('salt'))) {
setCookie('autoLogin',$this->obj->getUid().":".$this->obj->get('salt')
,time()+(3600*24*30));
$this->obj->set('auto_login','1');
$this->obj->fields('auto_login');
$this->obj->update();
return true;
}
return false;
}
/**
* De-activate AutoLogin
*/
public function resetAutoLogin() {
if (($this->obj->getUid()) && ($this->obj->get('salt'))) {
setCookie('autoLogin');
if ($this->obj->get('auto_login')) {
$this->obj->set('auto_login', 0);
$this->obj->fields('auto_login');
$this->obj->update();
}
return true;
}
return false;
}
/**
* Logout logs the user out (yes indeed)
* Note: This will destroy the session, and not just the session data!
*/
public function logout() {
$_SESSION = array();
// If you want to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time()-42000, '/');
}
// Finally, destroy the session.
@session_destroy();
// while you're at it, delete auto login
$this->resetAutoLogin();
// set internal variable
$this->isLoggedIn = false;
$this->isLoggingOut = true;
}
/**
* check if user is logged in. Do not load from DB by default
*/
public function isLoggedIn($load=false) {
// --- login previously checked ---
if (empty($_SESSION['appUserId'])) {
$_SESSION['appUserId'] = 0;
}
if ($this->isLoggedIn && !$this->isLoggingOut && $this->obj->getUid()) {
// user seems already logged in
if ($load) {
// load is requested
if ($this->obj->load() != $_SESSION['appUserId']) {
return false;
}
} else if ($this->obj->getUid() != $_SESSION['appUserId']) {
// ID in session and in object are different
return false;
}
}
// --- first login check ---
if ($_SESSION['appUserId'] == 0 || $this->isLoggingOut) {
// invalid ID in session or currently logging out
// $this->fc->addMessage('ERROR:#security_expired');
return false;
} else {
// login seems OK : initialize properties
$this->obj->set('id',$_SESSION['appUserId']);
$this->obj->set('username', $_SESSION['appUserName']);
// time zone
$this->obj->set('time_zone', $_SESSION['appUserTimeZone']);
$this->obj->setupTimeZone();
$this->isLoggedIn = true;
// check user IP
if ($_SESSION['appUserCurrentAddress'] && $_SERVER['REMOTE_ADDR'] != $_SESSION['appUserCurrentAddress']) {
$this->fc->addMessage('ERROR:#security_ip '.$_SERVER['REMOTE_ADDR'].' / '.$_SESSION['appUserCurrentAddress']);
return false;
}
// check that User ID is same in cookie and in session
if ($load) {
if ($this->obj->load() != $_SESSION['appUserId']) {
$this->fc->addMessage('ERROR:#security_expired');
return false;
}
}
// reset last session stats with previous login info
$this->obj->set('last_login_date', $_SESSION['appUserLastLogin']);
$this->obj->set('last_login_address', $_SESSION['appUserLastAddress']);
return true;
}
}
/**
* All in one user login and access check function
*/
public function checkLogin($canAutoLogin=true) {
if ($this->isLoggedIn(true)) {
return true;
} else {
if ($canAutoLogin && $this->checkAutoLogin()) {
// auto logged in
return true;
} else {
return false;
}
}
}
/**
* forgotten password? Try to get it back or generate new one
* type can be 'username' or 'email'
*/
public function forgotPassword($key, $value) {
if ($this->obj->get('salt') == "") {
if (!$this->obj->loadByKey($key,$value)) {
// user not found
$this->_error['forgot'] = $key." not found";
return false;
}
}
switch (APP_AUTH_PASSWORD_MODE) {
case 1:
$this->generateNewSalt();
$newpass = StringHelper::genRandom(6,"123456789");
$this->obj->set('password', crypt($pass1 , $this->obj->get('salt')));
$this->updatePassword();
break;
case 2:
$this->generateNewSalt();
$newpass = StringHelper::genRandom(6,"123456789");
$this->obj->set('password', self::getDbPass("ENCRYPT(\"".$pass1."\",\"".$this->obj->get('salt')."\")"));
$this->updatePassword();
break;
case 3:
$strSql = "SELECT DECODE(password, '".$this->obj->get('salt')
."') as pass FROM ".$this->_table
." WHERE ".$this->obj->dbUid()."='".$this->obj->getUid()."'";
if ($rows = DbConnnector::query($strSql)) {
if (!empty($rows[0])) {
$this->obj->password = $rows[0]->pass;
return $this->obj->password;
}
}
$this->_error['forgot'] = "can not decode?";
return false;
break;
case 4:
case 5:
$this->generateNewSalt();
$newpass = StringHelper::genRandom(6,"123456789");
$this->password = "MD5('$newpass')";
$this->updatePassword();
break;
default:
$iv = mcrypt_create_iv (mcrypt_get_iv_size (MCRYPT_3DES,
MCRYPT_MODE_ECB), MCRYPT_RAND);
$this->password = mcrypt_decrypt (MCRYPT_3DES, $this->obj->get('salt'),
$passBin, MCRYPT_MODE_ECB, $iv);
return $this->password;
break;
}
return $newpass;
}
public function getLoginCountry() {
$myIP = escapeshellarg($_SERVER['REMOTE_ADDR']);
$pCountry = '00000'; // $_SERVER['REMOTE_ADDR'];
if (@defined('APP_GEOLOCATION_SCRIPT') && is_file(APP_GEOLOCATION_SCRIPT)) {
$arrOutput = array();
exec(APP_GEOLOCATION_SCRIPT.' '.$myIP, $arrOutput);
$strOutput = implode(' ',$arrOutput);
if (preg_match('/(located in)/i',$strOutput)) {
$strOutput = trim(substr($strOutput,strpos($strOutput,'in')+2));
$pCountry = $strOutput;
}
}
return $pCountry;
}
/**
* get error
*/
public function getAuthError($clean=true) {
if (!$this->error) {
return false;
} else {
$str = $this->error;
if ($clean) {
$this->error = '';
}
return $str;
}
}
// ---- Database manipulation ---------------------------------------------
protected function generateNewSalt() {
$this->obj->set('salt',
StringHelper::genRandom(
8,
'abcdefghijklmnopqrstuvwxyz'
.'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
)
);
}
public static function checkDbPass($mode, $pass) {
return (self::getDbPass($mode) == $pass);
}
public static function getDbPass($mode) {
$sql = 'SELECT '.$mode.' as passhash';
if ($rows = DbConnector::query($sql)) {
if (!empty($rows[0])) {
return $rows[0]['passhash'];
}
}
return false;
}
protected function badAccess() {
$sql = "UPDATE ".$this->obj->dbTable()." SET"
." bad_access=bad_access+1"
." WHERE ".$this->obj->dbUid()." = '".$this->obj->getUid()."'";
DbConnector::query($sql);
}
protected function updatePassword() {
$sql = "UPDATE ".$this->obj->dbTable()." SET"
." password=".$this->obj->sql('password')
." WHERE ".$this->obj->dbUid()." = '".$this->obj->getUid()."'";
DbConnector::query($sql);
}
}

98
lib/helper/auth_acl.php Normal file
View File

@ -0,0 +1,98 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.2
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* AuthHelper ACL
*
* authentication system with ACL support
*/
class AuthAclHelper extends AuthHelper {
public $_arrAcl;
public function __construct($obj) {
parent::__construct($obj);
}
/**
* check ACL
*/
public function checkAcl($code) {
if (!is_array($this->_arrAcl)) {
$this->_arrAcl = explode(',', $this->obj->get('actags'));
}
return in_array($code, $this->_arrAcl);
}
/**
* update sessions variables (on login and when updating own profile)
*/
public function updateSessionVariables() {
parent::updateSessionVariables();
if ($this->obj->getPropertyType('actags')) {
$_SESSION['appUserAcl'] = $this->obj->get('actags');
}
}
/**
* Logout logs the user out (yes indeed)
*/
public function logout() {
parent::logout();
$this->obj->set('actags',''); // -TODO- set guest tags
}
/**
* check if user is logged in. Do not load from DB by default
*/
public function isLoggedIn($load=false) {
if (parent::isLoggedIn($load)) {
$this->obj->set('actags', $_SESSION['appUserAcl']);
return true;
} else {
return false;
}
}
/**
* save User ACL settings
*/
public function updateACL($section='') {
$id = $this->obj->getUid();
if (!$id) {
return false;
}
$objLst = new AclModel();
$objLst->connectDb();
if (is_string($section)) {
$objLst->where("section='$section'");
}
if (!$objLst->loadList()) {
return false;
}
$i=0;
$fc = FrontController::getInstance();
$db = DbConnector::getConnection();
$table = $objLst->dbTable('acl_user');
while ($objLst->next()) {
if ($fc->getReqVar('acl_'.$objLst->get('name'))) {
$db->query('INSERT IGNORE INTO `'.$table.'` SET `user_id`='.$id.', `acl_id`='.$objLst->getUid());
} else {
$db->query('DELETE IGNORE FROM `'.$table.'` WHERE `user_id`='.$id.' AND `acl_id`='.$objLst->getUid());
}
$i++;
}
return $i;
}
}

317
lib/helper/db.helpable.php Normal file
View File

@ -0,0 +1,317 @@
<?php
/**
* Tzn Framework
*
* @package tzn_deprecated
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* DbHelper
*
* Database helper to give Models (or other classes) database capabilities
*/
class DbHelper extends Helpable {
protected $obj;
protected $db;
protected $qry;
protected $idx;
protected $rows;
public function __construct($obj) {
parent::__construct();
$this->obj = $obj;
$this->db = DbConnector::getInstance();
$this->qry = new DbQueryHelper();
}
// ----- INSERTion queries ---------------------------------------------------
public function insert() {
}
public function replace() {
}
public function update() {
// -TODO- what about files ?
}
// ----- DELETE query -------------------------------------------------------
public function delete($filter) {
// -TODO- what about files ?
}
// ----- LOAD queries --------------------------------------------------------
/**
* build default SELECT query
*/
public function buildSelect() {
$this->qry->select('*');
// -TODO- only selected fields
$this->qry->from($this->obj->table);
// -TODO- JOIN for objects, and GROUP BY if necessary
}
/**
* load first item
*/
public function load($filter) {
$this->buildSelect();
$this->qry->where($filter);
$this->qry->limit(0,1); // only the first item
if ($data = $this->db->query($this->qry->build())) {
$this->obj->set($data[0]);
}
}
/**
* load a list of items
*/
public function loadList() {
$this->buildSelect();
if ($data = $this->db->query($this->qry->build())) {
$this->idx = 0;
$this->rows = $data;
}
}
public function count() {
return count($this->rows);
}
public function next() {
if (array_key_exists($this->idx, $this->rows)) {
$this->obj->set($this->rows[$this->idx]);
$this->idx++;
return true;
} else {
return false;
}
}
/**
* set limit by giving a page size and page number
*/
public function page($size=10, $page=1) {
if (!$size) {
$this->qry->reset('limit');
}
if (!$page) {
$page = 1;
}
$this->qry->limit(($page - 1) * $size, $size);
}
// ---- MISCELLANEOUS --------------------------------------------------------
public function setDbDebug($level) {
$this->db->setDbDebug($level);
}
public function __call($name, $args) {
if (method_exists($this->db, $name)) {
return call_user_func_array(array($this->db, $name), $args);
}
if (method_exists($this->qry, $name)) {
return call_user_func_array(array($this->qry, $name), $args);
}
throw new Exception('Unknown method '.$name.' in '.__CLASS__.', dbConnector or dbQueryHelper');
}
*/
}
/**
* DbQueryHelper
*
* SQL SELECT Query Builder
*/
class DbQueryHelper {
protected $sql;
public function __construct() {
$this->sql = array(
'select' => '',
'from' => '',
'join' => array(),
'where' => '',
'groupBy' => '',
'orderBy' => '',
'having' => '',
'limit' => ''
);
}
/**
* reset all or part of the SQL query
*/
public function reset($part='') {
if ($part) {
$this->sql[$part] = '';
} else {
foreach (array_keys($this->sql) as $key) {
$this->sql[$key] = '';
}
$this->sql['join'] = array();
}
}
/**
* add fields in SELECT
*/
public function select($field) {
if ($field == '*') {
$this->sql['select'] = '';
} else if (is_array($field)) {
$this->sql['select'] = implode(', ',$field);
} else {
$this->sql['select'] = $this->concatSQL($this->sql['select'], $field, ',');
}
}
/**
* add FROM statement
*/
public function from($table) {
if (is_array($table)) {
$this->sql['from'] = $table[0].' AS '.$table[1];
} else {
$this->sql['from'] = $table;
}
}
/**
* add any JOIN
*/
public function join($table, $on, $how = '') {
$str = ($how?($how.' '):'').'JOIN';
if (is_array($table)) {
$str .= $table[0].' AS '.$table[1];
} else {
$str .= $table;
}
$str .= ' ON ';
if (is_array($on)) {
$str .= $on[0].'='.$on[1];
} else {
$str .= $on;
}
$this->sql['join'][] = $str;
}
/**
* add INNDER JOIN
*/
public function innerJoin() {
$this->join($table, $on, 'INNER');
}
/**
* add LEFT JOIN
*/
public function leftJoin($table, $on) {
$this->join($table, $on, 'LEFT');
}
/**
* add WHERE condition
*/
public function where($filter, $sep='AND') {
$this->sql['where'] = $this->concatSQL($this->sql['where'], $filter, $sep);
}
/**
* add GROUP BY condition
*/
public function groupBy($filter, $sep=',') {
$this->sql['groupBy'] = $this->concatSQL($this->sql['groupBy'], $filter, $sep);
}
/**
* add ORDER BY statement
*/
public function orderBy($filter, $sep=',') {
$this->sql['orderBy'] = $this->concatSQL($this->sql['orderBy'], $filter, $sep);
}
/**
* add HAVING condition
*/
public function having($filter, $sep='AND') {
$this->sql['having'] = $this->concatSQL($this->sql['having'], $filter, $sep);
}
/**
* add a LIMIT statement (only one statement allowed)
*/
public function limit($start=0, $length=1) {
$this->sql['limit'] = $start.', '.$length;
}
/**
* Query builder routine
*/
public function build() {
if (!$this->sql['select']) {
$this->sql['select'] = '*';
}
$sql = 'SELECT '.$this->sql['select']
.' FROM '.$this->sql['from'];
foreach ($this->sql['join'] as $j) {
$sql .= ' '.$j;
}
if ($this->sql['where']) {
$sql .= ' WHERE '.$this->sql['where'];
}
if ($this->sql['groupBy']) {
$sql .= ' GROUP BY '.$this->sql['groupBy'];
}
if ($this->sql['orderBy']) {
$sql .= ' ORDER BY '.$this->sql['orderBy'];
}
if ($this->sql['having']) {
$sql .= ' HAVING '.$this->sql['having'];
}
if ($this->sql['limit']) {
$sql .= ' LIMIT '.$this->sql['limit'];
}
return $sql;
}
// --- MISCELLANEOUS ---------------------------------------------------------
/**
* concat string for SQL
*/
protected function concatSQL($begin, $end, $separator = 'AND', $instruction = '') {
$separator = ' '.trim($separator).' ';
if (!empty($instruction)) {
$instruction .= ' ';
}
if (empty($begin)) {
if (empty($end)) {
return false;
} else {
return $instruction.$end;
}
} else if (empty($end)) {
return $instruction.$begin;
} else {
return $begin.$separator.$end;
}
}
}

628
lib/helper/db.php Normal file
View File

@ -0,0 +1,628 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* DbHelper
*
* Database helper to give Models (or other classes) database capabilities
*/
class DbHelper extends Helper implements Callable {
protected $db;
protected $qry;
protected $idx;
protected $rows;
protected $total;
protected $table;
public function __construct($obj, $table) {
parent::__construct($obj);
$this->db = DbConnector::getConnection();
$this->qry = new DbQueryHelper();
$this->table = $table;
}
// ----- INSERTion queries ---------------------------------------------------
public function save() {
// check UID
$uid = $this->obj->getUid();
if (empty($uid)) {
return $this->insert();
} else {
return $this->update();
}
}
public function insert() {
// set creation date
if (!$this->_creationDate()) {
// if no field creation date, update last change date
$this->_lastChangeDate();
}
// -TODO- INSERT IGNORE INTO
$this->obj->ignore($this->obj->dbUid());
// INSERT
$sql = 'INSERT INTO `'.$this->obj->dbTable().'` SET '.$this->fields();
if ($id = $this->db->query($sql)) {
$this->obj->setUid($id);
return $id;
}
return false;
}
public function replace() {
$sql = 'REPLACE `'.$this->obj->dbTable().'` SET '.$this->fields();
return $this->db->query($sql);
}
public function update($filter='') {
if (!$this->buildWhere($filter)) {
return false;
}
// update last change date (if field is defined)
$this->_lastChangeDate();
// UPDATE
$sql = 'UPDATE `'.$this->obj->dbTable().'` SET '.$this->fields();
$sql .= ' WHERE '.$filter;
return $this->db->query($sql);
}
protected function fields() {
$fields = $this->obj->getFields();
$arr = '';
foreach ($fields as $key => $type) {
if (preg_match('/^OBJ/',$type)) {
$arr[] = "`${key}_id`=".$this->sql($key.'::id');
} else {
$arr[] = "`$key`=".$this->sql($key);
}
}
return implode(', ',$arr);
}
public function sql($key) {
$val = $this->obj->get($key);
return $this->sqlValue($val);
}
public function sqlValue($val) {
if (is_integer($val)) {
return $val;
} else if (empty($val)) {
return "''";
} else {
return "'".$this->db->escapeString($val)."'";
}
}
// ----- DELETE query -------------------------------------------------------
/**
* delete entry
* @param string $filter if not set, will delete by UID
* @todo delete files from IMG and DOC fields
*/
public function delete($filter='') {
if (!$this->buildWhere($filter)) {
return false;
}
$sql = 'DELETE FROM `'.$this->obj->dbTable().'`';
$sql .= ' WHERE '.$filter;
return $this->db->query($sql);
}
// ----- LOAD queries --------------------------------------------------------
/**
* load one item
* @param mixed filter either an array (key/value), a string, or nothing if you want to load by ID
* @param boolean $auto use automatic query (set to false if query has been generated before)
*/
public function load($filter='', $auto=true) {
if ($auto) {
$this->buildSelect();
}
if ($this->buildWhere($filter)) {
$this->qry->where($filter);
} else {
$this->obj->setUid(0);
return false;
}
$this->qry->limit(0,1); // only the first item
if ($data = $this->db->query($this->qry->build())) {
if (!empty($data[0])) {
$this->obj->set($data[0]);
return true;
}
}
$this->obj->setUid(0);
return false;
}
/**
* load a list of items
*/
public function loadList($auto=true) {
if ($auto) {
$this->buildSelect();
}
$this->obj->all();
if ($data = $this->db->query($this->qry->build())) {
$this->idx = 0;
$this->total = 0;
$this->rows = $data;
if ($this->doesLimit()) {
$res = $this->db->query($this->qry->build(true));
if (count($res) == 1) {
// simple result with one row
$this->total = intval($res[0]['total']);
} else {
// multiple rows, probably because of a group by
$this->total = count($res);
}
} else {
$this->total = count($this->rows);
}
return $this->total;
}
return false;
}
public function count() {
return count($this->rows);
}
public function total() {
return $this->total;
}
public function next() {
if (array_key_exists($this->idx, $this->rows)) {
$this->obj->set($this->rows[$this->idx]);
$this->idx++;
return true;
} else {
// $this->obj->resetProperties();
return false;
}
}
public function reset() {
$this->rows = array();
}
/**
* set limit by giving a page size and page number
*/
public function page($size=10, $page=1) {
if (!$size) {
$this->qry->reset('limit');
}
if (!$page) {
$page = 1;
}
$this->qry->limit(($page - 1) * $size, $size);
}
// ---- TABLE & FIELDS MANIPULATION ------------------------------------------
/**
* Get database table
* @todo add DB table prefix
*/
public function dbTable($table='') {
if (empty($table)) {
$table = $this->table;
}
return $this->qry->prefixTable($table);
}
/**
* returns a SQL statement table.field = value
*/
public function dbField($field, $value='') {
$str = $this->dbTable().'.'.$field;
if ($value) {
$str .= '='.$this->sqlValue($value);
}
return $str;
}
/**
*
*/
public function dbUid($ext=false) {
if ($ext) {
return $this->dbField($this->obj->proUid());
} else {
return $this->obj->proUid();
}
}
/**
* get SQL formatted UID
*/
public function sqlUid() {
return $this->sql($this->obj->proUid());
}
// ---- MISC QUERIES ---------------------------------------------------------
/**
* check if value already exists in table
*/
public function findOccurences($key, $val) {
$sql = 'SELECT COUNT(*) AS cnt FROM `'.$this->obj->dbTable().'`'
." WHERE `$key`='$val'";
if ($data = $this->db->query($sql)) {
if (!empty($data[0])) {
return intval($data[0]->cnt);
}
}
return 0;
}
// ---- QUERY BUILDER Methods ------------------------------------------------
/**
* build default SELECT query
* @todo might not want to reset query every time
*/
public function buildSelect() {
// reset query -TODO- not if in cache
$this->qry->reset('select,from,join');
// get list of fields to be selected
$select = $this->buildFields();
// add SELECT clause
$this->qry->select($select);
// add FROM clause
$this->qry->from($this->dbTable());
}
/**
* build SELECT and JOIN clauses
*/
public function buildFields($nested='') {
$select = array();
$arr = $this->obj->getFields();
foreach ($arr as $key => $type) {
if ($this->obj->isObjectProperty($key, $class, $nkey)) {
$obj = new $class;
$obj->connectDb();
$select = array_merge($select, $obj->buildFields($nkey));
// add table join
$this->qry->leftJoin($obj->dbTable(), array($this->dbField($key.'_id'),$obj->dbUid(true)));
} else if ($nested) {
// add joined table field to select
$select[] = '`'.$nested.'`.`'.$key.'` AS '.$nested.'__'.$key;
} else if ($key == 'id') {
// aah IDs...
$select[] = $this->dbUid(true);
} else {
// add field to select
$select[] = '`'.$key.'`';
}
}
return $select;
}
/**
* build WHERE clause based on filter
*/
public function buildWhere(&$filter) {
if (is_array($filter)) {
// filter
$filter = '`'.$filter[0]."`='".$filter[1]."'";
} else if (preg_match('/^[a-zA-Z0-9\_]+$/',$filter)) {
// filter on a field already set in DB
$filter .= "=".$this->sql($filter);
} else if (empty($filter)) {
// nothing set, try on ID
if ($id = $this->obj->getUid()) {
$filter = $this->obj->dbUid($this->qry->doesJoin())."=".$this->obj->sqlUid();
} else {
// not filter and no ID provided, don't even try
$filter = '';
return false;
}
}
// overwise, consider param as a ready set where statement
return true;
}
// ---- MISCELLANEOUS --------------------------------------------------------
protected function _creationDate() {
$fields = $this->obj->getFields();
if (array_key_exists('creation_date', $fields)) {
$this->obj->set('creation_date',APP_SQL_NOW);
return true;
}
return false;
}
protected function _lastChangeDate() {
$fields = $this->obj->getFields();
if (array_key_exists('last_change_date', $fields)) {
$this->obj->set('last_change_date',APP_SQL_NOW);
return true;
}
return false;
}
public function __call($name, $args) {
$arr = array('db','qry');
foreach ($arr as $class) {
if (method_exists($this->$class, $name)) {
return call_user_func_array(array($this->$class, $name), $args);
}
}
throw new AppException('Unknown method '.$name.' in '.__CLASS__.', '.implode(', ',$arr));
}
}
/**
* DbQueryHelper
*
* SQL SELECT Query Builder
*/
class DbQueryHelper {
protected $sql;
public function __construct() {
$this->sql = array(
'select' => '',
'from' => '',
'join' => array(),
'where' => '',
'groupBy' => '',
'orderBy' => '',
'having' => '',
'limit' => ''
);
}
/**
* reset all or part of the SQL query
*/
public function reset($part='') {
if ($part) {
$this->sql[$part] = '';
} else {
foreach (array_keys($this->sql) as $key) {
$this->sql[$key] = '';
}
$this->sql['join'] = array();
}
}
/**
* add fields in SELECT
*/
public function select($field) {
if (empty($field)) {
return false;
} else if ($field == '*') {
$this->sql['select'] = '';
} else {
$field = StringHelper::mixedToArray($field);
$this->sql['select'] = (($this->sql['select'])?($this->sql['select'].','):'').implode(',', $field);
}
}
/**
* add FROM statement
*/
public function from($table) {
if (is_array($table)) {
$this->sql['from'] = '`'.$this->prefixTable($table[0]).'` AS '.$table[1];
} else {
$this->sql['from'] = '`'.$this->prefixTable($table).'`';
}
}
/**
* add any JOIN
*/
public function join($table, $on, $how = '') {
$str = ($how?($how.' '):'').'JOIN ';
if (is_array($table)) {
$str .= '`'.$this->prefixTable($table[0]).'` AS '.$table[1];
} else {
$str .= $this->prefixTable($table);
}
$str .= ' ON ';
if (is_array($on)) {
$str .= $on[0].'='.$on[1];
} else {
$str .= $on;
}
$this->sql['join'][] = $str;
}
/**
* add INNER JOIN
*/
public function innerJoin($table, $on) {
$this->join($table, $on, 'INNER');
}
/**
* add LEFT JOIN
*/
public function leftJoin($table, $on) {
$this->join($table, $on, 'LEFT');
}
/**
* add WHERE condition
*/
public function where($filter, $sep='AND') {
$this->sql['where'] = $this->concatSQL($this->sql['where'], $filter, $sep);
}
/**
* add GROUP BY condition
*/
public function groupBy($filter, $sep=',') {
$this->sql['groupBy'] = $this->concatSQL($this->sql['groupBy'], $filter, $sep);
}
/**
* add ORDER BY statement
*/
public function orderBy($filter, $sep=',') {
$this->sql['orderBy'] = $this->concatSQL($this->sql['orderBy'], $filter, $sep);
}
/**
* add HAVING condition
*/
public function having($filter, $sep='AND') {
$this->sql['having'] = $this->concatSQL($this->sql['having'], $filter, $sep);
}
/**
* add a LIMIT statement (only one statement allowed)
*/
public function limit($start=0, $length=1) {
if ($length == 0) {
return false;
}
$this->sql['limit'] = $start.', '.$length;
}
/**
* Query builder routine
* @param boolean $total will query for total number of rows when set to true
*/
public function build($total=false) {
$sql = 'SELECT ';
if ($total) {
$sql .= 'COUNT(*) AS total';
/*
if ($this->doesJoin()) {
$sql .= 'COUNT(DISTINCT '.$this->sql['from'].'.id) AS total';
} else {
$sql .= 'COUNT(*) AS total';
}
*/
} else {
if (!$this->sql['select']) {
$this->sql['select'] = '*';
}
$sql .= $this->sql['select'];
}
$sql .= ' FROM '.$this->sql['from'];
foreach ($this->sql['join'] as $j) {
$sql .= ' '.$j;
}
if ($this->sql['where']) {
$sql .= ' WHERE '.$this->sql['where'];
}
if ($this->sql['groupBy']) {
$sql .= ' GROUP BY '.$this->sql['groupBy'];
}
if ($this->sql['having']) {
$sql .= ' HAVING '.$this->sql['having'];
}
if ($this->sql['orderBy']) {
$sql .= ' ORDER BY '.$this->sql['orderBy'];
}
if ($this->sql['limit'] && !$total) {
$sql .= ' LIMIT '.$this->sql['limit'];
}
return $sql;
}
// --- MISCELLANEOUS ---------------------------------------------------------
/**
* add prefix to table name
*/
public function prefixTable($table) {
if (defined('APP_DB_PREFIX') && APP_DB_PREFIX) {
if (preg_match('/^'.APP_DB_PREFIX.'_/', $table)) {
return $table;
} else {
return APP_DB_PREFIX.'_'.$table;
}
} else {
return $table;
}
}
/**
* check if joigning multi tables
*/
public function doesJoin() {
return empty($this->sql['join'])?false:true;
}
/**
* check if limit has been set
*/
public function doesLimit() {
return empty($this->sql['limit'])?false:true;
}
/**
* concat string for SQL
*/
protected function concatSQL($begin, $end, $separator = 'AND', $instruction = '') {
$separator = ' '.trim($separator).' ';
if (!empty($instruction)) {
$instruction .= ' ';
}
if (empty($begin)) {
if (empty($end)) {
return false;
} else {
return $instruction.$end;
}
} else if (empty($end)) {
return $instruction.$begin;
} else {
return $begin.$separator.$end;
}
}
/**
* build condition to include in a WHERE clause
* @param string param the value searched. Can contain wildcards.
*/
public static function parseSearch($param) {
if ($param = trim($param)) {
if ($param == '*') {
return false;
}
if (preg_match('/^".*"$/i',$param)) {
$param = '%'.str_replace('"','',$param).'%';
} else if (preg_match('/\*/',$param)) {
$param = str_replace('*','%',$param);
} else {
$param = '%'.str_replace(' ','%',$param).'%';
}
} else {
return false;
}
$db = DbConnector::getConnection();
return $db->escapeString($param);
}
}

View File

View File

134
lib/helper/html_asset.php Normal file
View File

@ -0,0 +1,134 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* HtmlAsset Helper
*
* HTML Asset, links to CSS, Javascripts, etc...
*/
class HtmlAssetHelper extends Collectable {
protected $css, $cssCode, $js, $jsCode, $jsEditor, $jsCalendar, $jsOnLoad, $rss;
public function __construct() {
parent::__construct();
}
protected function _init($key,$reset=false) {
if (!is_array($this->$key) || $reset) {
$this->$key = array();
if (!$reset && !empty($GLOBALS['config']['header'][$key])) {
$this->$key = StringHelper::mixedToArray($GLOBALS['config']['header'][$key]);
}
}
}
public function headerStuff() {
if (count($this->jsCalendar)) {
$this->add('css',APP_WWW_URI.'asset/css/calendar.css');
$this->add('js', APP_WWW_URI.'asset/js/calendar.js');
foreach($this->jsCalendar as $it) {
if (is_string($it)) {
$it=trim($it);
$this->add('jsOnLoad',"new Calendar({ '$it': 'd/m/y' })");
}
}
}
if (count($this->jsEditor)) {
$this->add('js',APP_WWW_URI.'asset/ckeditor/ckeditor.js');
$str = '';
foreach ($this->jsEditor as $kit => $mode) {
$str .= "CKEDITOR.replace('$kit',{toolbar:'$mode'";
if (strpos($mode, 'Upl')) {
$base = APP_WWW_URI.'asset/fmanager/';
$str .= ", filebrowserBrowseUrl : '${base}browser.php', "
."filebrowserImageBrowseUrl : '${base}browser.php?Type=images', "
."filebrowserFlashBrowseUrl : '${base}browser.php?Type=flash'"; //,
/*
."filebrowserUploadUrl : '${base}uploader.php?command=QuickUpload&type=docs', "
."filebrowserImageUploadUrl : '${base}uploader.php?command=QuickUpload&type=images', "
."filebrowserFlashUploadUrl : '${base}uploader.php?command=QuickUpload&type=flash'";
*/
}
$str .= "});\n";
}
$this->add('jsOnLoad',$str);
}
// css
if (count($this->css)) {
foreach($this->css as $it) {
if (preg_match('/(^\/|http:\/\/)/', $it)) {
echo '<link rel="stylesheet" type="text/css" href="'.$it.'" />'."\n";
} else {
foreach ($GLOBALS['config']['path']['css'] as $p) {
if (file_exists(APP_WWW_PATH.$p.$it)) {
echo '<link rel="stylesheet" type="text/css" href="'.APP_WWW_URI.$p.$it.'" />'."\n";
break;
}
}
}
}
}
// css code (ie tests)
if (count($this->cssCode)) {
echo implode("\n",$this->cssCode)."\n";
}
// javascrpt direct code
if (count($this->jsCode)) {
echo '<script type="text/javascript">'."\n";
echo implode("\n",$this->jsCode);
echo "\n</script>\n";
}
// javascript on load function
if (count($this->jsOnLoad)) {
echo '<script type="text/javascript">'."\n";
echo "window.onload=function(){\n";
echo implode("\n",$this->jsOnLoad);
echo "\n}\n</script>\n";
}
// xml/rss
if (count($this->rss)) {
foreach($this->rss as $it) {
echo '<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="'
.CMS_WWW_URL.$it.'" />'."\n";
}
}
}
public function footerStuff() {
// javascript (include)
if (count($this->js)) {
foreach($this->js as $it) {
$it = trim($it);
if (preg_match('/^(\/|http:\/\/)/', $it)) {
echo '<script type="text/javascript" src="'.$it.'" language="javascript"></script>'."\n";
} else {
foreach ($GLOBALS['config']['path']['js'] as $p) {
if (file_exists(APP_WWW_PATH.$p.$it)) {
echo '<script type="text/javascript" src="'.APP_WWW_URI.$p.$it.'" language="javascript"></script>'."\n";
break;
}
}
}
}
}
}
}

388
lib/helper/html_form.php Normal file
View File

@ -0,0 +1,388 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* HtmlFormHelper
*
* HTML form fields helper
*/
class HtmlFormHelper extends Helper {
// protected $fc;
public function __construct($obj=null) {
parent::__construct($obj);
// $this->fc = FrontController::getInstance();
}
/**
* generates a complete form for model object given to constructor
*/
public function iAutoForm($name, $method='post', $action='', $class='') {
if (!isset($this->obj)) {
throw AppException('Can not generate form : no object given in Helper');
}
$str = $this->iForm($name, $method, $action);
$str .= $this->obj->iFields($class);
$str .= '</form>';
return $str;
}
/**
* returns a <FORM> tag
* @param string $name name of form (id will be i_$name)
* @param string $method post, get, or file (post by default)
* @param string $action URL to which form is sent (current page by default)
*/
public function iForm($name, $method='post', $action='', $class='') {
$fc = FrontController::getInstance();
if (!$action) {
$action = $fc->thisUrl();
}
$str = '<form id="f_'.$name.'" name="'.$name.'"';
if ($method == 'file') {
$str .= ' method="post" enctype="multipart/form-data"';
} else {
$str .= ' method="'.$method.'"';
}
if ($class) {
$str .= ' class="'.$class.'"';
}
$str .= ' action="'.$action.'">';
return $str;
}
/**
* generates form field (only selected fields)
*/
public function iFields($class='side') {
if (!isset($this->obj)) {
throw AppException('Can not generate fields : no object given in Helper');
}
$str = '<ol class="fields'.($class?(' '.$class):'').'">';
foreach ($this->obj->getFields() as $key => $type) {
$type = substr($type,0,3);
// error_log(get_class($this->obj).' iFields : '.$key.' / '.$type);
if ($type == 'UID') {
// put UID in hidden field before <OL>
$str = $this->iHidden($key).$str;
continue;
} else if ($this->obj->isObjectProperty($key, $class, $nkey)) {
// get a simple input field for the ID
// error_log(get_class($this->obj).' iFields found '.$key.' as an object of '.$class.' ('.$nkey.')');
$obj = new $class;
$obj->addHelper('html_form');
$str .= '<li><label for="i_'.$nkey.'_id">'.$nkey.'</label>'
.$this->obj->iNested('id', $nkey);
} else if ($sf = $this->iFieldLabelled($key, $key, $type)) {
$str .= $sf;
}
}
$str .= '<li class="buttons"><button type="submit" name="save" value="1">Save</button></li>';
$str .= '</ol>';
return $str;
}
/**
* returns a field along with LABEL, all wrapped in a LI
* @todo make the wrapper optional
* @todo missing types
*/
public function iFieldLabelled($key, $label='', $type='', $wrap='li') {
if (empty($type)) {
$type = $this->obj->getPropertyType($key);
}
$sf = '';
switch($type) {
case 'UID': //-TODO- hidden ?
case 'INT':
case 'NUM':
case 'DEC':
case 'STR':
case 'EML':
case 'URL':
case 'TIM': // -TODO-
$sf = $this->iText($key);
break;
case 'DTE':
$sf = $this->iDate($key);
break;
case 'DTM':
$sf = $this->iDateTime($key);
break;
case 'USR':
$sf = $this->iText($key);
break;
case 'PSS':
$sf = $this->iPass($key);
break;
case 'TXT':
case 'BBS': // -TODO-
case 'HTM': // -TODO-
case 'XML': // -TODO-
$sf = $this->iTextArea($key);
break;
case 'BOL':
$sf = $this->iCheckBox($key);
break;
case 'LVL': // -TODO-
case 'DOC': // -TODO-
case 'IMG': // -TODO-
case 'OBJ': // -TODO-
break;
}
if (!$sf) {
return false;
}
if (empty($label)) {
$label = str_replace('_',' ',$key); // -TODO- translate
}
return $this->_htmlWrapBegin($wrap)
.'<label for="i_'.$key.'">'.$label.'</label>'
.$sf
.$this->_htmlWrapEnd($wrap);
}
/*
* generates a hidden field
*/
public function iHidden($key) {
$str = '<input type="hidden" id="i_'.$key.'" name="'.$key.'" value="'
.$this->_value($key)
.'" />';
return $str;
}
/*
* generates a text field
*/
public function iText($key) {
$str = '<input type="text" id="i_'.$key.'" name="'.$key.'" value="'
.$this->_value($key)
.'" />';
if ($err = $this->_htmlError($key)) {
$str .= $err;
}
return $str;
}
/*
* generates a text field
*/
public function iNested($key, $nested) {
$str = '<input type="text" id="i_'.$nested.'__'.$key.'" name="'.$nested.'__'.$key.'" value="'
.$this->_value("$nested::$key")
.'" />';
if ($err = $this->_htmlError($key)) {
$str .= $err;
}
return $str;
}
/**
* generates date field
* @todo real date field
*/
public function iDate() {
$args = func_get_args();
return call_user_func_array(array($this,'iText'),$args);
}
/**
* generates date time field
* @todo real date time field
*/
public function iDateTime() {
$args = func_get_args();
return call_user_func_array(array($this,'iText'),$args);
}
/**
* generates a password field
*/
public function iPass($key, $autocomplete=true) {
$str = '<input type="password" id="i_'.$key.'" name="'.$key.'" value=""';
if (!$autocomplete) {
$str .= ' autocomplete="off"';
}
$str .= ' />';
if ($err = $this->_htmlError($key)) {
$str .= $err;
}
return $str;
}
/**
* generates a simple text area
*/
public function iTextArea($key) {
$str = '<textarea id="i_'.$key.'" name="'.$key.'">'
.$this->_value($key)
.'</textarea>';
if ($err = $this->_htmlError($key)) {
$str .= $err;
}
return $str;
}
/**
* generates a check box
*/
public function iCheckBox($key, $label='') {
$str = '<input type="checkbox" id="i_'.$key.'" name="'.$key.'"';
if ($this->_value($key)) {
$str .= ' checked="checked"';
}
$str .= ' value="1" />';
if (empty($label)) {
$options = $this->obj->getPropertyOptions($key);
if (!empty($options['label'])) {
// -TODO- translate
$label = $options['label'];
}
}
if (!empty($label)) {
$str .= '<label for="_'.$key.'" class="inline">'.VarStr::html($label).'</label>';
}
if ($err = $this->_htmlError($key)) {
$str .= $err;
}
return $str;
}
/**
* generates a series of radio inputs
*/
public function iRadio($key, $options='', $wrap='ul', $subwrap='li') {
if (isset($this->obj)) {
$options = $this->obj->getPropertyOptions($key);
}
if (empty($options['options'])) {
FC::log_warn("iRadio $key does not provide options");
return $key.' (no option)';
}
$str = $this->_htmlWrapBegin($wrap);
$i = 1;
foreach($options['options'] as $val => $lbl) {
$str .= $this->_htmlWrapBegin($subwrap);
$str .= '<input type="radio" id="i_'.$key.'_'.$i.'" name="'.$key.'"';
if ($options['value'] == $val) {
$str .= ' checked="checked"';
}
$str .= ' value="'.$val.'" />';
$str .= '<label for="i_'.$key.'_'.$i.'">'.$lbl.'</label>';
$str .= $this->_htmlWrapEnd($subwrap);
$i++;
}
return $str.$this->_htmlWrapEnd($wrap);
}
/**
* generates a select (drop down)
*/
public function iSelect($key, $options='') {
if (isset($this->obj)) {
$options = $this->obj->getPropertyOptions($key);
}
if (empty($options['options'])) {
FC::log_warn("iRadio $key does not provide options");
return $key.' (no option)';
}
$str = '<select id="i_'.$key.'" name="'.$key.'">';
$i = 1;
foreach($options['options'] as $val => $lbl) {
$str .= '<option value="'.$val.'"';
if ($options['value'] == $val) {
$str .= ' selected="selected"';
}
$str .= ' value="'.$val.'">'.$lbl.'</option>';
$i++;
}
return $str.'</select>';
}
/**
* generates a time zone drop down list
*/
public function iTimeZone($key, $options='') {
if (isset($this->obj)) {
$default = $this->obj->get($key);
// $options = $this->obj->getPropertyOptions($key);
}
$timezone_identifiers = DateTimeZone::listIdentifiers();
$str = '<select id="id_'.$key.'" name="'.$key.'">';
$continent = '';
foreach( $timezone_identifiers as $value ) {
if ( preg_match( '/^(America|Antartica|Arctic|Asia|Atlantic|Europe|Indian|Pacific)\//', $value ) ) {
$ex=explode("/",$value);//obtain continent,city
if ($continent!=$ex[0]){
if ($continent!="") echo '</optgroup>';
$str .= '<optgroup label="'.$ex[0].'">';
}
$city=$ex[1];
$continent=$ex[0];
$str .= '<option value="'.$value.'"';
if ($value == $default) {
$str .= ' selected="selected"';
}
$str .='>'.$city.'</option>';
}
}
$str .= '</optgroup>';
return $str.'</select>';
}
/**
* returns a form field formatted value
*/
protected function _value($key) {
if (!isset($this->obj)) {
return '';
}
return $this->obj->value($key);
}
/**
* returns error (HTML formatted)
*/
protected function _htmlError($key) {
if (!isset($this->obj)) {
return '';
}
return $this->obj->htmlError($key);
}
/**
* HTML wrapper begin tag
*/
protected function _htmlWrapBegin($wrap) {
if ($wrap) {
return '<'.$wrap.'>';
} else {
return '';
}
}
/**
* HTML wrapper end tag
*/
protected function _htmlWrapEnd($wrap) {
if ($wrap) {
if ($idx = strpos($wrap, ' ')) {
$wrap = substr($wrap,0,$idx);
}
return '</'.$wrap.'>';
} else {
return '';
}
}
}

60
lib/helper/messaging.php Normal file
View File

@ -0,0 +1,60 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Messaging
*
* manages session messages, alerts, error messages
*/
class MessagingHelper {
public function __construct() {
if (!isset($_SESSION['appMessage'])) {
$_SESSION['appMessage'] = array();
}
}
public function addMessage($str) {
if (!array_search($str, $_SESSION['appMessage'])) {
$_SESSION['appMessage'][] = $str;
}
}
public function hasMessage() {
return count($_SESSION['appMessage']);
}
public function getMessages(&$isError, $html=true, $clean=true) {
$str = '';
foreach($_SESSION['appMessage'] as $mess) {
if (preg_match('/^ERROR:/i', $mess)) {
$isError = true;
$mess = substr($mess,6);
}
if ($str) {
$str .= "\n";
}
$str .= $mess;
}
if ($clean) {
$this->cleanMessages();
}
if ($html) {
return VarTxt::html($str);
} else {
return $str;
}
}
public function cleanMessages() {
unset($_SESSION['appMessage']);
}
}

222
lib/helper/navi.php Normal file
View File

@ -0,0 +1,222 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Navi
*
* manages referers, redirection and provides URL manipulation
*/
class NaviHelper {
public function __construct() {
if (!isset($_SESSION['appReferrers'])) {
$_SESSION['appReferrers'] = array();
}
}
// ---- REFERERS handling ----------------------------------------------------
public function autoReferrer($historic=false) {
$url = $_SERVER['REQUEST_URI'];
if ($historic) {
$url = substr($_SERVER['HTTP_REFERER'],strlen(APP_WWW_URL));
} else {
/*
$url = $_SERVER['PHP_SELF']
.(($_SERVER['QUERY_STRING'])?('?'.$_SERVER['QUERY_STRING']):'');
*/
}
if (!preg_match('/login/',$url)) {
return $url;
} else {
// skip login, register, logout and password reminder pages
return false;
}
}
public function setReferrer($url='') {
$_SESSION['appReferrers'] = array();
if ($url) {
$_SESSION['appReferrers'][] = StringHelper::camelToFlat($url);
}
}
public function addReferrer($url='', $historic=false) {
if (!$url) {
if (!$url = $this->autoReferrer($historic)) {
return false;
}
}
$url = StringHelper::camelToFlat($url);
$arr = $_SESSION['appReferrers']; // copy
// search for previous entry with same url
while ($tmp = @array_pop($arr)) {
if ($tmp == $url) {
// been to this page, need to clean referrers
$_SESSION['appReferrers'] = $arr;
break;
}
}
// add url to referrer
$_SESSION['appReferrers'][] = $url;
return true;
}
public function addAppReferrer($params=NULL) {
$fc = FrontController::getInstance();
if (!empty($params)) {
$params = StringHelper::mixedToArray($params);
$arr = array();
foreach($params as $p) {
if (array_key_exists($p, $fc->request)) {
$arr[$p] = $fc->request[$p];
}
}
$this->addReferrer($fc->thisUrl($arr));
} else {
$this->addReferrer($fc->thisUrl());
}
}
public function getReferrer($skip=true, $clean=false) {
$url = './';
$arr = $_SESSION['appReferrers']; // copy
if ($clean) {
$arr =& $_SESSION['appReferrers']; // point
}
if (!is_array($arr)) {
// normally not needed, but seems to happen for some reason
$arr = array();
}
FrontController::log_debug('Refs : '.implode(', ',$arr));
if ($skip) {
array_pop($arr); // skip last one
}
while (count($arr)) {
if ($tmp = array_pop($arr)) {
$url = $tmp;
break;
}
}
return $url;
}
public function naturalReferrer($default, $avoid='') {
$ref = $default;
if ($_REQUEST['ref']) {
$ref = $_REQUEST['ref'];
} else if ($_SERVER['HTTP_REFERER']) {
$ref = $_SERVER['HTTP_REFERER'];
}
if ($avoid && preg_match("/$avoid/i",$ref)) {
$ref = $default;
}
return $ref;
}
public function delReferrer() {
array_pop($_SESSION['appReferrers']);
}
public function printReferrers() {
foreach ($_SESSION['appReferrers'] as $url) {
echo ' &gt; <a href="'.$url.'">'.$url.'</a>';
}
}
// ---- URLs and REDIRECTION -------------------------------------------------
public function autoRedirect($message='') {
$this->redirect($this->getReferrer(true, true), $message);
}
public static function redirect($url,$message='',$forceRef=false)
{
if (@constant('TZN_TRANS_ID')) {
if (session_id() && (!preg_match('/'.session_id().'/i',$url))) {
$url = $this->concatUrl($url,session_name()
.'='.session_id());
}
}
if ($message) {
$message = preg_replace("/<script[^>]*>[^<]+<\/script[^>]*>/is"
,"", $message);
$message = preg_replace("/<\/?(div|span|iframe|frame|input|"
."textarea|script|style|applet|object|embed|form)[^>]*>/is"
,"", $message);
if (@constant('TZN_TRANS_STATUS')) {
$fc = FrontController::getInstance();
$fc->addMessage($message);
} else {
$url = self::concatUrl($url,'tznMessage='.urlencode($message));
}
}
if ($forceRef) {
$url = self::concatUrl($url,'ref='.rawurlencode($_SERVER['REQUEST_URI']));
}
header("Location: ".str_replace('&amp;','&',$url));
exit;
}
public static function concatUrl($url,$param)
{
// hash
$hash = '';
if ($pos = strpos($url,'#')) {
$hash = substr($url,$pos);
$url = substr($url,0,$pos);
}
if ($pos = strpos($param,'#')) {
$hash = substr($param,$pos);
}
// params
$url = str_replace('&amp;','&',$url);
if ($pos = strpos($url,'?')) {
$arrParam = explode('=',$param);
if (strpos($url,$arrParam[0].'=')) {
// parameter already in url
$strQuery = substr($url,$pos+1);
$arrQuery = explode('&',$strQuery);
$arrResult = array();
$found = false;
foreach ($arrQuery as $value) {
if (preg_match('/^'.$arrParam[0].'=/', $value)) {
if ($arrParam[1]) {
// add only if has a value
$arrResult[] = $param;
}
$found = true;
} else {
$arrResult[] = $value;
}
}
if ($found) {
$url = substr($url,0,$pos).'?'.implode('&',$arrResult);
} else {
$url .= '&'.$param;
}
} else {
$url .= '&'.$param;
}
} else {
$url .= '?'.$param;
}
return str_replace('&','&amp;',$url).$hash;
}
}

78
lib/helper/string.php Normal file
View File

@ -0,0 +1,78 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @version 0.1
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* String Helper
*
* common functions to manipulate strings
*/
class StringHelper {
public function __construct() {
}
/**
* transforms a string from CamelCasing to flat_with_underscores
*/
public static function camelToFlat($str, $sep='_') {
$str = preg_replace('/(?<=\\w)(?=[A-Z])/',"$sep$1", trim($str));
return strtolower($str);
}
/**
* transforms a string from flat_with_underscores to CamelCasing
*/
public static function flatToCamel($str, $firstCap=false, $sep='_') {
$arr = explode($sep,trim($str));
$str = '';
foreach($arr as $sep) {
if ((!$str && $firstCap) || $str) {
$str .= ucfirst($sep);
} else {
$str .= $sep;
}
}
return $str;
}
/**
* gets any kind of arguments and returns an array
* (an array, a string containing values separated by commas, or many arguments)
*/
public static function mixedToArray() {
$arg = func_get_arg(0);
if (empty($arg)) {
return array();
} else if (is_array($arg)) {
return $arg;
} else if (func_num_args() == 1) {
$arr = explode(',',$arg);
array_walk($arr, 'trim');
return $arr;
} else {
return func_get_args();
}
}
/**
* generate random string
*/
public static function genRandom($len = APP_KEY_LENGTH, $strChars = APP_KEY_STRING)
{
$strCode = "";
$intLenChars = strlen($strChars);
for ( $i = 0; $i < $len; $i++ ) {
$n = mt_rand(1, $intLenChars);
$strCode .= substr($strChars, ($n-1), 1);
}
return $strCode;
}
}

61
lib/helper/translator.php Normal file
View File

@ -0,0 +1,61 @@
<?php
/**
* Tzn Framework
*
* @package tzn_helpers
* @author Stan Ozier <framework@tirzen.com>
* @since 0.3
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Translator Helper
*
* translates everything
* @todo test it all
*/
class TranslatorHelper {
protected $langDefault;
protected $langUser;
public function __construct() {
$this->langDefault = $GLOBALS['config']['lang']['default'];
$this->langUser = $GLOBALS['config']['lang']['user'];
}
public function loadLangConfig() {
include_once(APP_LANGUAGE_PATH.$this->langDefault.'/config.php');
include_once(APP_LANGUAGE_PATH.$this->langUser.'/config.php');
}
public function loadLangFile($file) {
if (file_exists(APP_LANGUAGE_PATH.$this->langUser.'/'.$file.'.php')) {
include_once(APP_LANGUAGE_PATH.$this->langUser.'/'.$file.'.php');
}
if (file_exists(APP_LANGUAGE_PATH.$this->langDefault.'/'.$file.'.php')) {
include_once(APP_LANGUAGE_PATH.$this->langDefault.'/'.$file.'.php');
}
}
public function getTranslation($label, $section, $field='') {
if (!array_key_exists($section, $GLOBALS['lang'][$this->langDefault])) {
return $label;
}
if (array_key_exists($label, $GLOBALS['lang'][$this->langUser][$section])) {
if (is_array($GLOBALS['lang'][$this->langUser][$section][$label])) {
return $GLOBALS['lang'][$this->langUser][$section][$label][$field];
} else {
return $GLOBALS['lang'][$this->langUser][$section][$label];
}
} else if (array_key_exists($label, $GLOBALS['lang'][$this->langDefault][$section])) {
if (is_array($GLOBALS['lang'][$this->langDefault][$section][$label])) {
return $GLOBALS['lang'][$this->langDefault][$section][$label][$field];
} else {
return $GLOBALS['lang'][$this->langDefault][$section][$label];
}
} else {
return $label;
}
}
}

45
lib/model/acl.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* Tzn Framework
*
* @package tzn_models
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.2
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* ACL
*
* Object representing user rights
*/
class AclModel extends Model {
public function __construct() {
parent::__construct('acl');
$this->addProperties(array(
'id' => 'UID',
'name' => 'STR',
'section' => 'STR'
));
}
}
/**
* User ACL
*
* Object representing association between users and rights
*/
class AclUserModel extends Model {
public function __construct() {
parent::__construct('acl_user');
$this->addProperties(array(
'user_id' => 'NUM',
'acl_id' => 'NUM'
));
}
}

0
lib/model/email.php Normal file
View File

46
lib/model/page.php Normal file
View File

@ -0,0 +1,46 @@
<?php
/**
* Tzn Framework
*
* @package tzn_models
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Page
*
* Object representing a page : contain title, headers, browsing history, and stuff
*/
class PageModel extends Model {
public $header;
public function __construct() {
parent::__construct();
$this->addProperties(array(
'title' => 'STR',
'description' => 'STR',
'keywords' => 'STR',
'rank' => 'INT'
));
$this->addHelper('html_asset');
}
public function dispatchHeader() {
if ($this->isEmpty('description') && isset($GLOBALS['config']['page']['description'])) {
$this->set('description',$GLOBALS['config']['page']['description']);
}
if ($this->isEmpty('keywords') && isset($GLOBALS['config']['page']['keywords'])) {
$this->set('keywords',$GLOBALS['config']['page']['keywords']);
}
include APP_ASSET_PATH.'html/page-header.php';
}
public function dispatchFooter() {
include APP_ASSET_PATH.'html/page-footer.php';
}
}

28
lib/model/setting.php Normal file
View File

@ -0,0 +1,28 @@
<?php
/**
* Tzn Framework
*
* @package tzn_models
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* Setting
*
* @todo try it
*/
class SettingModel extends Model {
public function __construct() {
parent::__construct('setting');
$this->addProperties(array(
'setting_key' => 'STR',
'setting_value' => 'XML',
'section' => 'STR',
'user_id' => 'NUM'
));
}
}

143
lib/model/user.php Normal file
View File

@ -0,0 +1,143 @@
<?php
/**
* Tzn Framework
*
* @package tzn_models
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.1
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* User
*
* abstract model implementing common user properties and methods
*/
abstract class UserModel extends Model {
public function __construct($table) {
parent::__construct($table);
$this->_properties = array(
'id' => 'UID',
'username' => 'USR',
'password' => 'PSS',
'salt' => 'STR',
'auto_login' => 'BOL',
'time_zone' => 'STR',
'date_format_us' => 'BOL',
'creation_date' => 'DTM',
'expiration_date' => 'DTE',
'last_login_date' => 'DTM',
'last_login_address' => 'STR',
'last_change_date' => 'DTM',
'visits' => 'NUM',
'bad_access' => 'NUM',
'activation' => 'STR',
'enabled' => 'BOL',
);
}
public function enableAuthentication() {
$this->addHelper('auth', $this);
}
public function setLogin($login)
{
if (!trim($login)) {
return false;
}
switch (APP_AUTH_FIELD) {
case 'email':
// login ID is email
if (!$this->set('email',$login)) {
return false;
}
if ($this->findOccurences('email',$login)) {
$this->_error["email"] = 'email_exists';
return false;
}
break;
default:
if (!$this->set('username',$username)) {
return false;
}
}
// check nickname (username) is unique
if ($this->findOccurences('username',$login)) {
$this->_error['username'] = 'user_name_exists';
return false;
}
return true;
}
public function setPassword($pass1, $pass2=false, $emptyIsOk=false)
{
if ($pass1 || $emptyIsOk) {
// a pass has been set
if (($pass2 !== false) && ($pass1 != $pass2)) {
// a confirmation has been set but is different
$this->_error['password'] = 'user_pass_mismatch';
return false;
}
$this->set('salt',StringHelper::genRandom(8,
'abcdefghijklmnopqrstuvwxyz'
.'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'));
if ($pass1) {
if ((strlen($pass1) >= APP_USER_PASS_MIN)
&& (strlen($pass1) <= APP_USER_PASS_MAX))
{
$salt = $this->get('salt');
switch (APP_AUTH_PASSWORD_MODE) {
case 1:
$this->set('password',crypt($pass1 , $salt));
break;
case 2:
$this->set('password',AuthHelper::getDbPass("ENCRYPT('$pass1','$salt')"));
break;
case 3:
$this->set('password',AuthHelper::getDbPass("ENCODE('$pass1','$salt')"));
break;
case 4:
case 5:
$this->set('password',AuthHelper::getDbPass("MD5('$pass1')"));
break;
default:
$iv = mcrypt_create_iv (mcrypt_get_iv_size(MCRYPT_3DES
, MCRYPT_MODE_ECB), MCRYPT_RAND);
$crypttext = mcrypt_encrypt(APP_AUTH_PASSWORD_MODE, $salt
, $pass1, MCRYPT_MODE_ECB, $iv);
$this->set('password',bin2hex($crypttext));
}
} else {
$this->_error['password'] = 'user_pass_length';
return false;
}
} else {
$this->set('password','');
}
return true;
} else {
if (!$emptyIsOk) {
$this->_error['password'] = 'user_pass_empty';
return false;
}
return true;
}
}
public function setupTimeZone() {
if (!$this->isEmpty('time_zone')) {
try {
$GLOBALS['config']['datetime']['timezone_user'] = new DateTimeZone($this->get('time_zone'));
// FC::log_debug('User::setupTimeZone : new time zone '.$this->get('time_zone'));
} catch (Exception $e) {
FC::log_error('User::setupTimeZone : unknown time zone ('.$this->get('time_zone').')');
}
}
}
}

76
lib/model/user_acl.php Normal file
View File

@ -0,0 +1,76 @@
<?php
/**
* Tzn Framework
*
* @package tzn_models
* @author Stan Ozier <framework@tirzen.com>
* @version 0.2
* @since 0.2
* @copyright GNU Lesser General Public License (LGPL) version 3
*/
/**
* User ACL
*
* abstract model implementing common user properties and methods
* with ACL support
*/
abstract class UserAclModel extends UserModel {
public function __construct($table) {
parent::__construct($table);
}
public function enableAuthentication() {
$this->addProperties(array(
'actags' => 'STR'
));
$this->addHelper('auth_acl', $this);
}
public function load($filter='') {
$db = $this->getHelper('db');
if ($this->getPropertyType('actags')) {
$this->_generateQuery($db);
return $db->load($filter, false);
} else {
$args = func_get_args();
return $this->callHelperArray('db','load',$args);
}
}
public function loadList() {
$db = $this->getHelper('db');
if ($this->getPropertyType('actags')) {
$this->_generateQuery($db);
return $db->loadList(false);
} else {
return $db->loadList(true);
}
}
public function save($acl=false) {
$this->removeProperties('actags');
if (parent::save()) {
if ($acl) {
$this->saveACL($acl);
}
return true;
}
return false;
}
public function saveACL($section='') {
$this->addHelper('auth_acl', $this);
$this->updateACL($section);
}
protected function _generateQuery(&$db) {
$db->select($db->dbTable().".*, GROUP_CONCAT(ac.name SEPARATOR ',') as actags");
$db->from($db->dbTable());
$db->leftJoin(array('acl_user','aa'),array($this->dbUid(true),'aa.user_id'));
$db->leftJoin(array('acl','ac'),array('aa.acl_id','ac.id'));
$db->groupBy('member.id');
}
}

139
skin/default/css/colorbox.css Executable file
View File

@ -0,0 +1,139 @@
/*
ColorBox Core Style
The following rules are the styles that are consistant between themes.
Avoid changing this area to maintain compatability with future versions of ColorBox.
*/
#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:98; overflow:hidden;}
#cboxOverlay{position:fixed; width:100%; height:100%;}
#cboxMiddleLeft, #cboxBottomLeft{clear:left;}
#cboxContent{position:relative; overflow:visible;}
#cboxLoadedContent{overflow:auto;}
#cboxLoadedContent iframe{display:block; width:100%; height:100%; border:0;}
#cboxTitle{margin:0;}
#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%;}
#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;}
/*
ColorBox example user style
The following rules are ordered and tabbed in a way that represents the
order/nesting of the generated HTML, so that the structure easier to understand.
*/
#cboxOverlay{background:#fff;}
#colorbox{}
#cboxContent{
border:5px solid #ccc;-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
margin-top:32px;
}
#cboxLoadedContent{background:#eee; padding:1px;}
#cboxLoadedContent>div { padding:5px; }
#cboxLoadingGraphic{background:url(../img/loading.gif) center center no-repeat;}
#cboxLoadingOverlay{background:#eee;}
#cboxTitle{position:absolute; top:-22px; left:9px; color:#999;}
#cboxCurrent{position:absolute; top:-22px; right:205px; text-indent:-9999px;}
#cboxSlideshow, #cboxPrevious, #cboxNext, #cboxClose{text-indent:-9999px; width:20px; height:20px; position:absolute; top:-20px; background:url(../img/controls.png) 0 0 no-repeat;}
#cboxPrevious{background-position:0px 0px; right:44px;}
#cboxPrevious.hover{background-position:0px -25px;}
#cboxNext{background-position:-25px 0px; right:22px;}
#cboxNext.hover{background-position:-25px -25px;}
#cboxClose{background-position:-50px 0px; right:0; margin-top:-5px}
#cboxClose.hover{background-position:-50px -25px;}
.cboxSlideshow_on #cboxPrevious, .cboxSlideshow_off #cboxPrevious{right:66px;}
.cboxSlideshow_on #cboxSlideshow{background-position:-75px -25px; right:44px;}
.cboxSlideshow_on #cboxSlideshow.hover{background-position:-100px -25px;}
.cboxSlideshow_off #cboxSlideshow{background-position:-100px 0px; right:44px;}
.cboxSlideshow_off #cboxSlideshow.hover{background-position:-75px -25px;}
#cboxLoadedContent h1 {
color: #369;
font-size: 12px;
padding: 9px 20px 12px;
text-align: center;
}
#cboxLoadedContent table {
border-collapse: separate;
border-spacing: 1px;
}
#cboxLoadedContent #tab1 table {
width: 100%;
}
#cboxLoadedContent #tab1 table tr {
vertical-align: top;
}
#cboxLoadedContent #tab1 table tr th {
text-align: left;
font-weight: normal;
color: #666;
padding: 2px;
width: 20%;
}
#cboxLoadedContent #tab1 table tr td {
text-align: left;
padding: 2px;
width: 80%;
}
#cboxLoadedContent #tab2 table {
width: 100%;
}
#cboxLoadedContent #tab2 table tr {
}
#cboxLoadedContent #tab2 table tr td {
padding: 2px;
width: 40%;
}
#cboxLoadedContent #tab2 table tr td:last-child {
text-align: right;
width: 20%;
}
#cboxLoadedContent #tab2 table thead tr th {
padding: 2px;
font-weight: normal;
text-align: left;
background-color: #ccc;
}
#cboxLoadedContent #tab2 table thead tr th:last-child {
text-align: right;
}
#cboxLoadedContent #tab2 table tbody tr td {
border-top: 1px solid #ccc;
}
#cboxLoadedContent #tab2 table tfoot tr td {
border-top: 1px solid #999;
background-color: #ccc;
}
#cboxLoadedContent #tab2 table tbody tr td a.onhold {
display: none;
float: left;
background-image: url(../img/b-delete.png);
width: 16px;
height: 16px;
text-align: left;
text-indent: -9999px;
}
#cboxLoadedContent #tab2 table tbody tr:hover td {
background-color: #fff;
}
#cboxLoadedContent #tab2 table tbody tr:hover td a.onhold {
display: block;
}
#cboxLoadedContent #sidepanel {
float: right;
color: #666;
font-size: 10px;
}
#cboxLoadedContent #sidepanel h4 {
padding: 6px 0 3px;
}
#cboxLoadedContent #sidepanel p {
padding-bottom: 4px;
line-height: 1.35em;
}
#cboxLoadedContent #sidepanel small {
font-size: 10px;
color: #999;
}
#cboxLoadedContent #sidepanel ul li {
padding: 1px 0 2px 0;
}

250
skin/default/css/freak.css Normal file
View File

@ -0,0 +1,250 @@
html, body, div, h1, h2, h3, h4, h5, h6, ul, ol, dl, li, dt, dd, p, blockquote,
pre, form, fieldset, table, th, td, hr {
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
height: 100%;
font-family: Verdana, Helvetica, Arial, sans-serif;
font-size: 12px;
min-width: 960px;
background: #f3f3f3;
}
#global {
position: relative;
top: 0;
min-height: 100%;
}
* html #global {
height: 100%;
}
h1 {
}
h2 {
}
h3 {
}
p {
padding-bottom: 9px;
}
ul, ol {
padding: 0 0 5px 25px;
}
ul ul, ol ol {
padding: 0 0 5px 15px;
}
ul li {
padding: 2px 0 3px 0;
}
ol li {
padding: 2px 0 3px 0;
}
a img {
border: 0 none;
}
:focus, a, a:active {
outline: 0 none;
}
/* alignment */
.lft {
text-align: left;
}
.ctr {
text-align: center;
}
.rgt {
text-align: right;
}
.jty {
text-align: justify;
}
/* floating */
.flft {
float: left;
}
.frgt {
float: right;
}
.clft {
clear: left;
}
.crgt {
clear: right;
}
img.flft {
margin-right: 6px;
}
img.frgt {
margin-left: 6px;
}
/* header */
#dtop {
background: #1f87bb url(../img/top-back.png) repeat-x 0 bottom;
color: #fff;
height: 40px;
}
#dtop a {
color: #fff;
text-decoration: underline;
}
#dtop a:hover {
color: #000;
}
#dtop h1 {
float: left;
height: 40px;
margin-right: 20px;
width: 150px;
}
#dtop h1 a {
background: url(../img/logo.png) no-repeat;
display: block;
height: 40px;
text-indent: -999px;
width: 150px;
}
#dtop #duser {
float: right;
line-height: 1.125em;
padding: 4px 8px;
text-align: right;
width: 150px;
}
#dtop form#search {
float: right;
padding: 16px 30px 0 0;
}
#dtop form#search input {
background: url(../img/top-search.png) no-repeat left center;
border: 0 none;
color: #069;
float: left;
font-size: 10px;
padding: 3px 0 3px 4px;
width: 76px;
height: 10px;
}
#dtop form#search button {
background: url(../img/top-search.png) no-repeat right center;
border: 0 none;
padding: 0;
width: 20px;
height: 16px;
text-indent: -9999px;
}
#dtop ul {
padding: 12px 0 0 0;
height: 28px;
}
#dtop ul li {
float: left;
list-style: none;
margin-right: 8px;
padding: 0 0 8px 10px;
height: 20px;
}
#dtop ul li.active {
background: url(../img/top-tab.png) no-repeat left 0;
}
#dtop ul li a {
display: block;
color: #fff;
text-decoration: none;
padding: 6px 10px 0 0;
}
#dtop ul li a:hover {
}
#dtop ul li.active a {
color: #000;
background: url(../img/top-tab.png) no-repeat right 0;
}
/* working area */
#dwork {
padding-top: 10px;
padding-bottom: 50px;
}
/* layout : 1 column */
#dmain.full {
margin: 0 1%;
}
/* layout : 2 columns */
#dmain.two, #dmain.three {
margin-left: 135px;
}
#dleft {
float: left;
width: 110px;
overflow: hidden;
padding: 50px 10px 0 10px;
}
#dleft h4 {
color: #333;
font-size: 11px;
border-bottom: 1px solid #666;
padding-left: 4px;
margin: 5px 0 5px;
}
#dmain.three #dlist {
margin-right: 250px;
}
/* right panel (calendar) */
#dright {
float: right;
width: 220px;
padding-right: 15px;
background-color: aqua;
}
/* footer */
#dfoot {
background: #3298cb url(../img/bot-back.png) repeat-x 0 top;
font-size: 11px;
color: #fff;
height: 16px;
margin-top: -22px;
padding: 3px 10px;
position: relative;
}
#dfoot a {
color: #fff;
}
#dfoot.exp {
margin-top: -306px;
height: 300px;
}
#dfoot #dcopy {
float: right;
font-size: 10px;
}
#dfoot #dcopy a {
background: url(../img/bot-logo.png);
display: block;
float: left;
height: 16px;
text-indent: -9999px;
width: 70px;
}
#dfoot #dcopy span {
vertical-align: bottom;
font-size: 9px;
}
hr.clear {
clear: left;
border: 0 none;
height: 0;
visibility: hidden;
}
hr.sep {
clear: right;
border: 0 none;
height: 8px;
visibility: hidden;
}
hr.separator {
padding-bottom: 10px;
}
hr.footer {
padding-bottom: 40px;
}

308
skin/default/css/list.css Normal file
View File

@ -0,0 +1,308 @@
ul.links {
list-style: none;
}
ul.links li a, p.links a {
color: #333;
display: block;
font-size: 11px;
padding: 1px 4px 2px;
line-height: 1.25em;
text-decoration: none;
}
ul.verti {
padding-left: 0;
}
ul.verti li a, p.links a {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
ul.horiz {
float: left;
}
ul.horiz li {
float: left;
}
ul.horiz li a {
background: #ccc;
border-right: 1px solid #fff;
}
ul.horiz li:first-child a {
padding-left: 7px;
-webkit-border-top-left-radius: 8px;
-webkit-border-bottom-left-radius: 8px;
-moz-border-radius-topleft: 8px;
-moz-border-radius-bottomleft: 8px;
border-radius: 8px 0 0 8px;
}
ul.horiz li:last-child a {
padding-right: 7px;
-webkit-border-top-right-radius: 8px;
-webkit-border-bottom-right-radius: 8px;
-moz-border-radius-topright: 8px;
-moz-border-radius-bottomright: 8px;
border-radius: 0 8px 8px 0;
}
ul.links li a:hover {
background-color: #9cf;
}
ul.links li.active a, p.links a.active {
color: #fff;
background-color: #39c;
}
/* main panel : filters */
#dfilters {
padding: 0 20px 6px 0;
}
#dfilters p {
text-align: center;
}
#dfilters span {
float: right;
}
#dfilters form input {
padding: 1px 4px;
border: 1px solid #333;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
#dfilters form input:focus {
background-color: #ffc;
}
#dfilters form button {
padding: 1px 2px;
}
/* main panel (list) */
#dlist {
background: #fff;
border: 1px solid #e3e3e3;
margin: 0 10px 0 0;
min-height: 300px;
padding: 10px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
}
/* table */
#dlist table {
border-collapse: separate;
border-spacing: 1px;
/* border: 1px solid #ccc; */
font-size: 9pt;
width: 100%;
}
#dlist table th, #dlist table td {
padding: 4px 6px;
}
#dlist table thead th {
font-weight: normal;
background-color: #999;
color: #fff;
border-top: 1px solid #bbb;
border-bottom: 1px solid #999;
}
#dlist table thead th small {
padding-top: 2px;
float: right;
}
#dlist table tbody tr td {
border-bottom: 1px solid #ccc;
}
#dlist table tbody tr.overdue td {
color: #c00;
}
#dlist table tbody tr.today td {
color: #36c;
}
#dlist table tbody tr.future td {
/* font-size: .87em; */
color: #999;
border-bottom-color: #eee;
}
#dlist table tbody tr.current td {
color: #000;
background-color: #cef;
}
#dlist table tbody tr.noline td {
border-bottom: 1px solid #eee;
}
#dlist table tbody tr.timer td {
color: #666;
}
/* task list columns */
#dlist.tasks table thead th:first-child, #dlist.tasks table tbody td:first-child {
width: 2%;
}
#dlist.tasks table thead th:nth-child(2), #dlist.tasks table tbody td:nth-child(2) {
text-align: left;
width: 9%;
}
#dlist.tasks table thead th:nth-child(3), #dlist.tasks table tbody td:nth-child(3) {
text-align: left;
width: 62%;
}
#dlist.tasks table thead th:nth-child(4), #dlist.tasks table tbody td:nth-child(4),
#dlist.tasks table thead th:nth-child(5), #dlist.tasks table tbody td:nth-child(5) {
text-align: center;
width: 9%;
}
#dlist.tasks table thead th:nth-child(6), #dlist.tasks table tbody td:nth-child(6) {
text-align: right;
width: 9%;
}
/* user list columns */
#dlist.users table thead th:first-child, #dlist.users table tbody td:first-child {
text-align: left;
width: 80%;
}
#dlist.users table thead th:nth-child(2), #dlist.users table tbody td:nth-child(2) {
text-align: center;
width: 20%;
}
#dlist.users table tbody td small {
margin-left: 9px;
color: #369;
}
/* priorities */
#dlist table tbody tr td span.prio {
font-size: 10px;
color: #fff;
padding: 1px 3px;
background-color: #ccc;
}
#dlist table tbody tr td span.pr0 {
background-color: #bbb;
}
#dlist table tbody tr td span.pr1 {
background-color: #f00;
}
#dlist table tbody tr td span.pr2 {
background-color: #f63;
}
#dlist table tbody tr td span.pr3 {
background-color: #c69;
}
#dlist table tbody tr td span.pr4 {
background-color: #96c;
}
#dlist table tbody tr td span.pr5 {
background-color: #69c;
}
#dlist table tbody tr td span.pr6 {
background-color: #7ac;
}
#dlist table tbody tr td span.pr6 {
background-color: #8bc;
}
#dlist table tbody tr td span.pr7 {
background-color: #acc;
}
#dlist table tbody tr td span.pr8 {
background-color: #bcc;
}
#dlist table tbody tr td span.pr9 {
background-color: #ccc;
}
#dlist table tbody tr.future td span.prio {
background-color: #ccc;
}
#dlist table tbody tr.overdue td span.prio {
background-color: #f00;
}
/* links */
#dlist table tbody tr td a {
color: inherit;
text-decoration: none;
}
#dlist table tbody tr td a:hover {
text-decoration: underline;
}
#dlist table tbody tr td a.onhold {
display: none;
float: right;
background-image: url(../img/b-edit.png);
width: 16px;
height: 16px;
text-align: left;
text-indent: -9999px;
}
#dlist table tbody tr td a.onlock {
display: none;
}
#dlist table tbody tr td a.clock {
float: left;
background-image: url(../img/b-run.png);
}
#dlist table tbody tr td a.note {
padding-right: 20px;
background: url(../img/b-note.png) no-repeat right center;
}
/* hover */
#dlist table tbody tr:hover td {
background-color: #fec;
}
#dlist table tbody tr:hover td a.onhold {
display: block;
}
#dlist table tbody tr.current:hover td a.clock {
display: none;
}
#dlist table tbody tr.current:hover td {
background-color: #9cf;
}
/* footer */
#dlist table tfoot tr td {
background-color: #ddd;
font-weight: bold;
text-align: right;
}
#dlist table tfoot tr td:first-child {
text-align: left;
}
#dlist table tfoot tr td a {
font-weight: normal;
}
/* buttons */
span.button, span.button a {
color: #333;
font-size: 12px;
line-height: 1.5em;
text-decoration: none;
padding: 5px 0;
background-repeat: no-repeat;
}
span.button {
padding-left: 30px;
background-position: left center;
}
span.button a {
padding-right: 10px;
background-position: right center;
}
span.button a:hover {
color: #fff;
}
span.new, span.new a {
background-image: url('../img/b-new.png');
}
span a.new {
display: block;
width: 24px;
height: 24px;
text-indent: -9999px;
background: url('../img/b-new.png') no-repeat left center;
float: left;
}
span a.multi {
margin-left: 6px;
background-position: -24px center;
}
span a.reload {
margin-left: 6px;
background-position: -48px center;
}
p.empty {
padding: 20px 0 0;
text-align: center;
}

View File

@ -0,0 +1,14 @@
form {
position: relative;
top: 60px;
left: 50%;
width: 300px;
margin-left: -150px;
}
form p {
color: #c00;
text-align: center;
}
form input {
width: 120px;
}

View File

@ -0,0 +1,186 @@
/* timer */
#drun {
position: absolute;
top: 0;
left: 50%;
margin-left: -250px;
padding: 6px 10px;
width: 500px;
color: #036;
background-color: transparent;
z-index: 10;
}
#drun.running {
color: #036;
background-color: #6cf;
-webkit-border-bottom-left-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
-moz-border-radius-bottomleft: 5px;
-moz-border-radius-bottomright: 5px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
-moz-box-shadow: 0 0 5px #036;
-webkit-box-shadow: 0 0 5px #036;
box-shadow: 0 0 5px #036;
text-align: center;
}
#drun.paused {
color: #036;
background-color: #39c;
border: 1px solid #39c;
text-align: center;
}
#drun p {
font-size: 11px;
margin: 0;
padding: 0;
line-height: 1.75em;
}
#drun p input {
color: #036;
width: 300px;
border: 0;
padding: 2px 5px 3px;
background-color: #39c;
-moz-box-shadow: 0 0 3px #069;
-webkit-box-shadow: 0 0 3px #069;
box-shadow: 0 0 3px #069;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
#drun p input:focus {
color: #000;
background-color: #5be;
}
#drun p span#dtimer {
float: left;
font-size: 12px;
font-weight: bold;
color: #fff;
text-shadow: 0 0 2px #333;
}
#drun div {
float: right;
}
#drun button {
opacity: 0.5;
-moz-opacity: 0.5;
filter: alpha(opacity=50);
}
#drun button:hover, #drun button:focus {
opacity: 1;
-moz-opacity: 1;
filter: alpha(opacity=100);
outline: 0 none;
}
#timerstatus.loading {
background: url(../img/barloader.gif) no-repeat center center;
}
#timerstatus.loading button {
opacity: 0;
-moz-opacity: 0;
filter: alpha(opacity=0);
visibility: hidden;
}
/* form */
form#f_task_batch {
padding: 10px;
}
form#f_task_batch textarea {
width: 360px;
height: 280px;
}
form#f_task_edit label {
color: #666;
}
form#f_task_edit input#i_title {
width: 360px;
}
form#f_task_edit input#i_deadline {
width: 80px;
}
form#f_task_edit textarea {
width: 360px;
height: 150px;
}
form#f_task_timer ol.side li {
line-height: 2em;
}
form#f_task_timer ol.side li label {
color: #666;
width: 55px;
margin-right: 5px;
text-align: right;
}
p#vtip {
display: none;
position: absolute;
padding: 10px;
left: 5px;
margin-right: 8px;
max-width: 340px;
font-size: 0.8em;
background-color: white;
border: 1px solid #69c;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
-moz-box-shadow: 0 0 5px #ccc;
-webkit-box-shadow: 0 0 5px #ccc;
box-shadow: 0 0 5px #ccc;
z-index: 9999;
}
p#vtip #vtipArrow {
position: absolute;
top: -10px;
left: 5px;
}
p#vtip.inv {
max-width: 50px;
text-align: center;
}
p#vtip.inv #vtipArrow {
left: 40px;
}
div.tabs {
}
div.tabs ul.nav {
height: 20px;
padding: 0 0 0 10px;
margin: 0 5px;
border-bottom: 2px solid #999;
}
div.tabs ul.nav li {
float: left;
list-style: none;
margin: 0 0 0 2px;
}
div.tabs ul.nav li a {
display: block;
padding: 2px 8px;
font-size: 12px;
line-height: 14px;
color: #fff;
background-color: #ccc;
text-decoration: none;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
div.tabs ul.nav li a:hover {
background-color: #bbb;
}
div.tabs ul.nav li.activeli a {
background-color: #999;
}
div.tabs div.tab {
padding: 5px;
}
div.help {
font-size: 10px;
color: #666;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

BIN
skin/default/img/b-edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

BIN
skin/default/img/b-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
skin/default/img/b-note.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

BIN
skin/default/img/b-run.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Some files were not shown because too many files have changed in this diff Show More