--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ END OF TERMS AND CONDITIONS
--- /dev/null
+What is Babel?
+==============
+
+:Contact: joel@scopserv.com
+
+.. contents:: Contents
+.. section-numbering::
+
+Babel is a web interface to viewing and editing PO (gettext) files for all
+modules written in PHP and utilizing the `Horde Application Framework`_.
+
+This software is OSI Certified Open Source Software. OSI Certified is a
+certification mark of the `Open Source Initiative`_.
+
+.. _`Horde Application Framework`: http://www.horde.org/horde/
+.. _`Open Source Initiative`: http://www.opensource.org/
+
+
+Obtaining Babel
+---------------
+
+Further information on Babel and the latest version can be obtained at
+
+ http://.../
+
+
+Documentation
+-------------
+
+The following documentation is available in the Babel distribution:
+
+:README_: This file
+:COPYING_: Copyright and license information
+:`docs/BUGS`_: Known bugs
+:`docs/CHANGES`_: Changes by release
+:`docs/CREDITS`_: Project developers
+:`docs/INSTALL`_: Installation instructions and notes
+:`docs/TODO`_: Development TODO list
+
+
+Installation
+------------
+
+Instructions for installing Babel can be found in the file INSTALL_ in the
+``docs/`` directory of the Babel distribution.
+
+
+Assistance
+----------
+
+If you encounter problems with Babel, help is available!
+
+The Horde Frequently Asked Questions List (FAQ), available on the Web at
+
+ http://www.horde.org/faq/
+
+The Horde Project runs a number of mailing lists, for individual applications
+and for issues relating to the project as a whole. Information, archives, and
+subscription information can be found at
+
+ http://www.horde.org/mail/
+
+Lastly, Horde developers, contributors and users also make occasional
+appearances on IRC, on the channel #horde on the freenode Network
+(irc.freenode.net).
+
+
+Licensing
+---------
+
+For licensing and copyright information, please see the file COPYING_ in the
+Babel distribution.
+
+Thanks,
+
+The Babel team
+
+
+.. _README: ?f=README.html
+.. _COPYING: http://www.horde.org/licenses/gpl.php
+.. _docs/BUGS: ?f=BUGS.html
+.. _docs/CHANGES: ?f=CHANGES.html
+.. _docs/CREDITS: ?f=CREDITS.html
+.. _INSTALL:
+.. _docs/INSTALL: ?f=INSTALL.html
+.. _docs/TODO: ?f=TODO.html
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+// Define if we use Horde CVS or Custom Commit
+$custom_commit = true;
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+Translate_Display::header(_("Horde translation generator"));
+
+/* Do sanity check */
+Translate::sanity_check();
+
+/* Searching applications */
+Translate::check_binaries();
+
+Translate_Display::info(sprintf('Searching Horde applications in %s', realpath(HORDE_BASE)));
+$dirs = Translate::search_applications();
+
+$apps = Translate::strip_horde($dirs);
+$apps[0] = 'horde';
+Translate_Display::info(_("Found applications:"));
+Translate_Display::info(wordwrap(implode(', ', $apps)), false);
+Translate_Display::info();
+
+// Check if we must execute Custom commit or Horde CVS Commit (Developer)
+if ($custom_commit) {
+ Translate_Display::header(_("Commit PO files ..."));
+ foreach($dirs as $d => $dir) {
+ $dir = realpath($dir);
+ $po = $dir . '/po/' . $lang . '.po';
+
+ if (@file_exists($po)) {
+ Translate_Display::info(_("Commit") . " $po ($lang)");
+ Translation::callHook('commit', array($po, $lang));
+ }
+
+ }
+} else {
+ Translate::commit();
+}
+
+
+Translate_Display::info();
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+
+if (!function_exists('_translation_hook_commit')) {
+ function _translation_hook_commit($info)
+ {
+ $po = $info[0];
+ $lang = $info[1];
+ $cmd = "svn commit $po -m \"- Update $lang translation.\"";
+ Translate_Display::warning($cmd);
+ }
+}
+
+if (!function_exists('_translation_hook_reset')) {
+ function _translation_hook_reset($po)
+ {
+
+ $cmd = "rm -rf $po";
+ exec("sudo $cmd");
+
+ $cmd = "svn update $po";
+ exec("sudo $cmd");
+
+ if (!@file_exists($po)) {
+ Translate_Display::warning(_("File doesn't exist ... ") . $po);
+ Translate_Display::info();
+ }
+ }
+}
--- /dev/null
+----------
+v0.1-dev
+----------
+
+[jvandal] ...
--- /dev/null
+========================
+ Babel Development Team
+========================
+
+Joel Vandal <joel@scopserv.com>
+
+Thanks to Michael Wallner <mike@php.net> for File_Gettext PEAR library.
+
+
+Localization
+============
+
+===================== ======================================================
+French Joel Vandal <joel@scopserv.com>
+===================== ======================================================
--- /dev/null
+=============================
+ Babel Development TODO List
+=============================
+
+:Contact: joel@scopsev.com
+
+- Add support for Obsolete strings.
+
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+$no_compress = true;
+
+$title = _("Download File");
+
+@define('BABEL_BASE', dirname(__FILE__));
+require_once BABEL_BASE . '/lib/base.php';
+
+$files = array();
+$dirs = Translate::search_applications();
+foreach($dirs as $d => $dir) {
+ $dir = realpath($dir);
+
+ $app = str_replace(realpath(HORDE_BASE), '', $dir);
+ $app = str_replace('/', '', $app);
+ if (empty($app)) {
+ $app = 'horde';
+ }
+
+ $po = $dir . '/po/' . $lang . '.po';
+ if (@file_exists($po)) {
+ $files[$app] = $po;
+ }
+}
+
+$filename = "po-" . $lang . ".zip";
+@system("rm -rf /tmp/$filename");
+@system("rm -rf /tmp/translate");
+@mkdir("/tmp/translate");
+foreach($files as $app => $file) {
+ $cmd = "cp $file /tmp/translate/$app-" . basename($file);
+ @system($cmd);
+}
+$cmd = "zip -j /tmp/$filename /tmp/translate/*";
+@exec($cmd);
+
+$data = file_get_contents("/tmp/$filename");
+
+$browser->downloadHeaders($filename);
+echo $data;
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+$meta_params = array(
+ "Project-Id-Version" => @$_SESSION['translation']['language'],
+ "Report-Msgid-Bugs-To" => "support@scopserv.com",
+ "POT-Creation-Date" => "",
+ "PO-Revision-Date" => "",
+ "Last-Translator" => "",
+ "Language-Team" => "",
+ "MIME-Version" => "1.0",
+ "Content-Type" => "text/plain; charset=utf-8",
+ "Content-Transfer-Encoding" => "8bit",
+ "Plural-Forms" => "nplurals=2; plural=(n > 1);");
+
+
+require_once dirname(__FILE__) . '/lib/base.php';
+require_once BABEL_BASE . '/lib/Gettext/PO.php';
+
+require_once 'Horde/Form.php';
+require_once 'Horde/Variables.php';
+require_once 'Horde/Form/Renderer.php';
+require_once 'Horde/Form/Action.php';
+
+require_once 'Horde/UI/Tabs.php';
+
+$app = Util::getFormData('module');
+
+$show = 'edit';
+$vars = &Variables::getDefaultVariables();
+
+if ($app) {
+ $napp = ($app == 'horde') ? '' : $app;
+ $pofile = HORDE_BASE . '/' . $napp . '/po/' . $_SESSION['translation']['language'] . '.po';
+ $po = &new File_Gettext_PO();
+ $po->load($pofile);
+}
+
+/* Set up the template fields. */
+$template->set('menu', Translation::getMenu('string'));
+$template->set('notify', Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+/* Create upload form */
+$form = &new Horde_Form($vars, _("Edit Translation"), $show);
+
+/* Validate form if submitted */
+if ($app && Util::getFormData('submitbutton') == _("Save")) {
+
+ if ($form->validate($vars, false)) {
+ $form->getInfo($vars, $form_values);
+
+ foreach($meta_params as $k => $v) {
+ if ($val = Util::getFormData($k)) {
+ $po->meta[$k] = $val;
+ }
+ }
+
+ $po->save($pofile);
+
+ if (Util::getFormData('url') == 'view') {
+ $url = Horde::applicationUrl('view.php');
+ $url = Util::addParameter($url, array('module' => $app));
+ header('Location: ' . $url);
+ exit;
+ }
+ }
+}
+
+if (!$app) {
+ $form->setButtons(_("Edit"));
+ $form->addVariable(_("Module"), 'module', 'enum', true, false, null, array(Translation::listApps(), true));
+ $form->addVariable('', '', 'spacer', true);
+} else {
+
+ $form->setButtons(_("Save"));
+ $form->addHidden('', 'module', 'text', false);
+ $vars->set('module', $app);
+
+ $form->addHidden('', 'url', 'text', false);
+ $vars->set('url', Util::getFormData('url'));
+
+ foreach($meta_params as $k => $v) {
+ $form->addVariable($k, $k, 'text', false, false);
+ if (isset($po->meta[$k]) && !empty($po->meta[$k])) {
+ $vars->set($k, $po->meta[$k]);
+ } elseif (!empty($v)) {
+ $vars->set($k, $v);
+ }
+ }
+}
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+$renderer_params = array();
+$renderer = &new Horde_Form_Renderer($renderer_params);
+$renderer->setAttrColumnWidth('20%');
+
+$form->renderActive($renderer, $vars, Horde::selfURL(), 'post');
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+set_time_limit(0);
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+if ($app) {
+ Translation::RB_init();
+}
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+
+if ($app) {
+ Translation::RB_start(300);
+}
+
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+$vars = &Variables::getDefaultVariables();
+
+/* Create upload form */
+$form = &new Horde_Form($vars, _("Extract Translation"), 'extract');
+
+if (!$app) {
+ $form->setButtons(_("Extract"));
+ $form->addVariable(_("Module"), 'module', 'enum', true, false, null, array(Translation::listApps(true), true));
+ $form->addVariable('', '', 'spacer', true);
+
+ $renderer_params = array();
+ $renderer = &new Horde_Form_Renderer($renderer_params);
+ $renderer->setAttrColumnWidth('20%');
+
+ $form->renderActive($renderer, $vars, Horde::selfURL(), 'post');
+} else {
+ Translate_Display::header(_("Horde translation generator"));
+
+ /* Sanity checks */
+ if (!extension_loaded('gettext')) {
+ Translate_Display::error(_("Gettext extension not found!"));
+ footer();
+ }
+
+ Translate_Display::info(_("Loading libraries..."));
+ $libs_found = true;
+
+ foreach (array('Console_Getopt' => 'Console/Getopt.php',
+ 'Console_Table' => 'Console/Table.php',
+ 'File_Find' => 'File/Find.php')
+ as $class => $file) {
+ @include_once $file;
+ if (class_exists($class)) {
+ // Translate_Display::info("$class ...", false);
+ } else {
+ Translate_Display::error(sprintf(_("%s not found."), $class));
+ $libs_found = false;
+ }
+ }
+
+ if (!$libs_found) {
+ Translate_Display::info();
+ Translate_Display::info(_("Make sure that you have PEAR installed and in your include path."));
+ Translate_Display::info('include_path: ' . ini_get('include_path'));
+ }
+ Translate_Display::info();
+
+ /* Searching applications */
+ Translate::check_binaries();
+
+ Translate_Display::info(sprintf(_("Searching Horde applications in %s"), realpath(HORDE_BASE)));
+ $dirs = Translate::search_applications();
+
+ if ($app == 'ALL') {
+ Translate_Display::info(_("Found directories:"), false);
+ Translate_Display::info(implode("\n", $dirs), false);
+ }
+ Translate_Display::info();
+
+ $apps = Translate::strip_horde($dirs);
+ $apps[0] = 'horde';
+ if ($app == 'ALL') {
+ Translate_Display::info(_("Found applications:"));
+ Translate_Display::info(wordwrap(implode(', ', $apps)), false);
+ Translate_Display::info();
+ }
+
+ global $module;
+ if ($app != 'ALL') {
+ $module = $app;
+ }
+
+ Translate::init();
+ Translate::cleanup();
+
+ Translate_Display::header(_("Generate Compendium ..."));
+ Translate::compendium();
+ Translate_Display::info();
+
+ Translate::xtract();
+ Translate_Display::info();
+ Translate::merge();
+
+ Translate_Display::info();
+ Translate_Display::header(_("Done!"));
+
+ Translation::RB_close();
+}
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+require BABEL_TEMPLATES . '/index.php';
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+
+String.prototype.parseColor=function(){var color='#';if(this.slice(0,4)=='rgb('){var cols=this.slice(4,this.length-1).split(',');var i=0;do{color+=parseInt(cols[i]).toColorPart()}while(++i<3);}else{if(this.slice(0,1)=='#'){if(this.length==4)for(var i=1;i<4;i++)color+=(this.charAt(i)+this.charAt(i)).toLowerCase();if(this.length==7)color=this.toLowerCase();}}
+return(color.length==7?color:(arguments[0]||this));}
+Element.collectTextNodes=function(element){return $A($(element).childNodes).collect(function(node){return(node.nodeType==3?node.nodeValue:(node.hasChildNodes()?Element.collectTextNodes(node):''));}).flatten().join('');}
+Element.collectTextNodesIgnoreClass=function(element,className){return $A($(element).childNodes).collect(function(node){return(node.nodeType==3?node.nodeValue:((node.hasChildNodes()&&!Element.hasClassName(node,className))?Element.collectTextNodes(node):''));}).flatten().join('');}
+Element.setStyle=function(element,style){element=$(element);for(k in style)element.style[k.camelize()]=style[k];}
+Element.setContentZoom=function(element,percent){Element.setStyle(element,{fontSize:(percent/100)+'em'});if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);}
+Element.getOpacity=function(element){var opacity;if(opacity=Element.getStyle(element,'opacity'))
+return parseFloat(opacity);if(opacity=(Element.getStyle(element,'filter')||'').match(/alpha\(opacity=(.*)\)/))
+if(opacity[1])return parseFloat(opacity[1])/100;return 1.0;}
+Element.setOpacity=function(element,value){element=$(element);if(value==1){Element.setStyle(element,{opacity:(/Gecko/.test(navigator.userAgent)&&!/Konqueror|Safari|KHTML/.test(navigator.userAgent))?0.999999:null});if(/MSIE/.test(navigator.userAgent))
+Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});}else{if(value<0.00001)value=0;Element.setStyle(element,{opacity:value});if(/MSIE/.test(navigator.userAgent))
+Element.setStyle(element,{filter:Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')+'alpha(opacity='+value*100+')'});}}
+Element.getInlineOpacity=function(element){return $(element).style.opacity||'';}
+Element.childrenWithClassName=function(element,className){return $A($(element).getElementsByTagName('*')).select(function(c){return Element.hasClassName(c,className)});}
+Array.prototype.call=function(){var args=arguments;this.each(function(f){f.apply(this,args)});}
+var Effect={tagifyText:function(element){var tagifyStyle='position:relative';if(/MSIE/.test(navigator.userAgent))tagifyStyle+=';zoom:1';element=$(element);$A(element.childNodes).each(function(child){if(child.nodeType==3){child.nodeValue.toArray().each(function(character){element.insertBefore(Builder.node('span',{style:tagifyStyle},character==' '?String.fromCharCode(160):character),child);});Element.remove(child);}});},multiple:function(element,effect){var elements;if(((typeof element=='object')||(typeof element=='function'))&&(element.length))
+elements=element;else
+elements=$(element).childNodes;var options=Object.extend({speed:0.1,delay:0.0},arguments[2]||{});var masterDelay=options.delay;$A(elements).each(function(element,index){new effect(element,Object.extend(options,{delay:index*options.speed+masterDelay}));});},PAIRS:{'slide':['SlideDown','SlideUp'],'blind':['BlindDown','BlindUp'],'appear':['Appear','Fade']},toggle:function(element,effect){element=$(element);effect=(effect||'appear').toLowerCase();var options=Object.extend({queue:{position:'end',scope:(element.id||'global')}},arguments[2]||{});Effect[Element.visible(element)?Effect.PAIRS[effect][1]:Effect.PAIRS[effect][0]](element,options);}};var Effect2=Effect;Effect.Transitions={}
+Effect.Transitions.linear=function(pos){return pos;}
+Effect.Transitions.sinoidal=function(pos){return(-Math.cos(pos*Math.PI)/2)+0.5;}
+Effect.Transitions.reverse=function(pos){return 1-pos;}
+Effect.Transitions.flicker=function(pos){return((-Math.cos(pos*Math.PI)/4)+0.75)+Math.random()/4;}
+Effect.Transitions.wobble=function(pos){return(-Math.cos(pos*Math.PI*(9*pos))/2)+0.5;}
+Effect.Transitions.pulse=function(pos){return(Math.floor(pos*10)%2==0?(pos*10-Math.floor(pos*10)):1-(pos*10-Math.floor(pos*10)));}
+Effect.Transitions.none=function(pos){return 0;}
+Effect.Transitions.full=function(pos){return 1;}
+Effect.ScopedQueue=Class.create();Object.extend(Object.extend(Effect.ScopedQueue.prototype,Enumerable),{initialize:function(){this.effects=[];this.interval=null;},_each:function(iterator){this.effects._each(iterator);},add:function(effect){var timestamp=new Date().getTime();var position=(typeof effect.options.queue=='string')?effect.options.queue:effect.options.queue.position;switch(position){case'front':this.effects.findAll(function(e){return e.state=='idle'}).each(function(e){e.startOn+=effect.finishOn;e.finishOn+=effect.finishOn;});break;case'end':timestamp=this.effects.pluck('finishOn').max()||timestamp;break;}
+effect.startOn+=timestamp;effect.finishOn+=timestamp;this.effects.push(effect);if(!this.interval)
+this.interval=setInterval(this.loop.bind(this),40);},remove:function(effect){this.effects=this.effects.reject(function(e){return e==effect});if(this.effects.length==0){clearInterval(this.interval);this.interval=null;}},loop:function(){var timePos=new Date().getTime();this.effects.invoke('loop',timePos);}});Effect.Queues={instances:$H(),get:function(queueName){if(typeof queueName!='string')return queueName;if(!this.instances[queueName])
+this.instances[queueName]=new Effect.ScopedQueue();return this.instances[queueName];}}
+Effect.Queue=Effect.Queues.get('global');Effect.DefaultOptions={transition:Effect.Transitions.sinoidal,duration:1.0,fps:25.0,sync:false,from:0.0,to:1.0,delay:0.0,queue:'parallel'}
+Effect.Base=function(){};Effect.Base.prototype={position:null,start:function(options){this.options=Object.extend(Object.extend({},Effect.DefaultOptions),options||{});this.currentFrame=0;this.state='idle';this.startOn=this.options.delay*1000;this.finishOn=this.startOn+(this.options.duration*1000);this.event('beforeStart');if(!this.options.sync)
+Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).add(this);},loop:function(timePos){if(timePos>=this.startOn){if(timePos>=this.finishOn){this.render(1.0);this.cancel();this.event('beforeFinish');if(this.finish)this.finish();this.event('afterFinish');return;}
+var pos=(timePos-this.startOn)/(this.finishOn-this.startOn);var frame=Math.round(pos*this.options.fps*this.options.duration);if(frame>this.currentFrame){this.render(pos);this.currentFrame=frame;}}},render:function(pos){if(this.state=='idle'){this.state='running';this.event('beforeSetup');if(this.setup)this.setup();this.event('afterSetup');}
+if(this.state=='running'){if(this.options.transition)pos=this.options.transition(pos);pos*=(this.options.to-this.options.from);pos+=this.options.from;this.position=pos;this.event('beforeUpdate');if(this.update)this.update(pos);this.event('afterUpdate');}},cancel:function(){if(!this.options.sync)
+Effect.Queues.get(typeof this.options.queue=='string'?'global':this.options.queue.scope).remove(this);this.state='finished';},event:function(eventName){if(this.options[eventName+'Internal'])this.options[eventName+'Internal'](this);if(this.options[eventName])this.options[eventName](this);},inspect:function(){return'#<Effect:'+$H(this).inspect()+',options:'+$H(this.options).inspect()+'>';}}
+Effect.Parallel=Class.create();Object.extend(Object.extend(Effect.Parallel.prototype,Effect.Base.prototype),{initialize:function(effects){this.effects=effects||[];this.start(arguments[1]);},update:function(position){this.effects.invoke('render',position);},finish:function(position){this.effects.each(function(effect){effect.render(1.0);effect.cancel();effect.event('beforeFinish');if(effect.finish)effect.finish(position);effect.event('afterFinish');});}});Effect.Opacity=Class.create();Object.extend(Object.extend(Effect.Opacity.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);if(/MSIE/.test(navigator.userAgent)&&(!this.element.hasLayout))
+Element.setStyle(this.element,{zoom:1});var options=Object.extend({from:Element.getOpacity(this.element)||0.0,to:1.0},arguments[1]||{});this.start(options);},update:function(position){Element.setOpacity(this.element,position);}});Effect.Move=Class.create();Object.extend(Object.extend(Effect.Move.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);var options=Object.extend({x:0,y:0,mode:'relative'},arguments[1]||{});this.start(options);},setup:function(){Element.makePositioned(this.element);this.originalLeft=parseFloat(Element.getStyle(this.element,'left')||'0');this.originalTop=parseFloat(Element.getStyle(this.element,'top')||'0');if(this.options.mode=='absolute'){this.options.x=this.options.x-this.originalLeft;this.options.y=this.options.y-this.originalTop;}},update:function(position){Element.setStyle(this.element,{left:this.options.x*position+this.originalLeft+'px',top:this.options.y*position+this.originalTop+'px'});}});Effect.MoveBy=function(element,toTop,toLeft){return new Effect.Move(element,Object.extend({x:toLeft,y:toTop},arguments[3]||{}));};Effect.Scale=Class.create();Object.extend(Object.extend(Effect.Scale.prototype,Effect.Base.prototype),{initialize:function(element,percent){this.element=$(element)
+var options=Object.extend({scaleX:true,scaleY:true,scaleContent:true,scaleFromCenter:false,scaleMode:'box',scaleFrom:100.0,scaleTo:percent},arguments[2]||{});this.start(options);},setup:function(){this.restoreAfterFinish=this.options.restoreAfterFinish||false;this.elementPositioning=Element.getStyle(this.element,'position');this.originalStyle={};['top','left','width','height','fontSize'].each(function(k){this.originalStyle[k]=this.element.style[k];}.bind(this));this.originalTop=this.element.offsetTop;this.originalLeft=this.element.offsetLeft;var fontSize=Element.getStyle(this.element,'font-size')||'100%';['em','px','%'].each(function(fontSizeType){if(fontSize.indexOf(fontSizeType)>0){this.fontSize=parseFloat(fontSize);this.fontSizeType=fontSizeType;}}.bind(this));this.factor=(this.options.scaleTo-this.options.scaleFrom)/100;this.dims=null;if(this.options.scaleMode=='box')
+this.dims=[this.element.offsetHeight,this.element.offsetWidth];if(/^content/.test(this.options.scaleMode))
+this.dims=[this.element.scrollHeight,this.element.scrollWidth];if(!this.dims)
+this.dims=[this.options.scaleMode.originalHeight,this.options.scaleMode.originalWidth];},update:function(position){var currentScale=(this.options.scaleFrom/100.0)+(this.factor*position);if(this.options.scaleContent&&this.fontSize)
+Element.setStyle(this.element,{fontSize:this.fontSize*currentScale+this.fontSizeType});this.setDimensions(this.dims[0]*currentScale,this.dims[1]*currentScale);},finish:function(position){if(this.restoreAfterFinish)Element.setStyle(this.element,this.originalStyle);},setDimensions:function(height,width){var d={};if(this.options.scaleX)d.width=width+'px';if(this.options.scaleY)d.height=height+'px';if(this.options.scaleFromCenter){var topd=(height-this.dims[0])/2;var leftd=(width-this.dims[1])/2;if(this.elementPositioning=='absolute'){if(this.options.scaleY)d.top=this.originalTop-topd+'px';if(this.options.scaleX)d.left=this.originalLeft-leftd+'px';}else{if(this.options.scaleY)d.top=-topd+'px';if(this.options.scaleX)d.left=-leftd+'px';}}
+Element.setStyle(this.element,d);}});Effect.Highlight=Class.create();Object.extend(Object.extend(Effect.Highlight.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);var options=Object.extend({startcolor:'#ffff99'},arguments[1]||{});this.start(options);},setup:function(){if(Element.getStyle(this.element,'display')=='none'){this.cancel();return;}
+this.oldStyle={backgroundImage:Element.getStyle(this.element,'background-image')};Element.setStyle(this.element,{backgroundImage:'none'});if(!this.options.endcolor)
+this.options.endcolor=Element.getStyle(this.element,'background-color').parseColor('#ffffff');if(!this.options.restorecolor)
+this.options.restorecolor=Element.getStyle(this.element,'background-color');this._base=$R(0,2).map(function(i){return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16)}.bind(this));this._delta=$R(0,2).map(function(i){return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i]}.bind(this));},update:function(position){Element.setStyle(this.element,{backgroundColor:$R(0,2).inject('#',function(m,v,i){return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart());}.bind(this))});},finish:function(){Element.setStyle(this.element,Object.extend(this.oldStyle,{backgroundColor:this.options.restorecolor}));}});Effect.ScrollTo=Class.create();Object.extend(Object.extend(Effect.ScrollTo.prototype,Effect.Base.prototype),{initialize:function(element){this.element=$(element);this.start(arguments[1]||{});},setup:function(){Position.prepare();var offsets=Position.cumulativeOffset(this.element);if(this.options.offset)offsets[1]+=this.options.offset;var max=window.innerHeight?window.height-window.innerHeight:document.body.scrollHeight-
+(document.documentElement.clientHeight?document.documentElement.clientHeight:document.body.clientHeight);this.scrollStart=Position.deltaY;this.delta=(offsets[1]>max?max:offsets[1])-this.scrollStart;},update:function(position){Position.prepare();window.scrollTo(Position.deltaX,this.scrollStart+(position*this.delta));}});Effect.Fade=function(element){var oldOpacity=Element.getInlineOpacity(element);var options=Object.extend({from:Element.getOpacity(element)||1.0,to:0.0,afterFinishInternal:function(effect){with(Element){if(effect.options.to!=0)return;hide(effect.element);setStyle(effect.element,{opacity:oldOpacity});}}},arguments[1]||{});return new Effect.Opacity(element,options);}
+Effect.Appear=function(element){var options=Object.extend({from:(Element.getStyle(element,'display')=='none'?0.0:Element.getOpacity(element)||0.0),to:1.0,beforeSetup:function(effect){with(Element){setOpacity(effect.element,effect.options.from);show(effect.element);}}},arguments[1]||{});return new Effect.Opacity(element,options);}
+Effect.Puff=function(element){element=$(element);var oldStyle={opacity:Element.getInlineOpacity(element),position:Element.getStyle(element,'position')};return new Effect.Parallel([new Effect.Scale(element,200,{sync:true,scaleFromCenter:true,scaleContent:true,restoreAfterFinish:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:1.0,beforeSetupInternal:function(effect){with(Element){setStyle(effect.effects[0].element,{position:'absolute'});}},afterFinishInternal:function(effect){with(Element){hide(effect.effects[0].element);setStyle(effect.effects[0].element,oldStyle);}}},arguments[1]||{}));}
+Effect.BlindUp=function(element){element=$(element);Element.makeClipping(element);return new Effect.Scale(element,0,Object.extend({scaleContent:false,scaleX:false,restoreAfterFinish:true,afterFinishInternal:function(effect){with(Element){[hide,undoClipping].call(effect.element);}}},arguments[1]||{}));}
+Effect.BlindDown=function(element){element=$(element);var oldHeight=Element.getStyle(element,'height');var elementDimensions=Element.getDimensions(element);return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){with(Element){makeClipping(effect.element);setStyle(effect.element,{height:'0px'});show(effect.element);}},afterFinishInternal:function(effect){with(Element){undoClipping(effect.element);setStyle(effect.element,{height:oldHeight});}}},arguments[1]||{}));}
+Effect.SwitchOff=function(element){element=$(element);var oldOpacity=Element.getInlineOpacity(element);return new Effect.Appear(element,{duration:0.4,from:0,transition:Effect.Transitions.flicker,afterFinishInternal:function(effect){new Effect.Scale(effect.element,1,{duration:0.3,scaleFromCenter:true,scaleX:false,scaleContent:false,restoreAfterFinish:true,beforeSetup:function(effect){with(Element){[makePositioned,makeClipping].call(effect.element);}},afterFinishInternal:function(effect){with(Element){[hide,undoClipping,undoPositioned].call(effect.element);setStyle(effect.element,{opacity:oldOpacity});}}})}});}
+Effect.DropOut=function(element){element=$(element);var oldStyle={top:Element.getStyle(element,'top'),left:Element.getStyle(element,'left'),opacity:Element.getInlineOpacity(element)};return new Effect.Parallel([new Effect.Move(element,{x:0,y:100,sync:true}),new Effect.Opacity(element,{sync:true,to:0.0})],Object.extend({duration:0.5,beforeSetup:function(effect){with(Element){makePositioned(effect.effects[0].element);}},afterFinishInternal:function(effect){with(Element){[hide,undoPositioned].call(effect.effects[0].element);setStyle(effect.effects[0].element,oldStyle);}}},arguments[1]||{}));}
+Effect.Shake=function(element){element=$(element);var oldStyle={top:Element.getStyle(element,'top'),left:Element.getStyle(element,'left')};return new Effect.Move(element,{x:20,y:0,duration:0.05,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:40,y:0,duration:0.1,afterFinishInternal:function(effect){new Effect.Move(effect.element,{x:-20,y:0,duration:0.05,afterFinishInternal:function(effect){with(Element){undoPositioned(effect.element);setStyle(effect.element,oldStyle);}}})}})}})}})}})}});}
+Effect.SlideDown=function(element){element=$(element);Element.cleanWhitespace(element);var oldInnerBottom=Element.getStyle(element.firstChild,'bottom');var elementDimensions=Element.getDimensions(element);return new Effect.Scale(element,100,Object.extend({scaleContent:false,scaleX:false,scaleFrom:0,scaleMode:{originalHeight:elementDimensions.height,originalWidth:elementDimensions.width},restoreAfterFinish:true,afterSetup:function(effect){with(Element){makePositioned(effect.element);makePositioned(effect.element.firstChild);if(window.opera)setStyle(effect.element,{top:''});makeClipping(effect.element);setStyle(effect.element,{height:'0px'});show(element);}},afterUpdateInternal:function(effect){with(Element){setStyle(effect.element.firstChild,{bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});}},afterFinishInternal:function(effect){with(Element){undoClipping(effect.element);undoPositioned(effect.element.firstChild);undoPositioned(effect.element);setStyle(effect.element.firstChild,{bottom:oldInnerBottom});}}},arguments[1]||{}));}
+Effect.SlideUp=function(element){element=$(element);Element.cleanWhitespace(element);var oldInnerBottom=Element.getStyle(element.firstChild,'bottom');return new Effect.Scale(element,0,Object.extend({scaleContent:false,scaleX:false,scaleMode:'box',scaleFrom:100,restoreAfterFinish:true,beforeStartInternal:function(effect){with(Element){makePositioned(effect.element);makePositioned(effect.element.firstChild);if(window.opera)setStyle(effect.element,{top:''});makeClipping(effect.element);show(element);}},afterUpdateInternal:function(effect){with(Element){setStyle(effect.element.firstChild,{bottom:(effect.dims[0]-effect.element.clientHeight)+'px'});}},afterFinishInternal:function(effect){with(Element){[hide,undoClipping].call(effect.element);undoPositioned(effect.element.firstChild);undoPositioned(effect.element);setStyle(effect.element.firstChild,{bottom:oldInnerBottom});}}},arguments[1]||{}));}
+Effect.Squish=function(element){return new Effect.Scale(element,window.opera?1:0,{restoreAfterFinish:true,beforeSetup:function(effect){with(Element){makeClipping(effect.element);}},afterFinishInternal:function(effect){with(Element){hide(effect.element);undoClipping(effect.element);}}});}
+Effect.Grow=function(element){element=$(element);var options=Object.extend({direction:'center',moveTransistion:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.full},arguments[1]||{});var oldStyle={top:element.style.top,left:element.style.left,height:element.style.height,width:element.style.width,opacity:Element.getInlineOpacity(element)};var dims=Element.getDimensions(element);var initialMoveX,initialMoveY;var moveX,moveY;switch(options.direction){case'top-left':initialMoveX=initialMoveY=moveX=moveY=0;break;case'top-right':initialMoveX=dims.width;initialMoveY=moveY=0;moveX=-dims.width;break;case'bottom-left':initialMoveX=moveX=0;initialMoveY=dims.height;moveY=-dims.height;break;case'bottom-right':initialMoveX=dims.width;initialMoveY=dims.height;moveX=-dims.width;moveY=-dims.height;break;case'center':initialMoveX=dims.width/2;initialMoveY=dims.height/2;moveX=-dims.width/2;moveY=-dims.height/2;break;}
+return new Effect.Move(element,{x:initialMoveX,y:initialMoveY,duration:0.01,beforeSetup:function(effect){with(Element){hide(effect.element);makeClipping(effect.element);makePositioned(effect.element);}},afterFinishInternal:function(effect){new Effect.Parallel([new Effect.Opacity(effect.element,{sync:true,to:1.0,from:0.0,transition:options.opacityTransition}),new Effect.Move(effect.element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition}),new Effect.Scale(effect.element,100,{scaleMode:{originalHeight:dims.height,originalWidth:dims.width},sync:true,scaleFrom:window.opera?1:0,transition:options.scaleTransition,restoreAfterFinish:true})],Object.extend({beforeSetup:function(effect){with(Element){setStyle(effect.effects[0].element,{height:'0px'});show(effect.effects[0].element);}},afterFinishInternal:function(effect){with(Element){[undoClipping,undoPositioned].call(effect.effects[0].element);setStyle(effect.effects[0].element,oldStyle);}}},options))}});}
+Effect.Shrink=function(element){element=$(element);var options=Object.extend({direction:'center',moveTransistion:Effect.Transitions.sinoidal,scaleTransition:Effect.Transitions.sinoidal,opacityTransition:Effect.Transitions.none},arguments[1]||{});var oldStyle={top:element.style.top,left:element.style.left,height:element.style.height,width:element.style.width,opacity:Element.getInlineOpacity(element)};var dims=Element.getDimensions(element);var moveX,moveY;switch(options.direction){case'top-left':moveX=moveY=0;break;case'top-right':moveX=dims.width;moveY=0;break;case'bottom-left':moveX=0;moveY=dims.height;break;case'bottom-right':moveX=dims.width;moveY=dims.height;break;case'center':moveX=dims.width/2;moveY=dims.height/2;break;}
+return new Effect.Parallel([new Effect.Opacity(element,{sync:true,to:0.0,from:1.0,transition:options.opacityTransition}),new Effect.Scale(element,window.opera?1:0,{sync:true,transition:options.scaleTransition,restoreAfterFinish:true}),new Effect.Move(element,{x:moveX,y:moveY,sync:true,transition:options.moveTransition})],Object.extend({beforeStartInternal:function(effect){with(Element){[makePositioned,makeClipping].call(effect.effects[0].element)}},afterFinishInternal:function(effect){with(Element){[hide,undoClipping,undoPositioned].call(effect.effects[0].element);setStyle(effect.effects[0].element,oldStyle);}}},options));}
+Effect.Pulsate=function(element){element=$(element);var options=arguments[1]||{};var oldOpacity=Element.getInlineOpacity(element);var transition=options.transition||Effect.Transitions.sinoidal;var reverser=function(pos){return transition(1-Effect.Transitions.pulse(pos))};reverser.bind(transition);return new Effect.Opacity(element,Object.extend(Object.extend({duration:3.0,from:0,afterFinishInternal:function(effect){Element.setStyle(effect.element,{opacity:oldOpacity});}},options),{transition:reverser}));}
+Effect.Fold=function(element){element=$(element);var oldStyle={top:element.style.top,left:element.style.left,width:element.style.width,height:element.style.height};Element.makeClipping(element);return new Effect.Scale(element,5,Object.extend({scaleContent:false,scaleX:false,afterFinishInternal:function(effect){new Effect.Scale(element,1,{scaleContent:false,scaleY:false,afterFinishInternal:function(effect){with(Element){[hide,undoClipping].call(effect.element);setStyle(effect.element,oldStyle);}}});}},arguments[1]||{}));}
\ No newline at end of file
--- /dev/null
+/* Prototype JavaScript framework, version 1.6.0.2
+ * (c) 2005-2008 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0.2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div').__proto__ &&
+ document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__
+ },
+
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value, value = Object.extend((function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method), {
+ valueOf: function() { return method },
+ toString: function() { return method.toString() }
+ });
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (Object.isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (!Object.isUndefined(value))
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return object && object.nodeType == 1;
+ },
+
+ isArray: function(object) {
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
+ },
+
+ isHash: function(object) {
+ return object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this;
+ if (str.blank()) return false;
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+ escapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ },
+ unescapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ iterator = iterator.bind(context);
+ try {
+ this._each(function(value) {
+ iterator(value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ iterator = iterator.bind(context);
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ iterator = iterator.bind(context);
+ return this.map(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = function(iterable) {
+ if (!iterable) return [];
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ return this.uniq().findAll(function(item) {
+ return array.detect(function(value) { return item === value });
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (!Object.isUndefined(value)) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: function(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return values.map(toQueryPair.curry(key)).join('&');
+ }
+ return toQueryPair(key, values);
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ else if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && this.isSameOrigin() && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank())
+ return null;
+ try {
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, insert, tagName, childNodes;
+
+ for (var position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ insert = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $(element).select("*");
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ element.select(expression)[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ var t = Element._attributeTranslations.read;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ }
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = Object.isUndefined(value) ? true : value;
+
+ for (var attr in attributes) {
+ name = t.names[attr] || attr;
+ value = attributes[attr];
+ if (t.values[attr]) name = t.values[attr](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ var originalAncestor = ancestor;
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (element.sourceIndex && !Prototype.Browser.Opera) {
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
+ nextAncestor = ancestor.nextSibling;
+ if (!nextAncestor) {
+ do { ancestor = ancestor.parentNode; }
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+ }
+ if (nextAncestor && nextAncestor.sourceIndex)
+ return (e > a && e < nextAncestor.sourceIndex);
+ }
+
+ while (element = element.parentNode)
+ if (element == originalAncestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset();
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ return element;
+ },
+
+ cumulativeScrollOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ getOffsetParent: function(element) {
+ element = $(element);
+ var op = element.offsetParent, body = document.body, docEl = document.documentElement;
+
+ /* IE with strict doctype may try to return documentElement as offsetParent
+ on relatively positioned elements, we will return body instead */
+ if (op && op !== docEl) return $(op);
+ if (op === docEl || element === docEl || element === body) return $(body);
+
+ while ((element = element.parentNode) && element !== body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+
+ return $(body);
+ },
+
+ viewportOffset: function(forElement) {
+ forElement = $(forElement);
+
+ var element = forElement, valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ } while ((element = element.getOffsetParent()) != document.body);
+
+ element = forElement;
+ do {
+ if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find page position of source
+ source = $(source);
+ var p = source.viewportOffset();
+
+ // find coordinate system to use
+ element = $(element);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = element.getOffsetParent();
+ delta = parent.viewportOffset();
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+ return element;
+ }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ }
+};
+
+if (Prototype.Browser.Opera) {
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+ function(proceed, element, style) {
+ switch (style) {
+ case 'left': case 'top': case 'right': case 'bottom':
+ if (proceed(element, 'position') === 'static') return null;
+ case 'height': case 'width':
+ // returns '0px' for hidden elements; we want it to return null
+ if (!Element.visible(element)) return null;
+
+ // returns the border-box dimensions rather than the content-box
+ // dimensions, so we subtract padding and borders from the value
+ var dim = parseInt(proceed(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()])
+ return dim + 'px';
+
+ var properties;
+ if (style === 'height') {
+ properties = ['border-top-width', 'padding-top',
+ 'padding-bottom', 'border-bottom-width'];
+ }
+ else {
+ properties = ['border-left-width', 'padding-left',
+ 'padding-right', 'border-right-width'];
+ }
+ return properties.inject(dim, function(memo, property) {
+ var val = proceed(element, property);
+ return val === null ? memo : memo - parseInt(val, 10);
+ }) + 'px';
+ default: return proceed(element, style);
+ }
+ }
+ );
+
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+ function(proceed, element, attribute) {
+ if (attribute === 'title') return element.title;
+ return proceed(element, attribute);
+ }
+ );
+}
+
+else if (Prototype.Browser.IE) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+ });
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ Element._attributeTranslations = {
+ read: {
+ names: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
+ }
+ }
+ };
+
+ Element._attributeTranslations.write = {
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
+ values: {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ Element._attributeTranslations.has = {};
+
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+ 'encType maxLength readOnly longDesc').each(function(attr) {
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+ });
+
+ (function(v) {
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if(element.tagName == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ if (Element._insertionTranslations.tags[tagName]) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function(element, node) {
+ element.appendChild(node);
+ },
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName, property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { };
+ var B = Prototype.Browser;
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+ (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
+ },
+
+ shouldUseXPath: function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
+
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+ return false;
+
+ return true;
+ },
+
+ compileMatcher: function() {
+ if (this.shouldUseXPath())
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: function(m) {
+ m[1] = m[1].toLowerCase();
+ return new Template("[@#{1}]").evaluate(m);
+ },
+ attr: function(m) {
+ m[1] = m[1].toLowerCase();
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = _true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._countedByPrototype = Prototype.emptyFunction;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ var uTagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) return [];
+ if (!nodes && root == document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._countedByPrototype) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._countedByPrototype) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ },
+
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = $$(expression), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._countedByPrototype) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ expressions = Selector.split(expressions.join(','));
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+if (Prototype.Browser.IE) {
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
+}
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+ var key, value, submitted = false, submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name; value = $(element).getValue();
+ if (value != null && (element.type != 'submit' || (!submitted &&
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = { };
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function(element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ textarea: function(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, index) {
+ if (Object.isUndefined(index))
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, value, single = !Object.isArray(index);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ value = this.optionValue(opt);
+ if (single) {
+ if (value == index) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = index.include(value);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ var node = Event.extend(event).target;
+ return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ var elements = [element].concat(element.ancestors());
+ return Selector.findElement(elements, expression, 0);
+ },
+
+ pointer: function(event) {
+ return {
+ x: event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft)),
+ y: event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ event._extendedByPrototype = Prototype.emptyFunction;
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ target: event.srcElement,
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ if (element._prototypeEventID) return element._prototypeEventID[0];
+ arguments.callee.id = arguments.callee.id || 1;
+ return element._prototypeEventID = [++arguments.callee.id];
+ }
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element);
+ var c = getWrappersForEventName(id, eventName);
+ if (c.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ Event.extend(event);
+ handler.call(element, event);
+ };
+
+ wrapper.handler = handler;
+ c.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var c = getWrappersForEventName(id, eventName);
+ return c.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ }
+
+ function destroyCache() {
+ for (var id in cache)
+ for (var eventName in cache[id])
+ cache[id][eventName] = null;
+ }
+
+ if (window.attachEvent) {
+ window.attachEvent("onunload", destroyCache);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ element.attachEvent("on" + name, wrapper);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ var id = getEventID(element), name = getDOMEventName(eventName);
+
+ if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ element.stopObserving(eventName, wrapper.handler);
+ });
+ return element;
+
+ } else if (!eventName) {
+ Object.keys(getCacheForID(id)).each(function(eventName) {
+ element.stopObserving(eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ } else {
+ element.detachEvent("on" + name, wrapper);
+ }
+
+ destroyWrapper(id, eventName, handler);
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return Event.extend(event);
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards and John Resig. */
+
+ var timer;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (timer) window.clearInterval(timer);
+ document.fire("dom:loaded");
+ document.loaded = true;
+ }
+
+ if (document.addEventListener) {
+ if (Prototype.Browser.WebKit) {
+ timer = window.setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 0);
+
+ Event.observe(window, "load", fireContentLoadedEvent);
+
+ } else {
+ document.addEventListener("DOMContentLoaded",
+ fireContentLoadedEvent, false);
+ }
+
+ } else {
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+ $("__onDOMContentLoaded").onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ this.onreadystatechange = null;
+ fireContentLoadedEvent();
+ }
+ };
+ }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file
--- /dev/null
+/**
+ * Redbox.js
+ *
+ * $Horde: horde/js/src/redbox.js,v 1.10 2008/02/06 09:02:37 slusarz Exp $
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+var RedBox = {
+
+ overlay: true,
+ onDisplay: null,
+
+ showInline: function(id)
+ {
+ this.appearWindow();
+ this.cloneWindowContents(id);
+ },
+
+ showHtml: function(html)
+ {
+ this.appearWindow();
+ this.htmlWindowContents(html);
+ },
+
+ appearWindow: function()
+ {
+ var loading = $('RB_loading');
+ if (loading && loading.visible()) {
+ loading.hide();
+ } else {
+ this.showOverlay();
+ }
+ var opts = { duration: 0.4, queue: 'end' },
+ w = $('RB_window');
+ if (this.onDisplay) {
+ opts.afterFinish = this.onDisplay;
+ }
+ new Effect.Appear(w, opts);
+ w.scrollTo();
+ },
+
+ loading: function()
+ {
+ this.showOverlay();
+ var rl = $('RB_loading');
+ if (rl) {
+ rl.show();
+ }
+ this.setWindowPosition();
+ },
+
+ close: function()
+ {
+ new Effect.Fade('RB_window', { duration: 0.4 });
+ if (this.overlay) {
+ new Effect.Fade('RB_overlay', { duration: 0.4 });
+ }
+ },
+
+ showOverlay: function()
+ {
+ var rb = $('RB_redbox');
+ if (!rb) {
+ rb = new Element('DIV', { id: 'RB_redbox', align: 'center' });
+ $(document.body).insert(rb);
+
+ var ov = new Element('DIV', { id: 'RB_overlay' }).hide();
+ rb.insert({ top: new Element('DIV', { id: 'RB_window' }).hide() }).insert({ top: ov });
+
+ if (this.overlay) {
+ ov.insert({ top: new Element('DIV', { id: 'RB_loading' }).hide() });
+ }
+ }
+
+ if (this.overlay) {
+ this.setOverlaySize();
+ new Effect.Appear('RB_overlay', { duration: 0.4, to: 0.6, queue: 'end' });
+ }
+ },
+
+ setOverlaySize: function()
+ {
+ if (window.innerHeight && window.scrollMaxY) {
+ yScroll = window.innerHeight + window.scrollMaxY;
+ } else if (document.body.scrollHeight > document.body.offsetHeight) {
+ // all but Explorer Mac
+ yScroll = document.body.scrollHeight;
+ } else {
+ // Explorer Mac...would also work in Explorer 6 Strict, Mozilla
+ // and Safari
+ yScroll = document.body.offsetHeight;
+ }
+ Element.setStyle('RB_overlay', { height: yScroll + 'px' });
+ },
+
+ setWindowPosition: function()
+ {
+ var win = $('RB_window');
+ var d = win.getDimensions(),
+ v = document.viewport.getDimensions();
+ win.setStyle({ width: 'auto', height: 'auto', left: ((v.width - d.width) / 2) + 'px', top: ((v.height - d.height) / 2) + 'px' });
+ },
+
+ cloneWindowContents: function(id)
+ {
+ $('RB_window').appendChild($($(id).cloneNode(true)).setStyle({ display: 'block' }));
+ this.setWindowPosition();
+ },
+
+ htmlWindowContents: function(html)
+ {
+ $('RB_window').update(html);
+ this.setWindowPosition();
+ },
+
+ getWindowContents: function()
+ {
+ var w = $('RB_window');
+ return w.visible() ? w.down() : null;
+ },
+
+ overlayVisible: function()
+ {
+ var ov = $('RB_overlay');
+ return ov && ov.visible();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+class Translate_Display {
+
+ function header($msg, $msg2 = '') {
+ global $cnt_i, $registry;
+ $select_img = Horde::img('alerts/message.png', '', '', $registry->getImageDir('horde'));
+ print sprintf('<table cellspacing=0 cellpadding=0 width=100%%><tr><td class="header">%s</td><td align="right" class="header">%s</td><td class="header" width="20">%s</td></tr></table>', $msg, $msg2, $select_img);
+ flush();
+ }
+
+ function warning($msg, $bold = true) {
+ global $cnt_i, $registry;
+ $item = ($cnt_i++ % 2);
+ $select_img = Horde::img('alerts/warning.png', '', '', $registry->getImageDir('horde'));
+ if ($bold) {
+ print sprintf('<table cellspacing=0 cellpadding=0 width=100%%><tr><td class="item%d" style="color: #ff0000">%s</td><td class="item%1$d" width="20">%s</td></tr></table>', $item, $msg, $select_img);
+ } else {
+ print sprintf('<table cellspacing=0 cellpadding=0 width=100%%><tr><td class="item%d small" style="color: #ff0000">%s</td><td class="item%1$d" width="20">%s</td></tr></table>', $item, $msg, $select_img);
+ }
+ flush();
+ }
+
+ function error($msg) {
+ global $cnt_i, $registry;
+ $item = ($cnt_i++ % 2);
+ $select_img = Horde::img('alerts/error.png', '', '', $registry->getImageDir('horde'));
+ print sprintf('<table cellspacing=0 cellpadding=0 width=100%%><tr><td class="item%d" style="color: #ff0000"><b>%s</b></td><td class="item%1$d" width="20">%s</td></tr></table>', $item, $msg, $select_img);
+ flush();
+ }
+
+ function info($msg = "", $bold = true) {
+
+ global $cnt_i, $registry;
+
+ if (empty($msg)) {
+ echo "<br />";
+ } else {
+
+ $item = ($cnt_i++ % 2);
+
+ $select_img = Horde::img('select.png', '', '', $registry->getImageDir('horde'));
+ if ($bold) {
+ print sprintf('<table cellspacing=0 cellpadding=0 width=100%%><tr><td class="item%d"><b>%s</b></td><td class="item%1$d" width="20">%s</td></tr></table>', $item, $msg, $select_img);
+ } else {
+ print sprintf('<table cellspacing=0 cellpadding=0 width=100%%><tr><td class="item%d small">%s</td><td class="item%1$d" width="20">%s</td></tr></table>', $item, $msg, '');
+ }
+ flush();
+ }
+ }
+
+ function parseCharset($headers)
+ {
+ if (preg_match('/charset=(.*)/i', $headers, $m)) {
+ return $m[1];
+ }
+ return NLS::getCharset();
+ }
+
+ function convert_string($msg) {
+ global $po;
+
+ $f = array('/</', '/>/');
+ $t = array('<', '>');
+ $msg = preg_replace($f, $t, $msg);
+ return String::convertCharset(html_entity_decode($msg), NLS::getCharset(), Translate_Display::parseCharset($po->meta['Content-Type']));
+ }
+
+ function display_string($msg) {
+ global $po;
+
+ $f = array('/</', '/>/');
+ $t = array('<', '>');
+ $msg = preg_replace($f, $t, $msg);
+ return String::convertCharset($msg, Translate_Display::parseCharset($po->meta['Content-Type']), NLS::getCharset());
+ }
+
+ function get_percent($used, $total) {
+ if ($total > 0) {
+ $percent = sprintf("%2.2f", (($used * 100) / $total));
+ } else {
+ $percent = 0;
+ }
+
+ return $percent;
+ }
+
+ function create_bargraph ($used, $total, $text = true, $reverse = false, $small = false) {
+ if ($total > 0) {
+ $percent = round(($used * 100) / $total);
+ } else {
+ $percent = 0;
+ }
+
+ $html = '<table border="0" cellpadding="0" cellspacing="0"><tr><td nowrap="nowrap">';
+ $html .= '<table border="0" width="100" cellpadding="0" cellspacing="0">';
+ $html .= '<tr height="10">';
+
+ if ($percent > 0) {
+ $html .= '<td nowrap="nowrap" width="' . ($percent) . '" bgcolor="#00FF00"></td>';
+ }
+
+ if ($percent != 100) {
+ $html .= '<td nowrap="nowrap" width="' . (100 - $percent) . '" ';
+ if ($reverse) {
+ $html .= ' bgcolor="#FFFFFF"></td>';
+ } else {
+ $html .= ' bgcolor="#006699"></td>';
+ }
+ }
+
+ $html .= '</tr></table></td>';
+
+ if ($text) {
+ if ($small) {
+ $html .= '<td class="small"> ' . $percent .'% </td>';
+ } else {
+ $html .= '<td> ' . $percent .'% </td>';
+ }
+ }
+
+ $html .= '</tr></table>';
+
+ return $html;
+ }
+
+}
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * File::Gettext
+ *
+ * PHP versions 4 and 5
+ *
+ * @category FileFormats
+ * @package File_Gettext
+ * @author Michael Wallner <mike@php.net>
+ * @copyright 2004-2005 Michael Wallner
+ * @license BSD, revised
+ * @version CVS: $Id: Gettext.php,v 1.7 2005/11/08 18:57:03 mike Exp $
+ * @link http://pear.php.net/package/File_Gettext
+ */
+
+/**
+ * Use PHPs builtin error messages
+ */
+ini_set('track_errors', true);
+
+/**
+ * File_Gettext
+ *
+ * GNU gettext file reader and writer.
+ *
+ * #################################################################
+ * # All protected members of this class are public in its childs. #
+ * #################################################################
+ *
+ * @author Michael Wallner <mike@php.net>
+ * @version $Revision: 1.7 $
+ * @access public
+ */
+class File_Gettext
+{
+ /**
+ * strings
+ *
+ * associative array with all [msgid => msgstr] entries
+ *
+ * @access protected
+ * @var array
+ */
+ var $strings = array();
+ var $encstr = array();
+ var $refs = array();
+ var $status = array();
+ var $comments = array();
+
+ /**
+ * meta
+ *
+ * associative array containing meta
+ * information like project name or content type
+ *
+ * @access protected
+ * @var array
+ */
+ var $meta = array();
+
+ /**
+ * file path
+ *
+ * @access protected
+ * @var string
+ */
+ var $file = '';
+
+ /**
+ * Factory
+ *
+ * @static
+ * @access public
+ * @return object Returns File_Gettext_PO or File_Gettext_MO on success
+ * or PEAR_Error on failure.
+ * @param string $format MO or PO
+ * @param string $file path to GNU gettext file
+ */
+ function &factory($format, $file = '')
+ {
+ $format = strToUpper($format);
+ if (!@include_once 'File/Gettext/' . $format . '.php') {
+ return File_Gettext::raiseError($php_errormsg);
+ }
+ $class = 'File_Gettext_' . $format;
+ $obref = &new $class($file);
+ return $obref;
+ }
+
+ /**
+ * poFile2moFile
+ *
+ * That's a simple fake of the 'msgfmt' console command. It reads the
+ * contents of a GNU PO file and saves them to a GNU MO file.
+ *
+ * @static
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $pofile path to GNU PO file
+ * @param string $mofile path to GNU MO file
+ */
+ function poFile2moFile($pofile, $mofile)
+ {
+ if (!is_file($pofile)) {
+ return File_Gettext::raiseError("File $pofile doesn't exist.");
+ }
+
+ include_once 'File/Gettext/PO.php';
+
+ $PO = &new File_Gettext_PO($pofile);
+ if (true !== ($e = $PO->load())) {
+ return $e;
+ }
+
+ $MO = &$PO->toMO();
+ if (true !== ($e = $MO->save($mofile))) {
+ return $e;
+ }
+ unset($PO, $MO);
+
+ return true;
+ }
+
+ /**
+ * prepare
+ *
+ * @static
+ * @access protected
+ * @return string
+ * @param string $string
+ * @param bool $reverse
+ */
+ function prepare($string, $reverse = false)
+ {
+ if ($reverse) {
+ $smap = array('"', "\n", "\t", "\r");
+ $rmap = array('\\"', '\\n"' . "\n" . '"', '\\t', '\\r');
+ return (string) str_replace($smap, $rmap, $string);
+ } else {
+ $smap = array('/"\s+"/', '/\\\\n/', '/\\\\r/', '/\\\\t/', '/\\\\"/');
+ $rmap = array('', "\n", "\r", "\t", '"');
+ return (string) preg_replace($smap, $rmap, $string);
+ }
+ }
+
+ /**
+ * meta2array
+ *
+ * @static
+ * @access public
+ * @return array
+ * @param string $meta
+ */
+ function meta2array($meta)
+ {
+ $array = array();
+ foreach (explode("\n", $meta) as $info) {
+ if ($info = trim($info)) {
+ list($key, $value) = explode(':', $info, 2);
+ $array[trim($key)] = trim($value);
+ }
+ }
+ return $array;
+ }
+
+ /**
+ * toArray
+ *
+ * Returns meta info and strings as an array of a structure like that:
+ * <code>
+ * array(
+ * 'meta' => array(
+ * 'Content-Type' => 'text/plain; charset=iso-8859-1',
+ * 'Last-Translator' => 'Michael Wallner <mike@iworks.at>',
+ * 'PO-Revision-Date' => '2004-07-21 17:03+0200',
+ * 'Language-Team' => 'German <mail@example.com>',
+ * ),
+ * 'strings' => array(
+ * 'All rights reserved' => 'Alle Rechte vorbehalten',
+ * 'Welcome' => 'Willkommen',
+ * // ...
+ * )
+ * )
+ * </code>
+ *
+ * @see fromArray()
+ * @access protected
+ * @return array
+ */
+ function toArray()
+ {
+ return array('meta' => $this->meta, 'strings' => $this->strings);
+ }
+
+ /**
+ * fromArray
+ *
+ * Assigns meta info and strings from an array of a structure like that:
+ * <code>
+ * array(
+ * 'meta' => array(
+ * 'Content-Type' => 'text/plain; charset=iso-8859-1',
+ * 'Last-Translator' => 'Michael Wallner <mike@iworks.at>',
+ * 'PO-Revision-Date' => date('Y-m-d H:iO'),
+ * 'Language-Team' => 'German <mail@example.com>',
+ * ),
+ * 'strings' => array(
+ * 'All rights reserved' => 'Alle Rechte vorbehalten',
+ * 'Welcome' => 'Willkommen',
+ * // ...
+ * )
+ * )
+ * </code>
+ *
+ * @see toArray()
+ * @access protected
+ * @return bool
+ * @param array $array
+ */
+ function fromArray($array)
+ {
+ if (!array_key_exists('strings', $array)) {
+ if (count($array) != 2) {
+ return false;
+ } else {
+ list($this->meta, $this->strings) = $array;
+ }
+ } else {
+ $this->meta = @$array['meta'];
+ $this->strings = @$array['strings'];
+ }
+ return true;
+ }
+
+ /**
+ * toMO
+ *
+ * @access protected
+ * @return object File_Gettext_MO
+ */
+ function &toMO()
+ {
+ include_once 'File/Gettext/MO.php';
+ $MO = &new File_Gettext_MO;
+ $MO->fromArray($this->toArray());
+ return $MO;
+ }
+
+ /**
+ * toPO
+ *
+ * @access protected
+ * @return object File_Gettext_PO
+ */
+ function &toPO()
+ {
+ include_once 'File/Gettext/PO.php';
+ $PO = &new File_Gettext_PO;
+ $PO->fromArray($this->toArray());
+ return $PO;
+ }
+
+ /**
+ * Raise PEAR error
+ *
+ * @static
+ * @access protected
+ * @return object
+ * @param string $error
+ * @param int $code
+ */
+ function raiseError($error = null, $code = null)
+ {
+ include_once 'PEAR.php';
+ return PEAR::raiseError($error, $code);
+ }
+}
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * File::Gettext
+ *
+ * PHP versions 4 and 5
+ *
+ * @category FileFormats
+ * @package File_Gettext
+ * @author Michael Wallner <mike@php.net>
+ * @copyright 2004-2005 Michael Wallner
+ * @license BSD, revised
+ * @version CVS: $Id: MO.php,v 1.8 2006/01/07 09:45:25 mike Exp $
+ * @link http://pear.php.net/package/File_Gettext
+ */
+
+/**
+ * Requires File_Gettext
+ */
+require_once 'File/Gettext.php';
+
+/**
+ * File_Gettext_MO
+ *
+ * GNU MO file reader and writer.
+ *
+ * @author Michael Wallner <mike@php.net>
+ * @version $Revision: 1.8 $
+ * @access public
+ */
+class File_Gettext_MO extends File_Gettext
+{
+ /**
+ * file handle
+ *
+ * @access private
+ * @var resource
+ */
+ var $_handle = null;
+
+ /**
+ * big endianess
+ *
+ * Whether to write with big endian byte order.
+ *
+ * @access public
+ * @var bool
+ */
+ var $writeBigEndian = false;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @return object File_Gettext_MO
+ * @param string $file path to GNU MO file
+ */
+ function File_Gettext_MO($file = '')
+ {
+ $this->file = $file;
+ }
+
+ /**
+ * _read
+ *
+ * @access private
+ * @return mixed
+ * @param int $bytes
+ */
+ function _read($bytes = 1)
+ {
+ if (0 < $bytes = abs($bytes)) {
+ return fread($this->_handle, $bytes);
+ }
+ return null;
+ }
+
+ /**
+ * _readInt
+ *
+ * @access private
+ * @return int
+ * @param bool $bigendian
+ */
+ function _readInt($bigendian = false)
+ {
+ return current($array = unpack($bigendian ? 'N' : 'V', $this->_read(4)));
+ }
+
+ /**
+ * _writeInt
+ *
+ * @access private
+ * @return int
+ * @param int $int
+ */
+ function _writeInt($int)
+ {
+ return $this->_write(pack($this->writeBigEndian ? 'N' : 'V', (int) $int));
+ }
+
+ /**
+ * _write
+ *
+ * @access private
+ * @return int
+ * @param string $data
+ */
+ function _write($data)
+ {
+ return fwrite($this->_handle, $data);
+ }
+
+ /**
+ * _writeStr
+ *
+ * @access private
+ * @return int
+ * @param string $string
+ */
+ function _writeStr($string)
+ {
+ return $this->_write($string . "\0");
+ }
+
+ /**
+ * _readStr
+ *
+ * @access private
+ * @return string
+ * @param array $params associative array with offset and length
+ * of the string
+ */
+ function _readStr($params)
+ {
+ fseek($this->_handle, $params['offset']);
+ return $this->_read($params['length']);
+ }
+
+ /**
+ * Load MO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function load($file = null)
+ {
+ $this->strings = array();
+
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // open MO file
+ if (!is_resource($this->_handle = @fopen($file, 'rb'))) {
+ return parent::raiseError($php_errormsg . ' ' . $file);
+ }
+ // lock MO file shared
+ if (!@flock($this->_handle, LOCK_SH)) {
+ @fclose($this->_handle);
+ return parent::raiseError($php_errormsg . ' ' . $file);
+ }
+
+ // read (part of) magic number from MO file header and define endianess
+ switch ($magic = current($array = unpack('c', $this->_read(4))))
+ {
+ case -34:
+ $be = false;
+ break;
+
+ case -107:
+ $be = true;
+ break;
+
+ default:
+ return parent::raiseError("No GNU mo file: $file (magic: $magic)");
+ }
+
+ // check file format revision - we currently only support 0
+ if (0 !== ($_rev = $this->_readInt($be))) {
+ return parent::raiseError('Invalid file format revision: ' . $_rev);
+ }
+
+ // count of strings in this file
+ $count = $this->_readInt($be);
+
+ // offset of hashing table of the msgids
+ $offset_original = $this->_readInt($be);
+ // offset of hashing table of the msgstrs
+ $offset_translat = $this->_readInt($be);
+
+ // move to msgid hash table
+ fseek($this->_handle, $offset_original);
+ // read lengths and offsets of msgids
+ $original = array();
+ for ($i = 0; $i < $count; $i++) {
+ $original[$i] = array(
+ 'length' => $this->_readInt($be),
+ 'offset' => $this->_readInt($be)
+ );
+ }
+
+ // move to msgstr hash table
+ fseek($this->_handle, $offset_translat);
+ // read lengths and offsets of msgstrs
+ $translat = array();
+ for ($i = 0; $i < $count; $i++) {
+ $translat[$i] = array(
+ 'length' => $this->_readInt($be),
+ 'offset' => $this->_readInt($be)
+ );
+ }
+
+ // read all
+ for ($i = 0; $i < $count; $i++) {
+ $this->strings[$this->_readStr($original[$i])] =
+ $this->_readStr($translat[$i]);
+ }
+
+ // done
+ @flock($this->_handle, LOCK_UN);
+ @fclose($this->_handle);
+ $this->_handle = null;
+
+ // check for meta info
+ if (isset($this->strings[''])) {
+ $this->meta = parent::meta2array($this->strings['']);
+ unset($this->strings['']);
+ }
+
+ return true;
+ }
+
+ /**
+ * Save MO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function save($file = null)
+ {
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // open MO file
+ if (!is_resource($this->_handle = @fopen($file, 'wb'))) {
+ return parent::raiseError($php_errormsg . ' ' . $file);
+ }
+ // lock MO file exclusively
+ if (!@flock($this->_handle, LOCK_EX)) {
+ @fclose($this->_handle);
+ return parent::raiseError($php_errormsg . ' ' . $file);
+ }
+
+ // write magic number
+ if ($this->writeBigEndian) {
+ $this->_write(pack('c*', 0x95, 0x04, 0x12, 0xde));
+ } else {
+ $this->_write(pack('c*', 0xde, 0x12, 0x04, 0x95));
+ }
+
+ // write file format revision
+ $this->_writeInt(0);
+
+ $count = count($this->strings) + ($meta = (count($this->meta) ? 1 : 0));
+ // write count of strings
+ $this->_writeInt($count);
+
+ $offset = 28;
+ // write offset of orig. strings hash table
+ $this->_writeInt($offset);
+
+ $offset += ($count * 8);
+ // write offset transl. strings hash table
+ $this->_writeInt($offset);
+
+ // write size of hash table (we currently ommit the hash table)
+ $this->_writeInt(0);
+
+ $offset += ($count * 8);
+ // write offset of hash table
+ $this->_writeInt($offset);
+
+ // unshift meta info
+ if ($meta) {
+ $meta = '';
+ foreach ($this->meta as $key => $val) {
+ $meta .= $key . ': ' . $val . "\n";
+ }
+ $strings = array('' => $meta) + $this->strings;
+ } else {
+ $strings = $this->strings;
+ }
+
+ // write offsets for original strings
+ foreach (array_keys($strings) as $o) {
+ $len = strlen($o);
+ $this->_writeInt($len);
+ $this->_writeInt($offset);
+ $offset += $len + 1;
+ }
+
+ // write offsets for translated strings
+ foreach ($strings as $t) {
+ $len = strlen($t);
+ $this->_writeInt($len);
+ $this->_writeInt($offset);
+ $offset += $len + 1;
+ }
+
+ // write original strings
+ foreach (array_keys($strings) as $o) {
+ $this->_writeStr($o);
+ }
+
+ // write translated strings
+ foreach ($strings as $t) {
+ $this->_writeStr($t);
+ }
+
+ // done
+ @flock($this->_handle, LOCK_UN);
+ @fclose($this->_handle);
+ return true;
+ }
+}
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * File::Gettext
+ *
+ * PHP versions 4 and 5
+ *
+ * @category FileFormats
+ * @package File_Gettext
+ * @author Michael Wallner <mike@php.net>
+ * @copyright 2004-2005 Michael Wallner
+ * @license BSD, revised
+ * @version CVS: $Id: PO.php,v 1.6 2006/01/07 09:45:25 mike Exp $
+ * @link http://pear.php.net/package/File_Gettext
+ */
+
+/**
+ * Requires File_Gettext
+ */
+require_once dirname(__FILE__) . '/../Gettext.php';
+
+/**
+ * File_Gettext_PO
+ *
+ * GNU PO file reader and writer.
+ *
+ * @author Michael Wallner <mike@php.net>
+ * @version $Revision: 1.6 $
+ * @access public
+ */
+class File_Gettext_PO extends File_Gettext
+{
+ /**
+ * Constructor
+ *
+ * @access public
+ * @return object File_Gettext_PO
+ * @param string path to GNU PO file
+ */
+ function File_Gettext_PO($file = '')
+ {
+ $this->file = $file;
+ }
+
+ /**
+ * Load PO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function load($file = null)
+ {
+ $this->strings = array();
+
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // load file
+ if (!$contents = @file($file)) {
+ return parent::raiseError($php_errormsg . ' ' . $file);
+ }
+
+ $contents = explode("\n", implode('', $contents));
+
+ $state = 0;
+ $comment = '';
+
+ foreach($contents as $line) {
+ if( substr($line,0,1) == "#" ) {
+ $comment .= "$line\r\n";
+ continue;
+ }
+
+ switch($state) {
+ case 0:
+ if( preg_match( '/^msgid\s+"(.*)"/', $line, $container ) ) {
+ $state = 1;
+ $msgid = $container[1];
+ continue;
+ }
+ break;
+
+ case 1:
+ if( preg_match( '/^msgstr\s+"(.*)"/', $line, $container ) ) {
+ $msgstr = $container[1];
+ $state = 2;
+ } else {
+ $line = preg_replace( '/^"|"$/', "", $line );
+ $msgid .= $line;
+ }
+ continue;
+ break;
+
+ case 2:
+ if( preg_match( '/^\s*$/', $line ) ) {
+ if( $msgid != "" ) {
+ $this->status[parent::prepare($msgid)] = $this->status2array($comment);
+ $this->ref[parent::prepare($msgid)] = $this->ref2array($comment);
+
+ // $msgid = @preg_replace('/\s*msgid\s*"(.*)"\s*/s', '\\1', $matches[1][$i]);
+ // $msgstr= @preg_replace('/\s*msgstr\s*"(.*)"\s*/s', '\\1', $matches[4][$i]);
+
+ if( $msgstr == "" ) {
+ $this->status[parent::prepare($msgid)][] = 'untranslated';
+ } elseif (!in_array('fuzzy', $this->status[parent::prepare($msgid)])) {
+ $this->status[parent::prepare($msgid)][] = 'translated';
+ }
+
+ $this->strings[parent::prepare($msgid)] = parent::prepare($msgstr);
+ $this->encstr[base64_encode(parent::prepare($msgid))] = parent::prepare($msgid);
+ $comment = preg_replace('/\r/', '', $comment);
+ $this->comments[parent::prepare($msgid)] = $comment;
+
+ } else {
+ $this->meta = parent::meta2array(parent::prepare($msgstr));
+ }
+ $comment = "";
+ $state = 0;
+ } else {
+ $line = preg_replace( '/^"|"$/', "", $line );
+ $msgstr .= $line;
+ }
+ break;
+ }
+ }
+
+ return true;
+ }
+
+
+ function status2array($comment) {
+ $status = array();
+ $comment = explode("\n", $comment);
+ foreach($comment as $c) {
+ if (preg_match('/#,\s(.*)/', $c, $matches)) {
+ $st = preg_split('/,/', trim($matches[1]));
+ foreach($st as $s) {
+ $status[] = trim($s);
+ }
+ }
+ }
+ return $status;
+ }
+
+ function ref2array($comment) {
+ $refs = array();
+ $comment = explode("\n", $comment);
+ foreach($comment as $c) {
+ if (preg_match('/#:\s(.*)/', $c, $matches)) {
+ $st = preg_split('/\s/', trim($matches[1]));
+ foreach($st as $s) {
+ $refs[] = trim($s);
+ }
+ }
+ }
+ return $refs;
+ }
+
+ /**
+ * Save PO file
+ *
+ * @access public
+ * @return mixed Returns true on success or PEAR_Error on failure.
+ * @param string $file
+ */
+ function save($file = null)
+ {
+ if (!isset($file)) {
+ $file = $this->file;
+ }
+
+ // open PO file
+ if (!is_resource($fh = @fopen($file, 'w'))) {
+ return parent::raiseError($php_errormsg . ' ' . $file);
+ }
+ // lock PO file exclusively
+ if (!@flock($fh, LOCK_EX)) {
+ @fclose($fh);
+ return parent::raiseError($php_errmsg . ' ' . $file);
+ }
+
+ // write meta info
+ if (count($this->meta)) {
+ $meta = 'msgid ""' . "\nmsgstr " . '""' . "\n";
+ foreach ($this->meta as $k => $v) {
+ $meta .= '"' . $k . ': ' . $v . '\n"' . "\n";
+ }
+ fwrite($fh, $meta . "\n");
+ }
+ // write strings
+ foreach ($this->strings as $o => $t) {
+ $c = @$this->comments[$o];
+ fwrite($fh,
+ $c . "\n" .
+ 'msgid "' . parent::prepare($o, true) . '"' . "\n" .
+ 'msgstr "' . parent::prepare($t, true) . '"' . "\n\n"
+ );
+ }
+
+ //done
+ @flock($fh, LOCK_UN);
+ @fclose($fh);
+ return true;
+ }
+}
+?>
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author ???
+ * @package Babel
+ */
+
+class Translate {
+
+ function getPath($app) {
+ if ($app == 'horde') {
+ $app = '';
+ }
+ return realpath(HORDE_BASE . '/' . $app . '/po/');
+ }
+
+ function stats($app, $filter_lang = false) {
+ global $nls, $module, $apps, $dirs, $lang;
+
+ $report = array();
+ $dir = Translate::getPath($app);
+
+ $i = 0;
+ $handle = opendir($dir);
+ while ($file = readdir($handle)) {
+ if (preg_match('/(.*)\.po$/', $file, $matches)) {
+ $locale = $matches[1];
+ if ($filter_lang && $locale != $filter_lang) {
+ continue;
+ }
+
+ if (!isset($nls['languages'][$locale]) || $locale == 'en_US') {
+ continue;
+ }
+ $i++;
+
+ $pofile = $dir . "/$file";
+
+ $tmppo = &new File_Gettext_PO();
+ $tmppo->load($pofile);
+ $fuzzy = 0;
+ $untranslated = 0;
+ $translated = 0;
+ $obsolete = 0;
+
+ foreach($tmppo->status as $msgid => $status) {
+ if (in_array('untranslated', $status)) {
+ $untranslated++;
+ } elseif (in_array('fuzzy', $status)) {
+ $fuzzy++;
+ } elseif (in_array('obsolete', $status)) {
+ $obsolete++;
+ } else {
+ $translated++;
+ }
+ }
+
+ $all = $translated + $fuzzy + $untranslated;
+ $percent_done = round($translated / $all * 100, 2);
+ $report[$locale] = array($all, $percent_done, $translated, $fuzzy, $untranslated, $obsolete);
+ }
+ }
+ uasort ($report, 'my_usort_function');
+
+ return $report;
+ }
+
+ function sanity_check()
+ {
+
+ /* Sanity checks */
+ if (!extension_loaded('gettext')) {
+ Translate_Display::error(_("Gettext extension not found!"));
+ }
+
+ Translate_Display::info(_("Loading libraries..."));
+ $libs_found = true;
+
+ foreach (array('Console_Getopt' => 'Console/Getopt.php',
+ 'Console_Table' => 'Console/Table.php',
+ 'File_Find' => 'File/Find.php')
+ as $class => $file) {
+ @include_once $file;
+ if (class_exists($class)) {
+ // Translate_Display::info("$class ...", false);
+ } else {
+ Translate_Display::error(sprintf(_("%s not found."), $class));
+ $libs_found = false;
+ }
+ }
+
+ if (!$libs_found) {
+ Translate_Display::info();
+ Translate_Display::info(_("Make sure that you have PEAR installed and in your include path."));
+ Translate_Display::info('include_path: ' . ini_get('include_path'));
+ }
+ }
+
+ function check_binaries()
+ {
+ global $gettext_version, $c;
+
+ Translate_Display::info(_("Searching gettext binaries..."));
+ require_once 'System.php';
+ foreach (array('gettext', 'msgattrib', 'msgcat', 'msgcomm', 'msgfmt', 'msginit', 'msgmerge', 'xgettext') as $binary) {
+ $GLOBALS[$binary] = System::which($binary);
+ if ($GLOBALS[$binary]) {
+ // Translate_Display::info("$binary ... found: " . $GLOBALS[$binary], false);
+ } else {
+ Translate_Display::error(sprintf(_("%s not found."), $binary));
+ }
+ }
+
+ $out = '';
+ exec($GLOBALS['gettext'] . ' --version', $out, $ret);
+ $split = explode(' ', $out[0]);
+ // Translate_Display::info('gettext version: ' . $split[count($split) - 1]);
+ $gettext_version = explode('.', $split[count($split) - 1]);
+ if ($gettext_version[0] == 0 && $gettext_version[1] < 12) {
+ $GLOBALS['php_support'] = false;
+ Translate_Display::info();
+ Translate_Display::warning(_("Warning: Your gettext version is too old and does not support PHP natively."));
+ Translate_Display::warning(_("Not all strings will be extracted."), false);
+ } else {
+ $GLOBALS['php_support'] = true;
+ }
+ Translate_Display::info();
+ }
+
+ function search_file($file, $dir = '.', $local = false)
+ {
+ static $ff;
+ if (!isset($ff)) {
+ $ff = &new File_Find();
+ }
+
+ if (substr($file, 0, 1) != '/') {
+ $file = "/$file/";
+ }
+
+ if ($local) {
+ $files = $ff->glob($file, $dir, 'perl');
+ $files = array_map(create_function('$file', 'return "' . $dir . '/' . '" . $file;'), $files);
+ return $files;
+ } else {
+ return $ff->search($file, $dir, 'perl');
+ }
+ }
+
+ function search_ext($ext, $dir = '.', $local = false)
+ {
+ return Translate::search_file(".+\\.$ext\$", $dir, $local);
+ }
+
+ function get_po_files($dir)
+ {
+ $langs = Translate::search_ext('po', $dir);
+ if (($key = array_search($dir . '/' . 'messages.po', $langs)) !== false) {
+ unset($langs[$key]);
+ }
+ if (($key = array_search($dir . '/' . 'compendium.po', $langs)) !== false) {
+ unset($langs[$key]);
+ }
+ return $langs;
+ }
+
+ function get_languages($dir)
+ {
+ chdir($dir);
+ $langs = get_po_files('po');
+ $langs = array_map(create_function('$lang', 'return str_replace("po" . '/', "", str_replace(".po", "", $lang));'), $langs);
+ return $langs;
+ }
+
+ function search_applications()
+ {
+ $dirs = array();
+ $horde = false;
+ if (@is_dir(HORDE_BASE . '/' . 'po')) {
+ $dirs[] = HORDE_BASE;
+ $horde = true;
+ }
+ $dh = @opendir(HORDE_BASE);
+ if ($dh) {
+ while ($entry = @readdir($dh)) {
+ $dir = HORDE_BASE . '/' . $entry;
+ if (is_dir($dir) &&
+ substr($entry, 0, 1) != '.' &&
+ fileinode(HORDE_BASE) != fileinode($dir)) {
+ $sub = opendir($dir);
+ if ($sub) {
+ while ($subentry = readdir($sub)) {
+ if ($subentry == 'po' && is_dir($dir . '/' . $subentry)) {
+ $dirs[] = $dir;
+ if ($entry == 'horde') {
+ $horde = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!$horde) {
+ array_unshift($dirs, HORDE_BASE);
+ }
+ }
+
+ return $dirs;
+ }
+
+ function strip_horde($file)
+ {
+ if (is_array($file)) {
+ return array_map(create_function('$file', 'return Translate::strip_horde($file);'), $file);
+ } else {
+ return str_replace(HORDE_BASE . '/', '', $file);
+ }
+ }
+
+ function commit($help_only = false)
+ {
+ global $apps, $dirs, $lang, $module;
+
+ $docs = false;
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) continue;
+ if ($apps[$i] == 'horde') {
+ $dirs[] = $dirs[$i] . '/' . 'admin';
+ $apps[] = 'horde/admin';
+ if (!empty($module)) {
+ $module = 'horde/admin';
+ }
+ }
+ if (empty($lang)) {
+ if ($help_only) {
+ $files = array_merge($files, Translate::strip_horde(Translate::search_ext('xml', $dirs[$i] . '/' . 'locale')));
+ } else {
+ $files = array_merge($files, Translate::strip_horde(Translate::get_po_files($dirs[$i] . '/' . 'po')));
+ $files = array_merge($files, Translate::strip_horde(Translate::search_file('^[a-z]{2}_[A-Z]{2}', $dirs[$i] . '/' . 'locale', true)));
+ }
+ } else {
+ if ($help_only) {
+ if (!@file_exists($dirs[$i] . '/' . 'locale' . '/' . $lang . '/' . 'help.xml')) continue;
+ } else {
+ if (!@file_exists($dirs[$i] . '/po/' . $lang . '.po')) continue;
+ $files[] = Translate::strip_horde($dirs[$i] . '/' . 'po' . '/' . $lang . '.po');
+ }
+ $files[] = Translate::strip_horde($dirs[$i] . '/' . 'locale' . '/' . $lang);
+ }
+ if ($docs && !$help_only && $apps[$i]) {
+ $files[] = Translate::strip_horde($dirs[$i] . '/' . 'docs');
+ if ($apps[$i] == 'horde') {
+ $horde_conf = $dirs[array_search('horde', $dirs)] . '/' . 'config' . '/';
+ $files[] = Translate::strip_horde($horde_conf . 'nls.php.dist');
+ }
+ }
+ }
+ chdir(HORDE_BASE);
+ if (count($files)) {
+ if ($docs) {
+ Translate_Display::info(_("Adding new files to repository:"));
+ $sh = 'cvs add';
+ foreach ($files as $file) {
+ if (strstr($file, 'locale') || strstr($file, '.po')) {
+ $sh .= " $file";
+ Translate_Display::info($file, false);
+ }
+ }
+ $sh .= '; cvs add';
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ if ($help_only) {
+ $sh .= ' ' . $file . '/' . '*.xml';
+ Translate_Display::info($file . '/' . '*.xml', false);
+ } else {
+ $sh .= ' ' . $file . '/' . '*.xml ' . $file . '/' . 'LC_MESSAGES';
+ Translate_Display::info($file . '/' . "*.xml\n$file" . '/' . 'LC_MESSAGES', false);
+ }
+ }
+ }
+ if (!$help_only) {
+ $sh .= '; cvs add';
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ $add = $file . '/' . 'LC_MESSAGES' . '/' . '*.mo';
+ $sh .= ' ' . $add;
+ Translate_Display::info($add, false);
+ }
+ }
+ }
+ Translate_Display::info();
+ system($sh);
+ Translate_Display::info();
+ }
+ Translate_Display::header(_("Committing:"));
+ Translate_Display::info(implode(' ', $files), false);
+ if (!empty($lang)) {
+ $lang = ' ' . $lang;
+ }
+ if (empty($msg)) {
+ if ($docs) {
+ $msg = "Add $lang translation.";
+ } elseif ($help_only) {
+ $msg = "Update $lang help file.";
+ } else {
+ $msg = "Update $lang translation.";
+ }
+ }
+ $sh = 'cvs commit -m "' . $msg . '" ' . implode(' ', $files);
+ system($sh);
+ }
+ }
+
+ function xtract()
+ {
+ global $module, $apps, $dirs, $gettext_version;
+
+ require_once 'Horde/Array.php';
+ if ($GLOBALS['php_support']) {
+ $language = 'PHP';
+ } else {
+ $language = 'C++';
+ }
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ Translate_Display::header(sprintf(_("Extracting from %s... "), $apps[$i]));
+ chdir($dirs[$i]);
+ if ($apps[$i] == 'horde') {
+ $files = Translate::search_ext('php', '.', true);
+ foreach (array('admin', 'framework', 'lib', 'services', 'templates', 'util', 'config' . '/' . 'themes') as $search_dir) {
+ $files = array_merge($files, Translate::search_ext('(php|inc|js)', $search_dir));
+ }
+ $files = array_merge($files, Translate::search_ext('(php|dist)', 'config'));
+ $sh = $GLOBALS['xgettext'] . ' --language=' . $language .
+ ' --from-code=iso-8859-1 --keyword=_ --sort-output --copyright-holder="Horde Project"';
+ if ($gettext_version[0] > 0 || $gettext_version[1] > 11) {
+ $sh .= ' --msgid-bugs-address="dev@lists.horde.org"';
+ }
+ $file = $dirs[$i] . '/' . 'po' . '/' . $apps[$i] . '.pot';
+ if (file_exists($file) && !is_writable($file)) {
+ Translate_Display::error(sprintf(_('%s is not writable.', $file)));
+ }
+ $tmp_file = $file . '.tmp.pot';
+ $sh .= ' -o ' . $tmp_file . ' ' . implode(' ', $files);
+ if (@file_exists($dirs[$i] . '/po/translation.php')) {
+ $sh .= ' po/translation.php';
+ }
+ exec($sh);
+ } else {
+ $files = Translate::search_ext('(php|inc|js)');
+ $files = array_filter($files, create_function('$file', 'return substr($file, 0, 9) != "./config/";'));
+ $files = array_merge($files, Translate::search_ext('(php|dist)', 'config'));
+ $sh = $GLOBALS['xgettext'] . ' --language=' . $language .
+ ' --keyword=_ --sort-output --force-po --copyright-holder="Horde Project"';
+ if ($gettext_version[0] > 0 || $gettext_version[1] > 11) {
+ $sh .= ' --msgid-bugs-address="support@scopserv.com"';
+ }
+ $file = 'po' . '/' . $apps[$i] . '.pot';
+ if (file_exists($file) && !is_writable($file)) {
+ Translate_Display::error((sprintf(_("%s is not writable."), $file)));
+ }
+ $tmp_file = $file . '.tmp.pot';
+ $sh .= ' -o ' . $tmp_file . ' ' . implode(' ', $files);
+ exec($sh);
+ }
+
+ if (file_exists($tmp_file)) {
+ $files = Translate::search_ext('html', 'templates');
+ $tmp = fopen($file . '.templates', 'w');
+ foreach ($files as $template) {
+ $fp = fopen($template, 'r');
+ $lineno = 0;
+ while (($line = fgets($fp, 4096)) !== false) {
+ $lineno++;
+ $offset = 0;
+ while (($left = strpos($line, '<gettext>', $offset)) !== false) {
+ $left += 9;
+ $buffer = '';
+ $linespan = 0;
+ while (($end = strpos($line, '</gettext>', $left)) === false) {
+ $buffer .= substr($line, $left);
+ $left = 0;
+ $line = fgets($fp, 4096);
+ $linespan++;
+ if ($line === false) {
+ Translate_Display::error((sprintf(_("<gettext> tag not closed in file %s.\nOpening tag found in line %d."), $template, $lineno)));
+ break 2;
+ }
+ }
+ $buffer .= substr($line, $left, $end - $left);
+ fwrite($tmp, "#: $template:$lineno\n");
+ fwrite($tmp, 'msgid "' . str_replace(array('"', "\n"), array('\"', "\\n\"\n\""), $buffer) . "\"\n");
+ fwrite($tmp, 'msgstr ""' . "\n\n");
+
+ $offset = $end + 10;
+ }
+ }
+ fclose($fp);
+ }
+ fclose($tmp);
+ $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$tmp_file\" \"$file.templates\" --output-file \"$tmp_file\"";
+ exec($sh);
+ unlink($file . '.templates');
+
+ if (file_exists($file)) {
+ $diff = array_diff(file($tmp_file), file($file));
+ $diff = preg_grep('/^"POT-Creation-Date:/', $diff, PREG_GREP_INVERT);
+ }
+ }
+
+ if (!file_exists($file) || count($diff)) {
+ @unlink($file);
+ rename($tmp_file, $file);
+ Translate_Display::info(_("Updated!"));
+ } else {
+ @unlink($tmp_file);
+ Translate_Display::info(_("Not changed!"));
+ }
+ }
+ }
+
+ function merge()
+ {
+ global $apps, $dirs, $lang, $module;
+
+ $compendium = ' --compendium="' . HORDE_BASE . '/' . 'po' . '/' . 'compendium.po"';
+ // $compendium = ' --compendium=' . $option[1];
+ // $compendium = '';
+
+ if (!isset($lang) && !empty($compendium)) {
+ Translate_Display::error(_("Error: No locale specified."));
+ Translate_Display::info();
+ usage();
+ }
+
+ Translate::cleanup();
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ Translate_Display::header(sprintf(_("Merging translation for module %s..."), $apps[$i]));
+ $dir = $dirs[$i] . '/' . 'po' . '/';
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dir . $lang . '.po')) {
+ Translate_Display::info(_("Skipped..."));
+ Translate_Display::info();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ Translate_Display::info(sprintf(_("Merging locale %s..."), $locale));
+ $sh = $GLOBALS['msgmerge'] . ' --update -v' . $compendium . ' "' . $dir . $locale . '.po" "' . $dir . $apps[$i] . '.pot"';
+ exec($sh);
+ Translate_Display::info(_("Done!"));
+ }
+ }
+ }
+
+ function compendium()
+ {
+ global $dirs, $lang, $module;
+
+ $dir = HORDE_BASE . '/' . 'po' . '/';
+ $add = '';
+ if (!isset($lang)) {
+ Translate_Display::error(_("Error: No locale specified."));
+ Translate_Display::info();
+ usage();
+ }
+ Translate_Display::info(sprintf(_("Merging all %s.po files to the compendium... "), $lang));
+ $pofiles = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ $pofile = $dirs[$i] . '/' . 'po' . '/' . $lang . '.po';
+ if (file_exists($pofile)) {
+ $pofiles[] = $pofile;
+ }
+ }
+ if (!empty($dir) && substr($dir, -1) != '/') {
+ $dir .= '/';
+ }
+ $sh = $GLOBALS['msgcat'] . ' --sort-output ' . implode(' ', $pofiles) . $add . ' > ' . $dir . 'compendium.po ';
+ exec($sh, $out, $ret);
+
+ if ($ret == 0) {
+ Translate_Display::info(_("Done!"));
+ } else {
+ Translate_Display::error(_("Failed!"));
+ }
+ }
+
+ function init()
+ {
+ global $module, $apps, $dirs, $lang, $module;
+
+ if (empty($lang)) { $lang = getenv('LANG'); }
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (@file_exists($dirs[$i] . '/po/' . $lang . '.po')) {
+ continue;
+ }
+ if (!empty($module) && $module != $apps[$i]) { continue; }
+ $package = ucfirst($apps[$i]);
+ $package_u = String::upper($apps[$i]);
+ @include $dirs[$i] . '/lib/version.php';
+ $version = eval('return(defined("' . $package_u . '_VERSION") ? ' . $package_u . '_VERSION : "???");');
+ Translate_Display::header(sprintf(_("Initializing module %s..."), $apps[$i]));
+ if (!@file_exists($dirs[$i] . '/po/' . $apps[$i] . '.pot')) {
+ Translate_Display::error(_("Failed!"));
+ Translate_Display::info(sprintf(_("%s not found. Run 'Extract' first."), $dirs[$i] . '/' . 'po' . '/' . $apps[$i] . '.pot'));
+ continue;
+ }
+ $dir = $dirs[$i] . '/' . 'po' . '/';
+ $sh = $GLOBALS['msginit'] . ' --no-translator -i ' . $dir . $apps[$i] . '.pot ' .
+ (!empty($lang) ? ' -o ' . $dir . $lang . '.po --locale=' . $lang : '');
+
+ if (!empty($lang) && !OS_WINDOWS) {
+ $pofile = $dirs[$i] . '/po/' . $lang . '.po';
+ $sh .= "; sed 's/PACKAGE package/$package package/' $pofile " .
+ "| sed 's/PACKAGE VERSION/$package $version/' " .
+ "| sed 's/messages for PACKAGE/messages for $package/' " .
+ "| sed 's/Language-Team: none/Language-Team: i18n@lists.horde.org/' " .
+ "> $pofile.tmp";
+ }
+ exec($sh, $out, $ret);
+ rename($pofile . '.tmp', $pofile);
+ if ($ret == 0) {
+ Translate_Display::info(_("Done!"));
+ } else {
+ Translate_Display::error(_("Failed!"));
+ }
+ Translate_Display::info();
+ }
+ }
+
+ function make()
+ {
+ global $apps, $dirs, $lang, $module;
+
+ $compendium = HORDE_BASE . '/' . 'po' . '/' . 'compendium.po';
+ $save_stats = true;
+
+ $horde = array_search('horde', $dirs);
+ $horde_msg = array();
+ $stats_array = array();
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) continue;
+ Translate_Display::header(sprintf(_("Building MO files for module %s..."), $apps[$i]));
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dirs[$i] . '/' . 'po' . '/' . $lang . '.po')) {
+ Translate_Display::info(_("Skipped..."));
+ Translate_Display::info();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ Translate_Display::info(sprintf(_("Building locale %s..."), $locale));
+ $dir = $dirs[$i] . '/' . 'locale' . '/' . $locale . '/' . 'LC_MESSAGES';
+ if (!is_dir($dir)) {
+ require_once 'System.php';
+ if (!@System::mkdir("-p $dir")) {
+ Translate_Display::error(sprintf(_("Warning: Could not create locale directory for locale %s:"), $locale));
+ Translate_Display::info($dir, false);
+ Translate_Display::info();
+ continue;
+ }
+ }
+
+ /* Convert to unix linebreaks. */
+ $pofile = $dirs[$i] . '/' . 'po' . '/' . $locale . '.po';
+ $fp = fopen($pofile, 'r');
+ $content = fread($fp, filesize($pofile));
+ fclose($fp);
+
+ $content = str_replace("\r", '', $content);
+ $fp = fopen($pofile, 'wb');
+ fwrite($fp, $content);
+ fclose($fp);
+
+ /* Check PO file sanity. */
+ $sh = $GLOBALS['msgfmt'] . " --check \"$pofile\" 2>&1";
+ exec($sh, $out, $ret);
+ if ($ret != 0) {
+ Translate_Display::error(_("Warning: an error has occured:"));
+ Translate_Display::info(implode("\n", $out));
+ Translate_Display::info();
+ if ($apps[$i] == 'horde') {
+ continue 2;
+ }
+ continue;
+ }
+
+ /* Compile MO file. */
+ $sh = $GLOBALS['msgfmt'] . ' --statistics -o "' . $dir . '/' . $apps[$i] . '.mo"';
+ if ($apps[$i] != 'horde') {
+ $horde_po = $dirs[$horde] . '/' . 'po' . '/' . $locale . '.po';
+ if (!@is_readable($horde_po)) {
+ Translate_Display::error(sprintf(_("Warning: the Horde PO file for the locale %s does not exist:"), $locale));
+ Translate_Display::info($horde_po);
+ Translate_Display::info();
+ $sh .= $dirs[$i] . '/' . 'po' . '/' . $locale . '.po';
+ } else {
+ $sh = "export LANG=C ; " . $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$pofile\" \"$horde_po\" | $sh -";
+ }
+ } else {
+ $sh .= $pofile;
+ }
+ $sh .= ' 2>&1';
+ $out = '';
+
+ exec($sh, $out, $ret);
+
+ if ($ret == 0) {
+ Translate_Display::info(_("Done!"));
+ $messages = array(0, 0, 0);
+ if (preg_match('/(\d+) translated/', $out[0], $match)) {
+ $messages[0] = $match[1];
+ if (isset($horde_msg[$locale])) {
+ $messages[0] -= $horde_msg[$locale][0];
+ if ($messages[0] < 0) $messages[0] = 0;
+ }
+ }
+ if (preg_match('/(\d+) fuzzy/', $out[0], $match)) {
+ $messages[1] = $match[1];
+ if (isset($horde_msg[$locale])) {
+ $messages[1] -= $horde_msg[$locale][1];
+ if ($messages[1] < 0) $messages[1] = 0;
+ }
+ }
+ if (preg_match('/(\d+) untranslated/', $out[0], $match)) {
+ $messages[2] = $match[1];
+ if (isset($horde_msg[$locale])) {
+ $messages[2] -= $horde_msg[$locale][2];
+ if ($messages[2] < 0) $messages[2] = 0;
+ }
+ }
+ if ($apps[$i] == 'horde') {
+ $horde_msg[$locale] = $messages;
+ }
+ $stats_array[$apps[$i]][$locale] = $messages;
+ } else {
+ Translate_Display::error(_("Failed!"));
+ exec($sh, $out, $ret);
+ Translate_Display::info(implode("\n", $out));
+ }
+ if (count($langs) > 1) {
+ continue;
+ }
+
+ /* Merge translation into compendium. */
+ if (!empty($compendium)) {
+ Translate_Display::header(sprintf(_("Merging the PO file for %s to the compendium..."), $apps[$i]));
+ if (!empty($dir) && substr($dir, -1) != '/') {
+ $dir .= '/';
+ }
+ $sh = $GLOBALS['msgcat'] . " --sort-output \"$compendium\" \"$pofile\" > \"$compendium.tmp\"";
+ $out = '';
+ exec($sh, $out, $ret);
+ @unlink($compendium);
+ rename($compendium . '.tmp', $compendium);
+ if ($ret == 0) {
+ Translate_Display::info(_("Done!"));
+ } else {
+ Translate_Display::error(_("Failed!"));
+ }
+ }
+ Translate_Display::info();
+ }
+ }
+ if (empty($module)) {
+ Translate_Display::header(_("Results:"));
+ } else {
+ Translate_Display::header(_("Results (including Horde):"));
+ }
+
+ echo '<br />';
+ echo '<table cellspacing=0 cellpadding=0 width=90%>';
+ echo sprintf('<tr><td class="control" width="15%%"><b>%s</b></td><td class="control"><b>%s</b></td><td align="right" width="5%%" class="control"><b>%s</b></td><td align="right" width="5%%" class="control"><b>%s</b></td><td align="right" width="5%%" class="control"><b>%s</b></td></tr>', 'Module', 'Language', 'Translated', 'Fuzzy', 'Untranslated');
+
+ $i = 0;
+ foreach($stats_array as $app => $info) {
+ foreach($info as $locale => $message) {
+ echo sprintf('<tr class="item%d"><td>%s</td><td>%s</td><td align="right" >%s</td><td align="right" >%s</td><td align="right" >%s</td></tr>',
+ ($i++ %2), $app, $locale, $messages[0], $messages[1], $messages[2]);
+ }
+ }
+ echo '</table>';
+
+ if ($save_stats) {
+ $fp = @fopen('/tmp/translation_stats.txt', 'w');
+ if ($fp) {
+ fwrite($fp, serialize($stats_array));
+ fclose($fp);
+ }
+ }
+ }
+
+ function cleanup($keep_untranslated = false)
+ {
+ global $apps, $dirs, $lang, $module;
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) { continue; }
+ Translate_Display::header(sprintf(_("Cleaning up PO files for module %s..."), $apps[$i]));
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dirs[$i] . '/' . 'po' . '/' . $lang . '.po')) {
+ Translate_Display::info(_("Skipped..."));
+ Translate_Display::info();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ Translate_Display::info(sprintf(_("Cleaning up locale %s..."), $locale));
+ $pofile = $dirs[$i] . '/' . 'po' . '/' . $locale . '.po';
+ $sh = $GLOBALS['msgattrib'] . ($keep_untranslated ? '' : ' --translated') . " --no-obsolete --no-fuzzy --force-po $pofile > $pofile.tmp";
+ $out = '';
+ exec($sh, $out, $ret);
+ if ($ret == 0) {
+ @unlink($pofile);
+ rename($pofile . '.tmp', $pofile);
+ Translate_Display::info(_("Done!"));
+ } else {
+ @unlink($pofile . '.tmp', $pofile);
+ Translate_Display::error(_("Failed!"));
+ }
+ Translate_Display::info();
+ }
+ }
+ }
+}
+
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author ???
+ * @package Babel
+ */
+
+class Translate_Help {
+ function update_help()
+ {
+ global $dirs, $apps, $last_error_msg, $lang, $module;
+
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) { continue; }
+ if (!is_dir("$dirs[$i]/locale")) continue;
+ if ($apps[$i] == 'horde') {
+ $dirs[] = $dirs[$i] . '/' . 'admin';
+ $apps[] = 'horde/admin';
+ if (!empty($module)) {
+ $module = 'horde/admin';
+ }
+ }
+ if (empty($lang)) {
+ $files = search_file('help.xml', $dirs[$i] . '/' . 'locale');
+ } else {
+ $files = array($dirs[$i] . '/' . 'locale' . '/' . $lang . '/' . 'help.xml');
+ }
+ $file_en = $dirs[$i] . '/' . 'locale' . '/' . 'en_US' . '/' . 'help.xml';
+ if (!@file_exists($file_en)) {
+ Translate_Display::info(sprintf(_("Warning: There doesn't yet exist a help file for %s."), $apps[$i]));
+ Translate_Display::info();
+ continue;
+ }
+ foreach ($files as $file_loc) {
+ $locale = substr($file_loc, 0, strrpos($file_loc, '/'));
+ $locale = substr($locale, strrpos($locale, '/') + 1);
+ if ($locale == 'en_US') continue;
+ if (!@file_exists($file_loc)) {
+ Translate_Display::info(sprintf(_("Warning: The %s help file for %s doesn't yet exist. Creating a new one."), $locale, $apps[$i]));
+ $dir_loc = substr($file_loc, 0, -9);
+ if (!is_dir($dir_loc)) {
+ require_once 'System.php';
+ if (!@System::mkdir("-p $dir_loc")) {
+ Translate_Display::error(sprintf(_("Warning: Could not create locale directory for locale %s:"), $locale));
+ Translate_Display::info($dir_loc, false);
+ Translate_Display::info();
+ continue;
+ }
+ }
+
+ if (!@copy($file_en, $file_loc)) {
+ Translate_Display::error(sprintf(_("Warning: Could not copy %s to %s"), $file_en, $file_loc));
+ }
+ Translate_Display::info();
+ continue;
+ }
+ Translate_Display::info(sprintf(_("Updating %s help file for %s."), $locale, $apps[$i]));
+ $fp = fopen($file_loc, 'r');
+ $line = fgets($fp);
+ fclose($fp);
+ if (!strstr($line, '<?xml')) {
+ Translate_Display::info(sprintf(_("Warning: The help file %s didn't start with <?xml"), $file_loc));
+ Translate_Display::info();
+ continue;
+ }
+ $encoding = '';
+ if (preg_match('/encoding=(["\'])([^\\1]+)\\1/', $line, $match)) {
+ $encoding = $match[2];
+ }
+ $doc_en = domxml_open_file($file_en);
+ if (!is_object($doc_en)) {
+ Translate_Display::info(sprintf(_("Warning: There was an error opening the file %s."), $file_en));
+ Translate_Display::info();
+ continue 2;
+ }
+ $doc_loc = domxml_open_file($file_loc);
+ if (!is_object($doc_loc)) {
+ Translate_Display::info(sprintf(_("There was an error opening the file %s."), $file_loc));
+ Translate_Display::info();
+ continue;
+ }
+ $doc_new = domxml_new_doc('1.0');
+ $help_en = $doc_en->document_element();
+ $help_loc = $doc_loc->document_element();
+ $help_new = $help_loc->clone_node();
+ $entries_loc = array();
+ $entries_new = array();
+ $count_uptodate = 0;
+ $count_new = 0;
+ $count_changed = 0;
+ $count_unknown = 0;
+ foreach ($doc_loc->get_elements_by_tagname('entry') as $entry) {
+ $entries_loc[$entry->get_attribute('id')] = $entry;
+ }
+ foreach ($doc_en->get_elements_by_tagname('entry') as $entry) {
+ $id = $entry->get_attribute('id');
+ if (array_key_exists($id, $entries_loc)) {
+ if ($entries_loc[$id]->has_attribute('md5') &&
+ md5($entry->get_content()) != $entries_loc[$id]->get_attribute('md5')) {
+ $comment = $doc_loc->create_comment(" English entry:\n" . str_replace('--', '--', $doc_loc->dump_node($entry)));
+ $entries_loc[$id]->append_child($comment);
+ $entry_new = $entries_loc[$id]->clone_node(true);
+ $entry_new->set_attribute('state', 'changed');
+ $count_changed++;
+ } else {
+ if (!$entries_loc[$id]->has_attribute('state')) {
+ $comment = $doc_loc->create_comment(" English entry:\n" . str_replace('--', '--', $doc_loc->dump_node($entry)));
+ $entries_loc[$id]->append_child($comment);
+ $entry_new = $entries_loc[$id]->clone_node(true);
+ $entry_new->set_attribute('state', 'unknown');
+ $count_unknown++;
+ } else {
+ $entry_new = $entries_loc[$id]->clone_node(true);
+ $count_uptodate++;
+ }
+ }
+ } else {
+ $entry_new = $entry->clone_node(true);
+ $entry_new->set_attribute('state', 'new');
+ $count_new++;
+ }
+ $entries_new[] = $entry_new;
+ }
+ $doc_new->append_child($doc_new->create_comment(' $' . 'Horde$ '));
+ foreach ($entries_new as $entry) {
+ $help_new->append_child($entry);
+ }
+ Translate_Display::info(sprintf(_("Entries: %d total, %d up-to-date, %d new, %d changed, %d unknown"),
+ $count_uptodate + $count_new + $count_changed + $count_unknown,
+ $count_uptodate, $count_new, $count_changed, $count_unknown), false);
+ $doc_new->append_child($help_new);
+ $output = $doc_new->dump_mem(true, $encoding);
+ $fp = fopen($file_loc, 'w');
+ $line = fwrite($fp, $output);
+ fclose($fp);
+ Translate_Display::info(sprintf(_("%d bytes written."), strlen($output)), false);
+ Translate_Display::info();
+ }
+ }
+ }
+
+ function make_help()
+ {
+ global $dirs, $apps, $lang, $module;
+
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) continue;
+ if (!is_dir("$dirs[$i]/locale")) continue;
+ if ($apps[$i] == 'horde') {
+ $dirs[] = $dirs[$i] . '/' . 'admin';
+ $apps[] = 'horde/admin';
+ if (!empty($module)) {
+ $module = 'horde/admin';
+ }
+ }
+ if (empty($lang)) {
+ $files = search_file('help.xml', $dirs[$i] . '/' . 'locale');
+ } else {
+ $files = array($dirs[$i] . '/' . 'locale' . '/' . $lang . '/' . 'help.xml');
+ }
+ $file_en = $dirs[$i] . '/' . 'locale' . '/' . 'en_US' . '/' . 'help.xml';
+ if (!@file_exists($file_en)) {
+ continue;
+ }
+ foreach ($files as $file_loc) {
+ if (!@file_exists($file_loc)) {
+ Translate_Display::info(_("Skipped..."));
+ Translate_Display::info();
+ continue;
+ }
+ $locale = substr($file_loc, 0, strrpos($file_loc, '/'));
+ $locale = substr($locale, strrpos($locale, '/') + 1);
+ if ($locale == 'en_US') continue;
+ Translate_Display::info(sprintf(_("Updating %s help file for %s."), ($locale), ($apps[$i])));
+ $fp = fopen($file_loc, 'r');
+ $line = fgets($fp);
+ fclose($fp);
+ if (!strstr($line, '<?xml')) {
+ Translate_Display::info(sprintf(_("Warning: The help file %s didn't start with <?xml"), $file_loc));
+ Translate_Display::info();
+ continue;
+ }
+ $encoding = '';
+ if (preg_match('/encoding=(["\'])([^\\1]+)\\1/', $line, $match)) {
+ $encoding = $match[2];
+ }
+ $doc_en = domxml_open_file($file_en);
+ if (!is_object($doc_en)) {
+ Translate_Display::info(sprintf(_("Warning: There was an error opening the file %s."), $file_en));
+ Translate_Display::info();
+ continue 2;
+ }
+ $doc_loc = domxml_open_file($file_loc);
+ if (!is_object($doc_loc)) {
+ Translate_Display::info(sprintf(_("Warning: There was an error opening the file %s."), $file_loc));
+ Translate_Display::info();
+ continue;
+ }
+ $help_loc = $doc_loc->document_element();
+ $md5_en = array();
+ $count_all = 0;
+ $count = 0;
+ foreach ($doc_en->get_elements_by_tagname('entry') as $entry) {
+ $md5_en[$entry->get_attribute('id')] = md5($entry->get_content());
+ }
+ foreach ($doc_loc->get_elements_by_tagname('entry') as $entry) {
+ foreach ($entry->child_nodes() as $child) {
+ if ($child->node_type() == XML_COMMENT_NODE && strstr($child->node_value(), 'English entry')) {
+ $entry->remove_child($child);
+ }
+ }
+ $count_all++;
+ $id = $entry->get_attribute('id');
+ if (!array_key_exists($id, $md5_en)) {
+ Translate_Display::info(sprintf(_("No entry with the id '%s' exists in the original help file."), $id));
+ } else {
+ $entry->set_attribute('md5', $md5_en[$id]);
+ $entry->set_attribute('state', 'uptodate');
+ $count++;
+ }
+ }
+ $output = $doc_loc->dump_mem(true, $encoding);
+ $fp = fopen($file_loc, 'w');
+ $line = fwrite($fp, $output);
+ fclose($fp);
+
+ Translate_Display::info(sprintf(_("%d of %d entries marked as up-to-date"), $count, $count_all), false);
+ Translate_Display::info();
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+class Translation {
+
+ function callHook($fname, $info) {
+ /* Check if an hooks file exist */
+ if (file_exists(BABEL_BASE . '/config/hooks.php')) {
+ include_once BABEL_BASE . '/config/hooks.php';
+
+ $func = '_translation_hook_' . $fname;
+
+ if (function_exists($func)) {
+ $res = call_user_func($func, $info);
+ } else {
+ Translate_Display::warning(sprintf(_("Function doesn't exist: %s"), $func));
+ }
+ } else {
+ Translate_Display::warning(_("Hook file doesn't exist"));
+ }
+ }
+
+ function displayLanguage() {
+ global $nls, $lang, $app;
+
+ if (!isset($nls['languages'][$lang])) {
+ return;
+ }
+
+ $res = sprintf(_("Language: %s (%s)"), $nls['languages'][$lang], $lang);
+ if ($app) {
+ $res .= ' | ' . sprintf(_("Module: %s"), $app);
+ }
+
+ return $res;
+ }
+
+
+ function ModuleSelection() {
+ $html = '';
+ $html .= '<span style="float:right">';
+ $html .= '<form action="' . Horde::selfUrl() . '" method="post" name="moduleSelector">';
+ $html .= '<select name="module" onchange="moduleSubmit()">';
+
+ $apps = array('ALL' => _("All Applications")) + Translation::listApps();
+
+ foreach($apps as $app => $desc) {
+ if (!Translation::hasPermission("module:$app")) {
+ continue;
+ }
+
+ if (Util::getFormData('module') == $app) {
+ $html .= '<option class="control" value="' . $app . '" selected>' . '+ ' . $desc;
+ } else {
+ $html .= '<option value="' . $app . '">' . '– ' . $desc;
+ }
+ }
+
+ $html .= '</select>';
+ $html .= '</form>';
+ $html .= '</span>';
+
+ $html .= '<script language="JavaScript" type="text/javascript">' . "\n";
+ $html .= '<!--' . "\n";
+ $html .= 'var loading;' . "\n";
+ $html .= 'function moduleSubmit()' . "\n";
+ $html .= '{' . "\n";
+ $html .= 'document.moduleSelector.submit();' . "\n";
+ $html .= 'return false;' . "\n";
+ $html .= '}' . "\n";
+ $html .= '// -->' . "\n";
+ $html .= '</script>' . "\n";
+ return $html;
+ }
+
+ function LanguageSelection() {
+ global $nls, $app;
+
+ $html = '';
+ $html .= '<span style="float:right">';
+ $html .= '<form action="' . Horde::selfUrl() . '" method="post" name="languageSelector">';
+ $html .= ' ';
+ $html .= '<input type="hidden" name="module" value="' . $app . '">';
+ $html .= '<select name="display_language" onchange="languageSubmit()">';
+
+ $tests = $nls['languages'];
+
+ // Unset English
+ unset($tests['en_US']);
+
+ foreach($tests as $dir => $desc) {
+ if (!Translation::hasPermission("language:$dir")) {
+ continue;
+ }
+
+ if (isset($_SESSION['translation']['language']) && $dir == $_SESSION['translation']['language']) {
+ $html .= '<option class="control" value="' . $dir . '" selected>' . '+ ' . $desc;
+ } else {
+ $html .= '<option value="' . $dir . '">' . '– ' . $desc;
+ }
+ }
+
+ $html .= '</select>';
+ $html .= ' ';
+ $html .= '</form>';
+ $html .= '</span>';
+
+ $html .= '<script language="JavaScript" type="text/javascript">' . "\n";
+ $html .= '<!--' . "\n";
+ $html .= 'var loading;' . "\n";
+ $html .= 'function languageSubmit()' . "\n";
+ $html .= '{' . "\n";
+ $html .= 'document.languageSelector.submit();' . "\n";
+ $html .= 'return false;' . "\n";
+ $html .= '}' . "\n";
+ $html .= '// -->' . "\n";
+ $html .= '</script>' . "\n";
+ return $html;
+ }
+
+ function listApps($all = false) {
+ global $registry;
+
+ $res = array();
+
+ if ($all) {
+ $res['ALL'] = _("All Applications");
+ }
+
+ foreach ($registry->applications as $app => $params) {
+ if ($params['status'] == 'heading' || $params['status'] == 'block') {
+ continue;
+ }
+
+ if (isset($params['fileroot']) && !is_dir($params['fileroot'])) {
+ continue;
+ }
+
+ if (preg_match('/_reports$/', $app) || preg_match('/_tools$/', $app)) {
+ continue;
+ }
+
+ if (Translation::hasPermission("module:$app")) {
+ $res[$app] = sprintf("%s (%s)", $params['name'], $app);
+ }
+ }
+ return $res;
+ }
+
+ /**
+ * Returns the value of the specified permission for $userId.
+ *
+ * @return mixed Does user have $permission?
+ */
+ function hasPermission($permission, $filter = null, $perm = null)
+ {
+ global $perms;
+
+ $userId = Auth::getAuth();
+ $admin = ($userId == 'admin') ? true : false;
+
+ if ($admin || !$perms->exists('translation:' . $permission)) {
+ return true;
+ }
+
+ $allowed = $perms->getPermissions('translation:' . $permission);
+
+ switch ($filter) {
+ case 'tabs':
+ if ($perm) {
+ $allowed = $perms->hasPermission('translation:' . $permission, Auth::getAuth(), $perm);
+ }
+ break;
+ }
+ return $allowed;
+ }
+
+ /**
+ * Get the module main Menu.
+ **/
+ function getMenu($returnType = 'object')
+ {
+ global $registry;
+
+ require_once 'Horde/Menu.php';
+ $menu = &new Menu();
+
+ $menu->addArray(array('url' => Horde::applicationUrl('index.php'),
+ 'text' => _("_General"),
+ 'icon' => 'list.png'));
+
+ if (Translation::hasPermission('view')) {
+ $menu->addArray(array('url' => Horde::applicationUrl('view.php'),
+ 'text' => _("_View"),
+ 'icon' => 'view.png'));
+ }
+
+ if (Translation::hasPermission('stats')) {
+ $menu->addArray(array('url' => Horde::applicationUrl('stats.php'),
+ 'text' => _("_Stats"),
+ 'icon' => 'extract.png'));
+ }
+
+ if (Translation::hasPermission('extract')) {
+ $menu->addArray(array('url' => Horde::applicationUrl('extract.php'),
+ 'text' => _("_Extract"),
+ 'icon' => 'extract.png'));
+ }
+
+ if (Translation::hasPermission('make')) {
+ $menu->addArray(array('url' => Horde::applicationUrl('make.php'),
+ 'text' => _("_Make"),
+ 'icon' => 'make.png'));
+ }
+
+ if (Translation::hasPermission('upload')) {
+ $menu->addArray(array('url' => Horde::applicationUrl('upload.php'),
+ 'text' => _("_Upload"),
+ 'icon' => 'upload.png'));
+ }
+ if ($returnType == 'object') {
+ return $menu;
+ } else {
+ return $menu->render();
+ }
+ }
+
+
+ /**
+ * Send an Email.
+ **/
+ function sendEmail($email, $type = 'html', $attachments = array()) {
+ global $client, $scopserv;
+
+ include_once("Mail.php");
+ include_once("Mail/mime.php");
+
+ $headers["From"] = $email['from'];
+ $headers["Subject"] = $email['subject'];
+
+ $mime = new Mail_Mime();
+ if ($type == 'html') {
+ $mime->setHtmlBody($email['content']);
+ } else {
+ $mime->setTxtBody($email['content']);
+ }
+
+ if (!empty($attachments)) {
+ foreach ($attachments as $info) {
+ $mime->addAttachment($info['file'],
+ $info['type'],
+ $info['name'], false);
+ }
+ }
+
+ $body = $mime->get();
+ $hdrs = $mime->headers($headers);
+
+ $mail_object = &Mail::factory("mail");
+ return $mail_object->send($email['to'], $hdrs, $body);
+ }
+
+
+ function RB_init() {
+ Horde::addScriptFile('prototype.js', 'translation', true);
+ Horde::addScriptFile('effects.js', 'translation', true);
+ Horde::addScriptFile('redbox.js', 'translation', true);
+ }
+
+ function RB_start($secs = 30) {
+
+ $msg = '';
+ $msg .= '<table width=100% id="RB_confirm"><tr><td>';
+ $msg .= '<b>' . _("Please be patient ...") . '</b>';
+ $msg .= '<br />';
+ $msg .= '<br />';
+ if ($secs < 60) {
+ $msg .= addslashes(sprintf(_("Can take up to %d seconds !"), $secs));
+ } else {
+ $min = intval($secs / 60);
+ if ($min == 1) {
+ $msg .= addslashes(_("Can take up to 1 minute !"));
+ } else {
+ $msg .= addslashes(sprintf(_("Can take up to %d minutes !"), $min));
+ }
+ }
+
+ $msg .= '</td><td><img src="themes/graphics/redbox_spinner.gif">';
+
+ $msg .= '</td></tr></table>';
+ echo '<script>';
+ echo 'RedBox.loading();';
+ echo "RedBox.showHtml('$msg');";
+ echo '</script>';
+ flush();
+ }
+
+ function RB_close() {
+ echo '<script>';
+ echo 'RedBox.close();';
+ echo '</script>';
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Babel external API interface.
+ *
+ * This file defines Babel's external API interface. Other applications can
+ * interact with Babel through this API.
+ *
+ * @package Babel
+ */
+
+$_services['perms'] = array(
+ 'args' => array(),
+ // This is actually a hash of hashes
+ 'type' => '{urn:horde}hash'
+);
+
+function _translation_perms()
+{
+
+ global $nls, $registry;
+
+ static $perms = array();
+ if (!empty($perms)) {
+ return $perms;
+ }
+
+ $perms['tree']['translation']['language'] = array();
+ $perms['title']['translation:language'] = _("Languages");
+ $perms['type']['translation:language'] = 'none';
+
+ foreach($nls['languages'] as $langcode => $langdesc) {
+ $perms['tree']['translation']['language'][$langcode] = false;
+ $perms['title']['translation:language:' . $langcode] = sprintf("%s (%s)", $langdesc, $langcode);
+ $perms['type']['translation:language:' . $langcode] = 'boolean';
+ }
+
+ $perms['tree']['translation']['module'] = array();
+ $perms['title']['translation:module'] = _("Modules");
+ $perms['type']['translation:module'] = 'none';
+
+ foreach ($registry->applications as $app => $params) {
+ if ($params['status'] == 'heading' || $params['status'] == 'block') {
+ continue;
+ }
+
+ if (isset($params['fileroot']) && !is_dir($params['fileroot'])) {
+ continue;
+ }
+
+ if (preg_match('/_reports$/', $app) || preg_match('/_tools$/', $app)) {
+ continue;
+ }
+
+ $perms['tree']['translation']['module'][$app] = false;
+ $perms['title']['translation:module:' . $app] = sprintf("%s (%s)", $params['name'], $app);
+ $perms['type']['translation:module:' . $app] = 'boolean';
+ }
+
+ $tabdesc['download'] = _("Download");
+ $tabdesc['upload'] = _("Upload");
+ $tabdesc['stats'] = _("Statistics");
+ $tabdesc['view'] = _("View/Edit");
+ $tabdesc['viewsource'] = _("View Source");
+ $tabdesc['extract'] = _("Extract");
+ $tabdesc['make'] = _("Make");
+ $tabdesc['commit'] = _("Commit");
+ $tabdesc['reset'] = _("Reset");
+
+ foreach ($tabdesc as $cat => $desc) {
+ $perms['tree']['translation'][$cat] = array();
+ $perms['title']['translation:' . $cat] = $desc;
+ }
+
+ return $perms;
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+/* Check for a prior definition of HORDE_BASE (perhaps by an auto_prepend_file
+ * definition for site customization). */
+if (!defined('HORDE_BASE')) {
+ @define('HORDE_BASE', dirname(__FILE__) . '/../..');
+}
+
+if (!defined('BABEL_BASE')) {
+ @define('BABEL_BASE', dirname(__FILE__) . '/..');
+}
+
+/* Load the Horde Framework core, and set up inclusion paths. */
+require_once HORDE_BASE . '/lib/core.php';
+
+/* Notification system. */
+$notification = &Notification::singleton();
+$notification->attach('status');
+
+/* Registry. */
+$registry = &Registry::singleton();
+
+if (is_a(($pushed = $registry->pushApp('translation', !defined('AUTH_HANDLER'))), 'PEAR_Error')) {
+ if ($pushed->getCode() == 'permission_denied') {
+ Horde::authenticationFailureRedirect();
+ }
+ Horde::fatal($pushed, __FILE__, __LINE__, false);
+}
+
+$conf = &$GLOBALS['conf'];
+@define('BABEL_TEMPLATES', $registry->get('templates'));
+
+/* Horde base libraries */
+require_once 'Horde/Secret.php';
+
+/* Translation base library */
+require_once BABEL_BASE . '/lib/Translation.php';
+require_once BABEL_BASE . '/lib/Translate.php';
+require_once BABEL_BASE . '/lib/Translate_Help.php';
+require_once BABEL_BASE . '/lib/Display.php';
+
+/* Help */
+require_once 'Horde/Help.php';
+
+/* Menu */
+require_once 'Horde/Menu.php';
+
+/* Gettext (PO) */
+require_once BABEL_BASE . '/lib/Gettext/PO.php';
+
+/* Form and Variables */
+require_once 'Horde/Form.php';
+require_once 'Horde/Variables.php';
+require_once 'Horde/Form/Renderer.php';
+require_once 'Horde/Form/Action.php';
+
+/* Tabs and Pager UI */
+require_once 'Horde/UI/Tabs.php';
+require_once 'Horde/UI/Pager.php';
+
+/* Templates */
+require_once 'Horde/Template.php';
+$template = &new Horde_Template();
+
+/* Module selection */
+$app = Util::getFormData('module');
+
+/* Language selection */
+if (($lang = Util::getFormData('display_language')) !== null) {
+ $_SESSION['translation']['language'] = $lang;
+} elseif (isset($_SESSION['translation']['language'])) {
+ $lang = $_SESSION['translation']['language'];
+} else {
+
+ $tests = $nls['languages'];
+
+ // Unset English
+ unset($tests['en_US']);
+
+ foreach($tests as $dir => $desc) {
+ if (!Translation::hasPermission("language:$dir")) {
+ continue;
+ } else {
+ $lang = $dir;
+ break;
+ }
+ }
+ $_SESSION['translation']['language'] = $lang;
+}
+
+/* Set up the template fields. */
+$template->set('menu', Translation::getMenu('string'));
+$template->set('notify', Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+$template->set('lang', Translation::displayLanguage());
+$fmenu = Translation::LanguageSelection();
+
+// Only display the Module Selection widget if an application has been set
+if ($app) {
+ $fmenu .= Translation::ModuleSelection();
+}
+$template->set('fmenu', $fmenu);
+
+if ($lang && !Translation::hasPermission("language:$lang")) {
+ Horde::fatal(sprintf(_("Access forbidden to '%s'."), $lang), __FILE__, __LINE__, true);
+}
+
+if ($app && !Translation::hasPermission("module:$app")) {
+ Horde::fatal(sprintf(_("Access forbidden to '%s'."), $app), __FILE__, __LINE__, true);
+}
+
+/* Custom sort function */
+function my_usort_function($a, $b)
+{
+ if ($a[1] > $b[1]) { return -1; }
+ if ($a[1] < $b[1]) { return 1; }
+ return 0;
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+set_time_limit(0);
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+if ($app) {
+ /* Render the page. */
+ Translation::RB_init();
+}
+
+require BABEL_TEMPLATES . '/common-header.inc';
+
+if ($app) {
+ Translation::RB_start(30);
+}
+
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+$vars = &Variables::getDefaultVariables();
+
+/* Create upload form */
+$form = &new Horde_Form($vars, _("Make Translation"), 'make');
+
+if (!$app) {
+ $form->setButtons(_("Make"));
+ $form->addVariable(_("Module"), 'module', 'enum', true, false, null, array(Translation::listApps(true), true));
+ $form->addVariable('', '', 'spacer', true);
+
+ $renderer_params = array();
+ $renderer = &new Horde_Form_Renderer($renderer_params);
+ $renderer->setAttrColumnWidth('20%');
+
+ $form->renderActive($renderer, $vars, Horde::selfURL(), 'post');
+} else {
+
+ if ($app != 'ALL') {
+ $module = $app;
+ }
+
+ Translate::sanity_check();
+
+ /* Searching applications */
+ Translate::check_binaries();
+
+ Translate_Display::info(sprintf(_("Searching Horde applications in %s"), realpath(HORDE_BASE)));
+ $dirs = Translate::search_applications();
+
+ $apps = Translate::strip_horde($dirs);
+ $apps[0] = 'horde';
+ Translate_Display::info(_("Found applications:"));
+ Translate_Display::info(wordwrap(implode(', ', $apps)), false);
+ Translate_Display::info();
+
+ Translate_Display::header(_("Generate Compendium ..."));
+ Translate::compendium();
+ Translate_Display::info();
+
+ Translate::cleanup(true);
+ Translate_Display::info();
+ Translate::make();
+
+ Translation::RB_close();
+}
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+msgid ""
+msgstr ""
+"Project-Id-Version: Translation ???\n"
+"Report-Msgid-Bugs-To: support@scopserv.com\n"
+"POT-Creation-Date: 2009-01-18 17:15-0500\n"
+"PO-Revision-Date: 2009-01-12 23:02-0500\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: i18n@lists.horde.org\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: lib/Translate.php:257 lib/Translate.php:276
+#, php-format
+msgid "%s is not writable."
+msgstr "%s n'est pas accessible en écriture."
+
+#: extract.php:65 lib/Translate.php:94 lib/Translate.php:117
+#, php-format
+msgid "%s not found."
+msgstr "%s non trouvé."
+
+#: lib/Translate.php:428
+#, php-format
+msgid "%s not found. Run 'Extract' first."
+msgstr "%s non trouvé. Veuillez faire une 'Extraction'."
+
+#: view.php:301
+#, php-format
+msgid "%s to %s of %s"
+msgstr "%s à %s de %s"
+
+#: lib/Translate.php:302
+#, php-format
+msgid ""
+"<gettext> tag not closed in file %s.\n"
+"Opening tag found in line %d."
+msgstr ""
+"La balise <gettext> n'est pas fermé correctement dans le fichier %s. Balise "
+"d'ouverture trouvé à la ligne %d."
+
+#: viewsource.php:30
+#, php-format
+msgid "Access denied to %s"
+msgstr "Accès refusé à %s"
+
+#: lib/base.php:112 lib/base.php:116
+#, php-format
+msgid "Access forbidden to '%s'."
+msgstr "Accès refusé à '%s'."
+
+#: view.php:223 view.php:225
+msgid "All"
+msgstr "Tout"
+
+#: lib/Translation.php:53 lib/Translation.php:135
+msgid "All Applications"
+msgstr "Toutes les applications"
+
+#: templates/index.php:50
+msgid "Build binary MO files from the specified PO files."
+msgstr "Construire les fichiers binaire MO à partir des fichiers PO spécifié."
+
+#: lib/Translate.php:467
+#, php-format
+msgid "Building MO files for module %s..."
+msgstr "Génération des fichiers MO pour le module %s ..."
+
+#: lib/Translate.php:479
+#, php-format
+msgid "Building locale %s..."
+msgstr "Généré locale %s ..."
+
+#: lib/Translation.php:292
+#, php-format
+msgid "Can take up to %d minutes !"
+msgstr "Peut prendre jusqu'Ã %d minutes !"
+
+#: lib/Translation.php:286
+#, php-format
+msgid "Can take up to %d seconds !"
+msgstr "Peut prendre jusqu'Ã %d secondes !"
+
+#: lib/Translation.php:290
+msgid "Can take up to 1 minute !"
+msgstr "Peut prendre jusqu'Ã 1 minute !"
+
+#: view.php:442
+msgid "Cancel"
+msgstr "Annuler"
+
+#: lib/Translate.php:626
+#, php-format
+msgid "Cleaning up PO files for module %s..."
+msgstr "Nettoyage des fichiers PO pour le module %s ..."
+
+#: lib/Translate.php:638
+#, php-format
+msgid "Cleaning up locale %s..."
+msgstr "Netoyage de la locale %s ..."
+
+#: commit.php:42 templates/index.php:55 lib/api.php:66
+msgid "Commit"
+msgstr "Validez"
+
+#: commit.php:36
+msgid "Commit PO files ..."
+msgstr "Validez les fichier PO ..."
+
+#: templates/index.php:56
+msgid "Commit translations to the SVN server."
+msgstr "Validez (commit) les traductions sur le serveur SVN."
+
+#: extract.php:114 lib/Translate.php:374 lib/Translate.php:405
+#: lib/Translate.php:446 lib/Translate.php:536 lib/Translate.php:584
+#: lib/Translate.php:646
+msgid "Done!"
+msgstr "Terminé!"
+
+#: templates/index.php:25 lib/api.php:59
+msgid "Download"
+msgstr "Télécharger"
+
+#: download.php:14
+msgid "Download File"
+msgstr "Télécharger Fichier"
+
+#: templates/index.php:26
+msgid "Download all current PO files."
+msgstr "Télécharger tous les fichiers PO."
+
+#: edit.php:78
+msgid "Edit"
+msgstr "Modifier"
+
+#: view.php:164
+msgid "Edit Header"
+msgstr "Modifier Entête"
+
+#: view.php:229
+msgid "Edit Mode"
+msgstr "Mode Édition"
+
+#: edit.php:52 view.php:438
+msgid "Edit Translation"
+msgstr "Modifier Traduction"
+
+#: lib/Translate.php:347 lib/Translate.php:386
+msgid "Error: No locale specified."
+msgstr "Erreur: Il n'y a pas de locale de spécifié."
+
+#: extract.php:36 templates/index.php:43 lib/api.php:64
+msgid "Extract"
+msgstr "Extraire"
+
+#: extract.php:33
+msgid "Extract Translation"
+msgstr "Extraire Traduction"
+
+#: lib/Translate.php:242
+#, php-format
+msgid "Extracting from %s... "
+msgstr "Extraction à partir de %s ..."
+
+#: lib/Translate.php:407 lib/Translate.php:427 lib/Translate.php:448
+#: lib/Translate.php:564 lib/Translate.php:586 lib/Translate.php:649
+msgid "Failed!"
+msgstr "Échoué!"
+
+#: config/hooks.php:24
+msgid "File doesn't exist ... "
+msgstr "Fichier non existant ..."
+
+#: view.php:220
+msgid "Filter: "
+msgstr "Filtres: "
+
+#: commit.php:32 make.php:61 reset.php:34 extract.php:92
+msgid "Found applications:"
+msgstr "Applications Trouvée :"
+
+#: lib/Translation.php:24
+#, php-format
+msgid "Function doesn't exist: %s"
+msgstr "Fonction non-existante: %s"
+
+#: view.php:198 view.php:244 view.php:246 stats.php:81
+msgid "Fuzzy"
+msgstr "Floue"
+
+#: make.php:65 extract.php:105
+msgid "Generate Compendium ..."
+msgstr "Génération du Compendium ..."
+
+#: templates/index.php:44
+msgid "Generate and merge PO files."
+msgstr "Générer et fusionner les fichier PO."
+
+#: templates/index.php:38
+msgid "Get statistics about translations."
+msgstr "Obtenir les statistiques concernant les traductions."
+
+#: extract.php:50 lib/Translate.php:80
+msgid "Gettext extension not found!"
+msgstr "Extension Gettext non trouvé!"
+
+#: lib/Translation.php:27
+msgid "Hook file doesn't exist"
+msgstr "Fichier Hook non existant"
+
+#: commit.php:19 reset.php:24 stats.php:42 extract.php:46
+msgid "Horde translation generator"
+msgstr "Générateur de traduction Horde"
+
+#: lib/Translate.php:425
+#, php-format
+msgid "Initializing module %s..."
+msgstr "Initialisation du module %s ..."
+
+#: upload.php:37
+msgid "Invalid Translations file. Please submit a valid PO file!"
+msgstr ""
+"Fichier de traductions invalide. Veuillez soumettre un fichier PO valide !"
+
+#: view.php:194 stats.php:77
+msgid "Language"
+msgstr "Langue"
+
+#: lib/Translation.php:38
+#, php-format
+msgid "Language: %s (%s)"
+msgstr "Langue: %s (%s)"
+
+#: lib/api.php:28
+msgid "Languages"
+msgstr "Langues"
+
+#: extract.php:54 lib/Translate.php:83
+msgid "Loading libraries..."
+msgstr "Chargement des librairies ..."
+
+#: view.php:195 stats.php:78
+msgid "Locale"
+msgstr "Locale"
+
+#: view.php:430
+#, php-format
+msgid "Locked by %s"
+msgstr "Verouillé par %s"
+
+#: make.php:36 templates/index.php:49 lib/api.php:65
+msgid "Make"
+msgstr "Générer"
+
+#: make.php:33
+msgid "Make Translation"
+msgstr "Générer la Traduction"
+
+#: lib/Translate.php:101
+msgid "Make sure that you have PEAR installed and in your include path."
+msgstr ""
+"Veuillez vérifier que vous avez installé PEAR et qu'il sont include dans le "
+"chemin d'accès (include path)."
+
+#: lib/Translate.php:390
+#, php-format
+msgid "Merging all %s.po files to the compendium... "
+msgstr "Fusionner tous les fichiers %s.po vers le compendium..."
+
+#: lib/Translate.php:371
+#, php-format
+msgid "Merging locale %s..."
+msgstr "Fusion de la locale %s ..."
+
+#: lib/Translate.php:574
+#, php-format
+msgid "Merging the PO file for %s to the compendium..."
+msgstr "Fusionner tous les fichiers PO pour %s vers le compendium..."
+
+#: lib/Translate.php:358
+#, php-format
+msgid "Merging translation for module %s..."
+msgstr "Fusion de la traduction pour le module %s ..."
+
+#: view.php:175
+msgid "Meta Informations"
+msgstr "Informations Meta"
+
+#: viewsource.php:25
+msgid "Missing filename!"
+msgstr "Nom de fichier manquant!"
+
+#: edit.php:79 make.php:37 view.php:153 upload.php:20 stats.php:33
+#: extract.php:37
+msgid "Module"
+msgstr "Module"
+
+#: lib/Translation.php:40
+#, php-format
+msgid "Module: %s"
+msgstr "Module: %s"
+
+#: lib/api.php:38
+msgid "Modules"
+msgstr "Modules"
+
+#: lib/Translate.php:130
+msgid "Not all strings will be extracted."
+msgstr "Les chaînes de caractères n'ont pas toutes été extraite."
+
+#: lib/Translate.php:333
+msgid "Not changed!"
+msgstr "Non modifié!"
+
+#: view.php:200 stats.php:83
+msgid "Obsolete"
+msgstr "Obsolète"
+
+#: lib/Translation.php:282
+msgid "Please be patient ..."
+msgstr "Veuillez patienter..."
+
+#: upload.php:31
+msgid "Please select module of translations PO file!"
+msgstr "Veuillez sélectionner le module pour le fichier PO de traduction."
+
+#: templates/index.php:61 lib/api.php:67
+msgid "Reset"
+msgstr "Réinitialiser"
+
+#: reset.php:43
+msgid "Reset PO file on "
+msgstr "Ré-initialisation du fichier PO pour "
+
+#: reset.php:38
+msgid "Reset PO files ..."
+msgstr "Ré-initialisation des fichiers PO ..."
+
+#: lib/Translate.php:595
+msgid "Results (including Horde):"
+msgstr "Résultats (incluant Horde):"
+
+#: lib/Translate.php:593
+msgid "Results:"
+msgstr "Résultats:"
+
+#: edit.php:55 edit.php:83 view.php:440
+msgid "Save"
+msgstr "Enregistrer"
+
+#: view.php:267
+msgid "Search"
+msgstr "Recherche"
+
+#: lib/Translate.php:110
+msgid "Searching gettext binaries..."
+msgstr "Recherche des exécutables pour gettext ..."
+
+#: lib/Translate.php:364 lib/Translate.php:472 lib/Translate.php:631
+msgid "Skipped..."
+msgstr "Ignoré..."
+
+#: view.php:188
+msgid "Statistic"
+msgstr "Statistique"
+
+#: templates/index.php:37 lib/api.php:61
+msgid "Statistics"
+msgstr "Statistiques"
+
+#: view.php:196 stats.php:79
+msgid "Status"
+msgstr "Statut"
+
+#: templates/index.php:62
+msgid ""
+"The reset procedure will delete all PO files from the server for all modules "
+"and restore from SVN server."
+msgstr ""
+"La procédure de ré-initialisation effacera tous les fichiers PO du serveur "
+"et les restaurera à partir du serveur SVN."
+
+#: view.php:197 view.php:233 view.php:235 stats.php:80
+msgid "Translated"
+msgstr "Traduit"
+
+#: view.php:302
+msgid "Translations"
+msgstr "Traductions"
+
+#: upload.php:21
+msgid "Translations File (.PO)"
+msgstr "Fichier de Traduction (.PO)"
+
+#: view.php:199 view.php:254 view.php:256 stats.php:82
+msgid "Untranslated"
+msgstr "Non Traduit"
+
+#: lib/Translate.php:330
+msgid "Updated!"
+msgstr "Mis à jour!"
+
+#: upload.php:19 upload.php:25 templates/index.php:31 lib/api.php:60
+msgid "Upload"
+msgstr "Envoi"
+
+#: templates/index.php:32
+msgid "Upload new PO files."
+msgstr "Envoyer nouveaux fichiers PO."
+
+#: upload.php:18
+msgid "Upload new Translation"
+msgstr "Envoi une nouvelle Traduction"
+
+#: upload.php:45
+#, php-format
+msgid "Upload successful for %s (%s)"
+msgstr "Transfert de fichier réussi pour %s (%s)"
+
+#: view.php:152 stats.php:32 templates/index.php:19
+msgid "View"
+msgstr "Voir"
+
+#: lib/api.php:63
+msgid "View Source"
+msgstr "Visionner Source"
+
+#: stats.php:29
+msgid "View Statistics"
+msgstr "Voir Statistiques"
+
+#: view.php:149
+msgid "View Translation"
+msgstr "Voir Traduction"
+
+#: templates/index.php:20
+msgid "View all translations."
+msgstr "Visionner toutes les traductions."
+
+#: viewsource.php:39
+#, php-format
+msgid "View source: %s"
+msgstr "Voir source: %s"
+
+#: lib/api.php:62
+msgid "View/Edit"
+msgstr "Visionner/Modifier"
+
+#: lib/Translate.php:484
+#, php-format
+msgid "Warning: Could not create locale directory for locale %s:"
+msgstr "Avertissement: Impossible de créer le dossier pour le locale %s :"
+
+#: lib/Translate.php:129
+msgid ""
+"Warning: Your gettext version is too old and does not support PHP natively."
+msgstr ""
+"Avertissement: Votre version de gettext est trop ancienne et ne supporte pas "
+"PHP nativement."
+
+#: lib/Translate.php:506
+msgid "Warning: an error has occured:"
+msgstr "Avertissement: Une erreur s'est produite:"
+
+#: lib/Translate.php:520
+#, php-format
+msgid "Warning: the Horde PO file for the locale %s does not exist:"
+msgstr "Avertissement: le fichier PO de Horde pour le locale %s n'existe pas:"
+
+#: lib/Translation.php:214
+msgid "_Extract"
+msgstr "_Extraire"
+
+#: lib/Translation.php:197
+msgid "_General"
+msgstr "_Général"
+
+#: lib/Translation.php:220
+msgid "_Make"
+msgstr "Gé_nérer"
+
+#: lib/Translation.php:208
+msgid "_Stats"
+msgstr "_Stats"
+
+#: lib/Translation.php:226
+msgid "_Upload"
+msgstr "En_voi"
+
+#: lib/Translation.php:202
+msgid "_View"
+msgstr "_Visionner"
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Horde Project
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: support@scopserv.com\n"
+"POT-Creation-Date: 2009-01-18 17:15-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: lib/Translate.php:257 lib/Translate.php:276
+#, php-format
+msgid "%s is not writable."
+msgstr ""
+
+#: extract.php:65 lib/Translate.php:94 lib/Translate.php:117
+#, php-format
+msgid "%s not found."
+msgstr ""
+
+#: lib/Translate.php:428
+#, php-format
+msgid "%s not found. Run 'Extract' first."
+msgstr ""
+
+#: view.php:301
+#, php-format
+msgid "%s to %s of %s"
+msgstr ""
+
+#: lib/Translate.php:302
+#, php-format
+msgid ""
+"<gettext> tag not closed in file %s.\n"
+"Opening tag found in line %d."
+msgstr ""
+
+#: viewsource.php:30
+#, php-format
+msgid "Access denied to %s"
+msgstr ""
+
+#: lib/base.php:112 lib/base.php:116
+#, php-format
+msgid "Access forbidden to '%s'."
+msgstr ""
+
+#: view.php:223 view.php:225
+msgid "All"
+msgstr ""
+
+#: lib/Translation.php:53 lib/Translation.php:135
+msgid "All Applications"
+msgstr ""
+
+#: templates/index.php:50
+msgid "Build binary MO files from the specified PO files."
+msgstr ""
+
+#: lib/Translate.php:467
+#, php-format
+msgid "Building MO files for module %s..."
+msgstr ""
+
+#: lib/Translate.php:479
+#, php-format
+msgid "Building locale %s..."
+msgstr ""
+
+#: lib/Translation.php:292
+#, php-format
+msgid "Can take up to %d minutes !"
+msgstr ""
+
+#: lib/Translation.php:286
+#, php-format
+msgid "Can take up to %d seconds !"
+msgstr ""
+
+#: lib/Translation.php:290
+msgid "Can take up to 1 minute !"
+msgstr ""
+
+#: view.php:442
+msgid "Cancel"
+msgstr ""
+
+#: lib/Translate.php:626
+#, php-format
+msgid "Cleaning up PO files for module %s..."
+msgstr ""
+
+#: lib/Translate.php:638
+#, php-format
+msgid "Cleaning up locale %s..."
+msgstr ""
+
+#: commit.php:42 templates/index.php:55 lib/api.php:66
+msgid "Commit"
+msgstr ""
+
+#: commit.php:36
+msgid "Commit PO files ..."
+msgstr ""
+
+#: templates/index.php:56
+msgid "Commit translations to the SVN server."
+msgstr ""
+
+#: extract.php:114 lib/Translate.php:374 lib/Translate.php:405
+#: lib/Translate.php:446 lib/Translate.php:536 lib/Translate.php:584
+#: lib/Translate.php:646
+msgid "Done!"
+msgstr ""
+
+#: templates/index.php:25 lib/api.php:59
+msgid "Download"
+msgstr ""
+
+#: download.php:14
+msgid "Download File"
+msgstr ""
+
+#: templates/index.php:26
+msgid "Download all current PO files."
+msgstr ""
+
+#: edit.php:78
+msgid "Edit"
+msgstr ""
+
+#: view.php:164
+msgid "Edit Header"
+msgstr ""
+
+#: view.php:229
+msgid "Edit Mode"
+msgstr ""
+
+#: edit.php:52 view.php:438
+msgid "Edit Translation"
+msgstr ""
+
+#: lib/Translate.php:347 lib/Translate.php:386
+msgid "Error: No locale specified."
+msgstr ""
+
+#: extract.php:36 templates/index.php:43 lib/api.php:64
+msgid "Extract"
+msgstr ""
+
+#: extract.php:33
+msgid "Extract Translation"
+msgstr ""
+
+#: lib/Translate.php:242
+#, php-format
+msgid "Extracting from %s... "
+msgstr ""
+
+#: lib/Translate.php:407 lib/Translate.php:427 lib/Translate.php:448
+#: lib/Translate.php:564 lib/Translate.php:586 lib/Translate.php:649
+msgid "Failed!"
+msgstr ""
+
+#: config/hooks.php:24
+msgid "File doesn't exist ... "
+msgstr ""
+
+#: view.php:220
+msgid "Filter: "
+msgstr ""
+
+#: commit.php:32 make.php:61 reset.php:34 extract.php:92
+msgid "Found applications:"
+msgstr ""
+
+#: lib/Translation.php:24
+#, php-format
+msgid "Function doesn't exist: %s"
+msgstr ""
+
+#: view.php:198 view.php:244 view.php:246 stats.php:81
+msgid "Fuzzy"
+msgstr ""
+
+#: make.php:65 extract.php:105
+msgid "Generate Compendium ..."
+msgstr ""
+
+#: templates/index.php:44
+msgid "Generate and merge PO files."
+msgstr ""
+
+#: templates/index.php:38
+msgid "Get statistics about translations."
+msgstr ""
+
+#: extract.php:50 lib/Translate.php:80
+msgid "Gettext extension not found!"
+msgstr ""
+
+#: lib/Translation.php:27
+msgid "Hook file doesn't exist"
+msgstr ""
+
+#: commit.php:19 reset.php:24 stats.php:42 extract.php:46
+msgid "Horde translation generator"
+msgstr ""
+
+#: lib/Translate.php:425
+#, php-format
+msgid "Initializing module %s..."
+msgstr ""
+
+#: upload.php:37
+msgid "Invalid Translations file. Please submit a valid PO file!"
+msgstr ""
+
+#: view.php:194 stats.php:77
+msgid "Language"
+msgstr ""
+
+#: lib/Translation.php:38
+#, php-format
+msgid "Language: %s (%s)"
+msgstr ""
+
+#: lib/api.php:28
+msgid "Languages"
+msgstr ""
+
+#: extract.php:54 lib/Translate.php:83
+msgid "Loading libraries..."
+msgstr ""
+
+#: view.php:195 stats.php:78
+msgid "Locale"
+msgstr ""
+
+#: view.php:430
+#, php-format
+msgid "Locked by %s"
+msgstr ""
+
+#: make.php:36 templates/index.php:49 lib/api.php:65
+msgid "Make"
+msgstr ""
+
+#: make.php:33
+msgid "Make Translation"
+msgstr ""
+
+#: lib/Translate.php:101
+msgid "Make sure that you have PEAR installed and in your include path."
+msgstr ""
+
+#: lib/Translate.php:390
+#, php-format
+msgid "Merging all %s.po files to the compendium... "
+msgstr ""
+
+#: lib/Translate.php:371
+#, php-format
+msgid "Merging locale %s..."
+msgstr ""
+
+#: lib/Translate.php:574
+#, php-format
+msgid "Merging the PO file for %s to the compendium..."
+msgstr ""
+
+#: lib/Translate.php:358
+#, php-format
+msgid "Merging translation for module %s..."
+msgstr ""
+
+#: view.php:175
+msgid "Meta Informations"
+msgstr ""
+
+#: viewsource.php:25
+msgid "Missing filename!"
+msgstr ""
+
+#: edit.php:79 make.php:37 view.php:153 upload.php:20 stats.php:33
+#: extract.php:37
+msgid "Module"
+msgstr ""
+
+#: lib/Translation.php:40
+#, php-format
+msgid "Module: %s"
+msgstr ""
+
+#: lib/api.php:38
+msgid "Modules"
+msgstr ""
+
+#: lib/Translate.php:130
+msgid "Not all strings will be extracted."
+msgstr ""
+
+#: lib/Translate.php:333
+msgid "Not changed!"
+msgstr ""
+
+#: view.php:200 stats.php:83
+msgid "Obsolete"
+msgstr ""
+
+#: lib/Translation.php:282
+msgid "Please be patient ..."
+msgstr ""
+
+#: upload.php:31
+msgid "Please select module of translations PO file!"
+msgstr ""
+
+#: templates/index.php:61 lib/api.php:67
+msgid "Reset"
+msgstr ""
+
+#: reset.php:43
+msgid "Reset PO file on "
+msgstr ""
+
+#: reset.php:38
+msgid "Reset PO files ..."
+msgstr ""
+
+#: lib/Translate.php:595
+msgid "Results (including Horde):"
+msgstr ""
+
+#: lib/Translate.php:593
+msgid "Results:"
+msgstr ""
+
+#: edit.php:55 edit.php:83 view.php:440
+msgid "Save"
+msgstr ""
+
+#: view.php:267
+msgid "Search"
+msgstr ""
+
+#: lib/Translate.php:110
+msgid "Searching gettext binaries..."
+msgstr ""
+
+#: lib/Translate.php:364 lib/Translate.php:472 lib/Translate.php:631
+msgid "Skipped..."
+msgstr ""
+
+#: view.php:188
+msgid "Statistic"
+msgstr ""
+
+#: templates/index.php:37 lib/api.php:61
+msgid "Statistics"
+msgstr ""
+
+#: view.php:196 stats.php:79
+msgid "Status"
+msgstr ""
+
+#: templates/index.php:62
+msgid ""
+"The reset procedure will delete all PO files from the server for all modules "
+"and restore from SVN server."
+msgstr ""
+
+#: view.php:197 view.php:233 view.php:235 stats.php:80
+msgid "Translated"
+msgstr ""
+
+#: view.php:302
+msgid "Translations"
+msgstr ""
+
+#: upload.php:21
+msgid "Translations File (.PO)"
+msgstr ""
+
+#: view.php:199 view.php:254 view.php:256 stats.php:82
+msgid "Untranslated"
+msgstr ""
+
+#: lib/Translate.php:330
+msgid "Updated!"
+msgstr ""
+
+#: upload.php:19 upload.php:25 templates/index.php:31 lib/api.php:60
+msgid "Upload"
+msgstr ""
+
+#: templates/index.php:32
+msgid "Upload new PO files."
+msgstr ""
+
+#: upload.php:18
+msgid "Upload new Translation"
+msgstr ""
+
+#: upload.php:45
+#, php-format
+msgid "Upload successful for %s (%s)"
+msgstr ""
+
+#: view.php:152 stats.php:32 templates/index.php:19
+msgid "View"
+msgstr ""
+
+#: lib/api.php:63
+msgid "View Source"
+msgstr ""
+
+#: stats.php:29
+msgid "View Statistics"
+msgstr ""
+
+#: view.php:149
+msgid "View Translation"
+msgstr ""
+
+#: templates/index.php:20
+msgid "View all translations."
+msgstr ""
+
+#: viewsource.php:39
+#, php-format
+msgid "View source: %s"
+msgstr ""
+
+#: lib/api.php:62
+msgid "View/Edit"
+msgstr ""
+
+#: lib/Translate.php:484
+#, php-format
+msgid "Warning: Could not create locale directory for locale %s:"
+msgstr ""
+
+#: lib/Translate.php:129
+msgid ""
+"Warning: Your gettext version is too old and does not support PHP natively."
+msgstr ""
+
+#: lib/Translate.php:506
+msgid "Warning: an error has occured:"
+msgstr ""
+
+#: lib/Translate.php:520
+#, php-format
+msgid "Warning: the Horde PO file for the locale %s does not exist:"
+msgstr ""
+
+#: lib/Translation.php:214
+msgid "_Extract"
+msgstr ""
+
+#: lib/Translation.php:197
+msgid "_General"
+msgstr ""
+
+#: lib/Translation.php:220
+msgid "_Make"
+msgstr ""
+
+#: lib/Translation.php:208
+msgid "_Stats"
+msgstr ""
+
+#: lib/Translation.php:226
+msgid "_Upload"
+msgstr ""
+
+#: lib/Translation.php:202
+msgid "_View"
+msgstr ""
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+Translation::RB_init();
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+
+Translation::RB_start(15);
+
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+Translate_Display::header(_("Horde translation generator"));
+
+Translate_Display::info(sprintf('Searching Horde applications in %s', realpath(HORDE_BASE)));
+
+Translate::check_binaries();
+
+$dirs = Translate::search_applications();
+
+$apps = Translate::strip_horde($dirs);
+$apps[0] = 'horde';
+Translate_Display::info(_("Found applications:"));
+Translate_Display::info(wordwrap(implode(', ', $apps)), false);
+Translate_Display::info();
+
+Translate_Display::header(_("Reset PO files ..."));
+foreach($dirs as $d => $dir) {
+ $dir = realpath($dir);
+ $po = $dir . '/po/' . $lang . '.po';
+
+ Translate_Display::info(_("Reset PO file on ") . $po);
+ Translation::callHook('reset', $po);
+
+}
+
+Translate_Display::info();
+Translate::cleanup(true);
+
+Translation::RB_close();
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+#!/usr/bin/php -q
+<?php
+/**
+ * Translation helper application for the Horde framework.
+ *
+ * For usage information call:
+ * ./translation.php help
+ *
+ * $Horde: horde/po/translation.php,v 1.105 2005/12/30 01:05:51 selsky Exp $
+ */
+
+function footer()
+{
+ global $c, $curdir;
+
+ $c->writeln();
+ $c->writeln('Please report any bugs to i18n@lists.horde.org.');
+
+ chdir($curdir);
+ exit;
+}
+
+function usage()
+{
+ global $options, $c;
+
+ if (count($options[1]) &&
+ ($options[1][0] == 'help' && !empty($options[1][1]) ||
+ !empty($options[1][0]) && in_array($options[1][0], array('commit', 'compendium', 'extract', 'init', 'make', 'merge')))) {
+ if ($options[1][0] == 'help') {
+ $cmd = $options[1][1];
+ } else {
+ $cmd = $options[1][0];
+ }
+ $c->writeln('Usage:' . ' translation.php [options] ' . $cmd . ' [command-options]');
+ if (!empty($cmd)) {
+ $c->writeln();
+ $c->writeln('Command options:');
+ }
+ switch ($cmd) {
+ case 'cleanup':
+ $c->writeln(' -l, --locale=ll_CC Use only this locale.');
+ $c->writeln(' -m, --module=MODULE Cleanup PO files only for this (Horde) module.');
+ break;
+ case 'commit':
+ case 'commit-help':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Commit translations only for this (Horde) module.');
+ $c->writeln(' -M, --message=MESSAGE Use this commit message instead of the default ones.');
+ $c->writeln(' -n, --new This is a new translation, commit also CREDITS,');
+ $c->writeln(' CHANGES and nls.php.dist.');
+ break;
+ case 'compendium':
+ $c->writeln(' -a, --add=FILE Add this PO file to the compendium. Useful to');
+ $c->writeln(' include a compendium from a different branch to');
+ $c->writeln(' the generated compendium.');
+ $c->writeln(' -d, --directory=DIR Create compendium in this directory.');
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ break;
+ case 'extract':
+ $c->writeln(' -m, --module=MODULE Generate POT file only for this (Horde) module.');
+ break;
+ case 'init':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Create a PO file only for this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
+ $c->writeln(' one (compendium.po in the horde/po directory).');
+ $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
+ break;
+ case 'make':
+ $c->writeln(' -l, --locale=ll_CC Use only this locale.');
+ $c->writeln(' -m, --module=MODULE Build MO files only for this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Merge new translations to this compendium file');
+ $c->writeln(' instead of the default one (compendium.po in the');
+ $c->writeln(' horde/po directory.');
+ $c->writeln(' -n, --no-compendium Don\'t merge new translations to the compendium.');
+ $c->writeln(' -s, --statistics Save translation statistics in a local file.');
+ break;
+ case 'make-help':
+ case 'update-help':
+ $c->writeln(' -l, --locale=ll_CC Use only this locale.');
+ $c->writeln(' -m, --module=MODULE Update help files only for this (Horde) module.');
+ break;
+ case 'merge':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Merge PO files only for this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
+ $c->writeln(' one (compendium.po in the horde/po directory).');
+ $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
+ break;
+ case 'update':
+ $c->writeln(' -l, --locale=ll_CC Use this locale.');
+ $c->writeln(' -m, --module=MODULE Update only this (Horde) module.');
+ $c->writeln(' -c, --compendium=FILE Use this compendium file instead of the default');
+ $c->writeln(' one (compendium.po in the horde/po directory).');
+ $c->writeln(' -n, --no-compendium Don\'t use a compendium.');
+ break;
+ }
+ } else {
+ $c->writeln('Usage:' . ' translation.php [options] command [command-options]');
+ $c->writeln(str_repeat(' ', String::length('Usage:')) . ' translation.php [help|-h|--help] [command]');
+ $c->writeln();
+ $c->writeln('Helper application to create and maintain translations for the Horde');
+ $c->writeln('framework and its applications.');
+ $c->writeln('For an introduction read the file README in this directory.');
+ $c->writeln();
+ $c->writeln('Commands:');
+ $c->writeln(' help Show this help message.');
+ $c->writeln(' compendium Rebuild the compendium file. Warning: This overwrites the');
+ $c->writeln(' current compendium.');
+ $c->writeln(' extract Generate PO template (.pot) files.');
+ $c->writeln(' init Create one or more PO files for a new locale. Warning: This');
+ $c->writeln(' overwrites the existing PO files of this locale.');
+ $c->writeln(' merge Merge the current PO file with the current PO template file.');
+ $c->writeln(' update Run extract and merge sequent.');
+ $c->writeln(' update-help Extract all new and changed entries from the English XML help');
+ $c->writeln(' file and merge them with the existing ones.');
+ $c->writeln(' cleanup Cleans the PO files up from untranslated and obsolete entries.');
+ $c->writeln(' make Build binary MO files from the specified PO files.');
+ $c->writeln(' make-help Mark all entries in the XML help file being up-to-date and');
+ $c->writeln(' prepare the file for the next execution of update-help. You');
+ $c->writeln(' should only run make-help AFTER update-help and revising the');
+ $c->writeln(' help file.');
+ $c->writeln(' commit Commit translations to the CVS server.');
+ $c->writeln(' commit-help Commit help files to the CVS server.');
+ }
+ $c->writeln();
+ $c->writeln('Options:');
+ $c->writeln(' -b, --base=/PATH Full path to the (Horde) base directory that should be');
+ $c->writeln(' used.');
+ $c->writeln(' -d, --debug Show error messages from the executed binaries.');
+ $c->writeln(' -h, --help Show this help message.');
+ $c->writeln(' -t, --test Show the executed commands but don\'t run anything.');
+}
+
+function check_binaries()
+{
+ global $gettext_version, $c;
+
+ $c->writeln('Searching gettext binaries...');
+ require_once 'System.php';
+ foreach (array('gettext', 'msgattrib', 'msgcat', 'msgcomm', 'msgfmt', 'msginit', 'msgmerge', 'xgettext') as $binary) {
+ echo $binary . '... ';
+ $GLOBALS[$binary] = System::which($binary);
+ if ($GLOBALS[$binary]) {
+ $c->writeln($c->green('found: ') . $GLOBALS[$binary]);
+ } else {
+ $c->writeln($c->red('not found'));
+ footer();
+ }
+ }
+ $c->writeln();
+
+ $out = '';
+ exec($GLOBALS['gettext'] . ' --version', $out, $ret);
+ $split = explode(' ', $out[0]);
+ echo 'gettext version: ' . $split[count($split) - 1];
+ $gettext_version = explode('.', $split[count($split) - 1]);
+ if ($gettext_version[0] == 0 && $gettext_version[1] < 12) {
+ $GLOBALS['php_support'] = false;
+ $c->writeln();
+ $c->writeln($c->red('Warning: ') . 'Your gettext version is too old and does not support PHP natively.');
+ $c->writeln('Not all strings will be extracted.');
+ } else {
+ $GLOBALS['php_support'] = true;
+ $c->writeln($c->green(' ' . 'OK'));
+ }
+ $c->writeln();
+}
+
+function search_file($file, $dir = '.', $local = false)
+{
+ static $ff;
+ if (!isset($ff)) {
+ $ff = &new File_Find();
+ }
+
+ if (substr($file, 0, 1) != DS) {
+ $file = "/$file/";
+ }
+
+ if ($local) {
+ $files = $ff->glob($file, $dir, 'perl');
+ $files = array_map(create_function('$file', 'return "' . $dir . DS . '" . $file;'), $files);
+ return $files;
+ } else {
+ return $ff->search($file, $dir, 'perl');
+ }
+}
+
+function search_ext($ext, $dir = '.', $local = false)
+{
+ return search_file(".+\\.$ext\$", $dir, $local);
+}
+
+function get_po_files($dir)
+{
+ $langs = search_ext('po', $dir);
+ if (($key = array_search($dir . DS . 'messages.po', $langs)) !== false) {
+ unset($langs[$key]);
+ }
+ if (($key = array_search($dir . DS . 'compendium.po', $langs)) !== false) {
+ unset($langs[$key]);
+ }
+ return $langs;
+}
+
+function get_languages($dir)
+{
+ global $curdir;
+
+ chdir($dir);
+ $langs = get_po_files('po');
+ $langs = array_map(create_function('$lang', 'return str_replace("po" . DS, "", str_replace(".po", "", $lang));'), $langs);
+ chdir($curdir);
+ return $langs;
+}
+
+function search_applications()
+{
+ $dirs = array();
+ $horde = false;
+ if (@is_dir(BASE . DS . 'po')) {
+ $dirs[] = BASE;
+ $horde = true;
+ }
+ $dh = @opendir(BASE);
+ if ($dh) {
+ while ($entry = @readdir($dh)) {
+ $dir = BASE . DS . $entry;
+ if (is_dir($dir) &&
+ substr($entry, 0, 1) != '.' &&
+ fileinode(HORDE_BASE) != fileinode($dir)) {
+ $sub = opendir($dir);
+ if ($sub) {
+ while ($subentry = readdir($sub)) {
+ if ($subentry == 'po' && is_dir($dir . DS . $subentry)) {
+ $dirs[] = $dir;
+ if ($entry == 'horde') {
+ $horde = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!$horde) {
+ array_unshift($dirs, HORDE_BASE);
+ }
+ }
+
+ return $dirs;
+}
+
+function strip_horde($file)
+{
+ if (is_array($file)) {
+ return array_map(create_function('$file', 'return strip_horde($file);'), $file);
+ } else {
+ return str_replace(BASE . DS, '', $file);
+ }
+}
+
+function xtract()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $gettext_version, $silence, $curdir;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+ require_once 'Horde/Array.php';
+ if ($GLOBALS['php_support']) {
+ $language = 'PHP';
+ } else {
+ $language = 'C++';
+ }
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ echo sprintf('Extracting from %s... ', $apps[$i]);
+ chdir($dirs[$i]);
+ if ($apps[$i] == 'horde') {
+ $files = search_ext('php', '.', true);
+ foreach (array('admin', 'framework', 'lib', 'services', 'templates', 'util', 'config' . DS . 'themes') as $search_dir) {
+ $files = array_merge($files, search_ext('(php|inc|js)', $search_dir));
+ }
+ $files = array_merge($files, search_ext('(php|dist)', 'config'));
+ $sh = $GLOBALS['xgettext'] . ' --language=' . $language .
+ ' --from-code=iso-8859-1 --keyword=_ --sort-output --copyright-holder="Horde Project"';
+ if ($gettext_version[0] > 0 || $gettext_version[1] > 11) {
+ $sh .= ' --msgid-bugs-address="dev@lists.horde.org"';
+ }
+ $file = $dirs[$i] . DS . 'po' . DS . $apps[$i] . '.pot';
+ if (file_exists($file) && !is_writable($file)) {
+ $c->writeln($c->red(sprintf('%s is not writable.', $file)));
+ footer();
+ }
+ $tmp_file = $file . '.tmp.pot';
+ $sh .= ' -o ' . $tmp_file . ' ' . implode(' ', $files);
+ if (@file_exists($dirs[$i] . '/po/translation.php')) {
+ $sh .= ' po/translation.php';
+ }
+ if (!$debug) {
+ $sh .= $silence;
+ }
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) exec($sh);
+ } else {
+ $files = search_ext('(php|inc|js)');
+ $files = array_filter($files, create_function('$file', 'return substr($file, 0, 9) != "." . DS . "config" . DS;'));
+ $files = array_merge($files, search_ext('(php|dist)', 'config'));
+ $sh = $GLOBALS['xgettext'] . ' --language=' . $language .
+ ' --keyword=_ --sort-output --force-po --copyright-holder="Horde Project"';
+ if ($gettext_version[0] > 0 || $gettext_version[1] > 11) {
+ $sh .= ' --msgid-bugs-address="dev@lists.horde.org"';
+ }
+ $file = 'po' . DS . $apps[$i] . '.pot';
+ if (file_exists($file) && !is_writable($file)) {
+ $c->writeln($c->red(sprintf('%s is not writable.', $file)));
+ footer();
+ }
+ $tmp_file = $file . '.tmp.pot';
+ $sh .= ' -o ' . $tmp_file . ' ' . implode(' ', $files) . ($debug ? '' : $silence);
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) exec($sh);
+ }
+ if (file_exists($tmp_file)) {
+ $files = search_ext('html', 'templates');
+ if (!$test) $tmp = fopen($file . '.templates', 'w');
+ foreach ($files as $template) {
+ $fp = fopen($template, 'r');
+ $lineno = 0;
+ while (($line = fgets($fp, 4096)) !== false) {
+ $lineno++;
+ $offset = 0;
+ while (($left = strpos($line, '<gettext>', $offset)) !== false) {
+ $left += 9;
+ $buffer = '';
+ $linespan = 0;
+ while (($end = strpos($line, '</gettext>', $left)) === false) {
+ $buffer .= substr($line, $left);
+ $left = 0;
+ $line = fgets($fp, 4096);
+ $linespan++;
+ if ($line === false) {
+ $c->writeln($c->red(sprintf("<gettext> tag not closed in file %s.\nOpening tag found in line %d.", $template, $lineno)));
+ break 2;
+ }
+ }
+ $buffer .= substr($line, $left, $end - $left);
+ if (!$test) {
+ fwrite($tmp, "#: $template:$lineno\n");
+ fwrite($tmp, 'msgid "' . str_replace(array('"', "\n"), array('\"', "\\n\"\n\""), $buffer) . "\"\n");
+ fwrite($tmp, 'msgstr ""' . "\n\n");
+ }
+ $offset = $end + 10;
+ }
+ }
+ fclose($fp);
+ }
+ fclose($tmp);
+ $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$tmp_file\" \"$file.templates\" --output-file \"$tmp_file\"" . $silence;
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) {
+ exec($sh);
+ unlink($file . '.templates');
+ }
+ if (file_exists($file)) {
+ $diff = array_diff(file($tmp_file), file($file));
+ $diff = preg_grep('/^"POT-Creation-Date:/', $diff, PREG_GREP_INVERT);
+ }
+ }
+ if (!file_exists($file) || count($diff)) {
+ @unlink($file);
+ rename($tmp_file, $file);
+ $c->writeln($c->green('updated'));
+ } else {
+ @unlink($tmp_file);
+ $c->writeln($c->bold('not changed'));
+ }
+ chdir($curdir);
+ }
+}
+
+function merge()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ $compendium = ' --compendium="' . BASE . DS . 'po' . DS . 'compendium.po"';
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ case 'c':
+ case '--compendium':
+ $compendium = ' --compendium=' . $option[1];
+ break;
+ case 'n':
+ case '--no-compendium':
+ $compendium = '';
+ break;
+ }
+ }
+ if (!isset($lang) && !empty($compendium)) {
+ $c->writeln($c->red('Error: ' . 'No locale specified.'));
+ $c->writeln();
+ usage();
+ footer();
+ }
+
+ cleanup();
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $c->writeln(sprintf('Merging translation for module %s...', $c->bold($apps[$i])));
+ $dir = $dirs[$i] . DS . 'po' . DS;
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dir . $lang . '.po')) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Merging locale %s... ', $c->bold($locale)));
+ $sh = $GLOBALS['msgmerge'] . ' --update -v' . $compendium . ' "' . $dir . $locale . '.po" "' . $dir . $apps[$i] . '.pot"';
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) exec($sh);
+ $c->writeln($c->green('done'));
+ }
+ }
+}
+
+function status()
+{
+ return;
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ $output = "status.html";
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ case 'o':
+ case '--output':
+ $output = $option[1];
+ break;
+ }
+ }
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) {
+ continue;
+ }
+ $c->writeln(sprintf('Generating status for module %s...', $c->bold($apps[$i])));
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dirs[$i] . DS . 'po' . DS . $lang . '.po')) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Status for locale %s... ', $c->bold($locale)));
+ }
+ }
+}
+
+function compendium()
+{
+ global $cmd_options, $dirs, $debug, $test, $c, $silence;
+
+ $dir = BASE . DS . 'po' . DS;
+ $add = '';
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'd':
+ case '--directory':
+ $dir = $option[1];
+ break;
+ case 'a':
+ case '--add':
+ $add = ' ' . $option[1];
+ break;
+ }
+ }
+ if (!isset($lang)) {
+ $c->writeln($c->red('Error: ' . 'No locale specified.'));
+ $c->writeln();
+ usage();
+ footer();
+ }
+ echo sprintf('Merging all %s.po files to the compendium... ', $lang);
+ $pofiles = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ $pofile = $dirs[$i] . DS . 'po' . DS . $lang . '.po';
+ if (file_exists($pofile)) {
+ $pofiles[] = $pofile;
+ }
+ }
+ if (!empty($dir) && substr($dir, -1) != DS) {
+ $dir .= DS;
+ }
+ $sh = $GLOBALS['msgcat'] . ' --sort-output ' . implode(' ', $pofiles) . $add . ' > ' . $dir . 'compendium.po ' . ($debug ? '' : $silence);
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ } else {
+ $c->writeln($c->red('failed'));
+ }
+}
+
+function init()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+ if (empty($lang)) { $lang = getenv('LANG'); }
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) { continue; }
+ $package = ucfirst($apps[$i]);
+ $package_u = String::upper($apps[$i]);
+ @include $dirs[$i] . '/lib/version.php';
+ $version = eval('return(defined("' . $package_u . '_VERSION") ? ' . $package_u . '_VERSION : "???");');
+ echo sprintf('Initializing module %s... ', $apps[$i]);
+ if (!@file_exists($dirs[$i] . '/po/' . $apps[$i] . '.pot')) {
+ $c->writeln($c->red('failed'));
+ $c->writeln(sprintf('%s not found. Run \'translation extract\' first.', $dirs[$i] . DS . 'po' . DS . $apps[$i] . '.pot'));
+ continue;
+ }
+ $dir = $dirs[$i] . DS . 'po' . DS;
+ $sh = $GLOBALS['msginit'] . ' --no-translator -i ' . $dir . $apps[$i] . '.pot ' .
+ (!empty($lang) ? ' -o ' . $dir . $lang . '.po --locale=' . $lang : '') .
+ ($debug ? '' : $silence);
+ if (!empty($lang) && !OS_WINDOWS) {
+ $pofile = $dirs[$i] . '/po/' . $lang . '.po';
+ $sh .= "; sed 's/PACKAGE package/$package package/' $pofile " .
+ "| sed 's/PACKAGE VERSION/$package $version/' " .
+ "| sed 's/messages for PACKAGE/messages for $package/' " .
+ "| sed 's/Language-Team: none/Language-Team: i18n@lists.horde.org/' " .
+ "> $pofile.tmp";
+ }
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ rename($pofile . '.tmp', $pofile);
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ } else {
+ $c->writeln($c->red('failed'));
+ }
+ }
+}
+
+function make()
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c, $silence, $redir_err;
+
+ $compendium = BASE . DS . 'po' . DS . 'compendium.po';
+ $save_stats = false;
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ case 'c':
+ case '--compendium':
+ $compendium = $option[1];
+ break;
+ case 'n':
+ case '--no-compendium':
+ $compendium = '';
+ break;
+ case 's':
+ case '--statistics':
+ $save_stats = true;
+ break;
+ }
+ }
+ $horde = array_search('horde', $dirs);
+ $horde_msg = array();
+ $stats_array = array();
+
+ require_once 'Console/Table.php';
+ $stats = new Console_Table();
+ $stats->setHeaders(array('Module', 'Language', 'Translated', 'Fuzzy', 'Untranslated'));
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) continue;
+ $c->writeln(sprintf('Building MO files for module %s...', $c->bold($apps[$i])));
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dirs[$i] . DS . 'po' . DS . $lang . '.po')) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Building locale %s... ', $c->bold($locale)));
+ $dir = $dirs[$i] . DS . 'locale' . DS . $locale . DS . 'LC_MESSAGES';
+ if (!is_dir($dir)) {
+ require_once 'System.php';
+ if ($debug) {
+ $c->writeln(sprintf('Making directory %s', $dir));
+ }
+ if (!$test && !@System::mkdir("-p $dir")) {
+ $c->writeln($c->red('Warning: ') . sprintf('Could not create locale directory for locale %s:', $locale));
+ $c->writeln($dir);
+ $c->writeln();
+ continue;
+ }
+ }
+
+ /* Convert to unix linebreaks. */
+ $pofile = $dirs[$i] . DS . 'po' . DS . $locale . '.po';
+ $fp = fopen($pofile, 'r');
+ $content = fread($fp, filesize($pofile));
+ fclose($fp);
+
+ $content = str_replace("\r", '', $content);
+ $fp = fopen($pofile, 'wb');
+ fwrite($fp, $content);
+ fclose($fp);
+
+ /* Check PO file sanity. */
+ $sh = $GLOBALS['msgfmt'] . " --check \"$pofile\"$redir_err";
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ if ($ret != 0) {
+ $c->writeln($c->red('Warning: ') . 'an error has occured:');
+ $c->writeln(implode("\n", $out));
+ $c->writeln();
+ if ($apps[$i] == 'horde') {
+ continue 2;
+ }
+ continue;
+ }
+
+ /* Compile MO file. */
+ $sh = $GLOBALS['msgfmt'] . ' --statistics -o "' . $dir . DS . $apps[$i] . '.mo" ';
+ if ($apps[$i] != 'horde') {
+ $horde_po = $dirs[$horde] . DS . 'po' . DS . $locale . '.po';
+ if (!@is_readable($horde_po)) {
+ $c->writeln($c->red('Warning: ') . sprintf('the Horde PO file for the locale %s does not exist:', $locale));
+ $c->writeln($horde_po);
+ $c->writeln();
+ $sh .= $dirs[$i] . DS . 'po' . DS . $locale . '.po';
+ } else {
+ $sh = $GLOBALS['msgcomm'] . " --more-than=0 --sort-output \"$pofile\" \"$horde_po\" | $sh -";
+ }
+ } else {
+ $sh .= $pofile;
+ }
+ $sh .= $redir_err;
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ $out = '';
+ if ($test) {
+ $ret = 0;
+ } else {
+ putenv('LANG=en');
+ exec($sh, $out, $ret);
+ putenv('LANG=' . $GLOBALS['language']);
+ }
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ $messages = array(0, 0, 0);
+ if (preg_match('/(\d+) translated/', $out[0], $match)) {
+ $messages[0] = $match[1];
+ if (isset($horde_msg[$locale])) {
+ $messages[0] -= $horde_msg[$locale][0];
+ if ($messages[0] < 0) $messages[0] = 0;
+ }
+ }
+ if (preg_match('/(\d+) fuzzy/', $out[0], $match)) {
+ $messages[1] = $match[1];
+ if (isset($horde_msg[$locale])) {
+ $messages[1] -= $horde_msg[$locale][1];
+ if ($messages[1] < 0) $messages[1] = 0;
+ }
+ }
+ if (preg_match('/(\d+) untranslated/', $out[0], $match)) {
+ $messages[2] = $match[1];
+ if (isset($horde_msg[$locale])) {
+ $messages[2] -= $horde_msg[$locale][2];
+ if ($messages[2] < 0) $messages[2] = 0;
+ }
+ }
+ if ($apps[$i] == 'horde') {
+ $horde_msg[$locale] = $messages;
+ }
+ $stats_array[$apps[$i]][$locale] = $messages;
+ $stats->addRow(array($apps[$i], $locale, $messages[0], $messages[1], $messages[2]));
+ } else {
+ $c->writeln($c->red('failed'));
+ exec($sh, $out, $ret);
+ $c->writeln(implode("\n", $out));
+ }
+ if (count($langs) > 1) {
+ continue;
+ }
+
+ /* Merge translation into compendium. */
+ if (!empty($compendium)) {
+ echo sprintf('Merging the PO file for %s to the compendium... ', $c->bold($apps[$i]));
+ if (!empty($dir) && substr($dir, -1) != DS) {
+ $dir .= DS;
+ }
+ $sh = $GLOBALS['msgcat'] . " --sort-output \"$compendium\" \"$pofile\" > \"$compendium.tmp\"";
+ if (!$debug) {
+ $sh .= $silence;
+ }
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ $out = '';
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ @unlink($compendium);
+ rename($compendium . '.tmp', $compendium);
+ if ($ret == 0) {
+ $c->writeln($c->green('done'));
+ } else {
+ $c->writeln($c->red('failed'));
+ }
+ }
+ $c->writeln();
+ }
+ }
+ if (empty($module)) {
+ $c->writeln('Results:');
+ } else {
+ $c->writeln('Results (including Horde):');
+ }
+ $c->writeln($stats->getTable());
+ if ($save_stats) {
+ $fp = @fopen('translation_stats.txt', 'w');
+ if ($fp) {
+ fwrite($fp, serialize($stats_array));
+ fclose($fp);
+ }
+ }
+}
+
+function cleanup($keep_untranslated = false)
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) { continue; }
+ $c->writeln(sprintf('Cleaning up PO files for module %s...', $c->bold($apps[$i])));
+ if (empty($lang)) {
+ $langs = get_languages($dirs[$i]);
+ } else {
+ if (!@file_exists($dirs[$i] . DS . 'po' . DS . $lang . '.po')) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $langs = array($lang);
+ }
+ foreach ($langs as $locale) {
+ $c->writeln(sprintf('Cleaning up locale %s... ', $c->bold($locale)));
+ $pofile = $dirs[$i] . DS . 'po' . DS . $locale . '.po';
+ $sh = $GLOBALS['msgattrib'] . ($keep_untranslated ? '' : ' --translated') . " --no-obsolete --no-fuzzy --force-po $pofile > $pofile.tmp";
+ if (!$debug) {
+ $sh .= $silence;
+ }
+ if ($debug || $test) {
+ $c->writeln();
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ $out = '';
+ if ($test) {
+ $ret = 0;
+ } else {
+ exec($sh, $out, $ret);
+ }
+ if ($ret == 0) {
+ @unlink($pofile);
+ rename($pofile . '.tmp', $pofile);
+ $c->writeln($c->green('done'));
+ } else {
+ @unlink($pofile . '.tmp', $pofile);
+ $c->writeln($c->red('failed'));
+ }
+ $c->writeln();
+ }
+ }
+}
+
+function commit($help_only = false)
+{
+ global $cmd_options, $apps, $dirs, $debug, $test, $c;
+
+ $docs = false;
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ case 'n':
+ case '--new':
+ $docs = true;
+ break;
+ case 'M':
+ case '--message':
+ $msg = $option[1];
+ break;
+ }
+ }
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) continue;
+ if ($apps[$i] == 'horde') {
+ $dirs[] = $dirs[$i] . DS . 'admin';
+ $apps[] = 'horde/admin';
+ if (!empty($module)) {
+ $module = 'horde/admin';
+ }
+ }
+ if (empty($lang)) {
+ if ($help_only) {
+ $files = array_merge($files, strip_horde(search_ext('xml', $dirs[$i] . DS . 'locale')));
+ } else {
+ $files = array_merge($files, strip_horde(get_po_files($dirs[$i] . DS . 'po')));
+ $files = array_merge($files, strip_horde(search_file('^[a-z]{2}_[A-Z]{2}', $dirs[$i] . DS . 'locale', true)));
+ }
+ } else {
+ if ($help_only) {
+ if (!@file_exists($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml')) continue;
+ } else {
+ if (!@file_exists($dirs[$i] . '/po/' . $lang . '.po')) continue;
+ $files[] = strip_horde($dirs[$i] . DS . 'po' . DS . $lang . '.po');
+ }
+ $files[] = strip_horde($dirs[$i] . DS . 'locale' . DS . $lang);
+ }
+ if ($docs && !$help_only && $apps[$i]) {
+ $files[] = strip_horde($dirs[$i] . DS . 'docs');
+ if ($apps[$i] == 'horde') {
+ $horde_conf = $dirs[array_search('horde', $dirs)] . DS . 'config' . DS;
+ $files[] = strip_horde($horde_conf . 'nls.php.dist');
+ }
+ }
+ }
+ chdir(BASE);
+ if (count($files)) {
+ if ($docs) {
+ $c->writeln('Adding new files to repository:');
+ $sh = 'cvs add';
+ foreach ($files as $file) {
+ if (strstr($file, 'locale') || strstr($file, '.po')) {
+ $sh .= " $file";
+ $c->writeln($file);
+ }
+ }
+ $sh .= '; cvs add';
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ if ($help_only) {
+ $sh .= ' ' . $file . DS . '*.xml';
+ $c->writeln($file . DS . '*.xml');
+ } else {
+ $sh .= ' ' . $file . DS . '*.xml ' . $file . DS . 'LC_MESSAGES';
+ $c->writeln($file . DS . "*.xml\n$file" . DS . 'LC_MESSAGES');
+ }
+ }
+ }
+ if (!$help_only) {
+ $sh .= '; cvs add';
+ foreach ($files as $file) {
+ if (strstr($file, 'locale')) {
+ $add = $file . DS . 'LC_MESSAGES' . DS . '*.mo';
+ $sh .= ' ' . $add;
+ $c->writeln($add);
+ }
+ }
+ }
+ $c->writeln();
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) system($sh);
+ $c->writeln();
+ }
+ $c->writeln('Committing:');
+ $c->writeln(implode(' ', $files));
+ if (!empty($lang)) {
+ $lang = ' ' . $lang;
+ }
+ if (empty($msg)) {
+ if ($docs) {
+ $msg = "Add$lang translation.";
+ } elseif ($help_only) {
+ $msg = "Update$lang help file.";
+ } else {
+ $msg = "Update$lang translation.";
+ }
+ }
+ $sh = 'cvs commit -m "' . $msg . '" ' . implode(' ', $files);
+ if ($debug || $test) {
+ $c->writeln('Executing:');
+ $c->writeln($sh);
+ }
+ if (!$test) system($sh);
+ }
+}
+
+function update_help()
+{
+ global $cmd_options, $dirs, $apps, $debug, $test, $last_error_msg, $c;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) { continue; }
+ if (!is_dir("$dirs[$i]/locale")) continue;
+ if ($apps[$i] == 'horde') {
+ $dirs[] = $dirs[$i] . DS . 'admin';
+ $apps[] = 'horde/admin';
+ if (!empty($module)) {
+ $module = 'horde/admin';
+ }
+ }
+ if (empty($lang)) {
+ $files = search_file('help.xml', $dirs[$i] . DS . 'locale');
+ } else {
+ $files = array($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml');
+ }
+ $file_en = $dirs[$i] . DS . 'locale' . DS . 'en_US' . DS . 'help.xml';
+ if (!@file_exists($file_en)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('There doesn\'t yet exist a help file for %s.', $c->bold($apps[$i]))));
+ $c->writeln();
+ continue;
+ }
+ foreach ($files as $file_loc) {
+ $locale = substr($file_loc, 0, strrpos($file_loc, DS));
+ $locale = substr($locale, strrpos($locale, DS) + 1);
+ if ($locale == 'en_US') continue;
+ if (!@file_exists($file_loc)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('The %s help file for %s doesn\'t yet exist. Creating a new one.', $c->bold($locale), $c->bold($apps[$i]))));
+ $dir_loc = substr($file_loc, 0, -9);
+ if (!is_dir($dir_loc)) {
+ require_once 'System.php';
+ if ($debug || $test) {
+ $c->writeln(sprintf('Making directory %s', $dir_loc));
+ }
+ if (!$test && !@System::mkdir("-p $dir_loc")) {
+ $c->writeln($c->red('Warning: ') . sprintf('Could not create locale directory for locale %s:', $locale));
+ $c->writeln($dir_loc);
+ $c->writeln();
+ continue;
+ }
+ }
+ if ($debug || $test) {
+ $c->writeln(wordwrap(sprintf('Copying %s to %s', $file_en, $file_loc)));
+ }
+ if (!$test && !@copy($file_en, $file_loc)) {
+ $c->writeln($c->red('Warning: ') . sprintf('Could not copy %s to %s', $file_en, $file_loc));
+ }
+ $c->writeln();
+ continue;
+ }
+ $c->writeln(sprintf('Updating %s help file for %s.', $c->bold($locale), $c->bold($apps[$i])));
+ $fp = fopen($file_loc, 'r');
+ $line = fgets($fp);
+ fclose($fp);
+ if (!strstr($line, '<?xml')) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('The help file %s didn\'t start with <?xml', $file_loc)));
+ $c->writeln();
+ continue;
+ }
+ $encoding = '';
+ if (preg_match('/encoding=(["\'])([^\\1]+)\\1/', $line, $match)) {
+ $encoding = $match[2];
+ }
+ $doc_en = domxml_open_file($file_en);
+ if (!is_object($doc_en)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_en)));
+ $c->writeln();
+ continue 2;
+ }
+ $doc_loc = domxml_open_file($file_loc);
+ if (!is_object($doc_loc)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_loc)));
+ $c->writeln();
+ continue;
+ }
+ $doc_new = domxml_new_doc('1.0');
+ $help_en = $doc_en->document_element();
+ $help_loc = $doc_loc->document_element();
+ $help_new = $help_loc->clone_node();
+ $entries_loc = array();
+ $entries_new = array();
+ $count_uptodate = 0;
+ $count_new = 0;
+ $count_changed = 0;
+ $count_unknown = 0;
+ foreach ($doc_loc->get_elements_by_tagname('entry') as $entry) {
+ $entries_loc[$entry->get_attribute('id')] = $entry;
+ }
+ foreach ($doc_en->get_elements_by_tagname('entry') as $entry) {
+ $id = $entry->get_attribute('id');
+ if (array_key_exists($id, $entries_loc)) {
+ if ($entries_loc[$id]->has_attribute('md5') &&
+ md5($entry->get_content()) != $entries_loc[$id]->get_attribute('md5')) {
+ $comment = $doc_loc->create_comment(" English entry:\n" . str_replace('--', '--', $doc_loc->dump_node($entry)));
+ $entries_loc[$id]->append_child($comment);
+ $entry_new = $entries_loc[$id]->clone_node(true);
+ $entry_new->set_attribute('state', 'changed');
+ $count_changed++;
+ } else {
+ if (!$entries_loc[$id]->has_attribute('state')) {
+ $comment = $doc_loc->create_comment(" English entry:\n" . str_replace('--', '--', $doc_loc->dump_node($entry)));
+ $entries_loc[$id]->append_child($comment);
+ $entry_new = $entries_loc[$id]->clone_node(true);
+ $entry_new->set_attribute('state', 'unknown');
+ $count_unknown++;
+ } else {
+ $entry_new = $entries_loc[$id]->clone_node(true);
+ $count_uptodate++;
+ }
+ }
+ } else {
+ $entry_new = $entry->clone_node(true);
+ $entry_new->set_attribute('state', 'new');
+ $count_new++;
+ }
+ $entries_new[] = $entry_new;
+ }
+ $doc_new->append_child($doc_new->create_comment(' $' . 'Horde$ '));
+ foreach ($entries_new as $entry) {
+ $help_new->append_child($entry);
+ }
+ $c->writeln(wordwrap(sprintf('Entries: %d total, %d up-to-date, %d new, %d changed, %d unknown',
+ $count_uptodate + $count_new + $count_changed + $count_unknown,
+ $count_uptodate, $count_new, $count_changed, $count_unknown)));
+ $doc_new->append_child($help_new);
+ $output = $doc_new->dump_mem(true, $encoding);
+ if ($debug || $test) {
+ $c->writeln(wordwrap(sprintf('Writing updated help file to %s.', $file_loc)));
+ }
+ if (!$test) {
+ $fp = fopen($file_loc, 'w');
+ $line = fwrite($fp, $output);
+ fclose($fp);
+ }
+ $c->writeln(sprintf('%d bytes written.', strlen($output)));
+ $c->writeln();
+ }
+ }
+}
+
+function make_help()
+{
+ global $cmd_options, $dirs, $apps, $debug, $test, $c;
+
+ foreach ($cmd_options[0] as $option) {
+ switch ($option[0]) {
+ case 'h':
+ usage();
+ footer();
+ case 'l':
+ case '--locale':
+ $lang = $option[1];
+ break;
+ case 'm':
+ case '--module':
+ $module = $option[1];
+ break;
+ }
+ }
+ $files = array();
+ for ($i = 0; $i < count($dirs); $i++) {
+ if (!empty($module) && $module != $apps[$i]) continue;
+ if (!is_dir("$dirs[$i]/locale")) continue;
+ if ($apps[$i] == 'horde') {
+ $dirs[] = $dirs[$i] . DS . 'admin';
+ $apps[] = 'horde/admin';
+ if (!empty($module)) {
+ $module = 'horde/admin';
+ }
+ }
+ if (empty($lang)) {
+ $files = search_file('help.xml', $dirs[$i] . DS . 'locale');
+ } else {
+ $files = array($dirs[$i] . DS . 'locale' . DS . $lang . DS . 'help.xml');
+ }
+ $file_en = $dirs[$i] . DS . 'locale' . DS . 'en_US' . DS . 'help.xml';
+ if (!@file_exists($file_en)) {
+ continue;
+ }
+ foreach ($files as $file_loc) {
+ if (!@file_exists($file_loc)) {
+ $c->writeln('Skipped...');
+ $c->writeln();
+ continue;
+ }
+ $locale = substr($file_loc, 0, strrpos($file_loc, DS));
+ $locale = substr($locale, strrpos($locale, DS) + 1);
+ if ($locale == 'en_US') continue;
+ $c->writeln(sprintf('Updating %s help file for %s.', $c->bold($locale), $c->bold($apps[$i])));
+ $fp = fopen($file_loc, 'r');
+ $line = fgets($fp);
+ fclose($fp);
+ if (!strstr($line, '<?xml')) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('The help file %s didn\'t start with <?xml', $file_loc)));
+ $c->writeln();
+ continue;
+ }
+ $encoding = '';
+ if (preg_match('/encoding=(["\'])([^\\1]+)\\1/', $line, $match)) {
+ $encoding = $match[2];
+ }
+ $doc_en = domxml_open_file($file_en);
+ if (!is_object($doc_en)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_en)));
+ $c->writeln();
+ continue 2;
+ }
+ $doc_loc = domxml_open_file($file_loc);
+ if (!is_object($doc_loc)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('There was an error opening the file %s. Try running translation.php with the flag -d to see any error messages from the xml parser.', $file_loc)));
+ $c->writeln();
+ continue;
+ }
+ $help_loc = $doc_loc->document_element();
+ $md5_en = array();
+ $count_all = 0;
+ $count = 0;
+ foreach ($doc_en->get_elements_by_tagname('entry') as $entry) {
+ $md5_en[$entry->get_attribute('id')] = md5($entry->get_content());
+ }
+ foreach ($doc_loc->get_elements_by_tagname('entry') as $entry) {
+ foreach ($entry->child_nodes() as $child) {
+ if ($child->node_type() == XML_COMMENT_NODE && strstr($child->node_value(), 'English entry')) {
+ $entry->remove_child($child);
+ }
+ }
+ $count_all++;
+ $id = $entry->get_attribute('id');
+ if (!array_key_exists($id, $md5_en)) {
+ $c->writeln(wordwrap($c->red('Warning: ') . sprintf('No entry with the id "%s" exists in the original help file.', $id)));
+ } else {
+ $entry->set_attribute('md5', $md5_en[$id]);
+ $entry->set_attribute('state', 'uptodate');
+ $count++;
+ }
+ }
+ $output = $doc_loc->dump_mem(true, $encoding);
+ if (!$test) {
+ $fp = fopen($file_loc, 'w');
+ $line = fwrite($fp, $output);
+ fclose($fp);
+ }
+ $c->writeln(sprintf('%d of %d entries marked as up-to-date', $count, $count_all));
+ $c->writeln();
+ }
+ }
+}
+
+$curdir = getcwd();
+define('DS', DIRECTORY_SEPARATOR);
+
+$language = getenv('LANG');
+if (empty($language)) {
+ $language = getenv('LANGUAGE');
+}
+
+@define('HORDE_BASE', dirname(__FILE__) . '/../..');
+require_once HORDE_BASE . '/lib/core.php';
+require_once 'Horde/CLI.php';
+
+$c = &new Horde_CLI();
+if (!$c->runningFromCLI()) {
+ $c->fatal('This script must be run from the command line.');
+}
+$c->init();
+
+require HORDE_BASE . '/config/nls.php';
+require_once 'Horde/NLS.php';
+if (!empty($language)) {
+ $tmp = explode('.', $language);
+ $language = $tmp[0];
+ $language = NLS::_map(trim($language));
+ if (!NLS::isValid($language)) {
+ $language = NLS::_map(substr($language, 0, 2));
+ }
+ if (NLS::isValid($language)) {
+ setlocale(LC_ALL, $language);
+ bindtextdomain('horde', HORDE_BASE . '/locale');
+ textdomain('horde');
+ if (array_key_exists(1, $tmp) &&
+ function_exists('bind_textdomain_codeset')) {
+ bind_textdomain_codeset('horde', $tmp[1]);
+ }
+ }
+}
+
+$c->writeln($c->bold('---------------------------'));
+$c->writeln($c->bold('Horde translation generator'));
+$c->writeln($c->bold('---------------------------'));
+
+/* Sanity checks */
+if (!extension_loaded('gettext')) {
+ $c->writeln($c->red('Gettext extension not found!'));
+ footer();
+}
+
+$c->writeln('Loading libraries...');
+$libs_found = true;
+
+foreach (array('Console_Getopt' => 'Console/Getopt.php',
+ 'Console_Table' => 'Console/Table.php',
+ 'File_Find' => 'File/Find.php')
+ as $class => $file) {
+ echo $class . '... ';
+ @include_once $file;
+ if (class_exists($class)) {
+ $c->writeln($c->green('OK'));
+ } else {
+ $c->writeln($c->red(sprintf('%s not found.', $class)));
+ $libs_found = false;
+ }
+}
+
+if (!$libs_found) {
+ $c->writeln();
+ $c->writeln('Make sure that you have PEAR installed and in your include path.');
+ $c->writeln('include_path: ' . ini_get('include_path'));
+ footer();
+}
+$c->writeln();
+
+/* Commandline parameters */
+$args = Console_Getopt::readPHPArgv();
+$options = Console_Getopt::getopt($args, 'b:dht', array('base=', 'debug', 'help', 'test'));
+if (PEAR::isError($options) && $args[0] == $_SERVER['PHP_SELF']) {
+ array_shift($args);
+ $options = Console_Getopt::getopt($args, 'b:dht', array('base=', 'debug', 'help', 'test'));
+}
+if (PEAR::isError($options)) {
+ $c->writeln($c->red('Getopt Error: ' . str_replace('Console_Getopt:', '', $options->getMessage())));
+ $c->writeln();
+ usage();
+ footer();
+}
+if (empty($options[1][0])) {
+ $c->writeln($c->red('Error: ' . 'No command specified.'));
+ $c->writeln();
+ usage();
+ footer();
+}
+$debug = false;
+$test = false;
+foreach ($options[0] as $option) {
+ switch ($option[0]) {
+ case 'b':
+ case '--base':
+ if (substr($option[1], -1) == DS) {
+ $option[1] = substr($option[1], 0, -1);
+ }
+ define('BASE', $option[1]);
+ break;
+ case 'd':
+ case '--debug':
+ $debug = true;
+ break;
+ case 't':
+ case '--test':
+ $test = true;
+ break;
+ case 'h':
+ case '--help':
+ usage();
+ footer();
+ }
+}
+if (!$debug) {
+ ini_set('error_reporting', false);
+}
+if (!defined('BASE')) {
+ define('BASE', HORDE_BASE);
+}
+if ($options[1][0] == 'help') {
+ usage();
+ footer();
+}
+$silence = $debug || OS_WINDOWS ? '' : ' 2> /dev/null';
+$redir_err = OS_WINDOWS ? '' : ' 2>&1';
+$options_list = array(
+ 'cleanup' => array('hl:m:', array('module=', 'locale=')),
+ 'commit' => array('hl:m:nM:', array('module=', 'locale=', 'new', 'message=')),
+ 'commit-help'=> array('hl:m:nM:', array('module=', 'locale=', 'new', 'message=')),
+ 'compendium' => array('hl:d:a:', array('locale=', 'directory=', 'add=')),
+ 'extract' => array('hm:', array('module=')),
+ 'init' => array('hl:m:nc:', array('module=', 'locale=', 'no-compendium', 'compendium=')),
+ 'merge' => array('hl:m:c:n', array('module=', 'locale=', 'compendium=', 'no-compendium')),
+ 'make' => array('hl:m:c:ns', array('module=', 'locale=', 'compendium=', 'no-compendium', 'statistics')),
+ 'make-help' => array('hl:m:', array('module=', 'locale=')),
+ 'update' => array('hl:m:c:n', array('module=', 'locale=', 'compendium=', 'no-compendium')),
+ 'update-help'=> array('hl:m:', array('module=', 'locale=')),
+ 'status' => array('hl:m:o:', array('module=', 'locale=', 'output='))
+);
+$options_arr = $options[1];
+$cmd = array_shift($options_arr);
+if (array_key_exists($cmd, $options_list)) {
+ $cmd_options = Console_Getopt::getopt($options_arr, $options_list[$cmd][0], $options_list[$cmd][1]);
+ if (PEAR::isError($cmd_options)) {
+ $c->writeln($c->red('Error: ' . str_replace('Console_Getopt:', '', $cmd_options->getMessage())));
+ $c->writeln();
+ usage();
+ footer();
+ }
+}
+
+/* Searching applications */
+check_binaries();
+
+$c->writeln(sprintf('Searching Horde applications in %s', BASE));
+$dirs = search_applications();
+
+if ($debug) {
+ $c->writeln('Found directories:');
+ $c->writeln(implode("\n", $dirs));
+}
+
+$apps = strip_horde($dirs);
+$apps[0] = 'horde';
+$c->writeln(wordwrap(sprintf('Found applications: %s', implode(', ', $apps))));
+$c->writeln();
+
+switch ($cmd) {
+ case 'cleanup':
+ case 'commit':
+ case 'compendium':
+ case 'merge':
+ $cmd();
+ break;
+ case 'commit-help':
+ commit(true);
+ break;
+ case 'extract':
+ xtract();
+ break;
+ case 'init':
+ init();
+ $c->writeln();
+ merge();
+ break;
+ case 'make':
+ cleanup(true);
+ $c->writeln();
+ make();
+ break;
+ case 'make-help':
+ make_help();
+ break;
+ case 'update':
+ xtract();
+ $c->writeln();
+ merge();
+ break;
+ case 'update-help':
+ update_help();
+ break;
+ case 'status':
+// xtract();
+ merge();
+// status();
+ break;
+ default:
+ $c->writeln($c->red('Error: ') . sprintf('Unknown command: %s', $cmd));
+ $c->writeln();
+ usage();
+ footer();
+}
+
+footer();
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+if ($app) {
+ /* Render the page. */
+ Translation::RB_init();
+}
+require BABEL_TEMPLATES . '/common-header.inc';
+
+if ($app) {
+ Translation::RB_start(30);
+}
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+$vars = &Variables::getDefaultVariables();
+
+/* Create upload form */
+$form = &new Horde_Form($vars, _("View Statistics"), 'stats');
+
+if (!$app) {
+ $form->setButtons(_("View"));
+ $form->addVariable(_("Module"), 'module', 'enum', true, false, null, array(Translation::listApps(true), true));
+ $form->addVariable('', '', 'spacer', true);
+
+ $renderer_params = array();
+ $renderer = &new Horde_Form_Renderer($renderer_params);
+ $renderer->setAttrColumnWidth('20%');
+
+ $form->renderActive($renderer, $vars, Horde::selfURL(), 'post');
+} else {
+ Translate_Display::header(_("Horde translation generator"));
+
+ $dirs = Translate::search_applications();
+ $apps = Translate::strip_horde($dirs);
+ $apps[0] = 'horde';
+ Translate_Display::info();
+
+ foreach($dirs as $d => $dir) {
+ $dir = realpath($dir);
+ $pofile = $dir . '/po/' . $lang . '.po';
+
+ if (!@file_exists($pofile)) {
+ continue;
+ }
+
+ $_app = str_replace(realpath(HORDE_BASE), '', $dir);
+ $_app = str_replace('/', '', $_app);
+ if (empty($_app)) {
+ $_app = 'horde';
+ }
+
+ if ($app != 'ALL' && $app != $_app) {
+ continue;
+ }
+
+ if (!Translation::hasPermission("module:$_app")) {
+ continue;
+ }
+
+ Translate_Display::header($_app);
+
+ $report = Translate::stats($_app);
+
+ echo '<table width="100%" align="center" border="0" cellspacing="0" cellpadding="0">';
+ echo '<tr class="control">';
+ echo '<td class="control" style="border-bottom: 1px solid #999;"><b>' . _("Language") . '</b></td>';
+ echo '<td width="5%"><b>' . _("Locale") . '</b></td>';
+ echo '<td width="15%"><b>' . _("Status") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Translated") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Fuzzy") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Untranslated") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Obsolete") . '</b></td>';
+ echo '</tr>';
+
+ $i = 0;
+ $j = 0;
+ $line = 0;
+ $last_key = null;
+ foreach ($report as $key => $value) {
+
+ if (!Translation::hasPermission("language:$key")) {
+ continue;
+ }
+
+ if ($key == $_SESSION['translation']['language']) {
+ echo "\n<tr class=\"smallheader control\">";
+ } else {
+ echo "\n<tr class=\"item" . ($i++ % 2) . "\">";
+ }
+ echo "\n\t<td>" . $nls['languages'][$key] . "</td>";
+ echo "\n\t<td>" . Horde::link(Util::addParameter(Horde::applicationUrl('view.php'), array('display_language' => $key, 'module' => $_app))) . $key . '</a>' . "</td>";
+ echo "\n\t<td>" . Translate_Display::create_bargraph($value[2], $value[0]) . "</td>";
+ echo "\n\t<td>" . $value[2] . "</td>";
+ echo "\n\t<td>" . $value [3] . "</td>";
+ echo "\n\t<td>" . $value[4] . "</td>";
+ echo "\n\t<td>" . $value[5] . "</td>";
+ echo "\t</tr>";
+ $last_key = $key;
+ }
+
+ echo '</table>';
+
+ Translate_Display::info();
+ }
+
+ Translation::RB_close();
+}
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+if (isset($language)) {
+ header('Content-type: text/html; charset=' . NLS::getCharset());
+ header('Vary: Accept-Language');
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
+<?php echo !empty($language) ? '<html lang="' . strtr($language, '_', '-') . '">' : '<html>' ?>
+<head>
+<?php
+
+$page_title = $GLOBALS['registry']->get('name');
+if (!empty($title)) $page_title .= ' :: ' . $title;
+if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) {
+ echo "<meta http-equiv=\"refresh\" content=\"$refresh_time;url=$refresh_url\">\n";
+}
+
+Horde::includeScriptFiles();
+
+?>
+<title><?php echo $page_title ?></title>
+<?php echo Horde::stylesheetLink('translation') ?>
+</head>
+
+<body<?php if (Util::nonInputVar('bodyClass')) echo ' class="' . $bodyClass . '"' ?>>
--- /dev/null
+<table style="width:100%" cellspacing="0">
+<tr class="header">
+<td style="width:200px; padding-left:8px; vertical-align:top">
+ <h1 class="header">
+ <gettext>Command</gettext>
+ </h1>
+</td>
+<td style="padding-left:8px; vertical-align:top">
+ <h1 class="header">
+ <gettext>Description</gettext>
+ </h1>
+</td>
+
+<?php
+
+$cmds = array();
+
+$cmds['view'] = array(
+ 'desc' => _("View"),
+ 'text' => _("View all translations."),
+ 'url' => Horde::applicationUrl('view.php')
+);
+
+$cmds['download'] = array(
+ 'desc' => _("Download"),
+ 'text' => _("Download all current PO files."),
+ 'url' => Horde::applicationUrl('download.php')
+);
+
+$cmds['upload'] = array(
+ 'desc' => _("Upload"),
+ 'text' => _("Upload new PO files."),
+ 'url' => Horde::applicationUrl('upload.php')
+);
+
+$cmds['stats'] = array(
+ 'desc' => _("Statistics"),
+ 'text' => _("Get statistics about translations."),
+ 'url' => Horde::applicationUrl('stats.php')
+);
+
+$cmds['extract'] = array(
+ 'desc' => _("Extract"),
+ 'text' => _("Generate and merge PO files."),
+ 'url' => Horde::applicationUrl('extract.php')
+);
+
+$cmds['make'] = array(
+ 'desc' => _("Make"),
+ 'text' => _("Build binary MO files from the specified PO files."),
+ 'url' => Horde::applicationUrl('make.php')
+);
+
+$cmds['commit'] = array(
+ 'desc' => _("Commit"),
+ 'text' => _("Commit translations to the SVN server."),
+ 'url' => Horde::applicationUrl('commit.php')
+);
+
+$cmds['reset'] = array(
+ 'desc' => _("Reset"),
+ 'text' => _("The reset procedure will delete all PO files from the server for all modules and restore from SVN server."),
+ 'url' => Horde::applicationUrl('reset.php')
+);
+
+
+$i = 0;
+
+foreach($cmds as $cmdid => $cmd) {
+ if (Translation::hasPermission($cmdid)) {
+ echo '<tr height="50" class="item' . ($i++ % 2) . '">';
+ echo '<td align="center"><div style="width: 140px;" onclick="window.location=\'' . $cmd['url'] . '\';" class="button"><a class="smallheader" href="' . $cmd['url'] . '">' . $cmd['desc'] . '</a></div></td>';
+ echo '<td>' . $cmd['text'] . '</td>';
+ echo '</tr>';
+ }
+}
+?>
+</table>
--- /dev/null
+<div id="menu">
+ <span style="float:right">
+ <tag:fmenu />
+ </span>
+ <tag:menu />
+</div>
+<br />
+
+<tag:notify />
+
+<table cellspacing=0 cellpadding=0 width=100%>
+<tr><td class="header control"><tag:lang /></td></tr>
+</table>
+<br />
--- /dev/null
+/* Redbox styles. */
+#RB_overlay {
+ position: absolute;
+ z-index: 100;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ min-height: 100%;
+ background-color: #000;
+ opacity: .6;
+ filter: alpha(opacity=60);
+}
+#RB_loading {
+ z-index: 101;
+ width: 66;
+ margin-left: auto;
+ margin-right: auto;
+ margin-top: 200px;
+ padding-bottom: 66px;
+ text-align: center;
+ background: url("graphics/redbox_spinner.gif") no-repeat bottom center;
+}
+#RB_window {
+ z-index: 102;
+ background-color: #fff;
+ display: block;
+ text-align: left;
+ overflow: hidden;
+ margin: 20px auto 0 auto;
+ position: fixed;
+ position: absolute;
+}
+
+#RB_confirm {
+ width: 20em;
+ padding: 1em;
+ border: 1px solid #ccc;
+ background: #ffc;
+}
+#RB_confirm input {
+ margin: .2em;
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+@define('BABEL_BASE', dirname(__FILE__)) ;
+require_once BABEL_BASE . '/lib/base.php';
+
+$vars = &Variables::getDefaultVariables();
+
+/* Create upload form */
+$form = &new Horde_Form($vars, _("Upload new Translation"), 'upload');
+$form->setButtons(_("Upload"));
+$form->addVariable(_("Module"), 'module', 'enum', true, false, null, array(Translation::listApps(true), true));
+$form->addVariable(_("Translations File (.PO)"), 'po_file', 'file', true, false);
+$form->addVariable('', '', 'spacer', true);
+
+/* Validate form if submitted */
+if (Util::getFormData('submitbutton') == _("Upload")) {
+ if ($form->validate($vars, false)) {
+ $form->getInfo($vars, $form_values);
+
+ $po_module = @$form_values['module'];
+ if (empty($po_module)) {
+ $notification->push(_("Please select module of translations PO file!"), 'horde.error');
+ } else {
+ $po_file_path = @$form_values['po_file']['file'];
+ $po_file_name = @$form_values['po_file']['name'];
+ $po_file_size = @$form_values['po_file']['size'];
+ if (empty($po_file_path) || substr($po_file_name, -3) != '.po' || $po_file_size <= 0) {
+ $notification->push(_("Invalid Translations file. Please submit a valid PO file!"), 'horde.error');
+ } else {
+ if ($po_module == 'horde') {
+ $mod = '';
+ } else {
+ $mod = $po_module;
+ }
+
+ $notification->push(sprintf(_("Upload successful for %s (%s)"), $po_module, $lang), 'horde.success');
+ $cmd = "cp $po_file_path " . HORDE_BASE . "/$mod/po/$lang.po";
+ system($cmd);
+
+ $url = Horde::applicationUrl('upload.php');
+ // Redirect to page URL
+ header('Location: ' . $url);
+ exit;
+ }
+ }
+ }
+}
+
+/* Render upload page */
+require BABEL_TEMPLATES . '/common-header.inc';
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+$renderer_params = array();
+$renderer = &new Horde_Form_Renderer($renderer_params);
+$renderer->setAttrColumnWidth('20%');
+
+$form->renderActive($renderer, $vars, Horde::selfURL(), 'post');
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+
+require_once 'Horde/Lock.php';
+
+$meta_params = array(
+ "Project-Id-Version" => @$_SESSION['translation']['language'],
+ "Report-Msgid-Bugs-To" => "support@scopserv.com",
+ "POT-Creation-Date" => "",
+ "PO-Revision-Date" => "",
+ "Last-Translator" => "",
+ "Language-Team" => "",
+ "MIME-Version" => "1.0",
+ "Content-Type" => "text/plain; charset=utf-8",
+ "Content-Transfer-Encoding" => "8bit",
+ "Plural-Forms" => "nplurals=2; plural=(n > 1);");
+
+$app = Util::getFormData('module');
+$editmode = Util::getFormData('editmode', 0);
+$cstring = Util::getFormData('cstring');
+$page = Util::getFormData('page', 0);
+$filter = Util::getFormData('filter');
+$search = Util::getFormData('search');
+
+if ($app) {
+ /* Render the page. */
+ Translation::RB_init();
+}
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+
+if ($app) {
+ if ($editmode) {
+ Translation::RB_start(10);
+ } else {
+ Translation::RB_start(60);
+ }
+}
+
+echo $template->fetch(BABEL_TEMPLATES . '/layout.html');
+
+$app = Util::getFormData('module');
+$show = 'edit';
+$vars = &Variables::getDefaultVariables();
+
+if ($app) {
+
+ $napp = ($app == 'horde') ? '' : $app;
+ $pofile = HORDE_BASE . '/' . $napp . '/po/' . $lang . '.po';
+ $po = &new File_Gettext_PO();
+ $po->load($pofile);
+
+ // Set Scope
+ $lockscope = sprintf("babel-%s-%s", $app, $lang);
+
+ // Initialize Horde_Lock class
+ $locks = &Horde_Lock::singleton('sql');
+
+// $curlocks = $locks->getLocks($lockscope);
+// var_dump($curlocks);
+}
+
+//
+
+$f_cancel = Util::getFormData('cancel');
+$f_save = Util::getFormData('submit');
+
+if (($f_save || $f_cancel) && $cstring) {
+ if ($curlock = $locks->getLocks(md5($cstring), $lockscope)) {
+ foreach($curlock as $lid => $linfo) {
+ if ($linfo['lock_scope'] == md5($cstring)) {
+ $locks->clearLock($lid);
+ }
+ }
+ }
+}
+
+if ($f_save && $cstring) {
+
+ $decstr = $po->encstr[$cstring];
+ $msgstr = Util::getFormData('msgstr');
+ $comments = trim($po->comments[$decstr]);
+
+ $phpformat = Util::getFormData('phpformat');
+ $fuzzy = Util::getFormData('fuzzy');
+
+ $status = $po->status[$decstr];
+ foreach($status as $k => $v) {
+ if ($v == 'untranslated' && !empty($msgstr)) {
+ unset($status[$k]);
+ }
+
+ if ($v == 'php-format' && !$phpformat) {
+ unset($status[$k]);
+ }
+
+ if ($v == 'fuzzy' && !$fuzzy) {
+ unset($status[$k]);
+ }
+ }
+
+ if (!in_array('php-format', $status) && $phpformat) {
+ $status[] = 'php-format';
+ }
+
+ if (!in_array('fuzzy', $status) && $fuzzy) {
+ $status[] = 'fuzzy';
+ }
+
+ $status = array_unique($status);
+ $po->status[$decstr] = $status;
+
+ $status = '';
+ if (preg_match('/(#,.*)$/', $comments, $m)) {
+ $status = $m[1];
+ }
+
+ if (count($po->status[$decstr])) {
+ $newstatus = "#, " . implode(', ', $po->status[$decstr]);
+ } else {
+ $newstatus = "";
+ }
+
+ $newcomments = str_replace($status, $newstatus, $comments);
+
+ $po->comments[$decstr] = $newcomments;
+ $po->strings[$decstr] = Translate_Display::convert_string($msgstr);
+ $po->save($pofile);
+}
+
+//
+
+/* Set up the template fields. */
+$template->set('menu', Translation::getMenu('string'));
+$template->set('notify', Util::bufferOutput(array($notification, 'notify'), array('listeners' => 'status')));
+
+/* Create upload form */
+$form = &new Horde_Form($vars, _("View Translation"), $show);
+
+if (!$app) {
+ $form->setButtons(_("View"));
+ $form->addVariable(_("Module"), 'module', 'enum', true, false, null, array(Translation::listApps(), true));
+ $form->addVariable('', '', 'spacer', true);
+
+ $renderer_params = array();
+ $renderer = &new Horde_Form_Renderer($renderer_params);
+ $renderer->setAttrColumnWidth('20%');
+
+ $form->renderActive($renderer, $vars, Horde::selfURL(), 'post');
+} else {
+
+ if (Translation::hasPermission('view', 'tabs', PERMS_EDIT)) {
+ $hmenu_desc = _("Edit Header");
+ $url = Horde::applicationUrl('edit.php');
+ $url = Util::addParameter($url, array('module' => $app,
+ 'url' => 'view'));
+
+ $hmenu = Horde::link($url, $hmenu_desc, 'menuitem', null);
+ $hmenu .= Horde::img('edit.png', null, $hmenu_desc) . ' ' . $hmenu_desc . '</a> ';
+ } else {
+ $hmenu = '';
+ }
+
+ Translate_Display::header(_("Meta Informations"), $hmenu);
+ echo '<table border=0 width=100% style="border: solid 1px black" cellpadding=0 cellspacing=0>';
+ $i = 0;
+ foreach($po->meta as $k => $v) {
+ echo '<tr><td class="control" width=30%>';
+ echo $k;
+ echo '</td><td class="item' . ($i++ % 2) . '">';
+ echo htmlentities($v);
+ echo '</td><tr>';
+ }
+ echo '</table>';
+ Translate_Display::info();
+
+ Translate_Display::header(_("Statistic"));
+
+ $report = Translate::stats($app, $lang);
+
+ echo '<table width="100%" align="center" border="0" cellspacing="0" cellpadding="0">';
+ echo '<tr class="control">';
+ echo '<td class="control" style="border-bottom: 1px solid #999;"><b>' . _("Language") . '</b></td>';
+ echo '<td width="5%"><b>' . _("Locale") . '</b></td>';
+ echo '<td width="15%"><b>' . _("Status") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Translated") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Fuzzy") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Untranslated") . '</b></td>';
+ echo '<td valign="bottom" style="width: 80px;"><b>' . _("Obsolete") . '</b></td>';
+ echo '</tr>';
+
+ echo "\n<tr class=\"item" . ($i++ % 2) . "\">";
+ echo "\n\t<td>" . $nls['languages'][$lang] . "</td>";
+ echo "\n\t<td>" . $lang . "</td>";
+ echo "\n\t<td>" . Translate_Display::create_bargraph(@$report[$lang][2], @$report[$lang][0]) . "</td>";
+ echo "\n\t<td>" . @$report[$lang][2] . "</td>";
+ echo "\n\t<td>" . @$report[$lang] [3] . "</td>";
+ echo "\n\t<td>" . @$report[$lang][4] . "</td>";
+ echo "\n\t<td>" . @$report[$lang][5] . "</td>";
+ echo "\t</tr>";
+
+ echo '</table>';
+ Translate_Display::info();
+
+ $filter_html = '';
+ $filter_html .= '<form action="' . Horde::applicationUrl('view.php') . '" method="post" name="edit" id="edit">';
+ $filter_html .= '<span class="smallheader">';
+ $filter_html .= Horde::img('edit.png') . ' ';
+ $filter_html .= '<b>' . _("Filter: ") . '</b>';
+ $filter_html .= '[ ';
+ if (!$filter) {
+ $hmenu_desc = '<b>' . _("All") . '</b>';
+ } else {
+ $hmenu_desc = _("All");
+ }
+ $url = Horde::applicationUrl('view.php');
+ $url = Util::addParameter($url, array('module' => $app));
+ $filter_html .= Horde::link($url, _("Edit Mode"), 'menuitem', null). ' ' . $hmenu_desc . '</a> ';
+ $filter_html .= '| ';
+
+ if ($filter == 'translated') {
+ $hmenu_desc = '<b>' . _("Translated") . '</b>';
+ } else {
+ $hmenu_desc = _("Translated");
+ }
+ $url = Horde::applicationUrl('view.php');
+ $url = Util::addParameter($url, array('module' => $app, 'filter' => 'translated'));
+ $filter_html .= Horde::link($url, $hmenu_desc, 'menuitem', null). ' ' . $hmenu_desc . '</a> ';
+ $filter_html .= '| ';
+
+
+ if ($filter == 'fuzzy') {
+ $hmenu_desc = '<b>' . _("Fuzzy") . '</b>';
+ } else {
+ $hmenu_desc = _("Fuzzy");
+ }
+ $url = Horde::applicationUrl('view.php');
+ $url = Util::addParameter($url, array('module' => $app, 'filter' => 'fuzzy'));
+ $filter_html .= Horde::link($url, $hmenu_desc, 'menuitem', null). ' ' . $hmenu_desc . '</a> ';
+ $filter_html .= '| ';
+
+ if ($filter == 'untranslated') {
+ $hmenu_desc = '<b>' . _("Untranslated") . '</b>';
+ } else {
+ $hmenu_desc = _("Untranslated");
+ }
+ $url = Horde::applicationUrl('view.php');
+ $url = Util::addParameter($url, array('module' => $app, 'filter' => 'untranslated'));
+ $filter_html .= Horde::link($url, $hmenu_desc, 'menuitem', null). ' ' . $hmenu_desc . '</a> ';
+ $filter_html .= '] ';
+
+ $filter_html .= '<input type="hidden" name="module" value="' . $app . '">';
+ $filter_html .= '<input type="hidden" name="page" value="' . $page . '">';
+ $filter_html .= '<input type="hidden" name="filter" value="' . $filter . '">';
+ $filter_html .= '<input type="text" name="search" value="' . $search . '">';
+ $filter_html .= '<input type="submit" name="filter_btn" value="' . _("Search") . '">';
+ $filter_html .= '</span>';
+ $filter_html .= '</form>';
+
+ $perpage = 100;
+
+ foreach($po->strings as $msgid => $msgstr) {
+ if ($filter && !in_array($filter, $po->status[$msgid])) {
+ unset($po->strings[$msgid]);
+ unset($po->status[$msgid]);
+ unset($po->ref[$msgid]);
+ }
+ if ($search && !preg_match(';' . $search . ';i', $msgid)) {
+ unset($po->strings[$msgid]);
+ unset($po->status[$msgid]);
+ unset($po->ref[$msgid]);
+ }
+ }
+
+ $numitem = count($po->strings);
+ // Set list min/max values
+ $min = $page * $perpage;
+ while ($min > $numitem) {
+ $page--;
+ $min = $page * $perpage;
+ }
+ $max = $min + $perpage;
+
+ // Start start/end items (according to current page)
+ $start = ($page * $perpage) + 1;
+ $end = min($numitem, $start + $perpage - 1);
+
+ $cntstr = 0;
+
+ $pageinf = ' <span class="smallheader">[' . sprintf(_("%s to %s of %s"), $start, $end, $numitem) . ']</span>';
+ Translate_Display::header(_("Translations") . $pageinf, $filter_html);
+
+ foreach($po->strings as $msgid => $msgstr) {
+
+ $cntstr++;
+
+ if ($start && $cntstr < $start) {
+ continue;
+ }
+
+ if ($end && $cntstr > $end) {
+ break;
+ }
+
+ if ($filter && !in_array($filter, $po->status[$msgid])) {
+ continue;
+ }
+
+ $encstr = base64_encode($msgid);
+
+ $bgcolor = '1px #000000';
+ if (in_array('fuzzy', $po->status[$msgid])) {
+ $bgcolor = '3px #FFFF00';
+ }
+
+ if (in_array('untranslated', $po->status[$msgid])) {
+ $bgcolor = '3px #FF0000';
+ }
+
+ $locked = false;
+ if ($curlock = $locks->getLocks(md5($encstr), $lockscope)) {
+ foreach($curlock as $lid => $linfo) {
+ if ($linfo['lock_scope'] == md5($encstr)) {
+ $bgcolor = '3px #FF00FF';
+ $locked = $linfo['lock_owner'];
+ }
+ }
+ }
+
+ if ($editmode && $cstring == $encstr) {
+
+ // Lock the current item for 5 minutes
+ $locks->setLock(Auth::getAuth(), md5($encstr), $lockscope, 300);
+
+ echo '<form action="' . Horde::applicationUrl('view.php') . "#" . md5($encstr) . '" method="post" name="edit" id="edit">';
+ echo '<input type="hidden" name="module" value="' . $app . '">';
+ echo '<input type="hidden" name="page" value="' . $page . '">';
+ echo '<input type="hidden" name="filter" value="' . $filter . '">';
+ echo '<input type="hidden" name="search" value="' . $search . '">';
+ echo '<input type="hidden" name="cstring" value="' . $encstr . '">';
+ }
+
+
+ ?>
+<a name="<?= md5($encstr) ?>">
+<table border=0 width=100% style="border: solid <?= $bgcolor ?>;">
+<tr>
+ <td valign=top class="control" style="height: 18px; border-bottom: 1px solid #999;"><b>MSGID</b></td>
+ <td valign=top class="control" style="height: 18px; border-bottom: 1px solid #999;"><b>REFERENCES</b></td>
+ <td valign=top class="control" style="height: 18px; border-bottom: 1px solid #999;"><b>STATUS</b></td>
+</tr>
+<tr><td valign=top class="item0">
+ <?= Translate_Display::display_string($msgid) ?><br />
+</td>
+<td valign=top rowspan=3 width=30%>
+<table border=0 width=100% cellspacing=0 cellpadding=0>
+
+<?php
+ $ref = array();
+ foreach($po->ref[$msgid] as $k => $v) {
+ if (preg_match('/(.*):(.*)/', $v, $m)) {
+ $sfile = $m[1];
+ $sline = $m[2];
+
+ if (Translation::hasPermission('viewsource', 'tabs', PERMS_EDIT)) {
+ $surl = Horde::applicationUrl('viewsource.php');
+ $surl = Util::addParameter($surl, array('module' => $app,
+ 'file' => $sfile,
+ 'line' => $sline));
+
+ $onclick = "viewwindow=window.open('". $surl . "', 'viewsource', 'toolbar=no,location=no,status=yes,scrollbars=yes,resizable=yes,width=650,height=350,left=100,top=100'); if(window.focus) { viewwindow.focus()} ; return false;";
+
+ $surl = Horde::link('#', $sline, null, null, $onclick);
+ $surl .= $sline . '</a>';
+ $surl = str_replace('&', '&', $surl);
+ } else {
+ $surl = $sline;
+ }
+
+ $ref[$sfile][] = $surl;
+ }
+ }
+
+ $i = 0;
+ foreach($ref as $k => $v) {
+ echo sprintf("<tr class=item%s><td>%s</td><td align=right>[ %s ]</td></tr>", ($i++ %2), $k, implode(' | ', $v));
+ }
+ ?>
+</table>
+</td>
+<td valign=top rowspan=3 width=10%>
+<?php
+ if ($editmode && $cstring == $encstr) {
+ if (in_array('php-format', $po->status[$msgid])) {
+ echo '<input type="checkbox" checked name="phpformat">' . ' php-format<br>';
+ } else {
+ echo '<input type="checkbox" name="phpformat">' . ' php-format<br>';
+ }
+ if (in_array('fuzzy', $po->status[$msgid])) {
+ echo '<input type="checkbox" checked name="fuzzy">' . ' fuzzy<br>';
+ } else {
+ echo '<input type="checkbox" name="fuzzy">' . ' fuzzy<br>';
+ }
+ } else {
+ echo implode('<br />', $po->status[$msgid]);
+ }
+ ?>
+
+</td>
+</tr>
+<tr>
+ <td valign=top class="control" style="height: 18px; border-bottom: 1px solid #999;">
+ <table border="0" width="100%" cellspacing="0" cellpadding="0">
+ <tr>
+ <td><b>MSGSTR</b></td>
+ <td align="right">
+<?php
+ if ($locked) {
+ echo Horde::img('locked.png') . ' ' . sprintf(_("Locked by %s"), $locked);
+ } else {
+ if (Translation::hasPermission('view', 'tabs', PERMS_EDIT)) {
+ if (!$editmode || $cstring != $encstr) {
+ $surl = Horde::applicationUrl('view.php');
+ $surl = Util::addParameter($surl, array('module' => $app, 'cstring' => $encstr, 'editmode' => 1, 'page' => $page, 'filter' => $filter, 'search' => $search));
+ $surl .= "#" . md5($encstr);
+
+ echo Horde::link($surl, _("Edit Translation")) . Horde::img('translation.png') . ' ' ._("Edit Translation") . "</a>";
+ } elseif ($editmode && $cstring == $encstr) {
+ echo '<input type="submit" class="button" name="submit" value="' . _("Save") . '">';
+ echo ' ';
+ echo '<input type="submit" class="button" name="cancel" value="' . _("Cancel") . '">';
+ }
+ }
+ }
+?></td>
+ </tr>
+ </table>
+</tr>
+<tr><td valign=top class="item0">
+<?php
+ if ($editmode && $cstring == $encstr) {
+ echo '<textarea style="width:100%; height:100%" name="msgstr">' . Translate_Display::display_string($msgstr) . '</textarea><br />';
+ } else {
+ if (in_array('fuzzy', $po->status[$msgid]) && preg_match('/#-#-#-#-#/', $msgstr)) {
+ preg_match_all('/(#-#-#-#-#\s+(.*?)\s+#-#-#-#-#(\n)*(.*))+/', Translate_Display::display_string($msgstr), $matches, PREG_SET_ORDER);
+ foreach($matches as $m) {
+ echo sprintf("%s - %s<br />", $m[4], $m[2]);
+ }
+ } else {
+ echo Translate_Display::display_string($msgstr) . "<br />";
+ }
+ }
+?>
+</td>
+</tr>
+</table>
+<p />
+<?php
+ flush();
+
+ if ($editmode && $cstring == $encstr) {
+ echo '</form>';
+ }
+
+ // print "STAT " . implode(', ', $po->status[$msgid]) . "<br>";
+ }
+}
+
+?>
+<!-- START PAGER -->
+<?php if (isset($numitem) && $numitem > $perpage): ?>
+<table width="100%" class="item box">
+<tr><td>
+<?php
+ $viewurl = Horde::applicationUrl('view.php');
+$viewurl = Util::addParameter($viewurl, array('editmode' => $editmode,
+ 'module' => $app,
+ 'filter' => $filter,
+ 'search' => $search));
+ $pager = &new Horde_UI_Pager('page', $vars, array('num' => $numitem, 'url' => $viewurl, 'page_count' => 10, 'perpage' => $perpage));
+ echo $pager->render($page, $numitem, $viewurl);
+?>
+</td></tr></table>
+<?php endif; ?>
+<!-- END PAGER -->
+<?php
+if ($app) {
+ Translation::RB_close();
+}
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
--- /dev/null
+<?php
+/**
+ * Copyright 2000-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Babel
+ */
+
+require_once dirname(__FILE__) . '/lib/base.php';
+
+$app = Util::getFormData('module');
+$sfile = Util::getFormData('file');
+$sline = Util::getFormData('line');
+
+if ($app == 'horde') {
+ $srcfile = realpath(sprintf("%s/%s", HORDE_BASE, $sfile));
+} else {
+ $srcfile = realpath(sprintf("%s/%s/%s", HORDE_BASE, $app, $sfile));
+}
+
+if (empty($srcfile)) {
+ Horde::fatal(_("Missing filename!"), __FILE__, __LINE__, false);
+}
+
+$rpath = realpath(HORDE_BASE);
+if (!preg_match(";$rpath;", $srcfile)) {
+ Horde::fatal(sprintf(_("Access denied to %s"), $srcfile), __FILE__, __LINE__, false);
+}
+
+// Get File content
+$src = file_get_contents($srcfile);
+
+/* Render the page. */
+require BABEL_TEMPLATES . '/common-header.inc';
+
+Translate_Display::header(sprintf(_("View source: %s"), str_replace(realpath(HORDE_BASE) . '/', '', $srcfile)));
+
+printCode($src, $sline, 10);
+
+require $registry->get('templates', 'horde') . '/common-footer.inc';
+
+function printCode($code, $sline = 1, $sdiff = 10) {
+ if (!is_array($code)) $code = explode("\n", $code);
+
+ $count_lines = count($code);
+ $r = '';
+
+ $from = $sline - $sdiff;
+ $to = $sline + $sdiff;
+
+ foreach ($code as $line => $code_line) {
+
+ if ($from && $line < $from) {
+ continue;
+ }
+
+ if ($to && $line > $to) {
+ break;
+ }
+
+ $r1 = ($line + 1);
+
+ if (ereg("<\?(php)?[^[:graph:]]", $code_line)) {
+ $r2 = highlight_string($code_line, 1)."<br />";
+ } else {
+ $r2 = ereg_replace("(<\?php )+", "", highlight_string("<?php ".$code_line, 1))."<br />";
+ }
+
+ if ($r1 == $sline) {
+ $r .= sprintf('<tr><td align="right" class="control"><b>%s </b></td><td class="item0">%s</td></tr>', $r1, $r2);
+ } else {
+ $r .= sprintf('<tr><td align="right" class="control">%s </td><td>%s</td></tr>', $r1, $r2);
+ }
+ }
+
+ $r = '<table width="100%" cellspacing=0>' . $r . '</table>';
+
+ echo "<div class=\"code\">".$r."</div>";
+}