From 257c7da84d074d8afa650e8c20e0c1d6c4fd2352 Mon Sep 17 00:00:00 2001 From: Jan Schneider Date: Mon, 19 Jan 2009 10:44:39 +0100 Subject: [PATCH] Add Babel (Request #7853). --- babel/COPYING | 280 ++ babel/README | 86 + babel/commit.php | 59 + babel/config/hooks.php.dist | 28 + babel/docs/CHANGES | 5 + babel/docs/CREDITS | 15 + babel/docs/TODO | 8 + babel/download.php | 50 + babel/edit.php | 111 + babel/extract.php | 119 + babel/index.php | 21 + babel/js/effects.js | 65 + babel/js/prototype.js | 4221 ++++++++++++++++++++++++++++++ babel/js/redbox.js | 130 + babel/lib/Display.php | 136 + babel/lib/Gettext.php | 280 ++ babel/lib/Gettext/MO.php | 331 +++ babel/lib/Gettext/PO.php | 209 ++ babel/lib/Translate.php | 748 ++++++ babel/lib/Translate_Help.php | 238 ++ babel/lib/Translation.php | 312 +++ babel/lib/api.php | 76 + babel/lib/base.php | 126 + babel/make.php | 76 + babel/po/fr_FR.po | 493 ++++ babel/po/translation.pot | 489 ++++ babel/reset.php | 53 + babel/scripts/translate.php | 1527 +++++++++++ babel/stats.php | 120 + babel/templates/common-header.inc | 25 + babel/templates/index.php | 78 + babel/templates/layout.html | 14 + babel/themes/graphics/az.png | Bin 0 -> 183 bytes babel/themes/graphics/checked.gif | Bin 0 -> 75 bytes babel/themes/graphics/config.png | Bin 0 -> 901 bytes babel/themes/graphics/delete.png | Bin 0 -> 822 bytes babel/themes/graphics/down.png | Bin 0 -> 558 bytes babel/themes/graphics/edit.png | Bin 0 -> 1007 bytes babel/themes/graphics/extract.png | Bin 0 -> 872 bytes babel/themes/graphics/list.png | Bin 0 -> 1443 bytes babel/themes/graphics/locked.png | Bin 0 -> 750 bytes babel/themes/graphics/make.png | Bin 0 -> 854 bytes babel/themes/graphics/redbox_spinner.gif | Bin 0 -> 3236 bytes babel/themes/graphics/sample.png | Bin 0 -> 220 bytes babel/themes/graphics/skeleton.png | Bin 0 -> 743 bytes babel/themes/graphics/translation.png | Bin 0 -> 790 bytes babel/themes/graphics/unchecked.gif | Bin 0 -> 67 bytes babel/themes/graphics/up.png | Bin 0 -> 572 bytes babel/themes/graphics/upload.png | Bin 0 -> 946 bytes babel/themes/graphics/view.png | Bin 0 -> 1443 bytes babel/themes/graphics/za.png | Bin 0 -> 184 bytes babel/themes/screen.css | 45 + babel/upload.php | 68 + babel/view.php | 502 ++++ babel/viewsource.php | 82 + 55 files changed, 11226 insertions(+) create mode 100644 babel/COPYING create mode 100644 babel/README create mode 100644 babel/commit.php create mode 100644 babel/config/hooks.php.dist create mode 100644 babel/docs/CHANGES create mode 100644 babel/docs/CREDITS create mode 100644 babel/docs/TODO create mode 100644 babel/download.php create mode 100644 babel/edit.php create mode 100644 babel/extract.php create mode 100644 babel/index.php create mode 100644 babel/js/effects.js create mode 100644 babel/js/prototype.js create mode 100644 babel/js/redbox.js create mode 100644 babel/lib/Display.php create mode 100644 babel/lib/Gettext.php create mode 100644 babel/lib/Gettext/MO.php create mode 100644 babel/lib/Gettext/PO.php create mode 100644 babel/lib/Translate.php create mode 100644 babel/lib/Translate_Help.php create mode 100644 babel/lib/Translation.php create mode 100644 babel/lib/api.php create mode 100644 babel/lib/base.php create mode 100644 babel/make.php create mode 100644 babel/po/fr_FR.po create mode 100644 babel/po/translation.pot create mode 100644 babel/reset.php create mode 100755 babel/scripts/translate.php create mode 100644 babel/stats.php create mode 100644 babel/templates/common-header.inc create mode 100644 babel/templates/index.php create mode 100644 babel/templates/layout.html create mode 100644 babel/themes/graphics/az.png create mode 100644 babel/themes/graphics/checked.gif create mode 100644 babel/themes/graphics/config.png create mode 100644 babel/themes/graphics/delete.png create mode 100644 babel/themes/graphics/down.png create mode 100644 babel/themes/graphics/edit.png create mode 100644 babel/themes/graphics/extract.png create mode 100644 babel/themes/graphics/list.png create mode 100644 babel/themes/graphics/locked.png create mode 100644 babel/themes/graphics/make.png create mode 100644 babel/themes/graphics/redbox_spinner.gif create mode 100644 babel/themes/graphics/sample.png create mode 100644 babel/themes/graphics/skeleton.png create mode 100644 babel/themes/graphics/translation.png create mode 100644 babel/themes/graphics/unchecked.gif create mode 100644 babel/themes/graphics/up.png create mode 100644 babel/themes/graphics/upload.png create mode 100644 babel/themes/graphics/view.png create mode 100644 babel/themes/graphics/za.png create mode 100644 babel/themes/screen.css create mode 100644 babel/upload.php create mode 100644 babel/view.php create mode 100644 babel/viewsource.php diff --git a/babel/COPYING b/babel/COPYING new file mode 100644 index 000000000..a6b67561a --- /dev/null +++ b/babel/COPYING @@ -0,0 +1,280 @@ + 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. + + 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.) + +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. + + 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. + + 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 diff --git a/babel/README b/babel/README new file mode 100644 index 000000000..e05f7f649 --- /dev/null +++ b/babel/README @@ -0,0 +1,86 @@ +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 diff --git a/babel/commit.php b/babel/commit.php new file mode 100644 index 000000000..6bd098ae1 --- /dev/null +++ b/babel/commit.php @@ -0,0 +1,59 @@ + + * @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'; diff --git a/babel/config/hooks.php.dist b/babel/config/hooks.php.dist new file mode 100644 index 000000000..ae9ac5c3c --- /dev/null +++ b/babel/config/hooks.php.dist @@ -0,0 +1,28 @@ + + +Thanks to Michael Wallner for File_Gettext PEAR library. + + +Localization +============ + +===================== ====================================================== +French Joel Vandal +===================== ====================================================== diff --git a/babel/docs/TODO b/babel/docs/TODO new file mode 100644 index 000000000..44a17420c --- /dev/null +++ b/babel/docs/TODO @@ -0,0 +1,8 @@ +============================= + Babel Development TODO List +============================= + +:Contact: joel@scopsev.com + +- Add support for Obsolete strings. + diff --git a/babel/download.php b/babel/download.php new file mode 100644 index 000000000..fa20f8628 --- /dev/null +++ b/babel/download.php @@ -0,0 +1,50 @@ + + * @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; diff --git a/babel/edit.php b/babel/edit.php new file mode 100644 index 000000000..1dd567d0b --- /dev/null +++ b/babel/edit.php @@ -0,0 +1,111 @@ + + * @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'; diff --git a/babel/extract.php b/babel/extract.php new file mode 100644 index 000000000..377b82d59 --- /dev/null +++ b/babel/extract.php @@ -0,0 +1,119 @@ + + * @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'; diff --git a/babel/index.php b/babel/index.php new file mode 100644 index 000000000..06f00c219 --- /dev/null +++ b/babel/index.php @@ -0,0 +1,21 @@ + + * @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'; diff --git a/babel/js/effects.js b/babel/js/effects.js new file mode 100644 index 000000000..ff8cb9e48 --- /dev/null +++ b/babel/js/effects.js @@ -0,0 +1,65 @@ + +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.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 diff --git a/babel/js/prototype.js b/babel/js/prototype.js new file mode 100644 index 000000000..0bb8807da --- /dev/null +++ b/babel/js/prototype.js @@ -0,0 +1,4221 @@ +/* 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: ']*>([\\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,'>'); + }, + 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 '#'; + } +}; + +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 '#'; + }, + + 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: ['', '
', 1], + TBODY: ['', '
', 2], + TR: ['', '
', 3], + TD: ['
', '
', 4], + 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 "#"; + } +}); + +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("' . "\n"; + return $html; + } + + function LanguageSelection() { + global $nls, $app; + + $html = ''; + $html .= ''; + $html .= '
'; + $html .= ' '; + $html .= ''; + $html .= ''; + $html .= ' '; + $html .= '
'; + $html .= '
'; + + $html .= '' . "\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 .= '
'; + $msg .= '' . _("Please be patient ...") . ''; + $msg .= '
'; + $msg .= '
'; + 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 .= '
'; + + $msg .= '
'; + echo ''; + flush(); + } + + function RB_close() { + echo ''; + } + +} diff --git a/babel/lib/api.php b/babel/lib/api.php new file mode 100644 index 000000000..b5016e209 --- /dev/null +++ b/babel/lib/api.php @@ -0,0 +1,76 @@ + 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; + +} diff --git a/babel/lib/base.php b/babel/lib/base.php new file mode 100644 index 000000000..223530fae --- /dev/null +++ b/babel/lib/base.php @@ -0,0 +1,126 @@ + + * @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; +} diff --git a/babel/make.php b/babel/make.php new file mode 100644 index 000000000..cf54e7d29 --- /dev/null +++ b/babel/make.php @@ -0,0 +1,76 @@ + + * @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'; diff --git a/babel/po/fr_FR.po b/babel/po/fr_FR.po new file mode 100644 index 000000000..01bdf9eb3 --- /dev/null +++ b/babel/po/fr_FR.po @@ -0,0 +1,493 @@ +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 "" +" tag not closed in file %s.\n" +"Opening tag found in line %d." +msgstr "" +"La balise 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" diff --git a/babel/po/translation.pot b/babel/po/translation.pot new file mode 100644 index 000000000..1421ca3b6 --- /dev/null +++ b/babel/po/translation.pot @@ -0,0 +1,489 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Horde Project +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 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 \n" +"Language-Team: LANGUAGE \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 "" +" 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 "" diff --git a/babel/reset.php b/babel/reset.php new file mode 100644 index 000000000..01ae5fee4 --- /dev/null +++ b/babel/reset.php @@ -0,0 +1,53 @@ + + * @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'; diff --git a/babel/scripts/translate.php b/babel/scripts/translate.php new file mode 100755 index 000000000..7f65fde1f --- /dev/null +++ b/babel/scripts/translate.php @@ -0,0 +1,1527 @@ +#!/usr/bin/php -q +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, '', $offset)) !== false) { + $left += 9; + $buffer = ''; + $linespan = 0; + while (($end = strpos($line, '', $left)) === false) { + $buffer .= substr($line, $left); + $left = 0; + $line = fgets($fp, 4096); + $linespan++; + if ($line === false) { + $c->writeln($c->red(sprintf(" 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, 'writeln(wordwrap($c->red('Warning: ') . sprintf('The help file %s didn\'t start with 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, 'writeln(wordwrap($c->red('Warning: ') . sprintf('The help file %s didn\'t start with 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(); diff --git a/babel/stats.php b/babel/stats.php new file mode 100644 index 000000000..fb7a254ed --- /dev/null +++ b/babel/stats.php @@ -0,0 +1,120 @@ + + * @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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + $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"; + } else { + echo "\n"; + } + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\t"; + $last_key = $key; + } + + echo '
' . _("Language") . '' . _("Locale") . '' . _("Status") . '' . _("Translated") . '' . _("Fuzzy") . '' . _("Untranslated") . '' . _("Obsolete") . '
" . $nls['languages'][$key] . "" . Horde::link(Util::addParameter(Horde::applicationUrl('view.php'), array('display_language' => $key, 'module' => $_app))) . $key . '' . "" . Translate_Display::create_bargraph($value[2], $value[0]) . "" . $value[2] . "" . $value [3] . "" . $value[4] . "" . $value[5] . "
'; + + Translate_Display::info(); + } + + Translation::RB_close(); +} + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/babel/templates/common-header.inc b/babel/templates/common-header.inc new file mode 100644 index 000000000..a99bc08ca --- /dev/null +++ b/babel/templates/common-header.inc @@ -0,0 +1,25 @@ + + +' : '' ?> + +get('name'); +if (!empty($title)) $page_title .= ' :: ' . $title; +if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) { + echo "\n"; +} + +Horde::includeScriptFiles(); + +?> +<?php echo $page_title ?> + + + +> diff --git a/babel/templates/index.php b/babel/templates/index.php new file mode 100644 index 000000000..18fc496af --- /dev/null +++ b/babel/templates/index.php @@ -0,0 +1,78 @@ + + + + + + _("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 ''; + echo ''; + echo ''; + echo ''; + } +} +?> +
+

+ Command +

+
+

+ Description +

+
' . $cmd['text'] . '
diff --git a/babel/templates/layout.html b/babel/templates/layout.html new file mode 100644 index 000000000..ec84ebc8e --- /dev/null +++ b/babel/templates/layout.html @@ -0,0 +1,14 @@ + +
+ + + + + +
+
diff --git a/babel/themes/graphics/az.png b/babel/themes/graphics/az.png new file mode 100644 index 0000000000000000000000000000000000000000..1b9367d9cbf70cc6116b3bec78ce624740d188b6 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^3?%3Nf7cA+1o(uw?qU#2OG^XtdbXZ%0#b}6 zL4Lsu4$p3Y#2Ayj-CY`sf6&?{mGs*SI znu~x!>Ygr+Ar-fh6BruV*w~J7^6>a@OGw<9W?)dTHX&g`)X4)2wsPrkGx(*mEdRCd RraDjygQu&X%Q~loCIB~8ElU6Z literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/checked.gif b/babel/themes/graphics/checked.gif new file mode 100644 index 0000000000000000000000000000000000000000..56cacd3b5a527760fe9db83afe30a399c0c2e36a GIT binary patch literal 75 zcmZ?wbhEHbUkOIpyQ9 zeEHhWx{RN@H@-gCv3kSi^&57sS-Y*ND&_sHW%uVy3kvXVYoGAx?($8Wwts0YudgY; zG&R7=M0LlS-pxx}-ap#5bn!BEb)EAI+U_@0?+gk~iV4ckFFd$n`u9ECpPZimuC40W zn!>lY)|^q1TC!y8p)*?_UfsE7eto)%s;!k#SXkV-eN(3x%e%Sz|9`vs!ohj}Uu3?! z(A!XIwXi&QwwL)1IVola#<`ZJFZ88Xtk}4E^|sAhc3s&wySga;)umZ~Kku6}XHgXo z>;G>@i;63odD;GdJ}n@?ze})9QFC$x?q3TD?97nT$nVyRp#HOCH&6R_wQASa6}OMgSh{reg!-sfR_0?hwJ%=0Ey)V|`+DQ|=UdwRoonk` zeA$`)x8z?swsMN9;`{r%CiM0^zA$I*v~DIQrYEPTJ~%n~u8xenw8;5}IyVP{d*|l) z1x8PszGV4|)qn2K|Nmm~i&HJLw{8W-seg9HbRgAN666>Be=q@t+4H_s0bO?7)5S5Q z;#SfNS7si8Qw*ng8#y&vn^!V%uW;zw-(xf3QP%Y~jt;M*ZCk&6+jPV6Y{JyA{!X9m z;RP*HS`V^gV^roW4bA2j{P6mMhhbmw^fhJAo-uQ8*}0SDwaM$L8DSx;FP+bLgq^+q z^$hn5qs^2RyPZgzO)a8fDWR*EP_fVOj2P|Pd|m;`@8>h&)d1&sdy~Z z>F9PKggPVPAO#g`9eW#OpT8@CkS!BGmK=uGZnt9?z}#-&_IQBD>jmCGz>DLM0UQT7 zK?Dc_CrP*vBp?AvQACO&Qz?q3L5cwkB+W213s{;19AG$(;2 z1iUB;lE_OE5F|;IWkHrjMG;GiD9Ms6ONt_w6iB6{!j@DO$f{b>w34c-npV|yRo5Xk zUDpkxY8q9;&`ewg zdT8|3`lV0TZ!E8`4fM~S@;}ee`)m_6>sy}+PxPNr+kZ<=cGW~h4GrE0rAlq>Cb_m#HF}GM@a~JL1@73CwA_{NY WlB)+7#xFef!*ft%I2K$W(z(Cw`~N5a literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/down.png b/babel/themes/graphics/down.png new file mode 100644 index 0000000000000000000000000000000000000000..9fda51c3d9d3fdc2f1ba9ad9f382640c38e96773 GIT binary patch literal 558 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLlid|Ivr(-q~(3X z;s;kBeV=gpzhUL0`FmeJdinL!m!EZ0_WLJv7{zaL?0dic*}stG|1%dp_bXhIzTsC& z%VDc2|2^t1bCvxU@Y!GGrEX)@|>Xuh}eE@gE4ZqvlQr`q!r<$S)YgrxIY8*ZbrQ(A)}77srr_TS*_> zn0bzHR2XdXZko~OGv`V}3DOI!EO-aezH)2v_p2v3}|S*2vD zwX}BhiiJVT*0?=us$z;dXAoVo^T^d75+-K_uC3D2%!=5pu+urpuQl?T=*>hvr$ag` k^hzJ8AJt#Mpm2a8;JN+%WfQy?0o}mh>FVdQ&MBb@0EA^QT>t<8 literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/edit.png b/babel/themes/graphics/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3cd5a795f7bac3a7b9d51b928c43feb80095f4 GIT binary patch literal 1007 zcmW-fdraF^6vq$muI+^CFbuZQ)Pw-b(B(#B%pRykAR?)QhHaWzr{3)kO>~3c5@N=T z4$ahnC|tJ$80FDI3mavHmQi37pQ(~=LcDO z@?m}?9{>)grbsVvci2Jk0=P5wRA>f30LaoZlV)dU8Dq=K%MQoV+}zyW-X8a0e}8{x zXJ=()Wp{UXeSLjTwPsVSXiJ0=I7_v*4C~8FbWuBqtV!Zz4Wia zL8(+K0+^njE;%K0Oirre6COW$)YaLkx$&I@{3=e%zmP$u)E^-<<#0T0Yh#Qw6+uBk z0l|O(a&mIiN!aan%jI&tUOzfI>TIB;0%0*Ul|%2D&fCAH8nLcF+nJ4&}s?Bj6hFM4<(rXTGeAVkB^T(fBD7$rBQe5 z`g;3*d0$}Zvv|v)G#m~8J>1yP@c8@R+%6ZRhyoBms@mE*m6{SXVzr7yqSlrcj7f*7 zgH|;BW3vqo3{XO9Yie5b`Xu>DOfaDc1MPnH%j8U~pg)!$4C}CW@l@BW!0&hp)Ic}8*RaaGM>NUzrB|@NU(&b^8GXZ7v-zKm( zclCbr`~BNM{`bllW*Dok-b#%1PdtB)5ELfllBEm-mo5hZA)F=55dnn8#l^iG1D0xt zj*f1tQ2YJ_^Bws4(5u#a2Y5O1h1PGwEq#4uWo7qnf?0m7^CR(<;Fp}Wg8I5T@o|AE zO+0O~_*T~x3Pot@hlPcO{axmW+!DQ}adX*YFc=b!N!A3rhQ3C|q;q z2bPKx^MCqyQg!0B^4lh@_WVV$uI5@~RMC&J}rW6{^Hs@oT;jAWQZgV%6Vbits5DP7fl!zoM zH5|D#SDDUMK~ZG(n$(51e9clbjco2|d)+zjx7&Zwf%E-vemh@I%^nUjC~!p}f*?T| z>GT|uA^*I9Lf+U-OSmNF717u<1nJZ!2Gss0^Ku!3C*w*E%T5ZUC_)oSglJ?&iJ%D0 zK?S1IWxZ$;g7_)+uyPqQE-=l&p|THCRv;zU5Cs~@FV$k$kjdgIZnf=3KQfQ$1cC;k zu)*yGzV!VJ4s0bbN_6c*Akz?;l0JaBT}O02yZ*6 z+3Gg^oC}9zZB|ri7o+u_SxDX2Dp8uPHdnIvi_2~l)J%JvR-+v*-kk`JYz<3l5o@4K zjTb1~;gRJPuRy8+j~Ay_gX#6r0tIDE&XkJqPBfhRQH?k`I5x? zUlxzWWR6Hv1Ne62sbeeVsGQDpi7xRvm}g-7HQ1Vm=?v3{O5gbAASSb;K&p+B`v@Rx z4jB7sownl>-@m6SK-dZS58>#2(*{-tDrzh_Ke(Eyrn1#0iyJ4PwrR*U3p%X-$F$4t z0i9v0RSz$_2Uv3Rh+#CpOqoZIBJ^&0ej2oT8<66U*Y41(^kJ}rO z)>U}+tKKlVh1-?f2yyHVt8?~7*9f^|`7)cCM6E1`!~HkUe<7rrt-{Gx&ok(>+*8BC zDLQ3wQ`)yDq3cX6mVfi>6a1>cIu(nCzc9r;h%QuDUU_tDJ|#-ycmC-;DzAIQaG+{U zY>&2gctQA8O=yu|A-5*@Q;5@lQF>VX<%~LeFW(QUx3y8vWalT34J4>jP$9FJd+>DE19{~dUdmlgrtS8SQC6 z0}V6)AV7csSQBnw)R?JdPPt0CrD{*jt@|k}((30mSN`6Yqac5BXkev$m8sJdPZ~J! z&br}s<{WES=%nXgeD=nz@FXH}PlV<3RfLi_5HuD^e4ghFZ9@~%20TwGr z+~5_A4PjxZ8imLnIEO6lv&q@Mh6QaSErEAdFIa4jHFa7=llrlVHbAyms_e8(=)|1M zD1V2CVXpasqrheBSHU&_r*^~z04!H5I}pMEvDm^Q0Rs+xeOc~%toTZe` z238Y0TYPflP3t84BQN=c2}laX2BFR%{PZl%M5@B&N=`9*dnd~0ALG!Uk3mVepz-20D!ZS{J!UazDS}C z*iXDKK3zRU;*@L@mh~(fpTOwvr{A?}Y}sx>rX9xNo>+cNP&2VcFlV54(5KJenla-W z;~d{&UXGqE%oYK5&~;3N0~Y}4;8&=CvzX*zY8M*ZA+-#|wQuaA`n4(X3;n{>UsYfp zSOH7L5S;Oh9U*qDEl@DfW2mWD)oqP!b9Nrq(pkYhT~y%AWPH7%B;>nLJyyaSwAnBjdVtvk6AU4YtiP-X#yX((MD}1#4YhdQ!~GNnz2SX8L{RS1eUE6952z z4Wcn)o?T-L74-JH)oxME&pRi@c<1l#-;@T?WF1|Ai&g8do}qJwJQkhLBtKccsNcWX zUm<7d+8!;}(ZfTt7TSb%4K+_VT{!3-!L@cHLP?lJku9yfCA__y^kMSvNyvGE+GQWG zEfF;Jrd4xK1m*o8UBbE4?7m6Kt}!2_261uY?fM9Fxg%?)cLWuGxL6*T5ct|;&$d{+ zh?j~r+3MsV2M_2S<5=~CC;|j70HA;Z>YtNl(Kqb|TPFW>m%4KD@v^P$-f^y<)h~RI zkJ+r}DIYGQiD|e_PIjbYA=9D~TlYmp$`@xT*XQ@)Kk_34$=3YwE}p13lw$axAtLxo zNjN>EUB2=aq%bF#)^VAd?B)Qgn`1TZ7h04o+!qz;x0tKU9W_q)@5W=o@J4Ycv7zrH z;~{}OytAskKJWf!JWh{s?}fgUe6P?ydg@Yq)x7*3{0cwn|1|z{M5f1CKeEiiSRZ;_ x&|U6s=<*N!kV>@ewk~xi#&*F|cgDZ0e*neZ-yWvMgO>mR002ovPDHLkV1l&ct(X7+ literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/locked.png b/babel/themes/graphics/locked.png new file mode 100644 index 0000000000000000000000000000000000000000..da2752f457c6c6fa97f188580960a4d69b8c4208 GIT binary patch literal 750 zcmVsmTOMf>H=7Ly%CAwRb5*>sq*K2!wDGwwax7f*>fu zMXOv`(8`V0A~f(jlp#%LhH>6F&-d=U?-tXHQ<7(O?mhqi?_AEgb^H_4({CefZQ+Nl zt<8^`n(D6s$g-NsW~G^2uK0R#@_|&Vch?@Kre3v#!=Xex-v4Z9s5csmwbRwrLU(t2 zb0FZmolfW7@%a7K#l?AX4v_JRTV6 z3rC~P3pr#qW2szP^{qWTcnL}KA-1` z5L&fy6`&|8hM^;bAd|_Tn5MZ}sm;zNva+mbnpQ&BbreOd&aFzid1*6*==2I{AFrA9 zOb*U%0ouXUQnRmynpc7Kn&0|}rGU^Uq{>J8Q>tjN`oP-7@?A{9g=Ag|YM z=vajMo0`Kx^;9W&3VX|jP+tr@+4$@M$V%LckKGz=>c|1dfD?q^P%R*a1?0hT0>zz< zJ73@Legn#YqnwSU?m#ieh2a2_z}k>F%7xBmaF}y23M%-t3(O7_OK@<4Avql8oHIba z=wKB!@QS|;m9CZziVnvr7#3E^MZQ=Aa2!B2z^{R;4r~WZyKFRV&@Ct#VAx<-pu4O5 z>KZZZGD-gz@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;%t=H+RCwBA?C^QBY98aOC=LhF^dG zG5q`YpW(*S9}MU3e`EOZ`wzp{pFsTM55u>ge}Ndn{{-Z}m~F~1VZsCkfB<5FnWQVv z!oc|dKLZove+E4uJIa!q0caY-pa1_Dz5@;Z`}aS?-+v$sV*i7?03d+i2Jmw+GJOBX z$nf^-e}>#g{~45p7#Y}rwwiM@G5Cu>eEA2c`9IK*|NjvN00a;VvVZz7|6}O=&dBiP z$1jF|$KEn918tQT<78MT&B+iU`j5d=j)~#xpZ`FI{bOL`fan1TAh-d)fF@sg`j6q= ze`W@s@9!9VWPUNc{>i|w@69iUo&T5^K73$gFc)BA;N)Ur`1R)x+){u5f*Wx3(O-s@ zpP3mLc>XiE$#XHJn~E|3-OAwi>LffB=HCjHH+u z76t!d5M=+uaQ7`6gZExmhRS1%3@dIhF>JZO#BlF11H%OtK86!d7#KL&nBfKh1P~$+ ze}89q5A@nMVFm{Ff4>->v9U6AoCYcc#wG(J5P$p2pwIJ>K}(F0;V%Od156`80Kr}G z9jMtHD7jemGs8}e4-D?YUl zjEqbStgH+Sp=w+V49tuScdq|nc*6gNVY0*@hI2oDFmP#pX87>z6vO&ws7Vj{E5K7fcWe8{~up}{Qu|YkN^LH{I4J1{a>+S#eW!p<{^H70D`-KiHQltU`BQ( z23`&p22iT~#{~2@BO>5Hd7!Yc5R{aD0t67;02US&?70nUE*BR!vSyGdIDG>I5DQF@ gSb(e<1ONgI0FT)u5QzcN`v3p{07*qoM6N<$f**`aH#d)ab@kq01A#Rl5up}>63SDB5JCvZLqHG^i9tbBkcU7J2#=5i zd4}*Zpd^qOq7@Mzpw%KsK`ji{w_RPJtq*+Hv1_}X-39y4&MbfI%+7Srzvs-E^Zoq3 z-|x)f3j*9dl9&J!cn1JGcI*HEAT%_T!{G=70v{hAb8~ZBTifX9=(xBzp-_mTsF|7B z+?n5(%|DTiyvR*l-!R@9CpI|0Ve=1bzns+F+05M3>=Y(n5Woyc-=CVyR(GF_e6 za|g^rlEleOQ9jdmS5jVZdXm^Fb?=UJz~&c-LY=!|_59uQ4+5YkOU4ALN&4CGCnBND zt~~%X?~EY$sv}RWCh6B~nsH=voSm=k(Ph|38ZLjswjeC=vKwJ@&Dxv@0!N$Q0=cVv zH!MvzgUW2ydPH*0$KP;wFfNjJY`gsG^eHiol$*!*iG`>;X;4{ips2!nKkVzHVpF2y z+u{{o6ao?K(zhM)(wj7a#!B6>~<;l1lUy zzQs~B8p_-03AGgSV-uVPeIEG!H8i{}tKu;*8!sA8aq@WKleP1G@!{=N?~-S%v3rhK z?VXApz~}yR)c#ZZ?7a5auG#UUu>l-fdqCYST?9O=jug@P=YwPy245TxHnoeVE#It^ z#qP;tJgrd=qvjAZ9;MMXv{+!$*~{jKm}KZA#Wkgx83J=7nu4XRbr@uQ84O_4X z5@3W9I5LO{+!$woCUeaD^Ns|m=sg})G$@w)MH?E}|klapD@gJ7YM33ltN}d5P z2cE~L;7{UuYF-1QU5d*0D;qzp@7R3!^KN}#PfXe@H+)?VV+57q=Y2lc`U5(%M@T^_@rP?ek5U~;Z?K9uH7YGYB3gm!i+ z8z}V#NtdZW^~G`1fo@aR*-jm6!0ONi+QopL)GC)DHCv&S$_>}Qj6JQv4-LCt$*cTI zRYj2;xw=7#DerneJO<1Fhej_y`u-`jHTidI_% zNQ;-R;JI+k_DZkR(8{hB-MtM2S*%|6m({*WjbxK$dsOaLS2F=C?BOHw;v6J=+1j;) z0(J?U>JlmA6L;oXBdhY!noz^h_G4(L{^;7nXZcO60d>at zi);h674Hj*NSAT&u?n`}ieXQFngSZm8^_6!ip0XPRkeWZ?$()^d|`V{)*0+Y+p~44 ziAGkXCXsf3%md0_#KV2)rnLAuX*q($>C@QklKIrCpC`b*%M^tL~}@^7ETo-k0~r<69OJq_0$kd|Yd=^X45 z>Kgk&Kr1sdKh{BjHZBrvM&#AfoO(G&Lw$8Qv-D@maaO*F@yWWaEJC_-=6>tl9`?bi z1{`i`CV4Hdkb5*Afi-On9dN6xQ*H79rL1ZzB7%SiQ$70n;C9T&&l*Ji zAb8-6e->U#BcUiv11Gk|KsyTU!YFsqg5XE%!+>ijqSl1E0?6ITLEg=G(2puj^m99U z|Kn>*&BOp9XmW)ECi_!H^wOb0y9Jxz`M+_H%~^qIu8kPlvQg<6=4f_o+UvGmSb?K@ zW9i__c-~~)Pd4OYm#EM+5B({viJ5MKpG8z{*N?ZZ1U4UXr}#B{QBEDVwI`d}wtV*> z`=Ux@B^9pV+6xs*9IUD)<`%A3RI9;ym9P=4O_jJ+c6fq$r9udXiN(HLqmguEWuZqg z=>$y5mc&~Vk$C8=(d5sed*y)$GJq3}z2PO|Rp=dnavkXvZpQAQ*fj*M4Dp-7;p?D6 zaQYbrJy3-Byv0b@yBJychnfD1?*2(;WkhWdZQF)mKD1C1zWyWh^Ck2-!HQbZXg(OWK@Yv3;_>u(ny~WoOpyi#rj7SKcFw3eEV!9&uRi*HKqxhI( z99>Nwdv>SvfV1s_Rru!r#6adrCYAd-&t)Iv7Sb6IVlY(X#N%)e3imMxU`nA#Z$OCt zLzWN-4ks-4AsUQ02|grKMd=^yTInWl@7OHi@V0IJ15Ml|)QQ*QU;WtybH=3UGvjEEF)1!-1$M8XyF?ZSE$CUvKj7bK|~ z-COr+8tE;q4SIQLbe*TdnFz!CJVQztR8mh5H%D3ooa-kkEqN(@X(xI}ZU6`|kNJ;~g)ng{^hTx-c-yyegA*{Txgq`qD4N(eY4~9| ZUSfs$>qvfg4kBdETmro2{{bJ@zW~clslWgL literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/sample.png b/babel/themes/graphics/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..57fc33d10892b493c7bc79c748ba6894c1f4f955 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPGa2=EDU1=0+P3=Ay{#qWz-zPGIX zzxLe!bJzY~o75Cs29#nf3GxeOaCmkj4a7?W+K6L;9 literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/skeleton.png b/babel/themes/graphics/skeleton.png new file mode 100644 index 0000000000000000000000000000000000000000..af7b9ffddf9c42b69ef60e5d827b9a0022ffa09c GIT binary patch literal 743 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47#jk7LR^`cn8X+uWMpKtI2g3G z)D88uOpSG{tu6g*lpXAB0v$B+Lv?+9T!6*}2l-@1I%FogCniLfXZYsk{q1bL_~z7e^<5 zIokT`^4#Z_w*0!a?9=@{zn^XV`*q*R6UWb;y>#>1nJZVXJ-%}2%cH}eUtR@zZODO# znt}a5N4u5;`33){0$|wmZleXzK*l6*cNdqRTV@;taySb-B8!2v2N=7Z%(eqE)_b}* zhDcnF?a$?IG7xEP*SQ{|6#aXF^TACKN7KBLRA(ps{=a`^&B8;UpT^0(v%JH6#wTGG z$A8At5_9f;*zn~*(2M{@je~1V;}S$SX(TPbHd{xf=ljctJ9b}?Q#i%y{b=draGy^M zj7z5U2wk6XRyjP%Ie|gs`j(RGv9;&L5>7;jKHa$Lb^7D}g$CBAm%Wbj%G%AYpi$$K yvaz(|Su<Ac3i=Rt>sxYT<{k?5`hMZS!J=qU*I)kUHpUXO@geCx`wk6#F literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/translation.png b/babel/themes/graphics/translation.png new file mode 100644 index 0000000000000000000000000000000000000000..63ce64dad87ebfb910536b356a07d153724e3315 GIT binary patch literal 790 zcmV+x1L^#UP)N`(2)|r=HsEZJJ8Cwo_UibZaZNII}3HX~md} z1{)OR%Nn~D5i$ZvSg`PqsQwsjfg}A7L0> zwC`Wv2fm;00}qeNd(>m+BS$CNmJdnUw>G_`JU6=Tsc+oqnVDi45qIvhbye$P z&EKMgM`2}Ov3PGC8I=czMcS5&NVO)ZF6u%jSpuXwkKxJ%{E z@$A8W{xo~+CHf{HJ|Yl7roWF`u1NS7`K!)+9asd!{$F@T30rJA_U(n~weJi52PNDj Uf@Ogx*#H0l07*qoM6N<$g7~S0DF6Tf literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/unchecked.gif b/babel/themes/graphics/unchecked.gif new file mode 100644 index 0000000000000000000000000000000000000000..bb87f1c5ec3b4f018fd6eac73abaa606c4a05da3 GIT binary patch literal 67 zcmZ?wbhEHb7)VyD;Km5V6~ Rt5BYi_jy98XAlE}H2|a)6p8=< literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/up.png b/babel/themes/graphics/up.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc343c8464aa580301b5aeec4ea1ab9c76c72ab GIT binary patch literal 572 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl5&jbUk?a|L;Hl-u?7Hbmot?DQhz-XFPlMY{IdB9moHldi+nc z>Hod^4}2H=H>iJq@W$Va!d}U+J%9fGUwHDjMfca$>$a-4{9Cc&TE3ap6 z{IlxjfAhpenajRkdi;0kmH+uuE_dwwov{8t5by{_Bm+I}UlQaO4B}G{FxZA|*bFqf z%G1R$q~cc62WMuUBU~>yxE6A8{I-~(#GP^KRFFP<+~ xJ9zLpkC?%h=iAsM(~m7U>f97Bz@Tt|;b4j5522Kk>Ofa8c)I$ztaD0e0stvWK4Aa= literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/upload.png b/babel/themes/graphics/upload.png new file mode 100644 index 0000000000000000000000000000000000000000..e1cce6da34a78873f5eb465aac4e5fa2b062a1a9 GIT binary patch literal 946 zcmV;j15NyiP)? zR zG(3_x`~KO}l=@c|vx~Ju06<6y30&6$@d<fVn@=ig zq40?R7XYOUZp#Cu1e^^ zX9%Q3)2d;6dkvYpd;*FXG@*?`&9%iM1l{X7innFU|AMk zJu!%@S1&=z5J8~e&y6iOE&C;)rj+UFPo}%EcJGCdg(L9kl+1Gz^lnDE|(+j3!9 z0)$BTK7&sz84-*YQE;z&(>I(hBNg^%an=$gctqwl~LHe#Jo6rx3}F5X2ynSg1w zU^^asPoUBCuv@DO%KWMtdDFNsJvaGwpfEVH2~E)uh5;I)j$|SM|9BaVU>AgNSe6gV z@!?Yr+p;O6{*HQNtbL{*`Sb5HCr*ynb}Oadi`iSb+|So7%Pa%H-@&2X U5KR{PhX4Qo07*qoM6N<$f>|`R>Hq)$ literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/view.png b/babel/themes/graphics/view.png new file mode 100644 index 0000000000000000000000000000000000000000..c5bfaf352500f94e5308daa9f7d3a657a8a8131d GIT binary patch literal 1443 zcmV;U1zh@xP)lgrtS8SQC6 z0}V6)AV7csSQBnw)R?JdPPt0CrD{*jt@|k}((30mSN`6Yqac5BXkev$m8sJdPZ~J! z&br}s<{WES=%nXgeD=nz@FXH}PlV<3RfLi_5HuD^e4ghFZ9@~%20TwGr z+~5_A4PjxZ8imLnIEO6lv&q@Mh6QaSErEAdFIa4jHFa7=llrlVHbAyms_e8(=)|1M zD1V2CVXpasqrheBSHU&_r*^~z04!H5I}pMEvDm^Q0Rs+xeOc~%toTZe` z238Y0TYPflP3t84BQN=c2}laX2BFR%{PZl%M5@B&N=`9*dnd~0ALG!Uk3mVepz-20D!ZS{J!UazDS}C z*iXDKK3zRU;*@L@mh~(fpTOwvr{A?}Y}sx>rX9xNo>+cNP&2VcFlV54(5KJenla-W z;~d{&UXGqE%oYK5&~;3N0~Y}4;8&=CvzX*zY8M*ZA+-#|wQuaA`n4(X3;n{>UsYfp zSOH7L5S;Oh9U*qDEl@DfW2mWD)oqP!b9Nrq(pkYhT~y%AWPH7%B;>nLJyyaSwAnBjdVtvk6AU4YtiP-X#yX((MD}1#4YhdQ!~GNnz2SX8L{RS1eUE6952z z4Wcn)o?T-L74-JH)oxME&pRi@c<1l#-;@T?WF1|Ai&g8do}qJwJQkhLBtKccsNcWX zUm<7d+8!;}(ZfTt7TSb%4K+_VT{!3-!L@cHLP?lJku9yfCA__y^kMSvNyvGE+GQWG zEfF;Jrd4xK1m*o8UBbE4?7m6Kt}!2_261uY?fM9Fxg%?)cLWuGxL6*T5ct|;&$d{+ zh?j~r+3MsV2M_2S<5=~CC;|j70HA;Z>YtNl(Kqb|TPFW>m%4KD@v^P$-f^y<)h~RI zkJ+r}DIYGQiD|e_PIjbYA=9D~TlYmp$`@xT*XQ@)Kk_34$=3YwE}p13lw$axAtLxo zNjN>EUB2=aq%bF#)^VAd?B)Qgn`1TZ7h04o+!qz;x0tKU9W_q)@5W=o@J4Ycv7zrH z;~{}OytAskKJWf!JWh{s?}fgUe6P?ydg@Yq)x7*3{0cwn|1|z{M5f1CKeEiiSRZ;_ x&|U6s=<*N!kV>@ewk~xi#&*F|cgDZ0e*neZ-yWvMgO>mR002ovPDHLkV1l&ct(X7+ literal 0 HcmV?d00001 diff --git a/babel/themes/graphics/za.png b/babel/themes/graphics/za.png new file mode 100644 index 0000000000000000000000000000000000000000..a4577f29b64eb0c55105a0e6885c40be23ef1d6b GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^3?%3Nf7cA+1o(uw?qU#2OG^XtdbXZ%0#b}6 zL4Lsu4$p3Y#2Ayj-CY`sf6&?{mGwzsM z5wCzk8lEnWAr-fh6BwA*hVt+PX-i7%2s1F)pqrSG5Ov_d0SiWriwu)e85km;vn;QU S+3*FZhr!d;&t;ucLK6U#$S!LD literal 0 HcmV?d00001 diff --git a/babel/themes/screen.css b/babel/themes/screen.css new file mode 100644 index 000000000..f263e68f1 --- /dev/null +++ b/babel/themes/screen.css @@ -0,0 +1,45 @@ +/* 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; +} diff --git a/babel/upload.php b/babel/upload.php new file mode 100644 index 000000000..37fd38d26 --- /dev/null +++ b/babel/upload.php @@ -0,0 +1,68 @@ + + * @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'; diff --git a/babel/view.php b/babel/view.php new file mode 100644 index 000000000..4f60aa97f --- /dev/null +++ b/babel/view.php @@ -0,0 +1,502 @@ + + * @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 . ' '; + } else { + $hmenu = ''; + } + + Translate_Display::header(_("Meta Informations"), $hmenu); + echo ''; + $i = 0; + foreach($po->meta as $k => $v) { + echo ''; + } + echo '
'; + echo $k; + echo ''; + echo htmlentities($v); + echo '
'; + Translate_Display::info(); + + Translate_Display::header(_("Statistic")); + + $report = Translate::stats($app, $lang); + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo "\n"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\n\t"; + echo "\t"; + + echo '
' . _("Language") . '' . _("Locale") . '' . _("Status") . '' . _("Translated") . '' . _("Fuzzy") . '' . _("Untranslated") . '' . _("Obsolete") . '
" . $nls['languages'][$lang] . "" . $lang . "" . Translate_Display::create_bargraph(@$report[$lang][2], @$report[$lang][0]) . "" . @$report[$lang][2] . "" . @$report[$lang] [3] . "" . @$report[$lang][4] . "" . @$report[$lang][5] . "
'; + Translate_Display::info(); + + $filter_html = ''; + $filter_html .= '
'; + $filter_html .= ''; + $filter_html .= Horde::img('edit.png') . ' '; + $filter_html .= '' . _("Filter: ") . ''; + $filter_html .= '[ '; + if (!$filter) { + $hmenu_desc = '' . _("All") . ''; + } 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 . ' '; + $filter_html .= '| '; + + if ($filter == 'translated') { + $hmenu_desc = '' . _("Translated") . ''; + } 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 . ' '; + $filter_html .= '| '; + + + if ($filter == 'fuzzy') { + $hmenu_desc = '' . _("Fuzzy") . ''; + } 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 . ' '; + $filter_html .= '| '; + + if ($filter == 'untranslated') { + $hmenu_desc = '' . _("Untranslated") . ''; + } 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 . ' '; + $filter_html .= '] '; + + $filter_html .= ''; + $filter_html .= ''; + $filter_html .= ''; + $filter_html .= ''; + $filter_html .= ''; + $filter_html .= ''; + $filter_html .= '
'; + + $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 = ' [' . sprintf(_("%s to %s of %s"), $start, $end, $numitem) . ']'; + 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 '
'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + + ?> + + + + + + + + + + + + + + + +
MSGIDREFERENCESSTATUS
+
  +
+ + +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 . ''; + $surl = str_replace('&', '&', $surl); + } else { + $surl = $sline; + } + + $ref[$sfile][] = $surl; + } + } + + $i = 0; + foreach($ref as $k => $v) { + echo sprintf("", ($i++ %2), $k, implode(' | ', $v)); + } + ?> +
%s[ %s ]
+
+status[$msgid])) { + echo '' . ' php-format
'; + } else { + echo '' . ' php-format
'; + } + if (in_array('fuzzy', $po->status[$msgid])) { + echo '' . ' fuzzy
'; + } else { + echo '' . ' fuzzy
'; + } + } else { + echo implode('
', $po->status[$msgid]); + } + ?> + +
+ + + + + +
MSGSTR + $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") . ""; + } elseif ($editmode && $cstring == $encstr) { + echo ''; + echo ' '; + echo ''; + } + } + } +?>
+
+' . Translate_Display::display_string($msgstr) . '
'; + } 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
", $m[4], $m[2]); + } + } else { + echo Translate_Display::display_string($msgstr) . "
"; + } + } +?>  +
+

+'; + } + + // print "STAT " . implode(', ', $po->status[$msgid]) . "
"; + } +} + +?> + + $perpage): ?> + +
+ $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); +?> +
+ + +get('templates', 'horde') . '/common-footer.inc'; diff --git a/babel/viewsource.php b/babel/viewsource.php new file mode 100644 index 000000000..c76eb7e6a --- /dev/null +++ b/babel/viewsource.php @@ -0,0 +1,82 @@ + + * @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)."
"; + } else { + $r2 = ereg_replace("(<\?php )+", "", highlight_string(""; + } + + if ($r1 == $sline) { + $r .= sprintf('%s %s', $r1, $r2); + } else { + $r .= sprintf('%s %s', $r1, $r2); + } + } + + $r = '' . $r . '
'; + + echo "

".$r."
"; +} -- 2.11.0