--- /dev/null
+use Module::Build;
+
+use strict;
+
+my $requires = {
+ 'perl' => '5.6.0',
+ 'Test::More' => 0.01,
+ 'XML::Parser' => 0.01,
+ 'IO::Scalar' => 0.01,
+ 'File::Basename' => 0.01,
+ 'Spreadsheet::WriteExcel' => 0.42,
+ 'Spreadsheet::WriteExcel::Utility' => 0.01,
+};
+
+if ($] < 5.008) {
+ eval { require Unicode::String; };
+ if ($@) {
+ print "Note: If you want to work with Unicode, you will need to install";
+ print "the optional module Unicode::String and set USE_UNICODE to true.";
+ my $resp = Module::Build->prompt( "Do you want to install it now?", "y");
+ if ( $resp =~ /y/i ) {
+ $requires->{'Unicode::String'} => '0.01';
+ }
+ } else {
+ print "You have Unicode::String installed. If you warn to use Unicode,";
+ print "You will need to set USE_UNICODE to true."
+ }
+}
+
+my $build = Module::Build->new(
+ module_name => 'Excel::Template',
+ license => 'perl',
+ requires => $requires,
+ optional => {
+ },
+ build_requires => {
+ 'File::Path' => '0.01',
+ 'File::Temp' => '0.01',
+ 'Test::Deep' => '0.095',
+ 'Test::More' => '0.47',
+ 'Test::Exception' => '0.21',
+ },
+ create_makefile_pl => 'traditional',
+ add_to_cleanup => [
+ 'META.yml', '*.bak', '*.gz', 'Makefile.PL', 'cover_db',
+ ],
+# test_files => 't/??_*.t',
+);
+
+$build->create_build_script;
--- /dev/null
+Revision history for Perl distribution Excel::Template
+
+0.26 Fri Jun 02 15:30:00 2005
+ - Fixed how widths are whitelisted to allow '.' for fractions
+ - Fixed how certain formats are copied
+ - Added <image>
+ - Added <freezepanes>
+
+0.25 Thu May 26 11:00:00 2005
+ - Changed how the template file is opened to use 3-arg open() if available
+
+0.24 Thu Mar 10 11:00:00 2005
+ - Implemented the KEEP_LEADING_ZEROS node
+ - This wraps the keep_leading_zeros() worksheet method
+ - Improved code coverage with more and better tests
+ - Am now above 90% coverage.
+ - Fixed bug involving relative values.
+ - Fixed bug involving formats when multiple Excel-Template objects ran in
+ the same process.
+ - Improved POD linking
+ - Every module/node reference in POD should link to the appropriate POD
+
+0.23 Fri Feb 25 15:00:00 2005
+ - Improved code coverage with more and better tests
+ - Fixed POD bug with Devel::Cover results
+ - Fixed bugs found in Factory::register while writing tests
+ - Base class wasn't loaded when class was registered.
+ - If registered class didn't exist, wouldn't die until parse()
+
+0.22 Thu Feb 24 15:00:00 2005
+ - new() now accepts either FILE or FILENAME
+ - parse() now accepts a filehandle or a filename
+
+0.21 Thu Feb 24 12:00:00 2005
+ - Fixed documentation bug in BACKREF (Thanks to Paul Williams)
+ - Added code to Makefile.PL to skip .swp files in the Makefile
+ - Added RENDERER option to new()
+ - Deprecated BIG_FILE
+ - Added pod.t and pod_coverage.t
+ - Tests now run under 5.005_02 without warnings
+
+0.20 Wed Jan 26 12:00:00 2005
+ - Removed PM_FILTER by adding an optional USE_UNICODE runtime parameter.
+ - Added a "type" attribute to CELL to allow printing by the write_*() family
+
+0.19 Wed Dec 08 12:00:00 2004
+ - Fixed META.yml
+ - Added more values to the MANIFEST.SKIP
+ - Fixed PM_FILTER to work on Win32 (Thanks, Corion!)
+ - Improved POD
+
+0.18 Fri Nov 12 14:45:00 2004
+ - Removed 'use warnings' from all tests
+ - All warnings are suppressed unless $^W is true
+ - Added 'height' value for ROW
+ - Added 'width' value for CELL
+ - Fixed PM_FILTER to work on Redhat
+
+0.17 Sat Nov 06 23:30:00 2004
+ - Added worksheet protection
+ - Fixed several bugs found by adding more tests
+ - SCOPE node actually works
+ - CONDITIONAL / IF now handles string values correctly
+
+0.16 Fri Nov 05 13:30:00 2004
+ - Fixed Makefile.PL so that it uses PM_FILTER instead of rolling its own
+ - This means that the Unicode handling is cleaner from a source
+ perspective
+ - Added MANIFEST.SKIP to skip /.svn/ and 'vi' .swp files during distcheck
+ - Finally have a semi-real testing suite!
+ - Added minimal Spreadsheet::WriteExcel mockobject
+ - Fixed several bugs in formats found by building tests
+ - HIDDEN node now actually works
+ - LOCKED node now actually works
+
+0.15 Thu Nov 04 16:15:00 2004
+ - Fixed bugs that were:
+ - preventing a worksheet from using a variable name in a loop
+ - allowing two worksheets to have the same name
+ - preventing a worksheet from being called '0' or '0.0'
+ - Added back-references. This allows for one cell to refer to another
+ cell in an Excel-aware way, especially in formulas.
+ - Added the following nodes:
+ - BACKREF
+ - RANGE
+
+0.14 Thu Nov 04 13:30:00 2004
+ - Added new format support for (nearly) all formats supported by
+ Spreadsheet::WriteExcel
+ - Fixed email address everywhere
+
+0.13 Thu Oct 29 07:30:00 2004
+ - Fixed email address and added GoogleGroup
+
+0.12 Thu Apr 08 07:30:00 2004
+ - Fixed bug regarding empty arrays as loop variables
+
+0.11 Wed Mar 17 16:00:00 2004
+ - Fixed bug introduced in 0.10 (Loops were not case-insensitive)
+
+0.10 Wed Mar 17 16:00:00 2004
+ - Parameters are now case-insensitive
+
+0.09 Mon Feb 02 16:00:00 2004
+ - Fixed bug with multiple worksheets
+
+0.08 Fri Jan 30 14:00:00 2004
+ - Added Base to the params for XML::Parser to allow for entity includes
+
+0.07 Fri Jan 23 08:30:00 2004
+ - Fixed the MANIFEST to account for missing files
+
+0.06 Mon Jan 20 11:00:00 2004
+ - Added formulas (no back-references yet)
+ - Improved POD a little
+
+0.05 Wed Jan 16 12:30:00 2004
+ - Fixed a bug in formats
+
+0.04 Wed Jan 16 12:00:00 2004
+ - Added BIG_FILES as an option, which will use
+ Spreadsheet::WriteExcel::Big as the renderer (yet unimplemented)
+ - Changed the output() method to use a tied IO::Scalar (which is
+ now a requirement.
+ - Firmed up the infrastructure in Excel::Template::Format
+ - Added the following tags
+ - FORMAT
+ - HIDDEN
+ - LOCKED
+ - OUTLINE
+ - SHADOW
+ - STRIKEOUT
+
+0.03 Wed Dec 03 20:30:00 2003
+ - Added XML::Parser as a required pre-requisite module
+ - Added Italic format
+ - Removed $VERSION from Excel::Template::Base (Unneeded)
+ - No documentation or testing changes
+
+0.02 Sun Nov 30 17:00:00 2003
+ - documentation improvements
+ - No actual functional changes
+
+0.01 Tue Nov 18 14:23:42 2003
+ - original version; created by ExtUtils::ModuleMaker 0.32
--- /dev/null
+Terms of Perl itself
+
+a) the GNU General Public License as published by the Free
+ Software Foundation; either version 1, or (at your option) any
+ later version, or
+b) the "Artistic License"
+
+---------------------------------------------------------------------------
+
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, 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
+
+
+---------------------------------------------------------------------------
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of the
+package the right to use and distribute the Package in a more-or-less customary
+fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+- "Package" refers to the collection of files distributed by the Copyright
+ Holder, and derivatives of that collection of files created through textual
+ modification.
+- "Standard Version" refers to such a Package if it has not been modified,
+ or has been modified in accordance with the wishes of the Copyright
+ Holder.
+- "Copyright Holder" is whoever is named in the copyright or copyrights for
+ the package.
+- "You" is you, if you're thinking about copying or distributing this Package.
+- "Reasonable copying fee" is whatever you can justify on the basis of
+ media cost, duplication charges, time of people involved, and so on. (You
+ will not be required to justify it to the Copyright Holder, but only to the
+ computing community at large as a market that must bear the fee.)
+- "Freely Available" means that no fee is charged for the item itself, though
+ there may be fees involved in handling the item. It also means that
+ recipients of the item may redistribute it under the same conditions they
+ received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you duplicate
+all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived from
+the Public Domain or from the Copyright Holder. A Package modified in such a
+way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and when
+you changed that file, and provided that you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said modifications
+ to Usenet or an equivalent medium, or placing the modifications on
+ a major archive site such as ftp.uu.net, or by allowing the
+ Copyright Holder to include your modifications in the Standard
+ Version of the Package.
+
+ b) use the modified Package only within your corporation or
+ organization.
+
+ c) rename any non-standard executables so the names do not
+ conflict with standard executables, which must also be provided,
+ and provide a separate manual page for each non-standard
+ executable that clearly documents how it differs from the Standard
+ Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library
+ files, together with instructions (in the manual page or equivalent)
+ on where to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) accompany any non-standard executables with their
+ corresponding Standard Version executables, giving the
+ non-standard executables non-standard names, and clearly
+ documenting the differences in manual pages (or equivalent),
+ together with instructions on where to get the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this Package.
+You may charge any fee you choose for support of this Package. You may not
+charge a fee for this Package itself. However, you may distribute this Package in
+aggregate with other (possibly commercial) programs as part of a larger
+(possibly commercial) software distribution provided that you do not advertise
+this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output from
+the programs of this Package do not automatically fall under the copyright of this
+Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. Aggregation of this Package with a commercial distribution is always permitted
+provided that the use of this Package is embedded; that is, when no overt attempt
+is made to make this Package's interfaces visible to the end user of the
+commercial distribution. Such use shall not be construed as a distribution of
+this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The End
+
+
--- /dev/null
+Build.PL
+MANIFEST
+LICENSE
+README
+Todo
+Changes
+lib/Excel/Template.pm
+lib/Excel/Template/Base.pm
+lib/Excel/Template/Container.pm
+lib/Excel/Template/Context.pm
+lib/Excel/Template/Element.pm
+lib/Excel/Template/Factory.pm
+lib/Excel/Template/Format.pm
+lib/Excel/Template/Iterator.pm
+lib/Excel/Template/TextObject.pm
+lib/Excel/Template/Container/Bold.pm
+lib/Excel/Template/Container/Conditional.pm
+lib/Excel/Template/Container/Format.pm
+lib/Excel/Template/Container/Hidden.pm
+lib/Excel/Template/Container/Italic.pm
+lib/Excel/Template/Container/KeepLeadingZeros.pm
+lib/Excel/Template/Container/Loop.pm
+lib/Excel/Template/Container/Locked.pm
+lib/Excel/Template/Container/Outline.pm
+lib/Excel/Template/Container/Row.pm
+lib/Excel/Template/Container/Scope.pm
+lib/Excel/Template/Container/Strikeout.pm
+lib/Excel/Template/Container/Shadow.pm
+lib/Excel/Template/Container/Workbook.pm
+lib/Excel/Template/Container/Worksheet.pm
+lib/Excel/Template/Element/Backref.pm
+lib/Excel/Template/Element/Cell.pm
+lib/Excel/Template/Element/Formula.pm
+lib/Excel/Template/Element/FreezePanes.pm
+lib/Excel/Template/Element/Image.pm
+lib/Excel/Template/Element/Range.pm
+lib/Excel/Template/Element/Var.pm
+t/001_load.t
+t/002_workbook.t
+t/002.xml
+t/003_worksheet.t
+t/003.xml
+t/004_cell.t
+t/004.xml
+t/005_formats.t
+t/005.xml
+t/006_variables.t
+t/006.xml
+t/007_cell_formats.t
+t/007.xml
+t/008_formula.t
+t/008.xml
+t/009_loop.t
+t/009.xml
+t/010_scope.t
+t/010.xml
+t/011_conditional.t
+t/011.xml
+t/012_backref.t
+t/012.xml
+t/013_range.t
+t/013.xml
+t/014_heightwidth.t
+t/014.xml
+t/015_cell_type.t
+t/015.xml
+t/016_renderers.t
+t/016.xml
+t/017_filehandle.t
+t/018_register.t
+t/019_output.t
+t/020_worksheet_attributes.t
+t/021_loop_error.t
+t/022_keep_leading_zeros.t
+t/023_relative_values.t
+t/024_image.t
+t/025_freezepanes.t
+t/998_pod.t
+t/999_pod_coverage.t
+t/mock.pm
+t/Register_018.pm
+t/Spreadsheet/WriteExcel.pm
+t/Spreadsheet/WriteExcelXML.pm
+t/Spreadsheet/WriteExcel/Worksheet.pm
+t/Spreadsheet/WriteExcel/Big.pm
+Makefile.PL
+META.yml Module meta-data (added by MakeMaker)
--- /dev/null
+^_build
+^Build$
+^blib
+~$
+\.bak$
+^MANIFEST\.SKIP$
+CVS
+\.svn
+cover_db
+\..*\.sw.?$
+^Makefile$
+^pm_to_blib$
+^MakeMaker-\d
+^blibdirs$
+\.old$
+^#.*#$
+^\.#
+^\.DS_Store
+^__MACOSX
--- /dev/null
+Excel::Template is a layout system to use the data structure from HTML::Template
+and create a Microsoft Excel file.
+
+CAVEAT: All limitations stated in Spreadsheet::WriteExcel are in force, as that
+is the module used for rendering. If the XLS file is corrupted, I would first
+make sure you aren't doing anything that it says is bad.
--- /dev/null
+TODO list for Perl distribution Excel::Template
+
+- Add colspan (COL), rowspan (ROW), width (CELL), and height (ROW)
+- Anything else people suggest
+
+Missing S::WE features:
+ Workbook
+ set_tempdir
+ add_char_ext
+ set_custom_color
+ set_1904
+ set_codepage
+ Worksheet
+ write_unicode/write_unicode_le
+ write_url
+ write_url_range
+ write_date_time
+ write_comment
+ activate/select/set_first_sheet
+ set_selection
+ outline_settings
+ thaw_panes
+ merge_range
+ set_zoom
+ Page Setup
+ set_landscape/portrait
+ set_paper
+ center_horizontally/center_vertically
+ set_margins
+ set_header/set_footer
+ repeat_rows/repeat_columns
+ hide_gridlines
+ print_row_col_headers
+ print_area
+ fit_to_pages
+ set_print_scale
+ set_v_pagebreaks/set_h_pagebreaks
+ Formats
+ colors
+ Support for builtin formats
+ set_center_across
+ set_text_wrap
+ set_rotation
+ set_indent
+ set_shrink
+ set_text_justlast
+ set_pattern
+ set_bg_color
+ set_fg_color
+ set_border
+ set_border_color
+ user-defined formats
+ Outlines / Grouping
+ for rows
+ for columns
+ Handling for maximums
+ Maximum number of chars in a string 32767
+ Maximum number of columns 256
+ Maximum number of rows 65536
+ Maximum chars in a sheet name 31
+ Maximum chars in a header/footer 254
--- /dev/null
+package Excel::Template;
+
+use strict;
+
+BEGIN {
+ use Excel::Template::Base;
+ use vars qw ($VERSION @ISA);
+
+ $VERSION = '0.26';
+ @ISA = qw( Excel::Template::Base );
+}
+
+use File::Basename;
+use XML::Parser;
+use IO::Scalar;
+
+use constant RENDER_NML => 'normal';
+use constant RENDER_BIG => 'big';
+use constant RENDER_XML => 'xml';
+
+my %renderers = (
+ RENDER_NML, 'Spreadsheet::WriteExcel',
+ RENDER_BIG, 'Spreadsheet::WriteExcel::Big',
+ RENDER_XML, 'Spreadsheet::WriteExcelXML',
+);
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FILE} = $self->{FILENAME}
+ if !defined $self->{FILE} && defined $self->{FILENAME};
+
+ $self->parse_xml($self->{FILE})
+ if defined $self->{FILE};
+
+ my @renderer_classes = ( 'Spreadsheet::WriteExcel' );
+
+ if (exists $self->{RENDERER} && $self->{RENDERER})
+ {
+ if (exists $renderers{ lc $self->{RENDERER} })
+ {
+ unshift @renderer_classes, $renderers{ lc $self->{RENDERER} };
+ }
+ elsif ($^W)
+ {
+ warn "'$self->{RENDERER}' is not recognized\n";
+ }
+ }
+ elsif (exists $self->{BIG_FILE} && $self->{BIG_FILE})
+ {
+ warn "Use of BIG_FILE is deprecated.\n";
+ unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
+ }
+
+ $self->{RENDERER} = undef;
+ foreach my $class (@renderer_classes)
+ {
+ (my $filename = $class) =~ s!::!/!g;
+ eval {
+ require "$filename.pm";
+ $class->import;
+ };
+ if ($@) {
+ warn "Could not find or compile '$class'\n" if $^W;
+ } else {
+ $self->{RENDERER} = $class;
+ last;
+ }
+ }
+
+ defined $self->{RENDERER} ||
+ die "Could not find a renderer class. Tried:\n\t" .
+ join("\n\t", @renderer_classes) .
+ "\n";
+
+ $self->{USE_UNICODE} = ~~0
+ if $] >= 5.008;
+
+ return $self;
+}
+
+sub param
+{
+ my $self = shift;
+
+ # Allow an arbitrary number of hashrefs, so long as they're the first things # into param(). Put each one onto the end, de-referenced.
+ push @_, %{shift @_} while ref $_[0] eq 'HASH';
+
+ (@_ % 2)
+ && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
+
+ my %params = @_;
+ $params{uc $_} = delete $params{$_} for keys %params;
+ @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
+
+ return ~~1;
+}
+
+sub write_file
+{
+ my $self = shift;
+ my ($filename) = @_;
+
+ my $xls = $self->{RENDERER}->new($filename)
+ || die "Cannot create XLS in '$filename': $!\n";
+
+ eval {
+ $self->_prepare_output($xls);
+ };
+print $@ if $@;
+
+ $xls->close;
+
+ return if $@;
+
+ return ~~1;
+}
+
+sub output
+{
+ my $self = shift;
+
+ my $output;
+ tie *XLS, 'IO::Scalar', \$output;
+
+ $self->write_file(\*XLS)
+ or return;
+
+ return $output;
+}
+
+sub parse_xml
+{
+ my $self = shift;
+ my ($file) = @_;
+
+ my @stack;
+ my @parms = (
+ Handlers => {
+ Start => sub {
+ shift;
+
+ my $name = uc shift;
+
+ my $node = Excel::Template::Factory->_create_node($name, @_);
+ die "'$name' (@_) didn't make a node!\n" unless defined $node;
+
+ if ( $node->isa( 'WORKBOOK' ) )
+ {
+ $self->{WORKBOOK} = $node;
+ }
+ elsif ( $node->is_embedded )
+ {
+ return unless @stack;
+
+ if (exists $stack[-1]{TXTOBJ} &&
+ $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
+ {
+ push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
+ }
+
+ }
+ else
+ {
+ push @{$stack[-1]{ELEMENTS}}, $node
+ if @stack;
+ }
+ push @stack, $node;
+ },
+ Char => sub {
+ shift;
+ return unless @stack;
+
+ my $parent = $stack[-1];
+
+ if (
+ exists $parent->{TXTOBJ}
+ &&
+ $parent->{TXTOBJ}->isa('TEXTOBJECT')
+ ) {
+ push @{$parent->{TXTOBJ}{STACK}}, @_;
+ }
+ },
+ End => sub {
+ shift;
+ return unless @stack;
+
+ pop @stack if $stack[-1]->isa(uc $_[0]);
+ },
+ },
+ );
+
+ if ( ref $file )
+ {
+ *INFILE = $file;
+ }
+ else
+ {
+ my ($filename, $dirname) = fileparse($file);
+
+ push @parms, Base => $dirname;
+
+ eval q{
+ open( INFILE, '<', $file )
+ || die "Cannot open '$file' for reading: $!\n";
+ }; if ( $@ ) {
+ if ( $@ =~ /Too many arguments for open/ ) {
+ open( INFILE, "< $file" )
+ || die "Cannot open '$file' for reading: $!\n";
+ } else {
+ die $@;
+ }
+ }
+ }
+
+ my $parser = XML::Parser->new( @parms );
+ $parser->parse(do { local $/ = undef; <INFILE> });
+
+ close INFILE
+ unless ref $file;
+
+ return ~~1;
+}
+*parse = *parse = \&parse_xml;
+
+sub _prepare_output
+{
+ my $self = shift;
+ return unless $self->{WORKBOOK};
+
+ my ($xls) = @_;
+
+ my $context = Excel::Template::Factory->_create(
+ 'CONTEXT',
+
+ XLS => $xls,
+ PARAM_MAP => [ $self->{PARAM_MAP} ],
+ UNICODE => $self->{UNICODE},
+ );
+
+ $self->{WORKBOOK}->render($context);
+
+ return ~~1;
+}
+
+sub register { shift; Excel::Template::Factory->register(@_) }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template - Excel::Template
+
+=head1 SYNOPSIS
+
+First, make a template. This is an XML file, describing the layout of the
+spreadsheet.
+
+For example, test.xml:
+
+ <workbook>
+ <worksheet name="tester">
+ <cell text="$HOME" />
+ <cell text="$PATH" />
+ </worksheet>
+ </workbook>
+
+Now, create a small program to use it:
+
+ #!/usr/bin/perl -w
+
+ use strict;
+
+ use Excel::Template;
+
+ # Create the Excel template
+ my $template = Excel::Template->new(
+ filename => 'test.xml',
+ );
+
+ # Add a few parameters
+ $template->param(
+ HOME => $ENV{HOME},
+ PATH => $ENV{PATH},
+ );
+
+ $template->write_file('test.xls');
+
+If everything worked, then you should have a spreadsheet called text.xls in your working directory that looks something like:
+
+ A B C
+ +----------------+----------------+----------------
+ 1 | /home/me | /bin:/usr/bin |
+ +----------------+----------------+----------------
+ 2 | | |
+ +----------------+----------------+----------------
+ 3 | | |
+
+=head1 DESCRIPTION
+
+This is a module used for templating Excel files. Its genesis came from the need to use the same datastructure as L<HTML::Template>, but provide Excel files instead. The existing modules don't do the trick, as they require replication of logic that's already been done within L<HTML::Template>.
+
+=head1 MOTIVATION
+
+I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been, I've been asked for HTML, PDF, and Excel. L<HTML::Template> provides the first, and L<PDF::Template> does the second pretty well. But, generating Excel was the sticking point. I already had the data structure for the other templating modules, but I just didn't have an easy mechanism to get that data structure into an XLS file.
+
+=head1 USAGE
+
+=head2 new()
+
+This creates a Excel::Template object.
+
+=head3 Parameters
+
+=over 4
+
+=item * FILE / FILENAME
+
+Excel::Template will parse the template in the given file or filehandle automatically. (You can also use the parse() method, described below.)
+
+If you want to use the __DATA__ section, you can do so by passing
+
+ FILE => \*DATA
+
+=item * RENDERER
+
+The default rendering engine is L<Spreadsheet::WriteExcel>. You may, if you choose, change that to another choice. The legal values are:
+
+=over 4
+
+=item * Excel::Template->RENDER_NML
+
+This is the default of L<Spreadsheet::WriteExcel>.
+
+=item * Excel::Template->RENDER_BIG
+
+This attempts to load L<Spreadsheet::WriteExcel::Big>.
+
+=item * Excel::Template->RENDER_XML
+
+This attempts to load L<Spreadsheet::WriteExcelXML>.
+
+=back
+
+=item * USE_UNICODE
+
+This will use L<Unicode::String> to represent strings instead of Perl's internal string handling. You must already have L<Unicode::String> installed on your system.
+
+The USE_UNICODE parameter will be ignored if you are using Perl 5.8 or higher as Perl's internal string handling is unicode-aware.
+
+NOTE: Certain older versions of L<OLE::Storage_Lite> and mod_perl clash for some reason. Upgrading to the latest version of L<OLE::Storage_Lite> should fix the problem.
+
+=back
+
+=head3 Deprecated
+
+=over 4
+
+=item * BIG_FILE
+
+Instead, use RENDERER => Excel::Template->RENDER_BIG
+
+=back
+
+=head2 param()
+
+This method is exactly like L<HTML::Template>'s param() method.
+
+=head2 parse() / parse_xml()
+
+This method actually parses the template file. It can either be called separately or through the new() call. It will die() if it runs into a situation it cannot handle.
+
+If a filename is passed in (vs. a filehandle), the directory name will be passed in to L<XML::Parser> as the I<Base> parameter. This will allow for XML directives to work as expected.
+
+=head2 write_file()
+
+Create the Excel file and write it to the specified filename, if possible. (This is when the actual merging of the template and the parameters occurs.)
+
+=head2 output()
+
+It will act just like L<HTML::Template>'s output() method, returning the resultant file as a stream, usually for output to the web. (This is when the actual merging of the template and the parameters occurs.)
+
+=head2 register()
+
+This allows you to register a class as handling a node. q.v. L<Excel::Template::Factory> for more info.
+
+=head1 SUPPORTED NODES
+
+This is a partial list of nodes. See the other classes in this distro for more details on specific parameters and the like.
+
+Every node can set the ROW and COL parameters. These are the actual ROW/COL values that the next CELL-type tag will write into.
+
+=over 4
+
+=item * L<WORKBOOK|Excel::Template::Container::Workbook>
+
+This is the node representing the workbook. It is the parent for all other nodes.
+
+=item * L<WORKSHEET|Excel::Template::Container::Worksheet>
+
+This is the node representing a given worksheet.
+
+=item * L<IF|Excel::Template::Container::Conditional>
+
+This node represents a conditional expression. Its children may or may not be rendered. It behaves just like L<HTML::Template>'s TMPL_IF.
+
+=item * L<LOOP|Excel::Template::Container::Loop>
+
+This node represents a loop. It behaves just like L<HTML::Template>'s TMPL_LOOP.
+
+=item * L<ROW|Excel::Template::Container::Row>
+
+This node represents a row of data. This is the 1 in A1. There is no COLUMN node, as of yet.
+
+=item * L<FORMAT|Excel::Template::Container::Format>
+
+This node varies the format for its children. All formatting options supported in L<Spreadsheet::WriteExcel> are supported here. There are also a number of formatting shortcuts, such as L<BOLD|Excel::Template::Container::Bold> and L<ITALIC|Excel::Template::Container::Italic>.
+
+=item * L<BACKREF|Excel::Template::Element::Backref>
+
+This refers back to a cell previously named.
+
+=item * L<CELL|Excel::Template::Element::Cell>
+
+This is the actual cell in a spreadsheet.
+
+=item * L<FORMULA|Excel::Template::Element::Formula>
+
+This is a formula in a spreadsheet.
+
+=item * L<RANGE|Excel::Template::Element::Range>
+
+This is a BACKREF for a number of identically-named cells.
+
+=item * L<VAR|Excel::Template::Element::Var>
+
+This is a variable. It is generally used when the 'text' attribute isn't
+sufficient.
+
+=back
+
+=head1 BUGS
+
+None, that I know of.
+
+=head1 SUPPORT
+
+This is production quality software, used in several production web applications.
+
+=head1 AUTHOR
+
+ Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 CONTRIBUTORS
+
+There is a mailing list at http://groups.google.com/group/ExcelTemplate or exceltemplate@googlegroups.com
+
+=head2 Robert Graff
+
+=over 4
+
+=item * Finishing formats
+
+=item * Fixing several bugs in worksheet naming
+
+=back
+
+=head1 TEST COVERAGE
+
+I use L<Devel::Cover> to test the coverage of my tests. Every release, I intend to improve these numbers.
+
+Excel::Template is also part of the CPAN Kwalitee initiative, being one of the top 100 non-core modules downloaded from CPAN. If you wish to help out, please feel free to contribute tests, patches, and/or suggestions.
+
+ ---------------------------- ------ ------ ------ ------ ------ ------ ------
+ File stmt bran cond sub pod time total
+ ---------------------------- ------ ------ ------ ------ ------ ------ ------
+ blib/lib/Excel/Template.pm 93.8 60.0 58.8 100.0 100.0 24.6 83.3
+ ...ib/Excel/Template/Base.pm 94.4 50.0 n/a 100.0 0.0 7.0 80.0
+ ...cel/Template/Container.pm 100.0 50.0 33.3 100.0 0.0 4.1 83.3
+ ...emplate/Container/Bold.pm 100.0 n/a n/a 100.0 0.0 0.3 95.0
+ .../Container/Conditional.pm 95.9 90.0 66.7 100.0 0.0 1.2 91.0
+ ...plate/Container/Format.pm 100.0 n/a n/a 100.0 0.0 0.5 96.6
+ ...plate/Container/Hidden.pm 100.0 n/a n/a 100.0 0.0 0.0 95.0
+ ...plate/Container/Italic.pm 100.0 n/a n/a 100.0 0.0 0.1 95.0
+ ...ainer/KeepLeadingZeros.pm 100.0 100.0 n/a 100.0 0.0 0.1 96.3
+ ...plate/Container/Locked.pm 100.0 n/a n/a 100.0 0.0 0.0 95.0
+ ...emplate/Container/Loop.pm 96.8 50.0 50.0 100.0 0.0 0.3 82.7
+ ...late/Container/Outline.pm 100.0 n/a n/a 100.0 0.0 0.0 95.0
+ ...Template/Container/Row.pm 100.0 75.0 n/a 100.0 0.0 0.2 90.6
+ ...mplate/Container/Scope.pm 100.0 n/a n/a 100.0 n/a 0.0 100.0
+ ...plate/Container/Shadow.pm 100.0 n/a n/a 100.0 0.0 0.0 95.0
+ ...te/Container/Strikeout.pm 100.0 n/a n/a 100.0 0.0 0.0 95.0
+ ...ate/Container/Workbook.pm 100.0 n/a n/a 100.0 n/a 2.6 100.0
+ ...te/Container/Worksheet.pm 94.7 75.0 n/a 100.0 0.0 1.0 87.1
+ ...Excel/Template/Context.pm 98.0 80.0 75.0 100.0 73.3 21.4 90.7
+ ...Excel/Template/Element.pm 100.0 n/a n/a 100.0 n/a 0.3 100.0
+ ...mplate/Element/Backref.pm 100.0 50.0 33.3 100.0 0.0 0.4 87.1
+ .../Template/Element/Cell.pm 97.9 75.0 80.0 100.0 0.0 3.5 88.6
+ ...mplate/Element/Formula.pm 100.0 n/a n/a 100.0 0.0 0.2 94.1
+ ...te/Element/FreezePanes.pm 100.0 n/a n/a 100.0 0.0 0.0 95.5
+ ...Template/Element/Image.pm 100.0 100.0 n/a 100.0 0.0 0.1 94.3
+ ...Template/Element/Range.pm 100.0 66.7 n/a 100.0 0.0 0.1 88.9
+ ...l/Template/Element/Var.pm 100.0 n/a n/a 100.0 0.0 0.0 94.1
+ ...Excel/Template/Factory.pm 100.0 73.1 66.7 100.0 100.0 23.5 91.4
+ .../Excel/Template/Format.pm 98.4 75.0 33.3 100.0 66.7 5.4 90.5
+ ...xcel/Template/Iterator.pm 98.6 80.0 70.6 100.0 50.0 1.1 88.8
+ ...el/Template/TextObject.pm 92.9 62.5 33.3 100.0 0.0 1.8 80.9
+ Total 97.8 74.3 63.5 100.0 35.7 100.0 89.4
+ ---------------------------- ------ ------ ------ ------ ------ ------ ------
+
+=head1 COPYRIGHT
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the LICENSE file included with this module.
+
+=head1 SEE ALSO
+
+perl(1), L<HTML::Template>, L<Spreadsheet::WriteExcel>
+
+=cut
--- /dev/null
+package Excel::Template::Base;
+
+use strict;
+
+use Excel::Template::Factory;
+
+sub new
+{
+ my $class = shift;
+
+ push @_, %{shift @_} while ref $_[0] eq 'HASH';
+ (@_ % 2)
+ and die "$class->new() called with odd number of option parameters\n";
+
+ my %x = @_;
+
+ # Do not use a hashref-slice here because of the uppercase'ing
+ my $self = {};
+ $self->{uc $_} = $x{$_} for keys %x;
+
+ bless $self, $class;
+}
+
+*isa = *isa = \&Excel::Template::Factory::isa;
+*is_embedded = *is_embedded = \&Excel::Template::Factory::is_embedded;
+
+#sub calculate { ($_[1])->get(@_[0,2]) }
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# return $context->get($self, $attr);
+#}
+
+sub enter_scope { ($_[1])->enter_scope($_[0]) }
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# return $context->enter_scope($self);
+#}
+
+sub exit_scope { ($_[1])->exit_scope($_[0], $_[2]) }
+#{
+# my $self = shift;
+# my ($context, $no_delta) = @_;
+#
+# return $context->exit_scope($self, $no_delta);
+#}
+
+sub deltas
+{
+# my $self = shift;
+# my ($context) = @_;
+
+ return {};
+}
+
+# Everyone seems to have their own versions.
+# Maybe, it's part of the API to require that you have the right one of these
+# defined?
+#sub resolve
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# '';
+#}
+#
+#sub render
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# 1;
+#}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Base - Excel::Template::Base
+
+=head1 PURPOSE
+
+Base class for all Excel::Template classes
+
+=head1 NODE NAME
+
+None
+
+=head1 INHERITANCE
+
+None
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 METHODS
+
+=head2 calculate
+
+This is a wrapper around Excel::Template::Context->get()
+
+=head2 isa
+
+This is a wrapper around Excel::Template::Factory->isa()
+
+=head2 is_embedded
+
+This is a wrapper around Excel::Template::Factory->is_embedded()
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+# Containers are objects that can contain arbitrary elements, such as
+# PageDefs or Loops.
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{ELEMENTS} = []
+ unless exists $self->{ELEMENTS} &&
+ ref $self->{ELEMENTS} eq 'ARRAY';
+
+ return $self;
+}
+
+# Removed as unused code
+#sub _do_page
+#{
+# my $self = shift;
+# my ($method, $context) = @_;
+#
+# for my $e (@{$self->{ELEMENTS}})
+# {
+# $e->enter_scope($context);
+# $e->$method($context);
+# $e->exit_scope($context, 1);
+# }
+#
+# return 1;
+#}
+#
+#sub begin_page { _do_page 'begin_page', @_ }
+#sub end_page { _do_page 'end_page', @_ }
+
+sub iterate_over_children
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $continue = 1;
+
+ for my $e (
+ @{$self->{ELEMENTS}})
+ {
+ $e->enter_scope($context);
+
+ my $rc = $e->render($context);
+ $continue = $rc if $continue;
+
+ $e->exit_scope($context);
+ }
+
+ return $continue;
+}
+
+sub render { $_[0]->iterate_over_children($_[1]) }
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# return $self->iterate_over_children($context);
+#}
+
+# Removed as unused code
+#sub max_of
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# my $max = $context->get($self, $attr);
+#
+# ELEMENT:
+# foreach my $e (@{$self->{ELEMENTS}})
+# {
+# $e->enter_scope($context);
+#
+# my $v = $e->isa('CONTAINER')
+# ? $e->max_of($context, $attr)
+# : $e->calculate($context, $attr);
+#
+# $max = $v if $max < $v;
+#
+# $e->exit_scope($context, 1);
+# }
+#
+# return $max;
+#}
+#
+#sub total_of
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# my $total = 0;
+#
+# ELEMENT:
+# foreach my $e (@{$self->{ELEMENTS}})
+# {
+# $e->enter_scope($context);
+#
+# $total += $e->isa('CONTAINER')
+# ? $e->total_of($context, $attr)
+# : $e->calculate($context, $attr);
+#
+# $e->exit_scope($context, 1);
+# }
+#
+# return $total;
+#}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container::Bold;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{BOLD} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Bold - Excel::Template::Container::Bold
+
+=head1 PURPOSE
+
+To format all children in bold
+
+=head1 NODE NAME
+
+BOLD
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <bold>
+ ... Children here
+ </bold>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Conditional;
+
+#GGG Convert <conditional> to be a special case of <switch>?
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+my %isOp = (
+ '=' => '==',
+ (map { $_ => $_ } ( '>', '<', '==', '!=', '>=', '<=' )),
+ (map { $_ => $_ } ( 'gt', 'lt', 'eq', 'ne', 'ge', 'le' )),
+);
+
+sub _conditional_passes
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $name = $context->get($self, 'NAME');
+ return 0 unless $name =~ /\S/;
+
+ my $val = $context->param($name);
+ $val = @{$val} while ref $val eq 'ARRAY';
+ $val = ${$val} while ref $val eq 'SCALAR';
+
+ my $value = $context->get($self, 'VALUE');
+ if (defined $value)
+ {
+ my $op = $context->get($self, 'OP');
+ $op = defined $op && exists $isOp{$op}
+ ? $isOp{$op}
+ : '==';
+
+ my $res;
+ for ($op)
+ {
+ /^>$/ && do { $res = ($val > $value); last };
+ /^<$/ && do { $res = ($val < $value); last };
+ /^==$/ && do { $res = ($val == $value); last };
+ /^!=$/ && do { $res = ($val != $value); last };
+ /^>=$/ && do { $res = ($val >= $value); last };
+ /^<=$/ && do { $res = ($val <= $value); last };
+ /^gt$/ && do { $res = ($val gt $value); last };
+ /^lt$/ && do { $res = ($val lt $value); last };
+ /^eq$/ && do { $res = ($val eq $value); last };
+ /^ne$/ && do { $res = ($val ne $value); last };
+ /^ge$/ && do { $res = ($val ge $value); last };
+ /^le$/ && do { $res = ($val le $value); last };
+
+ die "Unknown operator '$op' in conditional resolve", $/;
+ }
+
+ return $res && 1;
+ }
+
+ my $istrue = $val && 1;
+
+ my $is = uc $context->get($self, 'IS') || 'TRUE';
+ if ($is eq 'TRUE')
+ {
+ return 0 unless $istrue;
+ }
+ else
+ {
+ warn "Conditional 'is' value was [$is], defaulting to 'FALSE'" . $/
+ if $is ne 'FALSE' && $^W;
+
+ return 0 if $istrue;
+ }
+
+ return 1;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ return 1 unless $self->_conditional_passes($context);
+
+ return $self->iterate_over_children($context);
+}
+
+#sub max_of
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# return 0 unless $self->_conditional_passes($context);
+#
+# return $self->SUPER::max_of($context, $attr);
+#}
+#
+#sub total_of
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# return 0 unless $self->_conditional_passes($context);
+#
+# return $self->SUPER::total_of($context, $attr);
+#}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Conditional - Excel::Template::Container::Conditional
+
+=head1 PURPOSE
+
+To provide conditional execution of children nodes
+
+=head1 NODE NAME
+
+IF
+
+=head1 INHERITANCE
+
+L<CONTAINER|Excel::Template::Container>
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the parameter to test. It is resolved like any other parameter name. (q.v. L<VAR|Excel::Template::Element::Var> for more info.)
+
+=item * VALUE
+
+If VALUE is set, then a comparison operation is done. The value of NAME is compared to VALUE using the value of OP.
+
+=item * OP
+
+If VALUE is set, then this is checked. If it isn't present, then '==' (numeric equality) is assumed. OP must be one of Perl the numeric comparison operators or the string comparison operators. All 6 of each kind is supported.
+
+B<Note>: If you want to use < or <=, you must instead use < or <=. This is to make sure it will parse with L<XML::Parser>. You should not need to use > or >= instead of > and >=, respectively.
+
+=item * IS
+
+If VALUE is not set, then IS is checked. IS is allowed to be either "TRUE" or "FALSE". The boolean value of NAME is checked against IS.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <if name="__ODD__" is="false">
+ ... Children here
+ </if>
+
+In the above example, the children will be executed if the value of __ODD__ (which is set by the L<LOOP|Excel::Template::Container::Loop> node) is false. So, for all even iterations.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+L<LOOP|Excel::Template::Container::Loop>, L<VAR|Excel::Template::Element::Var>
+
+=cut
--- /dev/null
+package Excel::Template::Container::Format;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container );
+
+ use Excel::Template::Container;
+}
+
+use Excel::Template::Format;
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $old_format = $context->active_format;
+ my $format = $context->format_object->copy(
+ $context, $old_format,
+
+ %{$self},
+ );
+ $context->active_format($format);
+
+ my $child_success = $self->iterate_over_children($context);
+
+ $context->active_format($old_format);
+
+ return $child_success;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Format - Excel::Template::Container::Format
+
+=head1 PURPOSE
+
+To format all children according to the parameters
+
+=head1 NODE NAME
+
+FORMAT
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+Boolean attributes should be set to 1, 0, true, or false.
+
+Color values can be the color name or the color index. See http://search.cpan.org/~jmcnamara/Spreadsheet-WriteExcel-2.11/lib/Spreadsheet/WriteExcel.pm#COLOURS_IN_EXCEL
+
+=over 4
+
+=item * align
+
+Set to either left, center, right, fill, or justify. Default is left. See also valign.
+
+=item * bg_color
+
+Set to a color value. Default is none.
+
+=item * bold
+
+This will set bold to on or off, depending on the boolean value.
+
+=item * border
+
+Set the border for all for edges of a cell. Also see bottom, top, left, and right.
+Valid values are 0 - 7.
+
+http://search.cpan.org/~jmcnamara/Spreadsheet-WriteExcel-2.11/lib/Spreadsheet/WriteExcel.pm#set_border()
+
+=item * border_color
+
+Sets the color value for the border. See also border, top_color, bottom_color, left_color
+and right_color.
+
+=item * bottom
+
+See border.
+
+=item * bottom_color
+
+See border_color
+
+=item * color
+
+This will set the color of the text, depending on color value. Default is black.
+
+=item * fg_color
+
+Set to a color value. This color will be used in foreground of some patterns. See color
+to change the color of text. Also see bg_color and pattern.
+
+=item * font
+
+This will sent the font face. Default is Arial.
+
+=item * font_outline
+
+This will set font_outline to on or off, depending on the boolean value. (q.v.
+OUTLINE tag)
+
+=item * font_shadow
+
+This will set font_shadow to on or off, depending on the boolean value. (q.v.
+SHADOW tag). This only applies to Excel for Macintosh.
+
+=item * font_strikeout
+
+This will set font_strikeout to on or off, depending on the boolean value. (q.v.
+STRIKEOUT tag)
+
+=item * hidden
+
+This will set whether the cell is hidden to on or off, depending on the boolean
+value.
+
+=item * indent
+
+Set the indentation level for a cell. Positive integers are allowed.
+
+=item * italic
+
+This will set italic to on or off, depending on the boolean value. (q.v. ITALIC
+tag)
+
+=item * left
+
+See border.
+
+=item * left_color
+
+See border_color.
+
+=item * num_format
+
+Set to the index of one of Excel's built-in number formats. See http://search.cpan.org/~jmcnamara/Spreadsheet-WriteExcel-2.11/lib/Spreadsheet/WriteExcel.pm#set_num_format()
+
+=item * pattern
+
+Set to an integer, 0 - 18. Sets the background fill pattern of a ell. Default is 1, solid.
+
+=item * right
+
+See border.
+
+=item * right_color
+
+See border color.
+
+=item * rotation
+
+Set the rotation of the text in a cell. The rotation can be any angle in the range -90 to 90 degrees.
+The angle 270 is also supported. This indicates text where the letters run from top to bottom.
+
+=item * shrink
+
+A boolean value. If true, text will shrink to fit a cell.
+
+=item * size
+
+This will set the size of the font. Default is 10. Unless a row height is
+specifically set, the row will grow taller as necessary.
+
+=item * text_justlast
+
+A boolean value to justify the last line. Only applies to Far Eastern versions of Excel.
+
+=item * text_wrap
+
+A boolean value. When set to true, text will wrap in a cell instead of crossing over
+into empty cells. If the row height is not set, the row will grow taller to accomodate
+the wrapping text.
+
+=item * top
+
+See border.
+
+=item * top_color
+
+See border_color
+
+=item * valign
+
+Set to top, vcenter, bottom, or vjustify. Default is vcenter. See also align.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <format bold="1">
+ ... Children here
+ </format>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+BOLD, HIDDEN, ITALIC, OUTLINE, SHADOW, STRIKEOUT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Hidden;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{HIDDEN} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Hidden - Excel::Template::Container::Hidden
+
+=head1 PURPOSE
+
+To format all children in hidden
+
+=head1 NODE NAME
+
+HIDDEN
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+You must have protected the worksheet containing any cells that are affected by
+this format. Otherwise, this node will have no effect.
+
+=head1 USAGE
+
+ <hidden>
+ ... Children here
+ </hidden>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a hidden format. All other formatting will remain the same and the
+"hidden"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+WORKSHEET, FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Italic;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{ITALIC} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Italic - Excel::Template::Container::Italic
+
+=head1 PURPOSE
+
+To format all children in italic
+
+=head1 NODE NAME
+
+ITALIC
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <italic>
+ ... Children here
+ </italic>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a italic format. All other formatting will remain the same and the
+"italic"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::KeepLeadingZeros;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $worksheet = $context->active_worksheet;
+
+ $worksheet
+ ? $worksheet->keep_leading_zeros( 1 )
+ : $context->mark( keep_leading_zeros => 1 );
+
+ my $rv = $self->SUPER::render($context);
+
+ $worksheet
+ ? $worksheet->keep_leading_zeros( 0 )
+ : $context->mark( keep_leading_zeros => 0 );
+
+ return $rv;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::KeepLeadingZeros - Excel::Template::Container::KeepLeadingZeros
+
+=head1 PURPOSE
+
+To set the keep_leading_zeros flag for the surrounding worksheet or any worksheets that might be contained within this node.
+
+=head1 NODE NAME
+
+KEEP_LEADING_ZEROS
+
+=head1 INHERITANCE
+
+L<CONTAINER|Excel::Template::Container>
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+Alters how leading zeros are interpreted by L<Spreadsheet::WriteExcel>.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <worksheet>
+ ... Cells here will NOT have leading-zeros preserved
+ <keep_leading_zeros>
+ ... Cells here will have leading-zeros preserved
+ </keep_leading_zeros>
+ ... Cells here will NOT have leading-zeros preserved
+ </worksheet>
+
+ <keep_leading_zeros>
+ <worksheet>
+ ... Cells here will have leading-zeros preserved
+ </worksheet>
+ <worksheet>
+ ... Cells here will have leading-zeros preserved
+ </worksheet>
+ </keep_leading_zeros>
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+L<CELL|Excel::Template::Element::Cell>, L<Spreadsheet::WriteExcel>
+
+=cut
--- /dev/null
+package Excel::Template::Container::Locked;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{LOCKED} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Locked - Excel::Template::Container::Locked
+
+=head1 PURPOSE
+
+To format all children in locked
+
+=head1 NODE NAME
+
+LOCKED
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+You must have protected the worksheet containing any cells that are affected by
+this format. Otherwise, this node will have no effect.
+
+=head1 USAGE
+
+ <locked>
+ ... Children here
+ </locked>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a locked format. All other formatting will remain the same and the
+"locked"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+WORKSHEET, FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Loop;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ if (exists $self->{MAXITERS} && $self->{MAXITERS} < 1)
+ {
+ die "<loop> MAXITERS must be greater than or equal to 1", $/;
+ }
+ else
+ {
+ $self->{MAXITERS} = 0;
+ }
+
+ return $self;
+}
+
+sub _make_iterator
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ return Excel::Template::Factory->_create('ITERATOR',
+ NAME => $context->get($self, 'NAME'),
+ MAXITERS => $context->get($self, 'MAXITERS'),
+ CONTEXT => $context,
+ );
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ unless ($self->{ITERATOR} && $self->{ITERATOR}->more_params)
+ {
+ $self->{ITERATOR} = $self->_make_iterator($context);
+ }
+ my $iterator = $self->{ITERATOR};
+
+ $iterator->enter_scope;
+
+ while ($iterator->can_continue)
+ {
+ $iterator->next;
+
+ $self->iterate_over_children($context);
+
+ # It doesn't seem that iterate_over_children() can ever fail, because
+ # I'm not sure that render() can return false. In PDF::Template, where
+ # this module got most of its code, render() can certainly return false,
+ # in the case of page-breaks. I left the code in because it didn't seem
+ # like it would hurt.
+ #unless ($self->iterate_over_children($context))
+ #{
+ # $iterator->back_up;
+ # last;
+ #}
+ }
+
+ $iterator->exit_scope;
+
+ return 0 if $iterator->more_params;
+
+ return 1;
+}
+
+# These methods are used in PDF::Template to calculate pagebreaks. I'm not sure
+# if they will ever be needed in Excel::Template.
+#sub total_of
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# my $iterator = $self->_make_iterator($context);
+#
+# my $total = 0;
+#
+# $iterator->enter_scope;
+# while ($iterator->can_continue)
+# {
+# $iterator->next;
+# $total += $self->SUPER::total_of($context, $attr);
+# }
+# $iterator->exit_scope;
+#
+# return $total;
+#}
+#
+#sub max_of
+#{
+# my $self = shift;
+# my ($context, $attr) = @_;
+#
+# my $iterator = $self->_make_iterator($context);
+#
+# my $max = $context->get($self, $attr);
+#
+# $iterator->enter_scope;
+# while ($iterator->can_continue)
+# {
+# $iterator->next;
+# my $v = $self->SUPER::max_of($context, $attr);
+#
+# $max = $v if $max < $v;
+# }
+# $iterator->exit_scope;
+#
+# return $max;
+#}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Loop
+
+=head1 PURPOSE
+
+To provide looping
+
+=head1 NODE NAME
+
+LOOP
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the loop. It's used to identify within the parameter set
+what variables to expose to the children nodes each iteration.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <loop name="LOOPY">
+ ... Children here ...
+ </loop>
+
+In the above example, the children nodes would have access to the LOOPY array
+of hashes as parameters. Each iteration through the array would expose a
+different hash of parameters to the children.
+
+These loops work just like HTML::Template's loops. (I promise I'll give more
+info here!)
+
+There is one difference - I prefer using Perl-like scoping, so accessing of
+variables outside the LOOP scope from within is perfectly acceptable. You can
+also hide outside variables with inner values, if you desire, just like Perl.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container::Outline;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FONT_OUTLINE} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Outline - Excel::Template::Container::Outline
+
+=head1 PURPOSE
+
+To format all children in outline
+
+=head1 NODE NAME
+
+OUTLINE
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <outline>
+ ... Children here
+ </outline>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a outline format. All other formatting will remain the same and the
+"outline"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Row;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ $context->{COL} = 0;
+
+ # Apply the height to the current row
+ if (my $height = $context->get($self, 'HEIGHT'))
+ {
+ $height =~ s/\D//g;
+ $height *= 1;
+ if ($height > 0)
+ {
+ $context->active_worksheet->set_row(
+ $context->get( $self, 'ROW' ),
+ $height,
+ );
+ }
+ }
+
+ return $self->SUPER::render($context);
+}
+
+sub deltas
+{
+ return {
+ ROW => +1,
+ };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Row - Excel::Template::Container::Row
+
+=head1 PURPOSE
+
+To provide a row context for CELL tags
+
+=head1 NODE NAME
+
+ROW
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * HEIGHT
+
+Sets the height of the row. The last setting for a given row will win out.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+Each ROW tag will consume one row of the workbook. When the ROW tag starts, it
+will set the COL value to 0.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <row>
+ ... Children here
+ </row>
+
+Generally, you will have CELL and/or FORMULA tags within a ROW.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+CELL, FORMULA
+
+=cut
--- /dev/null
+package Excel::Template::Container::Scope;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+# This is used as a placeholder for scoping values across any number
+# of children. It does nothing on its own.
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Scope
+
+=head1 PURPOSE
+
+To provide scoping of parameters for children
+
+=head1 NODE NAME
+
+SCOPE
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <scope param1="value1" param2="value2">
+ ... Children here ...
+ </scope>
+
+In the above example, the children would all have access to the parameters
+param1 and param2. This is useful if you have a section of your template that
+all has the same set of parameter values, but don't have a common parent.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Container::Shadow;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FONT_SHADOW} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Shadow - Excel::Template::Container::Shadow
+
+=head1 PURPOSE
+
+To format all children in shadow
+
+=head1 NODE NAME
+
+SHADOW
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <shadow>
+ ... Children here
+ </shadow>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a shadow format. All other formatting will remain the same and the
+"shadow"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Strikeout;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container::Format );
+
+ use Excel::Template::Container::Format;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{FONT_STRIKEOUT} = 1;
+
+ return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Strikeout - Excel::Template::Container::Strikeout
+
+=head1 PURPOSE
+
+To format all children in bold
+
+=head1 NODE NAME
+
+STRIKEOUT
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <bold>
+ ... Children here
+ </bold>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Container::Workbook;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw( Excel::Template::Container );
+
+ use Excel::Template::Container;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Workbook - Excel::Template::Container::Workbook
+
+=head1 PURPOSE
+
+The root node
+
+=head1 NODE NAME
+
+WORKBOOK
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+Currently, none. There will be attributes added here, regarding how the
+workbook as a whole will behave.
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <workbook>
+ ... Children here
+ </workbook>
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+Nothing
+
+=cut
--- /dev/null
+package Excel::Template::Container::Worksheet;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Container);
+
+ use Excel::Template::Container;
+}
+
+sub exit_scope { $_[1]->active_worksheet( undef ) }
+
+sub render
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $worksheet = $context->new_worksheet( $self );
+
+ my $password = $context->get( $self, 'PROTECT' );
+ if (defined $password)
+ {
+ $worksheet->protect( $password );
+ }
+
+ $worksheet->keep_leading_zeros( 1 )
+ if $context->mark( 'keep_leading_zeros' );
+
+ return $self->SUPER::render($context);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Worksheet - Excel::Template::Container::Worksheet
+
+=head1 PURPOSE
+
+To provide a new worksheet.
+
+=head1 NODE NAME
+
+WORKSHEET
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the worksheet to be added.
+
+=item * PROTECT
+
+If the attribute exists, it will mark the worksheet as being protected. Whatever
+value is set will be used as the password.
+
+This activates the HIDDEN and LOCKED nodes.
+
+=item * KEEP_LEADING_ZEROS
+
+This will change the behavior of the worksheet to preserve leading zeros.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <worksheet name="My Taxes">
+ ... Children here
+ </worksheet>
+
+In the above example, the children will be executed in the context of the
+"My Taxes" worksheet.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+ROW, CELL, FORMULA
+
+=cut
--- /dev/null
+package Excel::Template::Context;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+use Excel::Template::Format;
+
+# This is a helper object. It is not instantiated by the user, nor does it
+# represent an XML node. Rather, every container will use this object to
+# maintain the context for its children.
+
+my %isAbsolute = map { $_ => ~~1 } qw(
+ ROW
+ COL
+);
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{ACTIVE_WORKSHEET} = undef;
+ $self->{FORMAT_OBJECT} = Excel::Template::Format->new;
+ $self->{ACTIVE_FORMAT} = $self->{FORMAT_OBJECT}->blank_format($self);
+ $self->{WORKSHEET_NAMES} = undef;
+
+ $self->{__MARKS} = {};
+
+ # Removed NAME_MAP until I figure out what the heck it's for
+ for (qw( STACK PARAM_MAP ))
+ {
+ next if defined $self->{$_} && ref $self->{$_} eq 'ARRAY';
+ $self->{$_} = [];
+ }
+
+ $self->{$_} = 0 for keys %isAbsolute;
+
+ return $self;
+}
+
+sub use_unicode { $_[0]->{UNICODE} && 1 }
+
+sub _find_param_in_map
+{
+ my $self = shift;
+ my ($map, $param, $depth) = @_;
+ $param = uc $param;
+ $depth ||= 0;
+
+ my ($val, $found);
+ for my $map (reverse @{$self->{$map}})
+ {
+ next unless exists $map->{$param};
+ $depth--, next if $depth;
+
+ $found = ~~1;
+ $val = $map->{$param};
+ last;
+ }
+
+ die "Parameter '$param' not found\n"
+ if !$found && $self->{DIE_ON_NO_PARAM};
+
+ return $val;
+}
+
+sub param
+{
+ my $self = shift;
+ $self->_find_param_in_map(
+ 'PARAM_MAP',
+ @_,
+ );
+}
+
+#sub named_param
+#{
+# my $self = shift;
+# $self->_find_param_in_map(
+# 'NAME_MAP',
+# @_,
+# );
+#}
+
+sub resolve
+{
+ my $self = shift;
+ my ($obj, $key, $depth) = @_;
+ $key = uc $key;
+ $depth ||= 0;
+
+ my $obj_val = $obj->{$key};
+
+ $obj_val = $self->param($1)
+ if $obj_val =~ /^\$(\S+)$/o;
+
+#GGG Remove this once NAME_MAP is working
+# $obj_val = $self->named_param($1)
+# if $obj_val =~ /^\\(\S+)$/o;
+
+#GGG Does this adequately test values to make sure they're legal??
+ # A value is defined as:
+ # 1) An optional operator (+, -, *, or /)
+ # 2) A decimal number
+
+#GGG Convert this to use //x
+ my ($op, $val) = $obj_val =~ m/^\s*([\+\*\/\-])?\s*([\d.]*\d)\s*$/oi;
+
+ # Unless it's a relative value, we have what we came for.
+ return $obj_val unless $op;
+
+ my $prev_val = $isAbsolute{$key}
+ ? $self->{$key}
+ : $self->get($obj, $key, $depth + 1);
+
+ return $obj_val unless defined $prev_val;
+ return $prev_val unless defined $obj_val;
+
+ # Prevent divide-by-zero issues.
+ return $prev_val if $op eq '/' and $val == 0;
+
+ my $new_val;
+ for ($op)
+ {
+ /^\+$/ && do { $new_val = ($prev_val + $val); last; };
+ /^\-$/ && do { $new_val = ($prev_val - $val); last; };
+ /^\*$/ && do { $new_val = ($prev_val * $val); last; };
+ /^\/$/ && do { $new_val = ($prev_val / $val); last; };
+
+ die "Unknown operator '$op' in arithmetic resolve\n";
+ }
+
+ return $new_val if defined $new_val;
+ return;
+}
+
+sub enter_scope
+{
+ my $self = shift;
+ my ($obj) = @_;
+
+ push @{$self->{STACK}}, $obj;
+
+ for my $key (keys %isAbsolute)
+ {
+ next unless exists $obj->{$key};
+ $self->{$key} = $self->resolve($obj, $key);
+ }
+
+ return ~~1;
+}
+
+sub exit_scope
+{
+ my $self = shift;
+ my ($obj, $no_delta) = @_;
+
+ unless ($no_delta)
+ {
+ my $deltas = $obj->deltas($self);
+ $self->{$_} += $deltas->{$_} for keys %$deltas;
+ }
+
+ pop @{$self->{STACK}};
+
+ return ~~1;
+}
+
+sub get
+{
+ my $self = shift;
+ my ($dummy, $key, $depth) = @_;
+ $depth ||= 0;
+ $key = uc $key;
+
+ return unless @{$self->{STACK}};
+
+ my $obj = $self->{STACK}[-1];
+
+ return $self->{$key} if $isAbsolute{$key};
+
+ my $val = undef;
+ my $this_depth = $depth;
+ foreach my $e (reverse @{$self->{STACK}})
+ {
+ next unless exists $e->{$key};
+ next if $this_depth-- > 0;
+
+ $val = $self->resolve($e, $key, $depth);
+ last;
+ }
+
+ $val = $self->{$key} unless defined $val;
+ return $val unless defined $val;
+
+ return $self->param($1, $depth) if $val =~ /^\$(\S+)$/o;
+
+ return $val;
+}
+
+sub active_format
+{
+ my $self = shift;
+
+ $self->{ACTIVE_FORMAT} = $_[0]
+ if @_;
+
+ $self->{ACTIVE_FORMAT};
+}
+
+sub new_worksheet
+{
+ my $self = shift;
+ my ($worksheet) = @_;
+
+ $self->{ROW} = $self->{COL} = 0;
+ $self->{REFERENCES} = {};
+
+ my $name = $self->get( $worksheet, 'NAME' );
+
+ if ( defined $name && length $name )
+ {
+ if ( exists $self->{WORKSHEET_NAMES}{$name} )
+ {
+ $name = '';
+ }
+ else
+ {
+ $self->{WORKSHEET_NAMES}{$name} = undef;
+ }
+ }
+ else
+ {
+ $name = '';
+ }
+
+ return $self->active_worksheet(
+ $self->{XLS}->add_worksheet( $name ),
+ );
+}
+
+sub mark
+{
+ my $self = shift;
+
+ if ( @_ > 1 )
+ {
+ my %args = @_;
+
+ @{$self->{__MARKS}}{keys %args} = values %args;
+ }
+
+ return $self->{__MARKS}{$_[0]}
+}
+
+sub active_worksheet
+{
+ my $self = shift;
+
+ $self->{ACTIVE_WORKSHEET} = $_[0]
+ if @_;
+
+ $self->{ACTIVE_WORKSHEET};
+}
+
+sub add_reference
+{
+ my $self = shift;
+ my ($ref, $row, $col) = @_;
+
+ $self->{REFERENCES}{$ref} ||= [];
+
+ push @{$self->{REFERENCES}{$ref}}, [ $row, $col ];
+
+ return ~~1;
+}
+
+sub get_all_references
+{
+ my $self = shift;
+ my $ref = uc shift;
+
+ $self->{REFERENCES}{$ref} ||= [];
+
+ return @{ $self->{REFERENCES}{$ref} };
+}
+
+sub get_last_reference
+{
+ my $self = shift;
+ my $ref = uc shift;
+
+ $self->{REFERENCES}{$ref} ||= [];
+
+ return @{ $self->{REFERENCES}{$ref}[-1] };
+}
+
+sub format_object { $_[0]{FORMAT_OBJECT} }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Context
+
+=head1 PURPOSE
+
+This is a helper node that provides the global context for the nodes do their processing within. It provides attribute scoping, parameter resolution, and other very nice things.
+
+Documentation is provided for if you wish to subclass another node.
+
+=head1 NODE NAME
+
+None
+
+=head1 INHERITANCE
+
+None
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 AFFECTS
+
+Everything
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 METHODS
+
+=head2 active_format
+
+=head2 active_worksheet
+
+=head2 add_reference
+
+=head2 format_object
+
+=head2 get
+
+=head2 get_all_references
+
+=head2 get_last_reference
+
+=head2 mark
+
+=head2 new_worksheet
+
+=head2 param
+
+=head2 use_unicode
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Element;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Element::Backref;
+
+use strict;
+use Spreadsheet::WriteExcel::Utility;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub resolve
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $ref_name = $context->resolve($self, 'REF');
+
+ my ($row, $col) = $context->get_last_reference( $ref_name );
+ return '' unless defined $row && defined $col;
+
+ return xl_rowcol_to_cell( $row, $col );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Backref
+
+=head1 PURPOSE
+
+Returns the cell location (i.e. B2) of the last cell to name this reference. To
+return the location of the entire range of cells to name this reference see RANGE.
+
+=head1 NODE NAME
+
+BACKREF
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * REF
+
+This is the name of the reference to look up.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+In the example...
+
+ <row>
+ <cell ref="this_cell"/><cell ref="that_cell"><cell ref="that_cell">
+ </row>
+ <row>
+ <formula>=<backref ref="this_cell">+<backref ref="that_cell"></formula>
+ </row>
+
+The formula in row 2 would be =A1+C1. C1 is the last to reference "that_cell".
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL, RANGE
+
+=cut
--- /dev/null
+package Excel::Template::Element::Cell;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{TXTOBJ} = Excel::Template::Factory->_create('TEXTOBJECT');
+
+ return $self;
+}
+
+sub _get_text
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $txt = $context->get($self, 'TEXT');
+ if (defined $txt)
+ {
+ my $txt_obj = Excel::Template::Factory->_create('TEXTOBJECT');
+ push @{$txt_obj->{STACK}}, $txt;
+ $txt = $txt_obj->resolve($context);
+ }
+ else
+ {
+ $txt = $self->{TXTOBJ}->resolve($context)
+ }
+
+ return $txt;
+}
+
+my %legal_types = (
+ 'blank' => 'write_blank',
+ 'formula' => 'write_formula',
+ 'number' => 'write_number',
+ 'string' => 'write_string',
+ 'url' => 'write_url',
+);
+
+sub render
+{
+ my $self = shift;
+ my ($context, $method) = @_;
+
+ unless ( $method )
+ {
+ my $type = $context->get( $self, 'TYPE' );
+ if ( defined $type )
+ {
+ my $type = lc $type;
+
+ if ( exists $legal_types{ $type } )
+ {
+ $method = $legal_types{ $type };
+ }
+ else
+ {
+ warn "'$type' is not a legal cell type.\n"
+ if $^W;
+ }
+ }
+ }
+
+ $method ||= 'write';
+
+ my ($row, $col) = map { $context->get($self, $_) } qw(ROW COL);
+
+ my $ref = $context->get( $self, 'REF' );
+ if (defined $ref && length $ref)
+ {
+ $context->add_reference( uc( $ref ), $row, $col );
+ }
+
+ # Apply the cell width to the current column
+ if (my $width = $context->get($self, 'WIDTH'))
+ {
+ $width =~ s/[^\d.]//g;
+ $width *= 1;
+ if ($width > 0)
+ {
+ $context->active_worksheet->set_column($col, $col, $width);
+ }
+ }
+
+ $context->active_worksheet->$method(
+ $row, $col,
+ $self->_get_text($context),
+ $context->active_format,
+ );
+
+ return 1;
+}
+
+sub deltas
+{
+ return {
+ COL => +1,
+ };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Cell - Excel::Template::Element::Cell
+
+=head1 PURPOSE
+
+To actually write stuff to the worksheet
+
+=head1 NODE NAME
+
+CELL
+
+=head1 INHERITANCE
+
+L<ELEMENT|Excel::Template::Element>
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * TEXT
+
+This is the text to write to the cell. This can either be text or a parameter with a dollar-sign in front of the parameter name.
+
+=item * COL
+
+Optionally, you can specify which column you want this cell to be in. It can be either a number (zero-based) or an offset. See L<Excel::Template> for more info on offset-based numbering.
+
+=item * REF
+
+Adds the current cell to the a list of cells that can be backreferenced. This is useful when the current cell needs to be referenced by a formula. See L<BACKREF|Excel::Tepmlate::Element::Backref> and L<RANGE|Excel::Tepmlate::Container::Range>.
+
+=item * WIDTH
+
+Sets the width of the column the cell is in. The last setting for a given column
+will win out.
+
+=item * TYPE
+
+This allows you to specify what write_*() method will be used. The default is to call write() and let L<Spreadsheet::WriteExcel> make the right call. However, you may wish to override it. L<Excel::Template> will not do any form of validation on what you provide. You are assumed to know what you're doing.
+
+The legal types (taken from L<Spreadsheet::WriteExcel>) are:
+
+=over 4
+
+=item * blank
+
+=item * formula
+
+=item * number
+
+=item * string
+
+=item * url
+
+=back
+
+q.v. L<Spreadsheet::WriteExcel> for more info.
+
+=back
+
+=head1 CHILDREN
+
+L<FORMULA|Excel::Template::Element::Formula>
+
+=head1 EFFECTS
+
+This will consume one column in the current row.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <cell text="Some Text Here"/>
+ <cell>Some other text here</cell>
+
+ <cell text="$Param2"/>
+ <cell>Some <var name="Param"> text here</cell>
+
+In the above example, four cells are written out. The first two have text hard-coded. The second two have variables. The third and fourth items have another thing that should be noted. If you have text where you want a variable in the middle, you have to use the latter form. Variables within parameters are the entire parameter's value.
+
+Please see L<Spreadsheet::WriteExcel> for what constitutes a legal formula.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+L<ROW|Excel::Template::Container::Row>, L<VAR|Excel::Template::Element::Var>, L<FORMULA|Excel::Template::Element::Formula>
+
+=cut
--- /dev/null
+package Excel::Template::Element::Formula;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element::Cell);
+
+ use Excel::Template::Element::Cell;
+}
+
+sub render { $_[0]->SUPER::render( $_[1], 'write_formula' ) }
+#{
+# my $self = shift;
+# my ($context) = @_;
+#
+# return $self->SUPER::render( $context, 'write_formula' );
+#}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Formula - Excel::Template::Element::Formula
+
+=head1 PURPOSE
+
+To write formulas to the worksheet
+
+=head1 NODE NAME
+
+FORMULA
+
+=head1 INHERITANCE
+
+Excel::Template::Element::Cell
+
+=head1 ATTRIBUTES
+
+All attributes a CELL can have, a FORMULA can have, including the ability to be
+referenced using the 'ref' attribute.
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+This will consume one column on the current row.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <formula text="=(1 + 2)"/>
+ <formula>=SUM(A1:A5)</formula>
+
+ <formula text="$Param2"/>
+ <formula>=(A1 + <var name="Param">)</formula>
+
+In the above example, four formulas are written out. The first two have the
+formula hard-coded. The second two have variables. The third and fourth items
+have another thing that should be noted. If you have a formula where you want a
+variable in the middle, you have to use the latter form. Variables within
+parameters are the entire parameter's value.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+CELL
+
+=cut
--- /dev/null
+package Excel::Template::Element::FreezePanes;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub render {
+ my $self = shift;
+ my ($context) = @_;
+
+ my ($row, $col) = map { $context->get( $self, $_ ) } qw( ROW COL );
+ $context->active_worksheet->freeze_panes( $row, $col );
+
+ return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::FreezePanes - Excel::Template::Element::FreezePanes
+
+=head1 PURPOSE
+
+To insert an image into the worksheet
+
+=head1 NODE NAME
+
+FREEZEPANES
+
+=head1 INHERITANCE
+
+L<ELEMENT|Excel::Template::Element>
+
+=head1 EFFECTS
+
+This will not conume any columns or rows. It is a zero-width assertion.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <freezepanes />
+
+This will do a Freeze Pane at the current cell.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+Nothing
+
+=cut
--- /dev/null
+package Excel::Template::Element::Image;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub render {
+ my $self = shift;
+ my ($context) = @_;
+
+ my ($row, $col, $path, $offset, $scale) = map {
+ $context->get($self, $_)
+ } qw( ROW COL PATH OFFSET SCALE );
+
+ my @offsets = (0,0);
+ if ( $offset =~ /^\s*([\d.]+)\s*,\s*([\d.]+)/ ) {
+ @offsets = ($1,$2);
+ }
+
+ my @scales = (0,0);
+ if ( $scale =~ /^\s*([\d.]+)\s*,\s*([\d.]+)/ ) {
+ @scales = ($1,$2);
+ }
+
+ $context->active_worksheet->insert_bitmap(
+ $row, $col, $path, @offsets, @scales,
+ );
+
+ return 1;
+}
+
+sub deltas {
+ return {
+ COL => +1,
+ };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Image - Excel::Template::Element::Image
+
+=head1 PURPOSE
+
+To insert an image into the worksheet
+
+=head1 NODE NAME
+
+IMAGE
+
+=head1 INHERITANCE
+
+L<ELEMENT|Excel::Template::Element>
+
+=head1 EFFECTS
+
+This will consume one column in the current row.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+ <image path="/Some/Full/Path" />
+ <image path="/Some/Full/Path" offset="2,5" />
+ <image path="/Some/Full/Path" scale="2,0.4" />
+ <image path="/Some/Full/Path" offset="4,0" scale="0,2" />
+
+Please see L<Spreadsheet::WriteExcel/> for more information about the offset and scaling options as well as any other restrictions that might be in place. This node does B<NOT> perform any sort of validation upon your parameters. You are assumed to know what you are doing.
+
+Note that the offset and scaling values are "X,Y". You I<must> provide both values, even if the Y value is 0. If you provide a 0 value for either scaling option, L<Spreadsheet::WriteExcel/> will default that to 1.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+Nothing
+
+=cut
--- /dev/null
+package Excel::Template::Element::Range;
+
+use strict;
+use Spreadsheet::WriteExcel::Utility;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub min { $_[0] < $_[1] ? $_[0] : $_[1] }
+sub max { $_[0] > $_[1] ? $_[0] : $_[1] }
+
+sub resolve
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $ref_name = $context->resolve($self, 'REF');
+
+ my @refs = $context->get_all_references( $ref_name );
+ return '' unless @refs;
+
+ my ($top, $left, $bottom, $right) =
+ ( $refs[0][0], $refs[0][1] ) x 2;
+
+ shift @refs;
+ foreach my $ref ( @refs )
+ {
+ $top = min( $top, $ref->[0]);
+ $bottom = max( $bottom, $ref->[0]);
+ $left = min( $left, $ref->[1]);
+ $right = max( $right, $ref->[1]);
+ }
+
+ return join( ':',
+ xl_rowcol_to_cell($top, $left),
+ xl_rowcol_to_cell($bottom, $right)
+ );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Range
+
+=head1 PURPOSE
+
+Returns a range of cell locations (i.e. B2:C2) that contains all calls using
+this reference. To return the location of the last cell, use BACKREF.
+
+=head1 NODE NAME
+
+RANGE
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * REF
+
+This is the name of the reference to look up.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+In the example...
+
+ <row>
+ <cell ref="this_cell"/><cell ref="that_cell"><cell ref="that_cell">
+ </row>
+ <row>
+ <formula>=SUM(<range ref="that_cell">)</formula>
+ </row>
+
+The formula in row 2 would be =SUM(B1:C1).
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL, BACKREF
+
+=cut
--- /dev/null
+package Excel::Template::Element::Var;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Element);
+
+ use Excel::Template::Element;
+}
+
+sub resolve { ($_[1])->param($_[1]->resolve($_[0], 'NAME')) }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Var
+
+=head1 PURPOSE
+
+To provide parameter substitution.
+
+=head1 NODE NAME
+
+VAR
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the parameter to substitute here.
+
+=back
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+This is used exactly like HTML::Template's TMPL_VAR. There is one exception -
+since you can have variable names inside the parameters, you can do something
+like:
+
+ <loop name="LOOPY">
+ <var name="$SomeParam"/>
+ </loop>
+
+Where the actual name to be substituted is, itself, a parameter.
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+CELL
+
+=cut
--- /dev/null
+package Excel::Template::Factory;
+
+use strict;
+
+my %Manifest = (
+
+# These are the instantiable nodes
+ 'IF' => 'Excel::Template::Container::Conditional',
+ 'LOOP' => 'Excel::Template::Container::Loop',
+ 'ROW' => 'Excel::Template::Container::Row',
+ 'SCOPE' => 'Excel::Template::Container::Scope',
+ 'WORKBOOK' => 'Excel::Template::Container::Workbook',
+ 'WORKSHEET' => 'Excel::Template::Container::Worksheet',
+
+ 'BACKREF' => 'Excel::Template::Element::Backref',
+ 'CELL' => 'Excel::Template::Element::Cell',
+ 'FORMULA' => 'Excel::Template::Element::Formula',
+ 'FREEZEPANES' => 'Excel::Template::Element::FreezePanes',
+ 'IMAGE' => 'Excel::Template::Element::Image',
+ 'RANGE' => 'Excel::Template::Element::Range',
+ 'VAR' => 'Excel::Template::Element::Var',
+
+ 'FORMAT' => 'Excel::Template::Container::Format',
+
+# These are all the Format short-cut objects
+# They are also instantiable
+ 'BOLD' => 'Excel::Template::Container::Bold',
+ 'HIDDEN' => 'Excel::Template::Container::Hidden',
+ 'ITALIC' => 'Excel::Template::Container::Italic',
+ 'LOCKED' => 'Excel::Template::Container::Locked',
+ 'OUTLINE' => 'Excel::Template::Container::Outline',
+ 'SHADOW' => 'Excel::Template::Container::Shadow',
+ 'STRIKEOUT' => 'Excel::Template::Container::Strikeout',
+
+ 'KEEP_LEADING_ZEROS' => 'Excel::Template::Container::KeepLeadingZeros',
+
+# These are the helper objects
+# They are also in here to make E::T::Factory::isa() work.
+ 'CONTEXT' => 'Excel::Template::Context',
+ 'ITERATOR' => 'Excel::Template::Iterator',
+ 'TEXTOBJECT' => 'Excel::Template::TextObject',
+
+ 'CONTAINER' => 'Excel::Template::Container',
+ 'ELEMENT' => 'Excel::Template::Element',
+
+ 'BASE' => 'Excel::Template::Base',
+);
+
+my %isBuildable = map { $_ => ~~1 } qw(
+ WORKBOOK WORKSHEET
+ FORMAT BOLD HIDDEN ITALIC LOCKED OUTLINE SHADOW STRIKEOUT
+ IF ROW LOOP SCOPE KEEP_LEADING_ZEROS
+ CELL FORMULA FREEZEPANES IMAGE
+ VAR BACKREF RANGE
+);
+
+{
+ my %Loaded;
+ sub _load_class
+ {
+ my $self = shift;
+ my ($class) = @_;
+
+ unless ( exists $Loaded{$class} )
+ {
+ (my $filename = $class) =~ s!::!/!g;
+ eval {
+ require "$filename.pm";
+ }; if ($@) {
+ die "Cannot find or compile PM file for '$class' ($filename)\n";
+ }
+
+ $Loaded{$class} = ~~1;
+ }
+
+ return ~~1;
+ }
+}
+
+{
+ my @param_names = qw(name class isa);
+ sub register
+ {
+ my $self = shift;
+ my %params = @_;
+
+ for (@param_names)
+ {
+ unless ($params{$_})
+ {
+ warn "$_ was not supplied to register()\n" if $^W;
+ return;
+ }
+ }
+
+ my $name = uc $params{name};
+ if (exists $Manifest{$name})
+ {
+ warn "$params{name} already exists in the manifest.\n" if $^W;
+ return;
+ }
+
+ my $isa = uc $params{isa};
+ unless (exists $Manifest{$isa})
+ {
+ warn "$params{isa} does not exist in the manifest.\n" if $^W;
+ return;
+ }
+
+ {
+ no strict 'refs';
+ unshift @{"$params{class}::ISA"}, $Manifest{$isa};
+ }
+
+ $self->_load_class( $Manifest{$isa} );
+ $self->_load_class( $params{class} );
+
+ $Manifest{$name} = $params{class};
+ $isBuildable{$name} = ~~1;
+
+ return ~~1;
+ }
+}
+
+sub _create
+{
+ my $self = shift;
+ my $name = uc shift;
+
+ return unless exists $Manifest{$name};
+
+ $self->_load_class( $Manifest{$name} );
+
+ return $Manifest{$name}->new(@_);
+}
+
+sub _create_node
+{
+ my $self = shift;
+ my $name = uc shift;
+
+ return unless exists $isBuildable{$name};
+
+ return $self->_create($name, @_);
+}
+
+sub isa
+{
+ return unless @_ >= 2;
+ exists $Manifest{uc $_[1]}
+ ? UNIVERSAL::isa($_[0], $Manifest{uc $_[1]})
+ : UNIVERSAL::isa(@_)
+}
+
+sub is_embedded
+{
+ return unless @_ >= 1;
+
+ isa( $_[0], $_ ) && return ~~1 for qw( VAR BACKREF RANGE );
+ return;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Factory
+
+=head1 PURPOSE
+
+To provide a common way to instantiate Excel::Template nodes
+
+=head1 USAGE
+
+=head2 register()
+
+Use this to register your own nodes.
+
+Example forthcoming.
+
+=head1 METHODS
+
+=head2 isa
+
+This is a customized isa() wrapper for syntactic sugar
+
+=head2 is_embedded
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+package Excel::Template::Format;
+
+use strict;
+
+# This is the format repository. Spreadsheet::WriteExcel does not cache the
+# known formats. So, it is very possible to continually add the same format
+# over and over until you run out of RAM or addressability in the XLS file. In
+# real life, less than 10-20 formats are used, and they're re-used in various
+# places in the file. This provides a way of keeping track of already-allocated
+# formats and making new formats based on old ones.
+
+sub new { bless {}, shift }
+
+sub _assign { $_[0]{$_[1]} = $_[2]; $_[0]{$_[2]} = $_[1] }
+# my $self = shift;
+# my ($key, $format) = @_;
+# $self->{$key} = $format;
+# $self->{$format} = $key;
+#}
+
+sub _retrieve_key { $_[0]{ $_[1] } }
+# my $self = shift;
+# my ($format) = @_;
+# return $self->{$format};
+#}
+
+*_retrieve_format = \&_retrieve_key;
+#sub _retrieve_format {
+# my $self = shift;
+# my ($key) = @_;
+# return $self->{$key};
+#}
+
+{
+ my @_boolean_formats = qw(
+ bold italic locked hidden font_outline font_shadow font_strikeout
+ text_wrap text_justlast shrink
+ );
+
+ my @_integer_formats = qw(
+ size num_format underline rotation indent pattern border
+ bottom top left right
+ );
+
+ my @_string_formats = qw(
+ font color align valign bg_color fg_color border_color
+ bottom_color top_color left_color right_color
+ );
+
+ sub _params_to_key
+ {
+ my %params = @_;
+ $params{lc $_} = delete $params{$_} for keys %params;
+
+ my @parts = (
+ (map { $params{$_} ? 1 : '' } @_boolean_formats),
+ (map { $params{$_} ? $params{$_} + 0 : '' } @_integer_formats),
+ (map { $params{$_} || '' } @_string_formats),
+ );
+
+ return join( "\n", @parts );
+ }
+
+ sub _key_to_params
+ {
+ my ($key) = @_;
+
+ my @key_parts = split /\n/, $key;
+
+ my @boolean_parts = splice @key_parts, 0, scalar( @_boolean_formats );
+ my @integer_parts = splice @key_parts, 0, scalar( @_integer_formats );
+ my @string_parts = splice @key_parts, 0, scalar( @_string_formats );
+
+ my %params;
+ $params{ $_boolean_formats[$_] } = ~~1
+ for grep { $boolean_parts[$_] } 0 .. $#_boolean_formats;
+
+ $params{ $_integer_formats[$_] } = $integer_parts[$_]
+ for grep { defined $integer_parts[$_] && length $integer_parts[$_] } 0 .. $#_integer_formats;
+
+ $params{ $_string_formats[$_] } = $string_parts[$_]
+ for grep { $string_parts[$_] } 0 .. $#_string_formats;
+
+ return %params;
+ }
+
+ sub copy
+ {
+ my $self = shift;
+ my ($context, $old_fmt, %properties) = @_;
+
+ # This is a key used for non-format book-keeping.
+ delete $properties{ ELEMENTS };
+
+ defined(my $key = _retrieve_key($self, $old_fmt))
+ || die "Internal Error: Cannot find key for format '$old_fmt'!\n";
+
+ my %params = _key_to_params($key);
+ PROPERTY:
+ while ( my ($prop, $value) = each %properties )
+ {
+ $prop = lc $prop;
+ foreach (@_boolean_formats)
+ {
+ if ($prop eq $_) {
+ $params{$_} = ($value && $value !~ /false/i);
+ next PROPERTY;
+ }
+ }
+ foreach (@_integer_formats, @_string_formats)
+ {
+ if ($prop eq $_) {
+ $params{$_} = $value;
+ next PROPERTY;
+ }
+ }
+
+ warn "Property '$prop' is unrecognized\n" if $^W;
+ }
+
+ my $new_key = _params_to_key(%params);
+
+ my $format = _retrieve_format($self, $new_key);
+ return $format if $format;
+
+ $format = $context->{XLS}->add_format(%params);
+ _assign($self, $new_key, $format);
+ return $format;
+ }
+}
+
+sub blank_format
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $blank_key = _params_to_key();
+
+ my $format = _retrieve_format($self, $blank_key);
+ return $format if $format;
+
+ $format = $context->{XLS}->add_format;
+ _assign($self, $blank_key, $format);
+ return $format;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Format - Excel::Template::Format
+
+=head1 PURPOSE
+
+Helper class for FORMAT
+
+=head1 NODE NAME
+
+None
+
+=head1 INHERITANCE
+
+None
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 METHODS
+
+=head2 blank_format
+
+Provides a blank format for use
+
+=head2 copy
+
+Clones an existing format, so that a new format can be built from it
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
--- /dev/null
+package Excel::Template::Iterator;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ unless (Excel::Template::Factory::isa($self->{CONTEXT}, 'CONTEXT'))
+ {
+ die "Internal Error: No context object passed to ", __PACKAGE__, $/;
+ }
+
+ $self->{MAXITERS} ||= 0;
+
+ # This is the index we will work on NEXT, in whatever direction the
+ # iterator is going.
+ $self->{INDEX} = -1;
+
+ # This is a short-circuit parameter to let the iterator function in a
+ # null state.
+ $self->{NO_PARAMS} = 0;
+ unless ($self->{NAME} =~ /\w/)
+ {
+ $self->{NO_PARAMS} = 1;
+
+ warn "INTERNAL ERROR: 'NAME' was blank was blank when passed to ", __PACKAGE__, $/ if $^W;
+
+ return $self;
+ }
+
+ # Cache the reference to the appropriate data.
+ $self->{DATA} = $self->{CONTEXT}->param($self->{NAME});
+
+ unless (ref $self->{DATA} eq 'ARRAY')
+ {
+ $self->{NO_PARAMS} = 1;
+ warn "'$self->{NAME}' does not have a list of parameters", $/ if $^W;
+
+ return $self;
+ }
+
+ unless (@{$self->{DATA}})
+ {
+ $self->{NO_PARAMS} = 1;
+ }
+
+ $self->{MAX_INDEX} = $#{$self->{DATA}};
+
+ return $self;
+}
+
+sub enter_scope
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ for my $x ($self->{DATA}[$self->{INDEX}])
+ {
+ $x->{uc $_} = delete $x->{$_} for keys %$x;
+ }
+
+ push @{$self->{CONTEXT}{PARAM_MAP}}, $self->{DATA}[$self->{INDEX}];
+
+ return 1;
+}
+
+sub exit_scope
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ # There has to be the base parameter map and at least the one that
+ # Iterator::enter_scope() added on top.
+ @{$self->{CONTEXT}{PARAM_MAP}} > 1 ||
+ die "Internal Error: ", __PACKAGE__, "'s internal param_map off!", $/;
+
+ pop @{$self->{CONTEXT}{PARAM_MAP}};
+
+ return 1;
+}
+
+sub can_continue
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ return 1 if $self->more_params;
+
+ return 0;
+}
+
+sub more_params
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ return 1 if $self->{MAX_INDEX} > $self->{INDEX};
+
+ return 0;
+}
+
+# Call this method BEFORE incrementing the index to the next value.
+sub _do_globals
+{
+ my $self = shift;
+
+ my $data = $self->{DATA}[$self->{INDEX}];
+
+ # Perl's arrays are 0-indexed. Thus, the first element is at index "0".
+ # This means that odd-numbered elements are at even indices, and vice-versa.
+ # This also means that MAX (the number of elements in the array) can never
+ # be the value of an index. It is NOT the last index in the array.
+
+ $data->{'__FIRST__'} ||= ($self->{INDEX} == 0);
+ $data->{'__INNER__'} ||= (0 < $self->{INDEX} && $self->{INDEX} < $self->{MAX_INDEX});
+ $data->{'__LAST__'} ||= ($self->{INDEX} == $self->{MAX_INDEX});
+ $data->{'__ODD__'} ||= !($self->{INDEX} % 2);
+
+ return 1;
+}
+
+sub next
+{
+ my $self = shift;
+
+ return 0 if $self->{NO_PARAMS};
+
+ return 0 unless $self->more_params;
+
+ $self->exit_scope;
+
+ $self->{INDEX}++;
+
+ $self->_do_globals;
+
+ $self->enter_scope;
+
+ return 1;
+}
+
+# This method doesn't seem to be used ...
+# If it is reinstated, here's the POD for it
+#=head2 back_up
+#
+#Go to the previous iteration of the loop
+#
+#sub back_up
+#{
+# my $self = shift;
+#
+# return 0 if $self->{NO_PARAMS};
+#
+# $self->exit_scope;
+#
+# $self->{INDEX}--;
+#
+# $self->_do_globals;
+#
+# $self->enter_scope;
+#
+# return 1;
+#}
+
+# This method doesn't seem to be used ...
+# If it is reinstated, here's the POD for it
+#=head2 reset
+#
+#Resets the iterator
+#
+#sub reset
+#{
+# my $self = shift;
+#
+# return 0 if $self->{NO_PARAMS};
+#
+# $self->{INDEX} = -1;
+#
+# return 1;
+#}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Iterator
+
+=head1 PURPOSE
+
+This is meant for internal use only. Documentation is provided for subclassing.
+
+=head1 NODE NAME
+
+None
+
+=head1 INHERITANCE
+
+None
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 AFFECTS
+
+This is a helper class for LOOP
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 METHODS
+
+=head2 can_continue
+
+Determines if the iterator can continue.
+
+Currently, this wraps more_params(), but there other possible situations, such as the page ending.
+
+=head2 more_params
+
+Determines if the iterator for the loop has more parameters that it can consume
+
+=head2 next
+
+Go to the next iteration of the loop
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+LOOP
+
+=cut
--- /dev/null
+package Excel::Template::TextObject;
+
+use strict;
+
+BEGIN {
+ use vars qw(@ISA);
+ @ISA = qw(Excel::Template::Base);
+
+ use Excel::Template::Base;
+}
+
+# This is a helper object. It is not instantiated by the user,
+# nor does it represent an XML object. Rather, certain elements,
+# such as <textbox>, can use this object to do text with variable
+# substitutions.
+
+sub new
+{
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+
+ $self->{STACK} = []
+ unless defined $self->{STACK} &&
+ ref $self->{STACK} eq 'ARRAY';
+
+ return $self;
+}
+
+sub resolve
+{
+ my $self = shift;
+ my ($context) = @_;
+
+ my $use_unicode = $context->use_unicode;
+
+ my $t;
+ if ($use_unicode)
+ {
+ require Unicode::String;
+ $t = Unicode::String::utf8('');
+ }
+ else
+ {
+ $t = '';
+ }
+
+ for my $tok (@{$self->{STACK}})
+ {
+ my $val = $tok;
+ $val = $val->resolve($context)
+ if Excel::Template::Factory::is_embedded( $val );
+
+ $t .= $use_unicode
+ ? Unicode::String::utf8("$val")
+ : $val;
+ }
+
+ return $t;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::TextObject
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rob.kinyon@gmail.com)
+
+=head1 SEE ALSO
+
+=cut
--- /dev/null
+use strict;
+
+use Test::More tests => 2;
+
+my $CLASS = 'Excel::Template';
+
+use_ok( $CLASS );
+
+my $object = $CLASS->new ();
+isa_ok( $object, $CLASS );
+
+
--- /dev/null
+<workbook />
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/002.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet />
+ <worksheet name="foo"/>
+ <worksheet name="foo"/>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/003.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'foo' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="cell">
+ <cell>Test1</cell>
+ <cell text="Test2" />
+ <cell />
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/004.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'cell' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'Test1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'Test2', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', '', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <bold />
+ <hidden />
+ <italic />
+ <locked />
+ <outline />
+ <shadow />
+ <strikeout />
+
+ <format font_outline="1" />
+ <format shrink="1" />
+ <format text_wrap="1" />
+ <format text_justlast="1" />
+ <format size="3" />
+ <format num_format="3" />
+ <format underline="3" />
+ <format rotation="3" />
+ <format indent="3" />
+ <format pattern="3" />
+ <format border="3" />
+ <format bottom="3" />
+ <format top="3" />
+ <format left="3" />
+ <format right="3" />
+ <format font="3" />
+ <format color="3" />
+ <format align="3" />
+ <format valign="3" />
+ <format bg_color="3" />
+ <format fg_color="3" />
+ <format border_color="3" />
+ <format bottom_color="3" />
+ <format top_color="3" />
+ <format left_color="3" />
+ <format right_color="3" />
+
+
+ <bold><italic><hidden /></italic></bold>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/005.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_format( 'bold', '1' )
+Spreadsheet::WriteExcel::add_format( 'hidden', '1' )
+Spreadsheet::WriteExcel::add_format( 'italic', '1' )
+Spreadsheet::WriteExcel::add_format( 'locked', '1' )
+Spreadsheet::WriteExcel::add_format( 'font_outline', '1' )
+Spreadsheet::WriteExcel::add_format( 'font_shadow', '1' )
+Spreadsheet::WriteExcel::add_format( 'font_strikeout', '1' )
+Spreadsheet::WriteExcel::add_format( 'shrink', '1' )
+Spreadsheet::WriteExcel::add_format( 'text_wrap', '1' )
+Spreadsheet::WriteExcel::add_format( 'text_justlast', '1' )
+Spreadsheet::WriteExcel::add_format( 'size', '3' )
+Spreadsheet::WriteExcel::add_format( 'num_format', '3' )
+Spreadsheet::WriteExcel::add_format( 'underline', '3' )
+Spreadsheet::WriteExcel::add_format( 'rotation', '3' )
+Spreadsheet::WriteExcel::add_format( 'indent', '3' )
+Spreadsheet::WriteExcel::add_format( 'pattern', '3' )
+Spreadsheet::WriteExcel::add_format( 'border', '3' )
+Spreadsheet::WriteExcel::add_format( 'bottom', '3' )
+Spreadsheet::WriteExcel::add_format( 'top', '3' )
+Spreadsheet::WriteExcel::add_format( 'left', '3' )
+Spreadsheet::WriteExcel::add_format( 'right', '3' )
+Spreadsheet::WriteExcel::add_format( 'font', '3' )
+Spreadsheet::WriteExcel::add_format( 'color', '3' )
+Spreadsheet::WriteExcel::add_format( 'align', '3' )
+Spreadsheet::WriteExcel::add_format( 'valign', '3' )
+Spreadsheet::WriteExcel::add_format( 'bg_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'fg_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'border_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'bottom_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'top_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'left_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'right_color', '3' )
+Spreadsheet::WriteExcel::add_format( 'bold', '1', 'italic', '1' )
+Spreadsheet::WriteExcel::add_format( 'bold', '1', 'hidden', '1', 'italic', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="cell">
+ <cell><var name="test1" /></cell>
+ <cell text="$test2" />
+ <cell>PRE <var name="test1" /> POST</cell>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 5;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/006.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok(
+ $object->param(
+ test1 => 'test1',
+ test2 => 'test2',
+ ),
+ 'Parameters set',
+);
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'cell' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'test1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'test2', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', 'PRE test1 POST', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="cell">
+ <cell />
+ <bold>
+ <cell />
+ <italic>
+ <cell />
+ </italic>
+ </bold>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/007.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'cell' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '', '1' )
+Spreadsheet::WriteExcel::add_format( 'bold', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', '', '2' )
+Spreadsheet::WriteExcel::add_format( 'bold', '1', 'italic', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', '', '3' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="formula">
+ <formula>Test1</formula>
+ <formula text="Test2" />
+ <formula />
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/008.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'formula' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write_formula( '0', '0', 'Test1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write_formula( '0', '1', 'Test2', '1' )
+Spreadsheet::WriteExcel::Worksheet::write_formula( '0', '2', '', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="loops">
+ <loop name="loopy">
+ <row>
+ <cell text="$value" />
+ <cell text="text" />
+ </row>
+ </loop>
+
+ <loop name="outer">
+ <row>
+ <cell text="$iter" />
+ <loop name="inner">
+ <cell text="$value" />
+ </loop>
+ </row>
+ </loop>
+
+ <loop name="no_iters">
+ <cell text="$value" />
+ </loop>
+
+ <loop name="no_params">
+ <cell text="$value" />
+ </loop>
+
+ <loop name="empty" />
+
+ <loop name="" />
+
+ </worksheet>
+
+ <loop name="worksheets">
+ <worksheet name="$value" />
+ </loop>
+</workbook>
--- /dev/null
+BEGIN{ $^W = 0 }
+use strict;
+
+use Test::More tests => 5;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/009.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok(
+ $object->param(
+ loopy => [
+ { value => 1 },
+ { value => 2 },
+ { value => 3 },
+ ],
+ outer => [
+ { iter => 'a', inner => [ { value => 1 }, { value => 2 } ] },
+ { iter => 'b', inner => [ { value => 3 }, { value => 4 } ] },
+ ],
+ worksheets => [
+ { value => 1 },
+ { value => 2 },
+ { value => 3 },
+ ],
+ no_iters => [
+ ],
+ ),
+ 'Parameters set',
+);
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'loops' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'text', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '0', '2', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '1', 'text', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '0', '3', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '1', 'text', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '0', 'a', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '1', '1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '2', '2', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '4', '0', 'b', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '4', '1', '3', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '4', '2', '4', '1' )
+Spreadsheet::WriteExcel::add_worksheet( '1' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_worksheet( '2' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_worksheet( '3' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="scope">
+ <scope text="1">
+ <cell />
+ <cell />
+ </scope>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/010.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'scope' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', '1', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="conditional">
+ <loop name="loopy">
+ <row>
+ <if name="int">
+ <cell text="bool true" />
+ </if>
+ <if name="int" is="FALSE">
+ <cell text="bool false" />
+ </if>
+ <if name="int" op="==" value="0">
+ <cell text="num == passes" />
+ </if>
+ <if name="int" op="!=" value="0">
+ <cell text="num != passes" />
+ </if>
+ <if name="int" op=">" value="0">
+ <cell text="num > passes" />
+ </if>
+ <if name="int" op=">=" value="0">
+ <cell text="num >= passes" />
+ </if>
+ <if name="int" op='<' value="0">
+ <cell text="num < passes" />
+ </if>
+ <if name="int" op="<=" value="0">
+ <cell text="num <= passes" />
+ </if>
+ <if name="char" op="eq" value="y">
+ <cell text="char eq passes" />
+ </if>
+ <if name="char" op="ne" value="y">
+ <cell text="char ne passes" />
+ </if>
+ <if name="char" op="gt" value="y">
+ <cell text="char gt passes" />
+ </if>
+ <if name="char" op="ge" value="y">
+ <cell text="char ge passes" />
+ </if>
+ <if name="char" op="lt" value="y">
+ <cell text="char lt passes" />
+ </if>
+ <if name="char" op="le" value="y">
+ <cell text="char le passes" />
+ </if>
+ </row>
+ </loop>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 5;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/011.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok(
+ $object->param(
+ loopy => [
+ { int => 0, char => 'n' },
+ { int => 0, char => 'y' },
+ { int => 1, char => 'z' },
+ { int => -1, char => 'y' },
+ ],
+ ),
+ 'Parameters set',
+);
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'conditional' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'bool false', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'num == passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', 'num >= passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '3', 'num <= passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '4', 'char ne passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '5', 'char lt passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '6', 'char le passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '0', 'bool false', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '1', 'num == passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '2', 'num >= passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '3', 'num <= passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '4', 'char eq passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '5', 'char ge passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '6', 'char le passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '0', 'bool true', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '1', 'num != passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '2', 'num > passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '3', 'num >= passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '4', 'char ne passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '5', 'char gt passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '6', 'char ge passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '0', 'bool true', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '1', 'num != passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '2', 'num < passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '3', 'num <= passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '4', 'char eq passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '5', 'char ge passes', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '6', 'char le passes', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="backref">
+ <cell ref="foo" text="not me" />
+ <cell ref="foo" text="me" />
+ <cell>=<backref ref="foo"/></cell>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/012.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'backref' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'not me', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'me', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', '=B1', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="backref">
+ <cell ref="foo" text="1" />
+ <cell ref="foo" text="2" />
+ <cell ref="foo" text="3" />
+ <cell>=SUM(<range ref="foo"/>)</cell>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/013.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'backref' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '1', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', '2', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', '3', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '3', '=SUM(A1:C1)', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="heightwidth">
+ <row height="30">
+ <cell width="10" text="1" />
+ <cell width="0.5" text="1" />
+ </row>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/014.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'heightwidth' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '0', '30' )
+Spreadsheet::WriteExcel::Worksheet::set_column( '0', '0', '10' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '1', '1' )
+Spreadsheet::WriteExcel::Worksheet::set_column( '1', '1', '0.5' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', '1', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook>
+ <worksheet name="cell_type">
+ <cell type="string">String</cell>
+ <cell type="number">Number</cell>
+ <cell type="blank">Blank</cell>
+ <cell type="url">URL</cell>
+ <cell type="formula">Formula</cell>
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => 't/015.xml',
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'cell_type' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write_string( '0', '0', 'String', '1' )
+Spreadsheet::WriteExcel::Worksheet::write_number( '0', '1', 'Number', '1' )
+Spreadsheet::WriteExcel::Worksheet::write_blank( '0', '2', 'Blank', '1' )
+Spreadsheet::WriteExcel::Worksheet::write_url( '0', '3', 'URL', '1' )
+Spreadsheet::WriteExcel::Worksheet::write_formula( '0', '4', 'Formula', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
--- /dev/null
+<workbook />
--- /dev/null
+use strict;
+
+use Test::More tests => 10;
+
+use lib 't';
+use mock;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+{
+ mock::reset;
+ my $object = $CLASS->new(
+ renderer => 'big',
+ filename => 't/016.xml',
+ );
+ isa_ok( $object, $CLASS );
+
+ ok( $object->write_file( 'filename' ), 'Something returned' );
+
+ my @calls = mock::get_calls;
+ is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::Big::new( 'filename' )
+Spreadsheet::WriteExcel::Big::add_format( '' )
+Spreadsheet::WriteExcel::Big::close( '' )
+__END_EXPECTED__
+}
+
+{
+ mock::reset;
+ my $object = $CLASS->new(
+ renderer => Excel::Template->RENDER_XML,
+ filename => 't/016.xml',
+ );
+ isa_ok( $object, $CLASS );
+
+ ok( $object->write_file( 'filename' ), 'Something returned' );
+
+ my @calls = mock::get_calls;
+ is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcelXML::new( 'filename' )
+Spreadsheet::WriteExcelXML::add_format( '' )
+Spreadsheet::WriteExcelXML::close( '' )
+__END_EXPECTED__
+}
+
+{
+ mock::reset;
+ my $object = $CLASS->new(
+ renderer => Excel::Template->RENDER_NML,
+ filename => 't/016.xml',
+ );
+ isa_ok( $object, $CLASS );
+
+ ok( $object->write_file( 'filename' ), 'Something returned' );
+
+ my @calls = mock::get_calls;
+ is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+}
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'foo' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet />
+ <worksheet name="foo"/>
+ <worksheet name="foo"/>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 15;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+{
+ local $^W=0;
+ ok( !$CLASS->register(), "Must pass in class, name, and isa" );
+ ok( !$CLASS->register( class => 'Register_018' ), "Must pass in class, name, and isa" );
+ ok( !$CLASS->register( name => 'header' ), "Must pass in class, name, and isa" );
+ ok( !$CLASS->register( isa => 'cell' ), "Must pass in class, name, and isa" );
+ ok( !$CLASS->register( class => 'Register_018', isa => 'cell' ), "Must pass in class, name, and isa" );
+ ok( !$CLASS->register( class => 'Register_018', name => 'header' ), "Must pass in class, name, and isa" );
+ ok( !$CLASS->register( name => 'header', isa => 'cell' ), "Must pass in class, name, and isa" );
+
+ eval {
+ $CLASS->register(
+ class => 'NOT::A::CLASS',
+ name => 'not_a_node',
+ isa => 'cell',
+ );
+ };
+ like( $@, qr/Cannot find or compile/, "Verify registering a non-existent class fails" );
+
+ ok(
+ !$CLASS->register(
+ class => 'NOT::A::CLASS',
+ name => 'cell',
+ isa => 'row',
+ ), "Cannot add a nodename we already have",
+ );
+
+ ok(
+ !$CLASS->register(
+ class => 'NOT::A::CLASS',
+ name => 'new_node',
+ isa => 'not_a_node',
+ ), "Cannot inherit from a nodename we don't have",
+ );
+}
+
+ok(
+ $CLASS->register(
+ class => 'Register_018',
+ name => 'header',
+ isa => 'cell',
+ ), "Register Register_018 class",
+);
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::add_format( 'align', 'center', 'bold', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'test', '2' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet>
+ <header text="test" />
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( my $output = $object->output( 'filename' ), "Something returned" );
+
+my $val = <<__END_EXPECTED__;
+Spreadsheet::WriteExcel::new\\( 'GLOB\\([^)]+\\)' \\)
+Spreadsheet::WriteExcel::add_format\\( '' \\)
+Spreadsheet::WriteExcel::close\\( '' \\)
+__END_EXPECTED__
+
+like( $output, qr/$val/, 'Calls match up' );
+
+__DATA__
+<workbook />
--- /dev/null
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ filename => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Something returned' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( 'worksheet attributes' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '03', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet name="worksheet attributes">
+ <cell text="03" />
+ </worksheet>
+</workbook>
--- /dev/null
+BEGIN{ $^W = 0 }
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok(
+ $object->param(
+ test => [
+ { value => 1 },
+ { value => 2 },
+ [ value => 3 ],
+ ],
+ ),
+ 'Parameters set',
+);
+
+ok( !$object->write_file( 'filename' ), 'Failed to write file' );
+
+__DATA__
+<workbook>
+ <worksheet>
+ <loop name="test">
+ <cell text="$value" />
+ </loop>
+ </worksheet>
+</workbook>
--- /dev/null
+BEGIN{ $^W = 0 }
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Successfuly wrote file' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'before', '1' )
+Spreadsheet::WriteExcel::Worksheet::keep_leading_zeros( '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'inside', '1' )
+Spreadsheet::WriteExcel::Worksheet::keep_leading_zeros( '0' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', 'after', '1' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::keep_leading_zeros( '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'within', '1' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'after', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet>
+ <cell text="before" />
+ <keep_leading_zeros>
+ <cell text="inside" />
+ </keep_leading_zeros>
+ <cell text="after" />
+ </worksheet>
+ <keep_leading_zeros>
+ <worksheet>
+ <cell text="within" />
+ </worksheet>
+ </keep_leading_zeros>
+ <worksheet>
+ <cell text="after" />
+ </worksheet>
+</workbook>
+
--- /dev/null
+BEGIN{ $^W = 0 }
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Successfuly wrote file' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '0', '8' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', '', '1' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '1', '10' )
+Spreadsheet::WriteExcel::Worksheet::write( '1', '0', '', '1' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '2', '6' )
+Spreadsheet::WriteExcel::Worksheet::write( '2', '0', '', '1' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '3', '16' )
+Spreadsheet::WriteExcel::Worksheet::write( '3', '0', '', '1' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '4', '4' )
+Spreadsheet::WriteExcel::Worksheet::write( '4', '0', '', '1' )
+Spreadsheet::WriteExcel::Worksheet::set_row( '5', '8' )
+Spreadsheet::WriteExcel::Worksheet::write( '5', '0', '', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet height="8">
+ <row>
+ <cell />
+ </row>
+ <row height="+2">
+ <cell />
+ </row>
+ <row height="-2">
+ <cell />
+ </row>
+ <row height="*2">
+ <cell />
+ </row>
+ <row height="/2">
+ <cell />
+ </row>
+ <row height="/0">
+ <cell />
+ </row>
+ </worksheet>
+</workbook>
+
--- /dev/null
+BEGIN{ $^W = 0 }
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Successfuly wrote file' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'before', '1' )
+Spreadsheet::WriteExcel::Worksheet::insert_bitmap( '0', '1', '/full/path', '0', '0', '0', '0' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '2', 'after', '1' )
+Spreadsheet::WriteExcel::Worksheet::insert_bitmap( '0', '3', '/full/path', '2', '2', '0', '0' )
+Spreadsheet::WriteExcel::Worksheet::insert_bitmap( '0', '4', '/full/path', '0', '0', '2', '2' )
+Spreadsheet::WriteExcel::Worksheet::insert_bitmap( '0', '5', '/full/path', '0', '1', '1.1', '0' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet>
+ <cell text="before" />
+ <image path="/full/path" />
+ <cell text="after" />
+ <image path="/full/path" offset="2,2"/>
+ <image path="/full/path" scale="2,2"/>
+ <image path="/full/path" scale="1.1,0" offset="0,1"/>
+ </worksheet>
+</workbook>
--- /dev/null
+BEGIN{ $^W = 0 }
+use strict;
+
+use Test::More tests => 4;
+
+use lib 't';
+use mock;
+mock::reset;
+
+my $CLASS = 'Excel::Template';
+use_ok( $CLASS );
+
+my $object = $CLASS->new(
+ file => \*DATA,
+);
+isa_ok( $object, $CLASS );
+
+ok( $object->write_file( 'filename' ), 'Successfuly wrote file' );
+
+my @calls = mock::get_calls;
+is( join( $/, @calls, '' ), <<__END_EXPECTED__, 'Calls match up' );
+Spreadsheet::WriteExcel::new( 'filename' )
+Spreadsheet::WriteExcel::add_format( '' )
+Spreadsheet::WriteExcel::add_worksheet( '' )
+Spreadsheet::WriteExcel::Worksheet::new( '' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '0', 'before', '1' )
+Spreadsheet::WriteExcel::Worksheet::freeze_panes( '0', '1' )
+Spreadsheet::WriteExcel::Worksheet::write( '0', '1', 'after', '1' )
+Spreadsheet::WriteExcel::close( '' )
+__END_EXPECTED__
+
+__DATA__
+<workbook>
+ <worksheet>
+ <cell text="before" />
+ <freezepanes />
+ <cell text="after" />
+ </worksheet>
+</workbook>
--- /dev/null
+use strict;
+
+use Test::More;
+
+eval "use Test::Pod 1.14";
+plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
+
+all_pod_files_ok();
--- /dev/null
+use strict;
+
+use Test::More;
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@;
+
+# These are methods that need naming work
+my @private_methods = qw(
+ render new min max resolve deltas
+ enter_scope exit_scope iterate_over_children
+);
+
+# These are method names that have been commented out, for now
+# max_of total_of
+# begin_page end_page
+
+my $private_regex = do {
+ local $"='|';
+ qr/^(?:@private_methods)$/
+};
+
+all_pod_coverage_ok( {
+ also_private => [ $private_regex ],
+});
--- /dev/null
+package Register_018;
+
+use strict;
+
+sub render
+{
+ my ($self, $context) = @_;
+
+ my $old_format = $context->active_format;
+ my $format = $context->format_object->copy(
+ $context, $old_format,
+
+ align => 'center', bold => 1,
+ );
+
+ $context->active_format($format);
+
+ my $child_success = $self->SUPER::render($context);
+
+ $context->active_format($old_format);
+
+ return $child_success;
+
+}
+
+1;
+__END__
--- /dev/null
+package Spreadsheet::WriteExcel;
+
+use strict;
+
+use mock;
+use Spreadsheet::WriteExcel::Worksheet;
+
+sub new {
+ my $self = bless {
+ }, shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, ref($self) . "::new( '@_' )";
+ }
+
+ $self->{file} = shift;
+
+ return $self;
+}
+
+sub close {
+ my $self = shift;
+ {
+ local $" = "', '";
+ push @mock::calls, ref($self) . "::close( '@_' )";
+ }
+
+ if ( ref $self->{file} ) {
+ my $fh = $self->{file};
+ print $fh join "\n", @mock::calls, '';
+ }
+}
+
+sub add_worksheet {
+ my $self = shift;
+ {
+ local $" = "', '";
+ push @mock::calls, ref($self) . "::add_worksheet( '@_' )";
+ }
+ return Spreadsheet::WriteExcel::Worksheet->new;
+}
+
+my $format_num = 1;
+sub add_format {
+ my $self = shift;
+ my %x = @_;
+ my @x = map { $_ => $x{$_} } sort keys %x;
+ {
+ local $" = "', '";
+ push @mock::calls, ref($self) . "::add_format( '@x' )";
+ }
+ return $format_num++;
+}
+
+1;
+__END__
--- /dev/null
+package Spreadsheet::WriteExcel::Big;
+
+use strict;
+
+use vars qw/ @ISA /;
+@ISA = qw( Spreadsheet::WriteExcel );
+
+use Spreadsheet::WriteExcel;
+
+use mock;
+
+sub new {
+ my $self = bless {
+ }, shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, ref($self) . "::new( '@_' )";
+ }
+
+ return $self;
+}
+
+1;
+__END__
--- /dev/null
+package Spreadsheet::WriteExcel::Worksheet;
+
+use strict;
+
+use mock;
+
+sub new {
+ my $self = bless {
+ }, shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::new( '@_' )";
+ }
+
+ return $self;
+}
+
+sub write_string {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::write_string( '@_' )";
+ }
+}
+
+sub write_number {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::write_number( '@_' )";
+ }
+}
+
+sub write_blank {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::write_blank( '@_' )";
+ }
+}
+
+sub write_url {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::write_url( '@_' )";
+ }
+}
+
+sub write_formula {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::write_formula( '@_' )";
+ }
+}
+
+sub write {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::write( '@_' )";
+ }
+}
+
+sub set_row {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::set_row( '@_' )";
+ }
+}
+
+sub set_column {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::set_column( '@_' )";
+ }
+}
+
+sub keep_leading_zeros {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::keep_leading_zeros( '@_' )";
+ }
+}
+
+sub insert_bitmap {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::insert_bitmap( '@_' )";
+ }
+}
+
+sub freeze_panes {
+ my $self = shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, __PACKAGE__ . "::freeze_panes( '@_' )";
+ }
+}
+
+1;
+__END__
--- /dev/null
+package Spreadsheet::WriteExcelXML;
+
+use strict;
+
+use vars qw/ @ISA /;
+@ISA = qw( Spreadsheet::WriteExcel );
+
+use Spreadsheet::WriteExcel;
+
+use mock;
+
+sub new {
+ my $self = bless {
+ }, shift;
+
+ {
+ local $" = "', '";
+ push @mock::calls, ref($self) . "::new( '@_' )";
+ }
+
+ return $self;
+}
+
+1;
+__END__
--- /dev/null
+package mock;
+
+use strict;
+
+use vars qw/ @calls /;
+
+@calls = ();
+
+sub reset { @calls = (); }
+sub get_calls { @calls }
+
+
+1;
+__END__