diff -crB webform/components/select.inc webform_arthur/components/select.inc
*** webform/components/select.inc	2010-10-18 10:20:54.000000000 +0300
--- webform_arthur/components/select.inc	2010-11-19 10:15:48.000000000 +0300
***************
*** 1,5 ****
--- 1,5644 ----
  <?php
+ 
  // $Id: select.inc,v 1.39.2.26 2010/10/18 07:20:54 quicksketch Exp $
+ //include("phplot/phplot.php");
+ 
+ if (!defined(__FUNCTION__))
+     define(__FUNCTION__, '__FUNCTION__ Requires at least PHP 4.3.0.');
+ 
+ define('MINY', -1);        // Indexes in $data (for DrawXDataLine())
+ define('MAXY', -2);
+ define('TOTY', -3);
+ 
+ error_reporting(E_ALL);
+ 
+ $ColorArray = array(
+ 	"snow"			=> array(255, 250, 250),
+ 	"ghost white"	=> array(248, 248, 255),
+ 	"GhostWhite"	=> array(248, 248, 255),
+ 	"white smoke"	=> array(245, 245, 245),
+ 	"WhiteSmoke"	=> array(245, 245, 245),
+ 	"gainsboro"		=> array(220, 220, 220),
+ 	"floral white"	=> array(255, 250, 240),
+ 	"FloralWhite"	=> array(255, 250, 240),
+ 	"old lace"		=> array(253, 245, 230),
+ 	"OldLace"		=> array(253, 245, 230),
+ 	"linen"			=> array(250, 240, 230),
+ 	"antique white"	=> array(250, 235, 215),
+ 	"AntiqueWhite"	=> array(250, 235, 215),
+ 	"papaya whip"	=> array(255, 239, 213),
+ 	"PapayaWhip"	=> array(255, 239, 213),
+ 	"blanched almond"	=> array(255, 235, 205),
+ 	"BlanchedAlmond"	=> array(255, 235, 205),
+ 	"bisque"		=> array(255, 228, 196),
+ 	"peach puff"	=> array(255, 218, 185),
+ 	"PeachPuff"		=> array(255, 218, 185),
+ 	"navajo white"	=> array(255, 222, 173),
+ 	"NavajoWhite"	=> array(255, 222, 173),
+ 	"moccasin"		=> array(255, 228, 181),
+ 	"cornsilk"		=> array(255, 248, 220),
+ 	"ivory"			=> array(255, 255, 240),
+ 	"lemon chiffon"	=> array(255, 250, 205),
+ 	"LemonChiffon"	=> array(255, 250, 205),
+ 	"seashell"		=> array(255, 245, 238),
+ 	"honeydew"		=> array(240, 255, 240),
+ 	"mint cream"	=> array(245, 255, 250),
+ 	"MintCream"		=> array(245, 255, 250),
+ 	"azure"			=> array(240, 255, 255),
+ 	"alice blue"	=> array(240, 248, 255),
+ 	"AliceBlue"		=> array(240, 248, 255),
+ 	"lavender"		=> array(230, 230, 250),
+ 	"lavender blush"	=> array(255, 240, 245),
+ 	"LavenderBlush"		=> array(255, 240, 245),
+ 	"misty rose"		=> array(255, 228, 225),
+ 	"MistyRose"		=> array(255, 228, 225),
+ 	"white"			=> array(255, 255, 255),
+ 	"black"			=> array(  0,   0,   0),
+ 	"dark slate gray"	=> array( 47,  79,  79),
+ 	"DarkSlateGray"		=> array( 47,  79,  79),
+ 	"dark slate grey"	=> array( 47,  79,  79),
+ 	"DarkSlateGrey"		=> array( 47,  79,  79),
+ 	"dim gray"			=> array(105, 105, 105),
+ 	"DimGray"			=> array(105, 105, 105),
+ 	"dim grey"			=> array(105, 105, 105),
+ 	"DimGrey"			=> array(105, 105, 105),
+ 	"slate gray"		=> array(112, 128, 144),
+ 	"SlateGray"			=> array(112, 128, 144),
+ 	"slate grey"		=> array(112, 128, 144),
+ 	"SlateGrey"			=> array(112, 128, 144),
+ 	"light slate gray"	=> array(119, 136, 153),
+ 	"LightSlateGray"	=> array(119, 136, 153),
+ 	"light slate grey"	=> array(119, 136, 153),
+ 	"LightSlateGrey"	=> array(119, 136, 153),
+ 	"gray"			=> array(190, 190, 190),
+ 	"grey"			=> array(190, 190, 190),
+ 	"light grey"		=> array(211, 211, 211),
+ 	"LightGrey"			=> array(211, 211, 211),
+ 	"light gray"		=> array(211, 211, 211),
+ 	"LightGray"			=> array(211, 211, 211),
+ 	"midnight blue"		=> array( 25,  25, 112),
+ 	"MidnightBlue"		=> array( 25,  25, 112),
+ 	"navy"			=> array(  0,   0, 128),
+ 	"navy blue"			=> array(  0,   0, 128),
+ 	"NavyBlue"			=> array(  0,   0, 128),
+ 	"cornflower blue"	=> array(100, 149, 237),
+ 	"CornflowerBlue"	=> array(100, 149, 237),
+ 	"dark slate blue"	=> array( 72,  61, 139),
+ 	"DarkSlateBlue"		=> array( 72,  61, 139),
+ 	"slate blue"		=> array(106,  90, 205),
+ 	"SlateBlue"			=> array(106,  90, 205),
+ 	"medium slate blue"	=> array(123, 104, 238),
+ 	"MediumSlateBlue"	=> array(123, 104, 238),
+ 	"light slate blue"	=> array(132, 112, 255),
+ 	"LightSlateBlue"	=> array(132, 112, 255),
+ 	"medium blue"		=> array(  0,   0, 205),
+ 	"MediumBlue"		=> array(  0,   0, 205),
+ 	"royal blue"		=> array( 65, 105, 225),
+ 	"RoyalBlue"			=> array( 65, 105, 225),
+ 	"blue"			=> array(  0,   0, 255),
+ 	"dodger blue"		=> array( 30, 144, 255),
+ 	"DodgerBlue"		=> array( 30, 144, 255),
+ 	"deep sky blue"		=> array(  0, 191, 255),
+ 	"DeepSkyBlue"		=> array(  0, 191, 255),
+ 	"sky blue"			=> array(135, 206, 235),
+ 	"SkyBlue"			=> array(135, 206, 235),
+ 	"light sky blue"	=> array(135, 206, 250),
+ 	"LightSkyBlue"		=> array(135, 206, 250),
+ 	"steel blue"		=> array( 70, 130, 180),
+ 	"SteelBlue"			=> array( 70, 130, 180),
+ 	"light steel blue"	=> array(176, 196, 222),
+ 	"LightSteelBlue"	=> array(176, 196, 222),
+ 	"light blue"		=> array(173, 216, 230),
+ 	"LightBlue"			=> array(173, 216, 230),
+ 	"powder blue"		=> array(176, 224, 230),
+ 	"PowderBlue"		=> array(176, 224, 230),
+ 	"pale turquoise"	=> array(175, 238, 238),
+ 	"PaleTurquoise"		=> array(175, 238, 238),
+ 	"dark turquoise"	=> array(  0, 206, 209),
+ 	"DarkTurquoise"		=> array(  0, 206, 209),
+ 	"medium turquoise"	=> array( 72, 209, 204),
+ 	"MediumTurquoise"	=> array( 72, 209, 204),
+ 	"turquoise"			=> array( 64, 224, 208),
+ 	"cyan"			=> array(  0, 255, 255),
+ 	"light cyan"		=> array(224, 255, 255),
+ 	"LightCyan"			=> array(224, 255, 255),
+ 	"cadet blue"		=> array( 95, 158, 160),
+ 	"CadetBlue"			=> array( 95, 158, 160),
+ 	"medium aquamarine"	=> array(102, 205, 170),
+ 	"MediumAquamarine"	=> array(102, 205, 170),
+ 	"aquamarine"		=> array(127, 255, 212),
+ 	"dark green"		=> array(  0, 100,   0),
+ 	"DarkGreen"			=> array(  0, 100,   0),
+ 	"dark olive green"	=> array( 85, 107,  47),
+ 	"DarkOliveGreen"	=> array( 85, 107,  47),
+ 	"dark sea green"	=> array(143, 188, 143),
+ 	"DarkSeaGreen"		=> array(143, 188, 143),
+ 	"sea green"			=> array( 46, 139,  87),
+ 	"SeaGreen"			=> array( 46, 139,  87),
+ 	"medium sea green"	=> array( 60, 179, 113),
+ 	"MediumSeaGreen"	=> array( 60, 179, 113),
+ 	"light sea green"	=> array( 32, 178, 170),
+ 	"LightSeaGreen"		=> array( 32, 178, 170),
+ 	"pale green"		=> array(152, 251, 152),
+ 	"PaleGreen"			=> array(152, 251, 152),
+ 	"spring green"		=> array(  0, 255, 127),
+ 	"SpringGreen"		=> array(  0, 255, 127),
+ 	"lawn green"		=> array(124, 252,   0),
+ 	"LawnGreen"			=> array(124, 252,   0),
+ 	"green"			=> array(  0, 255,   0),
+ 	"chartreuse"		=> array(127, 255,   0),
+ 	"medium spring green"	=> array(  0, 250, 154),
+ 	"MediumSpringGreen"		=> array(  0, 250, 154),
+ 	"green yellow"			=> array(173, 255,  47),
+ 	"GreenYellow"			=> array(173, 255,  47),
+ 	"lime green"			=> array( 50, 205,  50),
+ 	"LimeGreen"			=> array( 50, 205,  50),
+ 	"yellow green"			=> array(154, 205,  50),
+ 	"YellowGreen"			=> array(154, 205,  50),
+ 	"forest green"			=> array( 34, 139,  34),
+ 	"ForestGreen"			=> array( 34, 139,  34),
+ 	"olive drab"			=> array(107, 142,  35),
+ 	"OliveDrab"			=> array(107, 142,  35),
+ 	"dark khaki"		=> array(189, 183, 107),
+ 	"DarkKhaki"			=> array(189, 183, 107),
+ 	"khaki"			=> array(240, 230, 140),
+ 	"pale goldenrod"	=> array(238, 232, 170),
+ 	"PaleGoldenrod"		=> array(238, 232, 170),
+ 	"light goldenrod yellow"	=> array(250, 250, 210),
+ 	"LightGoldenrodYellow"		=> array(250, 250, 210),
+ 	"light yellow"			=> array(255, 255, 224),
+ 	"LightYellow"			=> array(255, 255, 224),
+ 	"yellow"			=> array(255, 255,   0),
+ 	"gold"			=> array(255, 215,   0),
+ 	"light goldenrod"			=> array(238, 221, 130),
+ 	"LightGoldenrod"			=> array(238, 221, 130),
+ 	"goldenrod"			=> array(218, 165,  32),
+ 	"dark goldenrod"			=> array(184, 134,  11),
+ 	"DarkGoldenrod"			=> array(184, 134,  11),
+ 	"rosy brown"			=> array(188, 143, 143),
+ 	"RosyBrown"			=> array(188, 143, 143),
+ 	"indian red"			=> array(205,  92,  92),
+ 	"IndianRed"			=> array(205,  92,  92),
+ 	"saddle brown"			=> array(139,  69,  19),
+ 	"SaddleBrown"			=> array(139,  69,  19),
+ 	"sienna"			=> array(160,  82,  45),
+ 	"peru"			=> array(205, 133,  63),
+ 	"burlywood"			=> array(222, 184, 135),
+ 	"beige"			=> array(245, 245, 220),
+ 	"wheat"			=> array(245, 222, 179),
+ 	"sandy brown"			=> array(244, 164,  96),
+ 	"SandyBrown"			=> array(244, 164,  96),
+ 	"tan"			=> array(210, 180, 140),
+ 	"chocolate"			=> array(210, 105,  30),
+ 	"firebrick"			=> array(178,  34,  34),
+ 	"brown"			=> array(165,  42,  42),
+ 	"dark salmon"			=> array(233, 150, 122),
+ 	"DarkSalmon"			=> array(233, 150, 122),
+ 	"salmon"			=> array(250, 128, 114),
+ 	"light salmon"			=> array(255, 160, 122),
+ 	"LightSalmon"			=> array(255, 160, 122),
+ 	"orange"			=> array(255, 165,   0),
+ 	"dark orange"			=> array(255, 140,   0),
+ 	"DarkOrange"			=> array(255, 140,   0),
+ 	"coral"			=> array(255, 127,  80),
+ 	"light coral"			=> array(240, 128, 128),
+ 	"LightCoral"			=> array(240, 128, 128),
+ 	"tomato"			=> array(255,  99,  71),
+ 	"orange red"			=> array(255,  69,   0),
+ 	"OrangeRed"			=> array(255,  69,   0),
+ 	"red"			=> array(255,   0,   0),
+ 	"hot pink"			=> array(255, 105, 180),
+ 	"HotPink"			=> array(255, 105, 180),
+ 	"deep pink"			=> array(255,  20, 147),
+ 	"DeepPink"			=> array(255,  20, 147),
+ 	"pink"			=> array(255, 192, 203),
+ 	"light pink"		=> array(255, 182, 193),
+ 	"LightPink"			=> array(255, 182, 193),
+ 	"pale violet red"	=> array(219, 112, 147),
+ 	"PaleVioletRed"		=> array(219, 112, 147),
+ 	"maroon"			=> array(176,  48,  96),
+ 	"medium violet red"	=> array(199,  21, 133),
+ 	"MediumVioletRed"	=> array(199,  21, 133),
+ 	"violet red"		=> array(208,  32, 144),
+ 	"VioletRed"			=> array(208,  32, 144),
+ 	"magenta"			=> array(255,   0, 255),
+ 	"violet"			=> array(238, 130, 238),
+ 	"plum"			=> array(221, 160, 221),
+ 	"orchid"			=> array(218, 112, 214),
+ 	"medium orchid"		=> array(186,  85, 211),
+ 	"MediumOrchid"		=> array(186,  85, 211),
+ 	"dark orchid"		=> array(153,  50, 204),
+ 	"DarkOrchid"		=> array(153,  50, 204),
+ 	"dark violet"		=> array(148,   0, 211),
+ 	"DarkViolet"		=> array(148,   0, 211),
+ 	"blue violet"		=> array(138,  43, 226),
+ 	"BlueViolet"		=> array(138,  43, 226),
+ 	"purple"			=> array(160,  32, 240),
+ 	"medium purple"		=> array(147, 112, 219),
+ 	"MediumPurple"		=> array(147, 112, 219),
+ 	"thistle"			=> array(216, 191, 216),
+ 	"snow1"			=> array(255, 250, 250),
+ 	"snow2"			=> array(238, 233, 233),
+ 	"snow3"			=> array(205, 201, 201),
+ 	"snow4"			=> array(139, 137, 137),
+ 	"seashell1"			=> array(255, 245, 238),
+ 	"seashell2"			=> array(238, 229, 222),
+ 	"seashell3"			=> array(205, 197, 191),
+ 	"seashell4"			=> array(139, 134, 130),
+ 	"AntiqueWhite1"			=> array(255, 239, 219),
+ 	"AntiqueWhite2"			=> array(238, 223, 204),
+ 	"AntiqueWhite3"			=> array(205, 192, 176),
+ 	"AntiqueWhite4"			=> array(139, 131, 120),
+ 	"bisque1"			=> array(255, 228, 196),
+ 	"bisque2"			=> array(238, 213, 183),
+ 	"bisque3"			=> array(205, 183, 158),
+ 	"bisque4"			=> array(139, 125, 107),
+ 	"PeachPuff1"			=> array(255, 218, 185),
+ 	"PeachPuff2"			=> array(238, 203, 173),
+ 	"PeachPuff3"			=> array(205, 175, 149),
+ 	"PeachPuff4"			=> array(139, 119, 101),
+ 	"NavajoWhite1"			=> array(255, 222, 173),
+ 	"NavajoWhite2"			=> array(238, 207, 161),
+ 	"NavajoWhite3"			=> array(205, 179, 139),
+ 	"NavajoWhite4"			=> array(139, 121,  94),
+ 	"LemonChiffon1"			=> array(255, 250, 205),
+ 	"LemonChiffon2"			=> array(238, 233, 191),
+ 	"LemonChiffon3"			=> array(205, 201, 165),
+ 	"LemonChiffon4"			=> array(139, 137, 112),
+ 	"cornsilk1"			=> array(255, 248, 220),
+ 	"cornsilk2"			=> array(238, 232, 205),
+ 	"cornsilk3"			=> array(205, 200, 177),
+ 	"cornsilk4"			=> array(139, 136, 120),
+ 	"ivory1"			=> array(255, 255, 240),
+ 	"ivory2"			=> array(238, 238, 224),
+ 	"ivory3"			=> array(205, 205, 193),
+ 	"ivory4"			=> array(139, 139, 131),
+ 	"honeydew1"			=> array(240, 255, 240),
+ 	"honeydew2"			=> array(224, 238, 224),
+ 	"honeydew3"			=> array(193, 205, 193),
+ 	"honeydew4"			=> array(131, 139, 131),
+ 	"LavenderBlush1"	=> array(255, 240, 245),
+ 	"LavenderBlush2"	=> array(238, 224, 229),
+ 	"LavenderBlush3"	=> array(205, 193, 197),
+ 	"LavenderBlush4"	=> array(139, 131, 134),
+ 	"MistyRose1"		=> array(255, 228, 225),
+ 	"MistyRose2"		=> array(238, 213, 210),
+ 	"MistyRose3"		=> array(205, 183, 181),
+ 	"MistyRose4"		=> array(139, 125, 123),
+ 	"azure1"			=> array(240, 255, 255),
+ 	"azure2"			=> array(224, 238, 238),
+ 	"azure3"			=> array(193, 205, 205),
+ 	"azure4"			=> array(131, 139, 139),
+ 	"SlateBlue1"		=> array(131, 111, 255),
+ 	"SlateBlue2"		=> array(122, 103, 238),
+ 	"SlateBlue3"		=> array(105,  89, 205),
+ 	"SlateBlue4"		=> array( 71,  60, 139),
+ 	"RoyalBlue1"		=> array( 72, 118, 255),
+ 	"RoyalBlue2"		=> array( 67, 110, 238),
+ 	"RoyalBlue3"		=> array( 58,  95, 205),
+ 	"RoyalBlue4"		=> array( 39,  64, 139),
+ 	"blue1"			=> array(  0,   0, 255),
+ 	"blue2"			=> array(  0,   0, 238),
+ 	"blue3"			=> array(  0,   0, 205),
+ 	"blue4"			=> array(  0,   0, 139),
+ 	"DodgerBlue1"		=> array( 30, 144, 255),
+ 	"DodgerBlue2"		=> array( 28, 134, 238),
+ 	"DodgerBlue3"		=> array( 24, 116, 205),
+ 	"DodgerBlue4"		=> array( 16,  78, 139),
+ 	"SteelBlue1"		=> array( 99, 184, 255),
+ 	"SteelBlue2"		=> array( 92, 172, 238),
+ 	"SteelBlue3"		=> array( 79, 148, 205),
+ 	"SteelBlue4"		=> array( 54, 100, 139),
+ 	"DeepSkyBlue1"		=> array(  0, 191, 255),
+ 	"DeepSkyBlue2"		=> array(  0, 178, 238),
+ 	"DeepSkyBlue3"		=> array(  0, 154, 205),
+ 	"DeepSkyBlue4"		=> array(  0, 104, 139),
+ 	"SkyBlue1"			=> array(135, 206, 255),
+ 	"SkyBlue2"			=> array(126, 192, 238),
+ 	"SkyBlue3"			=> array(108, 166, 205),
+ 	"SkyBlue4"			=> array( 74, 112, 139),
+ 	"LightSkyBlue1"		=> array(176, 226, 255),
+ 	"LightSkyBlue2"		=> array(164, 211, 238),
+ 	"LightSkyBlue3"		=> array(141, 182, 205),
+ 	"LightSkyBlue4"		=> array( 96, 123, 139),
+ 	"SlateGray1"		=> array(198, 226, 255),
+ 	"SlateGray2"		=> array(185, 211, 238),
+ 	"SlateGray3"		=> array(159, 182, 205),
+ 	"SlateGray4"		=> array(108, 123, 139),
+ 	"LightSteelBlue1"	=> array(202, 225, 255),
+ 	"LightSteelBlue2"	=> array(188, 210, 238),
+ 	"LightSteelBlue3"	=> array(162, 181, 205),
+ 	"LightSteelBlue4"	=> array(110, 123, 139),
+ 	"LightBlue1"		=> array(191, 239, 255),
+ 	"LightBlue2"		=> array(178, 223, 238),
+ 	"LightBlue3"		=> array(154, 192, 205),
+ 	"LightBlue4"		=> array(104, 131, 139),
+ 	"LightCyan1"		=> array(224, 255, 255),
+ 	"LightCyan2"		=> array(209, 238, 238),
+ 	"LightCyan3"		=> array(180, 205, 205),
+ 	"LightCyan4"		=> array(122, 139, 139),
+ 	"PaleTurquoise1"	=> array(187, 255, 255),
+ 	"PaleTurquoise2"	=> array(174, 238, 238),
+ 	"PaleTurquoise3"	=> array(150, 205, 205),
+ 	"PaleTurquoise4"	=> array(102, 139, 139),
+ 	"CadetBlue1"		=> array(152, 245, 255),
+ 	"CadetBlue2"		=> array(142, 229, 238),
+ 	"CadetBlue3"		=> array(122, 197, 205),
+ 	"CadetBlue4"		=> array( 83, 134, 139),
+ 	"turquoise1"		=> array(  0, 245, 255),
+ 	"turquoise2"		=> array(  0, 229, 238),
+ 	"turquoise3"		=> array(  0, 197, 205),
+ 	"turquoise4"		=> array(  0, 134, 139),
+ 	"cyan1"			=> array(  0, 255, 255),
+ 	"cyan2"			=> array(  0, 238, 238),
+ 	"cyan3"			=> array(  0, 205, 205),
+ 	"cyan4"			=> array(  0, 139, 139),
+ 	"DarkSlateGray1"	=> array(151, 255, 255),
+ 	"DarkSlateGray2"	=> array(141, 238, 238),
+ 	"DarkSlateGray3"	=> array(121, 205, 205),
+ 	"DarkSlateGray4"	=> array( 82, 139, 139),
+ 	"aquamarine1"		=> array(127, 255, 212),
+ 	"aquamarine2"		=> array(118, 238, 198),
+ 	"aquamarine3"		=> array(102, 205, 170),
+ 	"aquamarine4"		=> array( 69, 139, 116),
+ 	"DarkSeaGreen1"		=> array(193, 255, 193),
+ 	"DarkSeaGreen2"		=> array(180, 238, 180),
+ 	"DarkSeaGreen3"		=> array(155, 205, 155),
+ 	"DarkSeaGreen4"		=> array(105, 139, 105),
+ 	"SeaGreen1"			=> array( 84, 255, 159),
+ 	"SeaGreen2"			=> array( 78, 238, 148),
+ 	"SeaGreen3"			=> array( 67, 205, 128),
+ 	"SeaGreen4"			=> array( 46, 139,  87),
+ 	"PaleGreen1"		=> array(154, 255, 154),
+ 	"PaleGreen2"		=> array(144, 238, 144),
+ 	"PaleGreen3"		=> array(124, 205, 124),
+ 	"PaleGreen4"		=> array( 84, 139,  84),
+ 	"SpringGreen1"		=> array(  0, 255, 127),
+ 	"SpringGreen2"		=> array(  0, 238, 118),
+ 	"SpringGreen3"		=> array(  0, 205, 102),
+ 	"SpringGreen4"		=> array(  0, 139,  69),
+ 	"green1"			=> array(  0, 255,   0),
+ 	"green2"			=> array(  0, 238,   0),
+ 	"green3"			=> array(  0, 205,   0),
+ 	"green4"			=> array(  0, 139,   0),
+ 	"chartreuse1"		=> array(127, 255,   0),
+ 	"chartreuse2"		=> array(118, 238,   0),
+ 	"chartreuse3"		=> array(102, 205,   0),
+ 	"chartreuse4"		=> array( 69, 139,   0),
+ 	"OliveDrab1"		=> array(192, 255,  62),
+ 	"OliveDrab2"		=> array(179, 238,  58),
+ 	"OliveDrab3"		=> array(154, 205,  50),
+ 	"OliveDrab4"		=> array(105, 139,  34),
+ 	"DarkOliveGreen1"	=> array(202, 255, 112),
+ 	"DarkOliveGreen2"	=> array(188, 238, 104),
+ 	"DarkOliveGreen3"	=> array(162, 205,  90),
+ 	"DarkOliveGreen4"	=> array(110, 139,  61),
+ 	"khaki1"			=> array(255, 246, 143),
+ 	"khaki2"			=> array(238, 230, 133),
+ 	"khaki3"			=> array(205, 198, 115),
+ 	"khaki4"			=> array(139, 134,  78),
+ 	"LightGoldenrod1"	=> array(255, 236, 139),
+ 	"LightGoldenrod2"	=> array(238, 220, 130),
+ 	"LightGoldenrod3"	=> array(205, 190, 112),
+ 	"LightGoldenrod4"	=> array(139, 129,  76),
+ 	"LightYellow1"		=> array(255, 255, 224),
+ 	"LightYellow2"		=> array(238, 238, 209),
+ 	"LightYellow3"		=> array(205, 205, 180),
+ 	"LightYellow4"		=> array(139, 139, 122),
+ 	"yellow1"			=> array(255, 255,   0),
+ 	"yellow2"			=> array(238, 238,   0),
+ 	"yellow3"			=> array(205, 205,   0),
+ 	"yellow4"			=> array(139, 139,   0),
+ 	"gold1"			=> array(255, 215,   0),
+ 	"gold2"			=> array(238, 201,   0),
+ 	"gold3"			=> array(205, 173,   0),
+ 	"gold4"			=> array(139, 117,   0),
+ 	"goldenrod1"		=> array(255, 193,  37),
+ 	"goldenrod2"		=> array(238, 180,  34),
+ 	"goldenrod3"		=> array(205, 155,  29),
+ 	"goldenrod4"		=> array(139, 105,  20),
+ 	"DarkGoldenrod1"	=> array(255, 185,  15),
+ 	"DarkGoldenrod2"	=> array(238, 173,  14),
+ 	"DarkGoldenrod3"	=> array(205, 149,  12),
+ 	"DarkGoldenrod4"	=> array(139, 101,   8),
+ 	"RosyBrown1"		=> array(255, 193, 193),
+ 	"RosyBrown2"		=> array(238, 180, 180),
+ 	"RosyBrown3"		=> array(205, 155, 155),
+ 	"RosyBrown4"		=> array(139, 105, 105),
+ 	"IndianRed1"		=> array(255, 106, 106),
+ 	"IndianRed2"		=> array(238,  99,  99),
+ 	"IndianRed3"		=> array(205,  85,  85),
+ 	"IndianRed4"		=> array(139,  58,  58),
+ 	"sienna1"			=> array(255, 130,  71),
+ 	"sienna2"			=> array(238, 121,  66),
+ 	"sienna3"			=> array(205, 104,  57),
+ 	"sienna4"			=> array(139,  71,  38),
+ 	"burlywood1"		=> array(255, 211, 155),
+ 	"burlywood2"		=> array(238, 197, 145),
+ 	"burlywood3"		=> array(205, 170, 125),
+ 	"burlywood4"		=> array(139, 115,  85),
+ 	"wheat1"			=> array(255, 231, 186),
+ 	"wheat2"			=> array(238, 216, 174),
+ 	"wheat3"			=> array(205, 186, 150),
+ 	"wheat4"			=> array(139, 126, 102),
+ 	"tan1"			=> array(255, 165,  79),
+ 	"tan2"			=> array(238, 154,  73),
+ 	"tan3"			=> array(205, 133,  63),
+ 	"tan4"			=> array(139,  90,  43),
+ 	"chocolate1"		=> array(255, 127,  36),
+ 	"chocolate2"		=> array(238, 118,  33),
+ 	"chocolate3"		=> array(205, 102,  29),
+ 	"chocolate4"		=> array(139,  69,  19),
+ 	"firebrick1"		=> array(255,  48,  48),
+ 	"firebrick2"		=> array(238,  44,  44),
+ 	"firebrick3"		=> array(205,  38,  38),
+ 	"firebrick4"		=> array(139,  26,  26),
+ 	"brown1"			=> array(255,  64,  64),
+ 	"brown2"			=> array(238,  59,  59),
+ 	"brown3"			=> array(205,  51,  51),
+ 	"brown4"			=> array(139,  35,  35),
+ 	"salmon1"			=> array(255, 140, 105),
+ 	"salmon2"			=> array(238, 130,  98),
+ 	"salmon3"			=> array(205, 112,  84),
+ 	"salmon4"			=> array(139,  76,  57),
+ 	"LightSalmon1"		=> array(255, 160, 122),
+ 	"LightSalmon2"		=> array(238, 149, 114),
+ 	"LightSalmon3"		=> array(205, 129,  98),
+ 	"LightSalmon4"		=> array(139,  87,  66),
+ 	"orange1"			=> array(255, 165,   0),
+ 	"orange2"			=> array(238, 154,   0),
+ 	"orange3"			=> array(205, 133,   0),
+ 	"orange4"			=> array(139,  90,   0),
+ 	"DarkOrange1"		=> array(255, 127,   0),
+ 	"DarkOrange2"		=> array(238, 118,   0),
+ 	"DarkOrange3"		=> array(205, 102,   0),
+ 	"DarkOrange4"		=> array(139,  69,   0),
+ 	"coral1"			=> array(255, 114,  86),
+ 	"coral2"			=> array(238, 106,  80),
+ 	"coral3"			=> array(205,  91,  69),
+ 	"coral4"			=> array(139,  62,  47),
+ 	"tomato1"			=> array(255,  99,  71),
+ 	"tomato2"			=> array(238,  92,  66),
+ 	"tomato3"			=> array(205,  79,  57),
+ 	"tomato4"			=> array(139,  54,  38),
+ 	"OrangeRed1"		=> array(255,  69,   0),
+ 	"OrangeRed2"		=> array(238,  64,   0),
+ 	"OrangeRed3"		=> array(205,  55,   0),
+ 	"OrangeRed4"		=> array(139,  37,   0),
+ 	"red1"			=> array(255,   0,   0),
+ 	"red2"			=> array(238,   0,   0),
+ 	"red3"			=> array(205,   0,   0),
+ 	"red4"			=> array(139,   0,   0),
+ 	"DeepPink1"			=> array(255,  20, 147),
+ 	"DeepPink2"			=> array(238,  18, 137),
+ 	"DeepPink3"			=> array(205,  16, 118),
+ 	"DeepPink4"			=> array(139,  10,  80),
+ 	"HotPink1"			=> array(255, 110, 180),
+ 	"HotPink2"			=> array(238, 106, 167),
+ 	"HotPink3"			=> array(205,  96, 144),
+ 	"HotPink4"			=> array(139,  58,  98),
+ 	"pink1"			=> array(255, 181, 197),
+ 	"pink2"			=> array(238, 169, 184),
+ 	"pink3"			=> array(205, 145, 158),
+ 	"pink4"			=> array(139,  99, 108),
+ 	"LightPink1"		=> array(255, 174, 185),
+ 	"LightPink2"		=> array(238, 162, 173),
+ 	"LightPink3"		=> array(205, 140, 149),
+ 	"LightPink4"		=> array(139,  95, 101),
+ 	"PaleVioletRed1"	=> array(255, 130, 171),
+ 	"PaleVioletRed2"	=> array(238, 121, 159),
+ 	"PaleVioletRed3"	=> array(205, 104, 137),
+ 	"PaleVioletRed4"	=> array(139,  71,  93),
+ 	"maroon1"			=> array(255,  52, 179),
+ 	"maroon2"			=> array(238,  48, 167),
+ 	"maroon3"			=> array(205,  41, 144),
+ 	"maroon4"			=> array(139,  28,  98),
+ 	"VioletRed1"		=> array(255,  62, 150),
+ 	"VioletRed2"		=> array(238,  58, 140),
+ 	"VioletRed3"		=> array(205,  50, 120),
+ 	"VioletRed4"		=> array(139,  34,  82),
+ 	"magenta1"			=> array(255,   0, 255),
+ 	"magenta2"			=> array(238,   0, 238),
+ 	"magenta3"			=> array(205,   0, 205),
+ 	"magenta4"			=> array(139,   0, 139),
+ 	"orchid1"			=> array(255, 131, 250),
+ 	"orchid2"			=> array(238, 122, 233),
+ 	"orchid3"			=> array(205, 105, 201),
+ 	"orchid4"			=> array(139,  71, 137),
+ 	"plum1"			=> array(255, 187, 255),
+ 	"plum2"			=> array(238, 174, 238),
+ 	"plum3"			=> array(205, 150, 205),
+ 	"plum4"			=> array(139, 102, 139),
+ 	"MediumOrchid1"		=> array(224, 102, 255),
+ 	"MediumOrchid2"		=> array(209,  95, 238),
+ 	"MediumOrchid3"		=> array(180,  82, 205),
+ 	"MediumOrchid4"		=> array(122,  55, 139),
+ 	"DarkOrchid1"		=> array(191,  62, 255),
+ 	"DarkOrchid2"		=> array(178,  58, 238),
+ 	"DarkOrchid3"		=> array(154,  50, 205),
+ 	"DarkOrchid4"		=> array(104,  34, 139),
+ 	"purple1"			=> array(155,  48, 255),
+ 	"purple2"			=> array(145,  44, 238),
+ 	"purple3"			=> array(125,  38, 205),
+ 	"purple4"			=> array( 85,  26, 139),
+ 	"MediumPurple1"		=> array(171, 130, 255),
+ 	"MediumPurple2"		=> array(159, 121, 238),
+ 	"MediumPurple3"		=> array(137, 104, 205),
+ 	"MediumPurple4"		=> array( 93,  71, 139),
+ 	"thistle1"			=> array(255, 225, 255),
+ 	"thistle2"			=> array(238, 210, 238),
+ 	"thistle3"			=> array(205, 181, 205),
+ 	"thistle4"			=> array(139, 123, 139),
+ 	"gray0"			=> array(  0,   0,   0),
+ 	"grey0"			=> array(  0,   0,   0),
+ 	"gray1"			=> array(  3,   3,   3),
+ 	"grey1"			=> array(  3,   3,   3),
+ 	"gray2"			=> array(  5,   5,   5),
+ 	"grey2"			=> array(  5,   5,   5),
+ 	"gray3"			=> array(  8,   8,   8),
+ 	"grey3"			=> array(  8,   8,   8),
+ 	"gray4"			=> array( 10,  10,  10),
+ 	"grey4"			=> array( 10,  10,  10),
+ 	"gray5"			=> array( 13,  13,  13),
+ 	"grey5"			=> array( 13,  13,  13),
+ 	"gray6"			=> array( 15,  15,  15),
+ 	"grey6"			=> array( 15,  15,  15),
+ 	"gray7"			=> array( 18,  18,  18),
+ 	"grey7"			=> array( 18,  18,  18),
+ 	"gray8"			=> array( 20,  20,  20),
+ 	"grey8"			=> array( 20,  20,  20),
+ 	"gray9"			=> array( 23,  23,  23),
+ 	"grey9"			=> array( 23,  23,  23),
+ 	"gray10"			=> array( 26,  26,  26),
+ 	"grey10"			=> array( 26,  26,  26),
+ 	"gray11"			=> array( 28,  28,  28),
+ 	"grey11"			=> array( 28,  28,  28),
+ 	"gray12"			=> array( 31,  31,  31),
+ 	"grey12"			=> array( 31,  31,  31),
+ 	"gray13"			=> array( 33,  33,  33),
+ 	"grey13"			=> array( 33,  33,  33),
+ 	"gray14"			=> array( 36,  36,  36),
+ 	"grey14"			=> array( 36,  36,  36),
+ 	"gray15"			=> array( 38,  38,  38),
+ 	"grey15"			=> array( 38,  38,  38),
+ 	"gray16"			=> array( 41,  41,  41),
+ 	"grey16"			=> array( 41,  41,  41),
+ 	"gray17"			=> array( 43,  43,  43),
+ 	"grey17"			=> array( 43,  43,  43),
+ 	"gray18"			=> array( 46,  46,  46),
+ 	"grey18"			=> array( 46,  46,  46),
+ 	"gray19"			=> array( 48,  48,  48),
+ 	"grey19"			=> array( 48,  48,  48),
+ 	"gray20"			=> array( 51,  51,  51),
+ 	"grey20"			=> array( 51,  51,  51),
+ 	"gray21"			=> array( 54,  54,  54),
+ 	"grey21"			=> array( 54,  54,  54),
+ 	"gray22"			=> array( 56,  56,  56),
+ 	"grey22"			=> array( 56,  56,  56),
+ 	"gray23"			=> array( 59,  59,  59),
+ 	"grey23"			=> array( 59,  59,  59),
+ 	"gray24"			=> array( 61,  61,  61),
+ 	"grey24"			=> array( 61,  61,  61),
+ 	"gray25"			=> array( 64,  64,  64),
+ 	"grey25"			=> array( 64,  64,  64),
+ 	"gray26"			=> array( 66,  66,  66),
+ 	"grey26"			=> array( 66,  66,  66),
+ 	"gray27"			=> array( 69,  69,  69),
+ 	"grey27"			=> array( 69,  69,  69),
+ 	"gray28"			=> array( 71,  71,  71),
+ 	"grey28"			=> array( 71,  71,  71),
+ 	"gray29"			=> array( 74,  74,  74),
+ 	"grey29"			=> array( 74,  74,  74),
+ 	"gray30"			=> array( 77,  77,  77),
+ 	"grey30"			=> array( 77,  77,  77),
+ 	"gray31"			=> array( 79,  79,  79),
+ 	"grey31"			=> array( 79,  79,  79),
+ 	"gray32"			=> array( 82,  82,  82),
+ 	"grey32"			=> array( 82,  82,  82),
+ 	"gray33"			=> array( 84,  84,  84),
+ 	"grey33"			=> array( 84,  84,  84),
+ 	"gray34"			=> array( 87,  87,  87),
+ 	"grey34"			=> array( 87,  87,  87),
+ 	"gray35"			=> array( 89,  89,  89),
+ 	"grey35"			=> array( 89,  89,  89),
+ 	"gray36"			=> array( 92,  92,  92),
+ 	"grey36"			=> array( 92,  92,  92),
+ 	"gray37"			=> array( 94,  94,  94),
+ 	"grey37"			=> array( 94,  94,  94),
+ 	"gray38"			=> array( 97,  97,  97),
+ 	"grey38"			=> array( 97,  97,  97),
+ 	"gray39"			=> array( 99,  99,  99),
+ 	"grey39"			=> array( 99,  99,  99),
+ 	"gray40"			=> array(102, 102, 102),
+ 	"grey40"			=> array(102, 102, 102),
+ 	"gray41"			=> array(105, 105, 105),
+ 	"grey41"			=> array(105, 105, 105),
+ 	"gray42"			=> array(107, 107, 107),
+ 	"grey42"			=> array(107, 107, 107),
+ 	"gray43"			=> array(110, 110, 110),
+ 	"grey43"			=> array(110, 110, 110),
+ 	"gray44"			=> array(112, 112, 112),
+ 	"grey44"			=> array(112, 112, 112),
+ 	"gray45"			=> array(115, 115, 115),
+ 	"grey45"			=> array(115, 115, 115),
+ 	"gray46"			=> array(117, 117, 117),
+ 	"grey46"			=> array(117, 117, 117),
+ 	"gray47"			=> array(120, 120, 120),
+ 	"grey47"			=> array(120, 120, 120),
+ 	"gray48"			=> array(122, 122, 122),
+ 	"grey48"			=> array(122, 122, 122),
+ 	"gray49"			=> array(125, 125, 125),
+ 	"grey49"			=> array(125, 125, 125),
+ 	"gray50"			=> array(127, 127, 127),
+ 	"grey50"			=> array(127, 127, 127),
+ 	"gray51"			=> array(130, 130, 130),
+ 	"grey51"			=> array(130, 130, 130),
+ 	"gray52"			=> array(133, 133, 133),
+ 	"grey52"			=> array(133, 133, 133),
+ 	"gray53"			=> array(135, 135, 135),
+ 	"grey53"			=> array(135, 135, 135),
+ 	"gray54"			=> array(138, 138, 138),
+ 	"grey54"			=> array(138, 138, 138),
+ 	"gray55"			=> array(140, 140, 140),
+ 	"grey55"			=> array(140, 140, 140),
+ 	"gray56"			=> array(143, 143, 143),
+ 	"grey56"			=> array(143, 143, 143),
+ 	"gray57"			=> array(145, 145, 145),
+ 	"grey57"			=> array(145, 145, 145),
+ 	"gray58"			=> array(148, 148, 148),
+ 	"grey58"			=> array(148, 148, 148),
+ 	"gray59"			=> array(150, 150, 150),
+ 	"grey59"			=> array(150, 150, 150),
+ 	"gray60"			=> array(153, 153, 153),
+ 	"grey60"			=> array(153, 153, 153),
+ 	"gray61"			=> array(156, 156, 156),
+ 	"grey61"			=> array(156, 156, 156),
+ 	"gray62"			=> array(158, 158, 158),
+ 	"grey62"			=> array(158, 158, 158),
+ 	"gray63"			=> array(161, 161, 161),
+ 	"grey63"			=> array(161, 161, 161),
+ 	"gray64"			=> array(163, 163, 163),
+ 	"grey64"			=> array(163, 163, 163),
+ 	"gray65"			=> array(166, 166, 166),
+ 	"grey65"			=> array(166, 166, 166),
+ 	"gray66"			=> array(168, 168, 168),
+ 	"grey66"			=> array(168, 168, 168),
+ 	"gray67"			=> array(171, 171, 171),
+ 	"grey67"			=> array(171, 171, 171),
+ 	"gray68"			=> array(173, 173, 173),
+ 	"grey68"			=> array(173, 173, 173),
+ 	"gray69"			=> array(176, 176, 176),
+ 	"grey69"			=> array(176, 176, 176),
+ 	"gray70"			=> array(179, 179, 179),
+ 	"grey70"			=> array(179, 179, 179),
+ 	"gray71"			=> array(181, 181, 181),
+ 	"grey71"			=> array(181, 181, 181),
+ 	"gray72"			=> array(184, 184, 184),
+ 	"grey72"			=> array(184, 184, 184),
+ 	"gray73"			=> array(186, 186, 186),
+ 	"grey73"			=> array(186, 186, 186),
+ 	"gray74"			=> array(189, 189, 189),
+ 	"grey74"			=> array(189, 189, 189),
+ 	"gray75"			=> array(191, 191, 191),
+ 	"grey75"			=> array(191, 191, 191),
+ 	"gray76"			=> array(194, 194, 194),
+ 	"grey76"			=> array(194, 194, 194),
+ 	"gray77"			=> array(196, 196, 196),
+ 	"grey77"			=> array(196, 196, 196),
+ 	"gray78"			=> array(199, 199, 199),
+ 	"grey78"			=> array(199, 199, 199),
+ 	"gray79"			=> array(201, 201, 201),
+ 	"grey79"			=> array(201, 201, 201),
+ 	"gray80"			=> array(204, 204, 204),
+ 	"grey80"			=> array(204, 204, 204),
+ 	"gray81"			=> array(207, 207, 207),
+ 	"grey81"			=> array(207, 207, 207),
+ 	"gray82"			=> array(209, 209, 209),
+ 	"grey82"			=> array(209, 209, 209),
+ 	"gray83"			=> array(212, 212, 212),
+ 	"grey83"			=> array(212, 212, 212),
+ 	"gray84"			=> array(214, 214, 214),
+ 	"grey84"			=> array(214, 214, 214),
+ 	"gray85"			=> array(217, 217, 217),
+ 	"grey85"			=> array(217, 217, 217),
+ 	"gray86"			=> array(219, 219, 219),
+ 	"grey86"			=> array(219, 219, 219),
+ 	"gray87"			=> array(222, 222, 222),
+ 	"grey87"			=> array(222, 222, 222),
+ 	"gray88"			=> array(224, 224, 224),
+ 	"grey88"			=> array(224, 224, 224),
+ 	"gray89"			=> array(227, 227, 227),
+ 	"grey89"			=> array(227, 227, 227),
+ 	"gray90"			=> array(229, 229, 229),
+ 	"grey90"			=> array(229, 229, 229),
+ 	"gray91"			=> array(232, 232, 232),
+ 	"grey91"			=> array(232, 232, 232),
+ 	"gray92"			=> array(235, 235, 235),
+ 	"grey92"			=> array(235, 235, 235),
+ 	"gray93"			=> array(237, 237, 237),
+ 	"grey93"			=> array(237, 237, 237),
+ 	"gray94"			=> array(240, 240, 240),
+ 	"grey94"			=> array(240, 240, 240),
+ 	"gray95"			=> array(242, 242, 242),
+ 	"grey95"			=> array(242, 242, 242),
+ 	"gray96"			=> array(245, 245, 245),
+ 	"grey96"			=> array(245, 245, 245),
+ 	"gray97"			=> array(247, 247, 247),
+ 	"grey97"			=> array(247, 247, 247),
+ 	"gray98"			=> array(250, 250, 250),
+ 	"grey98"			=> array(250, 250, 250),
+ 	"gray99"			=> array(252, 252, 252),
+ 	"grey99"			=> array(252, 252, 252),
+ 	"gray100"			=> array(255, 255, 255)
+ );
+ 
+ class PHPlot
+ {
+     /* I have removed internal variable declarations, some isset() checking was required,
+      * but now the variables left are those which can be tweaked by the user. This is intended to
+      * be the first step towards moving most of the Set...() methods into a subclass which will be
+      * used only when strictly necessary. Many users will be able to put default values here in the
+      * class and thus avoid memory overhead and reduce parsing times.
+      */
+ 
+     //////////////// CONFIG PARAMETERS //////////////////////
+ 
+     var $is_inline = FALSE;             // FALSE = Sends headers, TRUE = sends just raw image data
+     var $browser_cache = FALSE;         // FALSE = Sends headers for browser to not cache the image,
+     // (only if is_inline = FALSE also)
+     var $safe_margin = 5;               // Extra margin used in several places. In pixels
+     var $x_axis_position = '';          // Where to draw both axis (world coordinates),
+     var $y_axis_position = '';          // leave blank for X axis at 0 and Y axis at left of plot.
+     var $xscale_type = 'linear';        // linear, log
+     var $yscale_type = 'linear';
+ //Fonts
+     var $use_ttf = FALSE;                  // Use True Type Fonts?
+     var $ttf_path = '.';                    // Default path to look in for TT Fonts.
+     var $default_ttfont = 'benjamingothic.ttf';
+     var $line_spacing = 4;                  // Pixels between lines.
+     // Font angles: 0 or 90 degrees for fixed fonts, any for TTF
+     var $x_label_angle = 0;                 // For labels on X axis (tick and data)
+     var $y_label_angle = 0;                 // For labels on Y axis (tick and data)
+     var $x_title_angle = 0;                 // Don't change this if you don't want to screw things up!
+     var $y_title_angle = 90;                // Nor this.
+     var $title_angle = 0;                   // Or this.
+ //Formats
+     var $file_format = 'png';
+     var $output_file = '';                  // For output to a file instead of stdout
+ //Data
+     var $data_type = 'text-data';           // text-data, data-data-error, data-data, text-data-single
+     var $plot_type = 'linepoints';           // bars, lines, linepoints, area, points, pie, thinbarline, squared
+     var $label_scale_position = 0.5;        // Shifts data labes in pie charts. 1 = top, 0 = bottom
+     var $group_frac_width = 0.7;            // value from 0 to 1 = width of bar groups
+     var $bar_width_adjust = 1;              // 1 = bars of normal width, must be > 0
+     var $y_precision = 1;
+     var $x_precision = 1;
+     var $data_units_text = '';              // Units text for 'data' labels (i.e: '�', '$', etc.)
+ // Titles
+     var $title_txt = '';
+     var $x_title_txt = '';
+     var $x_title_pos = 'plotdown';          // plotdown, plotup, both, none
+     var $y_title_txt = '';
+     var $y_title_pos = 'plotleft';          // plotleft, plotright, both, none
+ //Labels
+     // There are two types of labels in PHPlot:
+     //    Tick labels: they follow the grid, next to ticks in axis.   (DONE)
+     //                 they are drawn at grid drawing time, by DrawXTicks() and DrawYTicks()
+     //    Data labels: they follow the data points, and can be placed on the axis or the plot (x/y)  (TODO)
+     //                 they are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc.
+     //                 Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_lines
+     // Tick Labels
+     var $x_tick_label_pos = 'plotdown';     // plotdown, plotup, both, xaxis, none
+     var $y_tick_label_pos = 'plotleft';     // plotleft, plotright, both, yaxis, none
+     // Data Labels:
+     var $x_data_label_pos = 'plotdown';     // plotdown, plotup, both, plot, all, none
+     var $y_data_label_pos = 'plotleft';     // plotleft, plotright, both, plot, all, none
+     var $draw_x_data_label_lines = FALSE;   // Draw a line from the data point to the axis?
+     var $draw_y_data_label_lines = FALSE;   // TODO
+     // Label types: (for tick, data and plot labels)
+     var $x_label_type = '';                 // data, time. Leave blank for no formatting.
+     var $y_label_type = '';                 // data, time. Leave blank for no formatting.
+     var $x_time_format = '%H:%m:%s';        // See http://www.php.net/manual/html/function.strftime.html
+     var $y_time_format = '%H:%m:%s';        // SetYTimeFormat() too...
+     // Skipping labels
+     var $x_label_inc = 1;                   // Draw a label every this many (1 = all) (TODO)
+     var $y_label_inc = 1;
+     var $_x_label_cnt = 0;                  // internal count FIXME: work in progress
+     // Legend
+     var $legend = '';                       // An array with legend titles
+     var $legend_x_pos = '';
+     var $legend_y_pos = '';
+ //Ticks
+     var $x_tick_length = 5;                 // tick length in pixels for upper/lower axis
+     var $y_tick_length = 5;                 // tick length in pixels for left/right axis
+     var $x_tick_cross = 3;                  // ticks cross x axis this many pixels
+     var $y_tick_cross = 3;                  // ticks cross y axis this many pixels
+     var $x_tick_pos = 'plotdown';           // plotdown, plotup, both, xaxis, none
+     var $y_tick_pos = 'plotleft';           // plotright, plotleft, both, yaxis, none
+     var $num_x_ticks = '';
+     var $num_y_ticks = '';
+     var $x_tick_inc = '';                   // Set num_x_ticks or x_tick_inc, not both.
+     var $y_tick_inc = '';                   // Set num_y_ticks or y_tick_inc, not both.
+     var $skip_top_tick = FALSE;
+     var $skip_bottom_tick = FALSE;
+     var $skip_left_tick = FALSE;
+     var $skip_right_tick = FALSE;
+ //Grid Formatting
+     var $draw_x_grid = FALSE;
+     var $draw_y_grid = TRUE;
+     var $dashed_grid = TRUE;
+     var $grid_at_foreground = FALSE;        // Chooses whether to draw the grid below or above the graph
+ //Colors and styles       (all colors can be array (R,G,B) or named color)
+     var $color_array = 'small';             // 'small', 'large' or array (define your own colors)
+     // See rgb.inc.php and SetRGBArray()
+     var $i_border = array(194, 194, 194);
+     var $plot_bg_color = 'white';
+     var $bg_color = 'white';
+     var $label_color = 'black';
+     var $text_color = 'black';
+     var $grid_color = 'black';
+     var $light_grid_color = 'gray';
+     var $tick_color = 'black';
+     var $title_color = 'black';
+     var $data_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
+     var $error_bar_colors = array('SkyBlue', 'green', 'orange', 'blue', 'orange', 'red', 'violet', 'azure1');
+     var $data_border_colors = array('black');
+     var $line_widths = 1;                  // single value or array
+     var $line_styles = array('solid', 'solid', 'dashed');   // single value or array
+     var $dashed_style = '2-4';              // colored dots-transparent dots
+     var $point_sizes = array(5, 5, 3);         // single value or array
+     var $point_shapes = array('diamond');   // rect, circle, diamond, triangle, dot, line, halfline, cross
+     var $error_bar_size = 5;                // right and left size of tee
+     var $error_bar_shape = 'tee';           // 'tee' or 'line'
+     var $error_bar_line_width = 1;          // single value (or array TODO)
+     var $plot_border_type = 'sides';        // left, sides, none, full
+     var $image_border_type = 'none';        // 'raised', 'plain', 'none'
+     var $shading = 5;                       // 0 for no shading, > 0 is size of shadows in pixels
+     var $draw_plot_area_background = FALSE;
+     var $draw_broken_lines = FALSE;          // Tells not to draw lines for missing Y data.
+ 
+ //////////////////////////////////////////////////////
+ //BEGIN CODE
+ //////////////////////////////////////////////////////
+ 
+     /* !
+      * Constructor: Setup img resource, colors and size of the image, and font sizes.
+      *
+      * \param which_width       int    Image width in pixels.
+      * \param which_height      int    Image height in pixels.
+      * \param which_output_file string Filename for output.
+      * \param which_input_fule  string Path to a file to be used as background.
+      */
+     function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL)
+     {
+         /*
+          * Please see http://www.php.net/register_shutdown_function
+          * PLEASE NOTE: register_shutdown_function() will take a copy of the object rather than a reference
+          * so we put an ampersand. However, the function registered will work on the object as it
+          * was upon registration. To solve this, one of two methods can be used:
+          *      $obj = new object();
+          *      register_shutdown_function(array(&$obj,'shutdown'));
+          * OR
+          *      $obj = &new object();
+          * HOWEVER, as the second statement assigns $obj a reference to the current object, it might be that
+          * several instances mess things up... (CHECK THIS)
+          *
+          * AND
+          *    as $this->img is set upon construction of the object, problems will not arise for us (for the
+          *    moment maybe, so I put all this here just in case)
+          */
+         register_shutdown_function(array(&$this, '_PHPlot'));
+ 
+         $this->SetRGBArray($this->color_array);
+ 
+         $this->background_done = FALSE;     // Set to TRUE after background image is drawn once
+ 
+         if ($which_output_file)
+             $this->SetOutputFile($which_output_file);
+ 
+         if ($which_input_file)
+             $this->SetInputFile($which_input_file);
+         else
+         {
+             $this->image_width = $which_width;
+             $this->image_height = $which_height;
+ 
+             $this->img = ImageCreate($this->image_width, $this->image_height);
+             if (!$this->img)
+                 $this->PrintError('PHPlot(): Could not create image resource.');
+         }
+ 
+         $this->SetDefaultStyles();
+         $this->SetDefaultFonts();
+ 
+         $this->SetTitle('');
+         $this->SetXTitle('');
+         $this->SetYTitle('');
+ 
+         $this->print_image = TRUE;      // Use for multiple plots per image (TODO: automatic)
+     }
+ 
+     /* !
+      * Destructor. Image resources not deallocated can be memory hogs, I think
+      * it is safer to automatically call imagedestroy upon script termination than
+      * do it ourselves.
+      * See notes in the constructor code.
+      */
+ 
+     function _PHPlot()
+     {
+         ImageDestroy($this->img);
+         return;
+     }
+ 
+ /////////////////////////////////////////////
+ //////////////                         COLORS
+ /////////////////////////////////////////////
+ 
+     /* !
+      * Returns an index to a color passed in as anything (string, hex, rgb)
+      *
+      * \param which_color * Color (can be '#AABBCC', 'Colorname', or array(r,g,b))
+      */
+     function SetIndexColor($which_color)
+     {
+         list ($r, $g, $b) = $this->SetRGBColor($which_color);  //Translate to RGB
+         $index = ImageColorExact($this->img, $r, $g, $b);
+         if ($index == -1)
+         {
+             return ImageColorResolve($this->img, $r, $g, $b);
+         }
+         else
+         {
+             return $index;
+         }
+     }
+ 
+     /* !
+      * Returns an index to a slightly darker color than the one requested.
+      */
+ 
+     function SetIndexDarkColor($which_color)
+     {
+         list ($r, $g, $b) = $this->SetRGBColor($which_color);
+ 
+         $r -= 0x30;
+         $r = ($r < 0) ? 0 : $r;
+         $g -= 0x30;
+         $g = ($g < 0) ? 0 : $g;
+         $b -= 0x30;
+         $b = ($b < 0) ? 0 : $b;
+ 
+         $index = ImageColorExact($this->img, $r, $g, $b);
+         if ($index == -1)
+         {
+             return ImageColorResolve($this->img, $r, $g, $b);
+         }
+         else
+         {
+             return $index;
+         }
+     }
+ 
+     /* !
+      * Sets/reverts all colors and styles to their defaults. If session is set, then only updates indices,
+      * as they are lost with every script execution, else, sets the default colors by name or value and
+      * then updates indices too.
+      *
+      * FIXME Isn't this too slow?
+      *
+      */
+ 
+     function SetDefaultStyles()
+     {
+         /* Some of the Set*() functions use default values when they get no parameters. */
+ 
+         if (!isset($this->session_set))
+         {
+             // If sessions are enabled, this variable will be preserved, so upon future executions, we
+             // will have it set, as well as color names (though not color indices, that's why we
+             // need to rebuild them)
+             $this->session_set = TRUE;
+ 
+             // These only need to be set once
+             $this->SetLineWidths();
+             $this->SetLineStyles();
+             $this->SetDefaultDashedStyle($this->dashed_style);
+             $this->SetPointSizes($this->point_sizes);
+         }
+ 
+         $this->SetImageBorderColor($this->i_border);
+         $this->SetPlotBgColor($this->plot_bg_color);
+         $this->SetBackgroundColor($this->bg_color);
+         $this->SetLabelColor($this->label_color);
+         $this->SetTextColor($this->text_color);
+         $this->SetGridColor($this->grid_color);
+         $this->SetLightGridColor($this->light_grid_color);
+         $this->SetTickColor($this->tick_color);
+         $this->SetTitleColor($this->title_color);
+         $this->SetDataColors();
+         $this->SetErrorBarColors();
+         $this->SetDataBorderColors();
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetBackgroundColor($which_color)
+     {
+         $this->bg_color = $which_color;
+         $this->ndx_bg_color = $this->SetIndexColor($this->bg_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetPlotBgColor($which_color)
+     {
+         $this->plot_bg_color = $which_color;
+         $this->ndx_plot_bg_color = $this->SetIndexColor($this->plot_bg_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetTitleColor($which_color)
+     {
+         $this->title_color = $which_color;
+         $this->ndx_title_color = $this->SetIndexColor($this->title_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetTickColor($which_color)
+     {
+         $this->tick_color = $which_color;
+         $this->ndx_tick_color = $this->SetIndexColor($this->tick_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetLabelColor($which_color)
+     {
+         $this->label_color = $which_color;
+         $this->ndx_title_color = $this->SetIndexColor($this->label_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetTextColor($which_color)
+     {
+         $this->text_color = $which_color;
+         $this->ndx_text_color = $this->SetIndexColor($this->text_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetLightGridColor($which_color)
+     {
+         $this->light_grid_color = $which_color;
+         $this->ndx_light_grid_color = $this->SetIndexColor($this->light_grid_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetGridColor($which_color)
+     {
+         $this->grid_color = $which_color;
+         $this->ndx_grid_color = $this->SetIndexColor($this->grid_color);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetImageBorderColor($which_color)
+     {
+         $this->i_border = $which_color;
+         $this->ndx_i_border = $this->SetIndexColor($this->i_border);
+         $this->ndx_i_border_dark = $this->SetIndexDarkColor($this->i_border);
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function SetTransparentColor($which_color)
+     {
+         ImageColorTransparent($this->img, $this->SetIndexColor($which_color));
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the array of colors to be used. It can be user defined, a small predefined one
+      * or a large one included from 'rgb.inc.php'.
+      *
+      * \param which_color_array If an array, the used as color array. If a string can
+      *        be one of 'small' or 'large'.
+      */
+ 
+     function SetRGBArray($which_color_array)
+     {
+         if (is_array($which_color_array))
+         {           // User defined array
+             $this->rgb_array = $which_color_array;
+             return TRUE;
+         }
+         elseif ($which_color_array == 'small')
+         {      // Small predefined color array
+             $this->rgb_array = array(
+                 'white' => array(255, 255, 255),
+                 'snow' => array(255, 250, 250),
+                 'PeachPuff' => array(255, 218, 185),
+                 'ivory' => array(255, 255, 240),
+                 'lavender' => array(230, 230, 250),
+                 'black' => array(0, 0, 0),
+                 'DimGrey' => array(105, 105, 105),
+                 'gray' => array(190, 190, 190),
+                 'grey' => array(190, 190, 190),
+                 'navy' => array(0, 0, 128),
+                 'SlateBlue' => array(106, 90, 205),
+                 'blue' => array(0, 0, 255),
+                 'SkyBlue' => array(135, 206, 235),
+                 'cyan' => array(0, 255, 255),
+                 'DarkGreen' => array(0, 100, 0),
+                 'green' => array(0, 255, 0),
+                 'YellowGreen' => array(154, 205, 50),
+                 'yellow' => array(255, 255, 0),
+                 'orange' => array(255, 165, 0),
+                 'gold' => array(255, 215, 0),
+                 'peru' => array(205, 133, 63),
+                 'beige' => array(245, 245, 220),
+                 'wheat' => array(245, 222, 179),
+                 'tan' => array(210, 180, 140),
+                 'brown' => array(165, 42, 42),
+                 'salmon' => array(250, 128, 114),
+                 'red' => array(255, 0, 0),
+                 'pink' => array(255, 192, 203),
+                 'maroon' => array(176, 48, 96),
+                 'magenta' => array(255, 0, 255),
+                 'violet' => array(238, 130, 238),
+                 'plum' => array(221, 160, 221),
+                 'orchid' => array(218, 112, 214),
+                 'purple' => array(160, 32, 240),
+                 'azure1' => array(240, 255, 255),
+                 'aquamarine1' => array(127, 255, 212)
+             );
+             return TRUE;
+         }
+         elseif ($which_color_array === 'large')
+         {    // Large color array
+             //include("./rgb.inc.php");
+             $this->rgb_array = $RGBArray;
+         }
+         else
+         {                                        // Default to black and white only.
+             $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0));
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Returns an array in R, G, B format 0-255
+      *
+      *  \param color_asked array(R,G,B) or string (named color or '#AABBCC')
+      */
+ 
+     function SetRGBColor($color_asked)
+     {
+         if ($color_asked == '')
+         {
+             $color_asked = array(0, 0, 0);
+         };
+ 
+         if (count($color_asked) == 3)
+         {    // already array of 3 rgb
+             $ret_val = $color_asked;
+         }
+         else
+         {                             // asking for a color by string
+             if (substr($color_asked, 0, 1) == '#')
+             {         // asking in #FFFFFF format.
+                 $ret_val = array(hexdec(substr($color_asked, 1, 2)), hexdec(substr($color_asked, 3, 2)),
+                     hexdec(substr($color_asked, 5, 2)));
+             }
+             else
+             {                                        // asking by color name
+                 $ret_val = $this->rgb_array[$color_asked];
+             }
+         }
+         return $ret_val;
+     }
+ 
+     /* !
+      * Sets the colors for the data.
+      */
+ 
+     function SetDataColors($which_data = NULL, $which_border = NULL)
+     {
+         if (is_null($which_data) && is_array($this->data_colors))
+         {
+             // use already set data_colors
+         }
+         else if (!is_array($which_data))
+         {
+             $this->data_colors = ($which_data) ? array($which_data) : array('blue', 'red', 'green', 'orange');
+         }
+         else
+         {
+             $this->data_colors = $which_data;
+         }
+ 
+         $i = 0;
+         foreach ($this->data_colors as $col)
+         {
+             $this->ndx_data_colors[$i] = $this->SetIndexColor($col);
+             $this->ndx_data_dark_colors[$i] = $this->SetIndexDarkColor($col);
+             $i++;
+         }
+ 
+         // For past compatibility:
+         $this->SetDataBorderColors($which_border);
+     }
+ 
+ // function SetDataColors()
+ 
+ 
+     /* !
+      *
+      */
+ 
+     function SetDataBorderColors($which_br = NULL)
+     {
+         if (is_null($which_br) && is_array($this->data_border_colors))
+         {
+             // use already set data_border_colors
+         }
+         else if (!is_array($which_br))
+         {
+             // Create new array with specified color
+             $this->data_border_colors = ($which_br) ? array($which_br) : array('black');
+         }
+         else
+         {
+             $this->data_border_colors = $which_br;
+         }
+ 
+         $i = 0;
+         foreach ($this->data_border_colors as $col)
+         {
+             $this->ndx_data_border_colors[$i] = $this->SetIndexColor($col);
+             $i++;
+         }
+     }
+ 
+ // function SetDataBorderColors()
+ 
+ 
+     /* !
+      * Sets the colors for the data error bars.
+      */
+ 
+     function SetErrorBarColors($which_err = NULL)
+     {
+         if (is_null($which_err) && is_array($this->error_bar_colors))
+         {
+             // use already set error_bar_colors
+         }
+         else if (!is_array($which_err))
+         {
+             $this->error_bar_colors = ($which_err) ? array($which_err) : array('black');
+         }
+         else
+         {
+             $this->error_bar_colors = $which_err;
+         }
+ 
+         $i = 0;
+         foreach ($this->error_bar_colors as $col)
+         {
+             $this->ndx_error_bar_colors[$i] = $this->SetIndexColor($col);
+             $i++;
+         }
+         return TRUE;
+     }
+ 
+ // function SetErrorBarColors()
+ 
+ 
+     /* !
+      * Sets the default dashed style.
+      *  \param which_style A string specifying order of colored and transparent dots,
+      *         i.e: '4-3' means 4 colored, 3 transparent;
+      *              '2-3-1-2' means 2 colored, 3 transparent, 1 colored, 2 transparent.
+      */
+ 
+     function SetDefaultDashedStyle($which_style)
+     {
+         // String: "numcol-numtrans-numcol-numtrans..."
+         $asked = explode('-', $which_style);
+ 
+         if (count($asked) < 2)
+         {
+             $this->DrawError("SetDefaultDashedStyle(): Wrong parameter '$which_style'.");
+             return FALSE;
+         }
+ 
+         // Build the string to be eval()uated later by SetDashedStyle()
+         $this->default_dashed_style = 'array( ';
+ 
+         $t = 0;
+         foreach ($asked as $s)
+         {
+             if ($t % 2 == 0)
+             {
+                 $this->default_dashed_style .= str_repeat('$which_ndxcol,', $s);
+             }
+             else
+             {
+                 $this->default_dashed_style .= str_repeat('IMG_COLOR_TRANSPARENT,', $s);
+             }
+             $t++;
+         }
+         // Remove trailing comma and add closing parenthesis
+         $this->default_dashed_style = substr($this->default_dashed_style, 0, -1);
+         $this->default_dashed_style .= ')';
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style
+      *   \param which_ndxcol Color index to be used.
+      */
+ 
+     function SetDashedStyle($which_ndxcol)
+     {
+         // See SetDefaultDashedStyle() to understand this.
+         eval("\$style = $this->default_dashed_style;");
+         return imagesetstyle($this->img, $style);
+     }
+ 
+     /* !
+      * Sets line widths on a per-line basis.
+      */
+ 
+     function SetLineWidths($which_lw=NULL)
+     {
+         if (is_null($which_lw))
+         {
+             // Do nothing, use default value.
+         }
+         else if (is_array($which_lw))
+         {
+             // Did we get an array with line widths?
+             $this->line_widths = $which_lw;
+         }
+         else
+         {
+             $this->line_widths = array($which_lw);
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      *
+      */
+ 
+     function SetLineStyles($which_ls=NULL)
+     {
+         if (is_null($which_ls))
+         {
+             // Do nothing, use default value.
+         }
+         else if (!is_array($which_ls))
+         {
+             // Did we get an array with line styles?
+             $this->line_styles = $which_ls;
+         }
+         else
+         {
+             $this->line_styles = ($which_ls) ? array($which_ls) : array('solid');
+         }
+         return TRUE;
+     }
+ 
+ /////////////////////////////////////////////
+ //////////////                          FONTS
+ /////////////////////////////////////////////
+ 
+ 
+     /* !
+      * Sets number of pixels between lines of the same text.
+      */
+     function SetLineSpacing($which_spc)
+     {
+         $this->line_spacing = $which_spc;
+     }
+ 
+     /* !
+      * Enables use of TrueType fonts in the graph. Font initialisation methods
+      * depend on this setting, so when called, SetUseTTF() resets the font
+      * settings
+      */
+ 
+     function SetUseTTF($which_ttf)
+     {
+         $this->use_ttf = $which_ttf;
+         if ($which_ttf)
+             $this->SetDefaultFonts();
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the directory name to look into for TrueType fonts.
+      */
+ 
+     function SetTTFPath($which_path)
+     {
+         // Maybe someone needs really dynamic config. He'll need this:
+         // clearstatcache();
+ 
+         if (is_dir($which_path) && is_readable($which_path))
+         {
+             $this->ttf_path = $which_path;
+             return TRUE;
+         }
+         else
+         {
+             $this->PrintError("SetTTFPath(): $which_path is not a valid path.");
+             return FALSE;
+         }
+     }
+ 
+     /* !
+      * Sets the default TrueType font and updates all fonts to that.
+      */
+ 
+     function SetDefaultTTFont($which_font)
+     {
+         if (is_file($which_font) && is_readable($which_font))
+         {
+             $this->default_ttfont = $which_font;
+             return $this->SetDefaultFonts();
+         }
+         else
+         {
+             $this->PrintError("SetDefaultTTFont(): $which_font is not a valid font file.");
+             return FALSE;
+         }
+     }
+ 
+     /* !
+      * Sets fonts to their defaults
+      */
+ 
+     function SetDefaultFonts()
+     {
+         // TTF:
+         if ($this->use_ttf)
+         {
+             //$this->SetTTFPath(dirname($_SERVER['PHP_SELF']));
+             $this->SetTTFPath(getcwd());
+             $this->SetFont('generic', $this->default_ttfont, 8);
+             $this->SetFont('title', $this->default_ttfont, 14);
+             $this->SetFont('legend', $this->default_ttfont, 8);
+             $this->SetFont('x_label', $this->default_ttfont, 6);
+             $this->SetFont('y_label', $this->default_ttfont, 6);
+             $this->SetFont('x_title', $this->default_ttfont, 10);
+             $this->SetFont('y_title', $this->default_ttfont, 10);
+         }
+         // Fixed:
+         else
+         {
+             $this->SetFont('generic', 2);
+             $this->SetFont('title', 5);
+             $this->SetFont('legend', 2);
+             $this->SetFont('x_label', 1);
+             $this->SetFont('y_label', 1);
+             $this->SetFont('x_title', 3);
+             $this->SetFont('y_title', 3);
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets Fixed/Truetype font parameters.
+      *  \param $which_elem Is the element whose font is to be changed.
+      *         It can be one of 'title', 'legend', 'generic',
+      *         'x_label', 'y_label', x_title' or 'y_title'
+      *  \param $which_font Can be a number (for fixed font sizes) or
+      *         a string with the filename when using TTFonts.
+      *  \param $which_size Point size (TTF only)
+      * Calculates and updates internal height and width variables.
+      */
+ 
+     function SetFont($which_elem, $which_font, $which_size = 12)
+     {
+         // TTF:
+         if ($this->use_ttf)
+         {
+             $path = $this->ttf_path . '/' . $which_font;
+ 
+             if (!is_file($path) || !is_readable($path))
+             {
+                 $this->DrawError("SetFont(): True Type font $path doesn't exist");
+                 return FALSE;
+             }
+ 
+             switch ($which_elem)
+             {
+                 case 'generic':
+                     $this->generic_font['font'] = $path;
+                     $this->generic_font['size'] = $which_size;
+                     break;
+                 case 'title':
+                     $this->title_font['font'] = $path;
+                     $this->title_font['size'] = $which_size;
+                     break;
+                 case 'legend':
+                     $this->legend_font['font'] = $path;
+                     $this->legend_font['size'] = $which_size;
+                     break;
+                 case 'x_label':
+                     $this->x_label_font['font'] = $path;
+                     $this->x_label_font['size'] = $which_size;
+                     break;
+                 case 'y_label':
+                     $this->y_label_font['font'] = $path;
+                     $this->y_label_font['size'] = $which_size;
+                     break;
+                 case 'x_title':
+                     $this->x_title_font['font'] = $path;
+                     $this->x_title_font['size'] = $which_size;
+                     break;
+                 case 'y_title':
+                     $this->y_title_font['font'] = $path;
+                     $this->y_title_font['size'] = $which_size;
+                     break;
+                 default:
+                     $this->DrawError("SetFont(): Unknown element '$which_elem' specified.");
+                     return FALSE;
+             }
+             return TRUE;
+         }
+ 
+         // Fixed fonts:
+         if ($which_font > 5 || $which_font < 0)
+         {
+             $this->DrawError('SetFont(): Non-TTF font size must be 1, 2, 3, 4 or 5');
+             return FALSE;
+         }
+ 
+         switch ($which_elem)
+         {
+             case 'generic':
+                 $this->generic_font['font'] = $which_font;
+                 $this->generic_font['height'] = ImageFontHeight($which_font);
+                 $this->generic_font['width'] = ImageFontWidth($which_font);
+                 break;
+             case 'title':
+                 $this->title_font['font'] = $which_font;
+                 $this->title_font['height'] = ImageFontHeight($which_font);
+                 $this->title_font['width'] = ImageFontWidth($which_font);
+                 break;
+             case 'legend':
+                 $this->legend_font['font'] = $which_font;
+                 $this->legend_font['height'] = ImageFontHeight($which_font);
+                 $this->legend_font['width'] = ImageFontWidth($which_font);
+                 break;
+             case 'x_label':
+                 $this->x_label_font['font'] = $which_font;
+                 $this->x_label_font['height'] = ImageFontHeight($which_font);
+                 $this->x_label_font['width'] = ImageFontWidth($which_font);
+                 break;
+             case 'y_label':
+                 $this->y_label_font['font'] = $which_font;
+                 $this->y_label_font['height'] = ImageFontHeight($which_font);
+                 $this->y_label_font['width'] = ImageFontWidth($which_font);
+                 break;
+             case 'x_title':
+                 $this->x_title_font['font'] = $which_font;
+                 $this->x_title_font['height'] = ImageFontHeight($which_font);
+                 $this->x_title_font['width'] = ImageFontWidth($which_font);
+                 break;
+             case 'y_title':
+                 $this->y_title_font['font'] = $which_font;
+                 $this->y_title_font['height'] = ImageFontHeight($which_font);
+                 $this->y_title_font['width'] = ImageFontWidth($which_font);
+                 break;
+             default:
+                 $this->DrawError("SetFont(): Unknown element '$which_elem' specified.");
+                 return FALSE;
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Returns an array with the size of the bounding box of an
+      * arbitrarily placed (rotated) TrueType text string.
+      */
+ 
+     function TTFBBoxSize($size, $angle, $font, $string)
+     {
+         // First, assume angle < 90
+         $arr = ImageTTFBBox($size, 0, $font, $string);
+         $flat_width = $arr[2] - $arr[0];
+         $flat_height = abs($arr[3] - $arr[5]);
+ 
+         // Now the bounding box
+         $angle = deg2rad($angle);
+         $width = ceil(abs($flat_width * cos($angle) + $flat_height * sin($angle))); //Must be integer
+         $height = ceil(abs($flat_width * sin($angle) + $flat_height * cos($angle))); //Must be integer
+ 
+         return array($width, $height);
+     }
+ 
+     /* !
+      * Draws a string of text. Horizontal and vertical alignment are relative to
+      * to the drawing. That is: vertical text (90 deg) gets centered along y-axis
+      * with v_align = 'center', and adjusted to the left of x-axis with h_align = 'right',
+      *
+      * \note Original multiple lines code submitted by Remi Ricard.
+      * \note Original vertical code submitted by Marlin Viss.
+      */
+ 
+     function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text, $which_halign = 'left', $which_valign = 'bottom')
+     {
+         // TTF:
+         if ($this->use_ttf)
+         {
+             $size = $this->TTFBBoxSize($which_font['size'], $which_angle, $which_font['font'], $which_text);
+             $rads = deg2rad($which_angle);
+ 
+             if ($which_valign == 'center')
+                 $which_ypos += $size[1] / 2;
+ 
+             if ($which_valign == 'bottom')
+                 $which_ypos += $size[1];
+ 
+             if ($which_halign == 'center')
+                 $which_xpos -= ( $size[0] / 2) * cos($rads);
+ 
+             if ($which_halign == 'left')
+                 $which_xpos += $size[0] * sin($rads);
+ 
+             if ($which_halign == 'right')
+                 $which_xpos -= $size[0] * cos($rads);
+ 
+             ImageTTFText($this->img, $which_font['size'], $which_angle,
+                     $which_xpos, $which_ypos, $which_color, $which_font['font'], $which_text);
+         }
+         // Fixed fonts:
+         else
+         {
+             // Split the text by its lines, and count them
+             $which_text = ereg_replace("\r", "", $which_text);
+             $str = split("\n", $which_text);
+             $nlines = count($str);
+             $spacing = $this->line_spacing * ($nlines - 1);
+ 
+             // Vertical text:
+             // (Remember the alignment convention with vertical text)
+             if ($which_angle == 90)
+             {
+                 // The text goes around $which_xpos.
+                 if ($which_halign == 'center')
+                     $which_xpos -= ( $nlines * ($which_font['height'] + $spacing)) / 2;
+ 
+                 // Left alignment requires no modification to $xpos...
+                 // Right-align it. $which_xpos designated the rightmost x coordinate.
+                 else if ($which_halign == 'right')
+                     $which_xpos += ( $nlines * ($which_font['height'] + $spacing));
+ 
+                 $ypos = $which_ypos;
+                 for ($i = 0; $i < $nlines; $i++)
+                 {
+                     // Center the text vertically around $which_ypos (each line)
+                     if ($which_valign == 'center')
+                         $ypos = $which_ypos + (strlen($str[$i]) * $which_font['width']) / 2;
+                     // Make the text finish (vertically) at $which_ypos
+                     if ($which_valign == 'bottom')
+                         $ypos = $which_ypos + strlen($str[$i]) * $which_font['width'];
+ 
+                     ImageStringUp($this->img, $which_font['font'],
+                             $i * ($which_font['height'] + $spacing) + $which_xpos,
+                             $ypos, $str[$i], $which_color);
+                 }
+             }
+             // Horizontal text:
+             else
+             {
+                 // The text goes above $which_ypos
+                 if ($which_valign == 'top')
+                     $which_ypos -= $nlines * ($which_font['height'] + $spacing);
+                 // The text is centered around $which_ypos
+                 if ($which_valign == 'center')
+                     $which_ypos -= ( $nlines * ($which_font['height'] + $spacing)) / 2;
+                 // valign = 'bottom' requires no modification
+ 
+                 $xpos = $which_xpos;
+                 for ($i = 0; $i < $nlines; $i++)
+                 {
+                     // center the text around $which_xpos
+                     if ($which_halign == 'center')
+                         $xpos = $which_xpos - (strlen($str[$i]) * $which_font['width']) / 2;
+                     // make the text finish at $which_xpos
+                     if ($which_halign == 'right')
+                         $xpos = $which_xpos - strlen($str[$i]) * $which_font['width'];
+ 
+                     ImageString($this->img, $which_font['font'], $xpos,
+                             $i * ($which_font['height'] + $spacing) + $which_ypos,
+                             $str[$i], $which_color);
+                 }
+             }
+         }
+         return TRUE;
+     }
+ 
+ // function DrawText()
+ /////////////////////////////////////////////
+ ///////////            INPUT / OUTPUT CONTROL
+ /////////////////////////////////////////////
+ 
+     /* !
+      * Sets output file format.
+      */
+     function SetFileFormat($format)
+     {
+         $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__);
+ 
+         switch ($asked)
+         {
+             case 'jpg':
+                 if (imagetypes() & IMG_JPG)
+                     $this->file_format = 'jpg';
+                 return TRUE;
+                 break;
+             case 'png':
+                 if (imagetypes() & IMG_PNG)
+                     $this->file_format = 'png';
+                 return TRUE;
+                 break;
+             case 'gif':
+                 if (imagetypes() & IMG_GIF)
+                     $this->file_format = 'gif';
+                 return TRUE;
+                 break;
+             case 'wbmp':
+                 if (imagetypes() & IMG_WBMP)
+                     $this->file_format = 'wbmp';
+                 return TRUE;
+                 break;
+             default:
+                 $this->PrintError("SetFileFormat():File format '$format' not supported");
+                 return FALSE;
+         }
+     }
+ 
+     /* !
+      * Selects an input file to be used as graph background and scales or tiles this image
+      * to fit the sizes.
+      *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
+      *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
+      */
+ 
+     function SetBgImage($input_file, $mode='centeredtile')
+     {
+         $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
+         $this->bgimg = $input_file;
+     }
+ 
+     /* !
+      * Selects an input file to be used as plot area background and scales or tiles this image
+      * to fit the sizes.
+      *  \param input_file string Path to the file to be used (jpeg, png and gif accepted)
+      *  \param mode       string 'centeredtile', 'tile', 'scale' (the image to the graph's size)
+      */
+ 
+     function SetPlotAreaBgImage($input_file, $mode='tile')
+     {
+         $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__);
+         $this->plotbgimg = $input_file;
+     }
+ 
+     /* !
+      * Sets the name of the file to be used as output file.
+      */
+ 
+     function SetOutputFile($which_output_file)
+     {
+         $this->output_file = $which_output_file;
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the output image as 'inline', that is: no Content-Type headers are sent
+      * to the browser. Needed if you want to embed the images.
+      */
+ 
+     function SetIsInline($which_ii)
+     {
+         $this->is_inline = (bool) $which_ii;
+         return TRUE;
+     }
+ 
+     /* !
+      * Performs the actual outputting of the generated graph, and
+      * destroys the image resource.
+      */
+ 
+     function PrintImage()
+     {
+         // Browser cache stuff submitted by Thiemo Nagel
+         if ((!$this->browser_cache) && (!$this->is_inline))
+         {
+             header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+             header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
+             header('Cache-Control: no-cache, must-revalidate');
+             header('Pragma: no-cache');
+         }
+ 
+         switch ($this->file_format)
+         {
+             case 'png':
+                 if (!$this->is_inline)
+                 {
+                     Header('Content-type: image/png');
+                 }
+                 if ($this->is_inline && $this->output_file != '')
+                 {
+                     ImagePng($this->img, $this->output_file);
+                 }
+                 else
+                 {
+                     ImagePng($this->img);
+                 }
+                 break;
+             case 'jpg':
+                 if (!$this->is_inline)
+                 {
+                     Header('Content-type: image/jpeg');
+                 }
+                 if ($this->is_inline && $this->output_file != '')
+                 {
+                     ImageJPEG($this->img, $this->output_file);
+                 }
+                 else
+                 {
+                     ImageJPEG($this->img);
+                 }
+                 break;
+             case 'gif':
+                 if (!$this->is_inline)
+                 {
+                     Header('Content-type: image/gif');
+                 }
+                 if ($this->is_inline && $this->output_file != '')
+                 {
+                     ImageGIF($this->img, $this->output_file);
+                 }
+                 else
+                 {
+                     ImageGIF($this->img);
+                 }
+ 
+                 break;
+             case 'wbmp':        // wireless bitmap, 2 bit.
+                 if (!$this->is_inline)
+                 {
+                     Header('Content-type: image/wbmp');
+                 }
+                 if ($this->is_inline && $this->output_file != '')
+                 {
+                     ImageWBMP($this->img, $this->output_file);
+                 }
+                 else
+                 {
+                     ImageWBMP($this->img);
+                 }
+ 
+                 break;
+             default:
+                 $this->PrintError('PrintImage(): Please select an image type!');
+                 break;
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Prints an error message to stdout and dies
+      */
+ 
+     function PrintError($error_message)
+     {
+         echo "<p><b>Fatal error</b>: $error_message<p>";
+         die;
+     }
+ 
+     /* !
+      * Prints an error message inline into the generated image and draws it centered
+      * around the given coordinates (defaults to center of the image)
+      *   \param error_message Message to be drawn
+      *   \param where_x       X coordinate
+      *   \param where_y       Y coordinate
+      */
+ 
+     function DrawError($error_message, $where_x = NULL, $where_y = NULL)
+     {
+         if (!$this->img)
+             $this->PrintError('_DrawError(): Warning, no image resource allocated. ' .
+                     'The message to be written was: ' . $error_message);
+ 
+         $ypos = (!$where_y) ? $this->image_height / 2 : $where_y;
+         $xpos = (!$where_x) ? $this->image_width / 2 : $where_x;
+         ImageRectangle($this->img, 0, 0, $this->image_width, $this->image_height,
+                 ImageColorAllocate($this->img, 255, 255, 255));
+ 
+         $this->DrawText($this->generic_font, 0, $xpos, $ypos, ImageColorAllocate($this->img, 0, 0, 0),
+                 $error_message, 'center', 'center');
+ 
+         $this->PrintImage();
+         exit;
+ //        return TRUE;
+     }
+ 
+ /////////////////////////////////////////////
+ ///////////                            LABELS
+ /////////////////////////////////////////////
+ 
+ 
+     /* !
+      * Sets position for X labels following data points.
+      */
+     function SetXDataLabelPos($which_xdlp)
+     {
+         $this->x_data_label_pos = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none',
+                         __FUNCTION__);
+         if ($which_xdlp != 'none')
+             $this->x_tick_label_pos = 'none';
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets position for Y labels following data points.
+      */
+ 
+     function SetYDataLabelPos($which_ydlp)
+     {
+         $this->y_data_label_pos = $this->CheckOption($which_ydlp, 'plotleft, plotright, both, yaxis, all, none',
+                         __FUNCTION__);
+         if ($which_ydlp != 'none')
+             $this->y_tick_label_pos = 'none';
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets position for X labels following ticks (hence grid lines)
+      */
+ 
+     function SetXTickLabelPos($which_xtlp)
+     {
+         $this->x_tick_label_pos = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none',
+                         __FUNCTION__);
+         if ($which_xtlp != 'none')
+             $this->x_data_label_pos = 'none';
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets position for Y labels following ticks (hence grid lines)
+      */
+ 
+     function SetYTickLabelPos($which_ytlp)
+     {
+         $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none',
+                         __FUNCTION__);
+         if ($which_ytlp != 'none')
+             $this->y_data_label_pos = 'none';
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets type for tick and data labels on X axis.
+      * \note 'title' type left for backwards compatibility.
+      */
+ 
+     function SetXLabelType($which_xlt)
+     {
+         $this->x_label_type = $this->CheckOption($which_xlt, 'data, time, title', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets type for tick and data labels on Y axis.
+      */
+ 
+     function SetYLabelType($which_ylt)
+     {
+         $this->y_label_type = $this->CheckOption($which_ylt, 'data, time', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     function SetXTimeFormat($which_xtf)
+     {
+         $this->x_time_format = $which_xtf;
+         return TRUE;
+     }
+ 
+     function SetYTimeFormat($which_ytf)
+     {
+         $this->y_time_format = $which_ytf;
+         return TRUE;
+     }
+ 
+     function SetXLabelAngle($which_xla)
+     {
+         $this->x_label_angle = $which_xla;
+         return TRUE;
+     }
+ 
+     function SetYLabelAngle($which_yla)
+     {
+         $this->y_label_angle = $which_yla;
+         return TRUE;
+     }
+ 
+ /////////////////////////////////////////////
+ ///////////                              MISC
+ /////////////////////////////////////////////
+ 
+     /* !
+      * Checks the valididy of an option.
+      *  \param which_opt  String to check.
+      *  \param which_acc  String of accepted choices.
+      *  \param which_func Name of the calling function, for error messages.
+      *  \note If checking everywhere for correctness slows things down, we could provide a
+      *        child class overriding every Set...() method which uses CheckOption(). Those new
+      *        methods could proceed in the unsafe but faster way.
+      */
+     function CheckOption($which_opt, $which_acc, $which_func)
+     {
+         $asked = trim($which_opt);
+ 
+         // FIXME: this for backward compatibility, as eregi() fails with empty strings.
+         if ($asked == '')
+             return '';
+ 
+         $asked = strtolower($asked);
+         if (@ eregi($asked, $which_acc))
+         {
+             return $asked;
+         }
+         else
+         {
+             $this->DrawError("$which_func(): '$which_opt' not in available choices: '$which_acc'.");
+             return NULL;
+         }
+     }
+ 
+     /* !
+      *  \note Submitted by Thiemo Nagel
+      */
+ 
+     function SetBrowserCache($which_browser_cache)
+     {
+         $this->browser_cache = $which_browser_cache;
+         return TRUE;
+     }
+ 
+     /* !
+      * Whether to show the final image or not
+      */
+ 
+     function SetPrintImage($which_pi)
+     {
+         $this->print_image = $which_pi;
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the graph's legend. If argument is not an array, appends it to the legend.
+      */
+ 
+     function SetLegend($which_leg)
+     {
+         if (is_array($which_leg))
+         {             // use array
+             $this->legend = $which_leg;
+             return TRUE;
+         }
+         else if (!is_null($which_leg))
+         {     // append string
+             $this->legend[] = $which_leg;
+             return TRUE;
+         }
+         else
+         {
+             $this->DrawError("SetLegend(): argument must not be null.");
+             return FALSE;
+         }
+     }
+ 
+     /* !
+      * Specifies the absolute (relative to image's up/left corner) position
+      * of the legend's upper/leftmost corner.
+      *  $which_type not yet used (TODO)
+      */
+ 
+     function SetLegendPixels($which_x, $which_y, $which_type=NULL)
+     {
+         $this->legend_x_pos = $which_x;
+         $this->legend_y_pos = $which_y;
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Specifies the relative (to graph's origin) position of the legend's
+      * upper/leftmost corner. MUST be called after scales are set up.
+      *   $which_type not yet used (TODO)
+      */
+ 
+     function SetLegendWorld($which_x, $which_y, $which_type=NULL)
+     {
+         if (!isset($this->scale_is_set))
+             $this->CalcTranslation();
+ 
+         $this->legend_x_pos = $this->xtr($which_x);
+         $this->legend_y_pos = $this->ytr($which_y);
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Accepted values are: left, sides, none, full
+      */
+ 
+     function SetPlotBorderType($pbt)
+     {
+         $this->plot_border_type = $this->CheckOption($pbt, 'left, sides, none, full', __FUNCTION__);
+     }
+ 
+     /* !
+      * Accepted values are: raised, plain
+      */
+ 
+     function SetImageBorderType($sibt)
+     {
+         $this->image_border_type = $this->CheckOption($sibt, 'raised, plain', __FUNCTION__);
+     }
+ 
+     /* !
+      * \param dpab bool
+      */
+ 
+     function SetDrawPlotAreaBackground($dpab)
+     {
+         $this->draw_plot_area_background = (bool) $dpab;
+     }
+ 
+     /* !
+      * \param dyg bool
+      */
+ 
+     function SetDrawYGrid($dyg)
+     {
+         $this->draw_y_grid = (bool) $dyg;
+         return TRUE;
+     }
+ 
+     /* !
+      * \param dxg bool
+      */
+ 
+     function SetDrawXGrid($dxg)
+     {
+         $this->draw_x_grid = (bool) $dxg;
+         return TRUE;
+     }
+ 
+     /* !
+      * \param ddg bool
+      */
+ 
+     function SetDrawDashedGrid($ddg)
+     {
+         $this->dashed_grid = (bool) $ddg;
+         return TRUE;
+     }
+ 
+     /* !
+      * \param dxdl bool
+      */
+ 
+     function SetDrawXDataLabelLines($dxdl)
+     {
+         $this->draw_x_data_label_lines = (bool) $dxdl;
+         return TRUE;
+     }
+ 
+     /* !
+      * TODO: draw_y_data_label_lines not implemented.
+      * \param dydl bool
+      */
+ 
+     function SetDrawYDataLabelLines($dydl)
+     {
+         $this->draw_y_data_label_lines = $dydl;
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the graph's title.
+      * TODO: add parameter to choose title placement: left, right, centered=
+      */
+ 
+     function SetTitle($which_title)
+     {
+         $this->title_txt = $which_title;
+ 
+         if ($which_title == '')
+         {
+             $this->title_height = 0;
+             return TRUE;
+         }
+ 
+         $str = split("\n", $which_title);
+         $lines = count($str);
+         $spacing = $this->line_spacing * ($lines - 1);
+ 
+         if ($this->use_ttf)
+         {
+             $size = $this->TTFBBoxSize($this->title_font['size'], 0, $this->title_font['font'], $which_title);
+             $this->title_height = $size[1] * $lines;
+         }
+         else
+         {
+             $this->title_height = ($this->title_font['height'] + $spacing) * $lines;
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the X axis title and position.
+      */
+ 
+     function SetXTitle($which_xtitle, $which_xpos = 'plotdown')
+     {
+         if ($which_xtitle == '')
+             $which_xpos = 'none';
+ 
+         $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__);
+ 
+         $this->x_title_txt = $which_xtitle;
+ 
+         $str = split("\n", $which_xtitle);
+         $lines = count($str);
+         $spacing = $this->line_spacing * ($lines - 1);
+ 
+         if ($this->use_ttf)
+         {
+             $size = $this->TTFBBoxSize($this->x_title_font['size'], 0, $this->x_title_font['font'], $which_xtitle);
+             $this->x_title_height = $size[1] * $lines;
+         }
+         else
+         {
+             $this->x_title_height = ($this->y_title_font['height'] + $spacing) * $lines;
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the Y axis title and position.
+      */
+ 
+     function SetYTitle($which_ytitle, $which_ypos = 'plotleft')
+     {
+         if ($which_ytitle == '')
+             $which_ypos = 'none';
+ 
+         $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__);
+ 
+         $this->y_title_txt = $which_ytitle;
+ 
+         $str = split("\n", $which_ytitle);
+         $lines = count($str);
+         $spacing = $this->line_spacing * ($lines - 1);
+ 
+         if ($this->use_ttf)
+         {
+             $size = $this->TTFBBoxSize($this->y_title_font['size'], 90, $this->y_title_font['font'],
+                             $which_ytitle);
+             $this->y_title_width = $size[0] * $lines;
+         }
+         else
+         {
+             $this->y_title_width = ($this->y_title_font['height'] + $spacing) * $lines;
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the size of the drop shadow for bar and pie charts.
+      * \param which_s int Size in pixels.
+      */
+ 
+     function SetShading($which_s)
+     {
+         $this->shading = (int) $which_s;
+         return TRUE;
+     }
+ 
+     function SetPlotType($which_pt)
+     {
+         $this->plot_type = $this->CheckOption($which_pt,
+                         'bars, stackedbars, lines, linepoints, area, points, pie, thinbarline, squared',
+                         __FUNCTION__);
+     }
+ 
+     /* !
+      * Sets the position of Y axis.
+      * \param pos int Position in world coordinates.
+      */
+ 
+     function SetYAxisPosition($pos)
+     {
+         $this->y_axis_position = (int) $pos;
+         if (isset($this->scale_is_set))
+         {
+             $this->CalcTranslation();
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the position of X axis.
+      * \param pos int Position in world coordinates.
+      */
+ 
+     function SetXAxisPosition($pos)
+     {
+         $this->x_axis_position = (int) $pos;
+         if (isset($this->scale_is_set))
+         {
+             $this->CalcTranslation();
+         }
+         return TRUE;
+     }
+ 
+     function SetXScaleType($which_xst)
+     {
+         $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     function SetYScaleType($which_yst)
+     {
+         $this->yscale_type = $this->CheckOption($which_yst, 'linear, log', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     function SetPrecisionX($which_prec)
+     {
+         $this->x_precision = $which_prec;
+         $this->SetXLabelType('data');
+         return TRUE;
+     }
+ 
+     function SetPrecisionY($which_prec)
+     {
+         $this->y_precision = $which_prec;
+         $this->SetYLabelType('data');
+         return TRUE;
+     }
+ 
+     function SetErrorBarLineWidth($which_seblw)
+     {
+         $this->error_bar_line_width = $which_seblw;
+         return TRUE;
+     }
+ 
+     function SetLabelScalePosition($which_blp)
+     {
+         //0 to 1
+         $this->label_scale_position = $which_blp;
+         return TRUE;
+     }
+ 
+     function SetErrorBarSize($which_ebs)
+     {
+         //in pixels
+         $this->error_bar_size = $which_ebs;
+         return TRUE;
+     }
+ 
+     /* !
+      * Can be one of: 'tee', 'line'
+      */
+ 
+     function SetErrorBarShape($which_ebs)
+     {
+         $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__);
+     }
+ 
+     /* !
+      * Sets point shape for each data set via an array.
+      * Shape can be one of: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
+      * 'diamond', 'triangle', 'trianglemid'
+      */
+ 
+     function SetPointShapes($which_pt)
+     {
+         if (is_null($which_pt))
+         {
+             // Do nothing, use default value.
+         }
+         else if (is_array($which_pt))
+         {
+             // Did we get an array with point shapes?
+             $this->point_shapes = $which_pt;
+         }
+         else
+         {
+             // Single value into array
+             $this->point_shapes = array($which_pt);
+         }
+ 
+         foreach ($this->point_shapes as $shape)
+         {
+             // TODO, better check, per element rectification
+             $this->CheckOption($shape,
+                     'halfline, line, plus, cross, rect, circle, dot, diamond, triangle, trianglemid',
+                     __FUNCTION__);
+         }
+ 
+         // Make both point_shapes and point_sizes same size.
+         $ps = count($this->point_sizes);
+         $pt = count($this->point_shapes);
+ 
+         if ($ps < $pt)
+         {
+             array_pad_array($this->point_sizes, $pt);
+         }
+         else if ($pt > $ps)
+         {
+             array_pad_array($this->point_shapes, $ps);
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets the point size for point plots.
+      * \param ps int Size in pixels.
+      * \note Test this more extensively
+      */
+ 
+     function SetPointSizes($which_ps)
+     {
+         if (is_null($which_ps))
+         {
+             // Do nothing, use default value.
+         }
+         else if (is_array($which_ps))
+         {
+             // Did we get an array with point sizes?
+             $this->point_sizes = $which_ps;
+         }
+         else
+         {
+             // Single value into array
+             $this->point_sizes = array($which_ps);
+         }
+ 
+         // Make both point_shapes and point_sizes same size.
+         $ps = count($this->point_sizes);
+         $pt = count($this->point_shapes);
+ 
+         if ($ps < $pt)
+         {
+             array_pad_array($this->point_sizes, $pt);
+         }
+         else if ($pt > $ps)
+         {
+             array_pad_array($this->point_shapes, $ps);
+         }
+ 
+         // Fix odd point sizes for point shapes which need it
+         for ($i = 0; $i < $pt; $i++)
+         {
+             if ($this->point_shapes[$i] == 'diamond' or $this->point_shapes[$i] == 'triangle')
+             {
+                 if ($this->point_sizes[$i] % 2 != 0)
+                 {
+                     $this->point_sizes[$i]++;
+                 }
+             }
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Tells not to draw lines for missing Y data. Only works with 'lines' and 'squared' plots.
+      * \param bl bool
+      */
+ 
+     function SetDrawBrokenLines($bl)
+     {
+         $this->draw_broken_lines = (bool) $bl;
+     }
+ 
+     /* !
+      *  text-data: ('label', y1, y2, y3, ...)
+      *  text-data-single: ('label', data), for some pie charts.
+      *  data-data: ('label', x, y1, y2, y3, ...)
+      *  data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...)
+      */
+ 
+     function SetDataType($which_dt)
+     {
+         //The next four lines are for past compatibility.
+         if ($which_dt == 'text-linear')
+         {
+             $which_dt = 'text-data';
+         };
+         if ($which_dt == 'linear-linear')
+         {
+             $which_dt = 'data-data';
+         };
+         if ($which_dt == 'linear-linear-error')
+         {
+             $which_dt = 'data-data-error';
+         };
+         if ($which_dt == 'text-data-pie')
+         {
+             $which_dt = 'text-data-single';
+         }
+ 
+ 
+         $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, ' .
+                         'data-data, data-data-error', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     /* !
+      * Copy the array passed as data values. We convert to numerical indexes, for its
+      * use for (or while) loops, which sometimes are faster. Performance improvements
+      * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions.
+      */
+ 
+     function SetDataValues(&$which_dv)
+     {
+         unset($this->data_limits_done);        // Reset this for every new data_set
+         $this->num_data_rows = count($which_dv);
+         $this->total_records = 0;               // Perform some useful calculations.
+         $this->records_per_group = 1;
+         for ($i = 0, $recs = 0; $i < $this->num_data_rows; $i++)
+         {
+             // Copy
+             $this->data[$i] = array_values($which_dv[$i]);   // convert to numerical indices.
+             // Compute some values
+             $recs = count($this->data[$i]);
+             $this->total_records += $recs;
+ 
+             if ($recs > $this->records_per_group)
+                 $this->records_per_group = $recs;
+ 
+             $this->num_recs[$i] = $recs;
+         }
+     }
+ 
+     /* !
+      * Pad styles arrays for later use by plot drawing functions:
+      * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors
+      * in DrawBars(), DrawLines(), etc.
+      */
+ 
+     function PadArrays()
+     {
+         array_pad_array($this->line_widths, $this->records_per_group);
+         array_pad_array($this->line_styles, $this->records_per_group);
+ 
+         array_pad_array($this->data_colors, $this->records_per_group);
+         array_pad_array($this->data_border_colors, $this->records_per_group);
+         array_pad_array($this->error_bar_colors, $this->records_per_group);
+ 
+         $this->SetDataColors();
+         $this->SetDataBorderColors();
+         $this->SetErrorBarColors();
+ 
+         return TRUE;
+     }
+ 
+ //////////////////////////////////////////////////////////
+ ///////////         DATA ANALYSIS, SCALING AND TRANSLATION
+ //////////////////////////////////////////////////////////
+ 
+     /* !
+      * Analizes data and sets up internal maxima and minima
+      * Needed by: CalcMargins(), ...
+      *   Text-Data is different than data-data graphs. For them what
+      *   we have, instead of X values, is # of records equally spaced on data.
+      *   text-data is passed in as $data[] = (title, y1, y2, y3, y4, ...)
+      *   data-data is passed in as $data[] = (title, x, y1, y2, y3, y4, ...)
+      */
+     function FindDataLimits()
+     {
+         // Set some default min and max values before running through the data
+         switch ($this->data_type)
+         {
+             case 'text-data':
+                 $minx = 0;
+                 $maxx = $this->num_data_rows - 1;
+                 $miny = $this->data[0][1];
+                 $maxy = $miny;
+                 break;
+             default:  //Everything else: data-data, etc, take first value
+                 $minx = $this->data[0][1];
+                 $maxx = $minx;
+                 $miny = $this->data[0][2];
+                 $maxy = $miny;
+                 break;
+         }
+ 
+         $mine = 0;  // Maximum value for the -error bar (assume error bars always > 0)
+         $maxe = 0;  // Maximum value for the +error bar (assume error bars always > 0)
+         $maxt = 0;  // Maximum number of characters in text labels
+ 
+         $minminy = $miny;
+         $maxmaxy = $maxy;
+ 
+         if ($this->plot_type == 'stackedbars')
+         {
+             $maxmaxy = $minminy = 0;
+         }
+ 
+         // Process each row of data
+         for ($i = 0; $i < $this->num_data_rows; $i++)
+         {
+             $j = 0;
+             // Extract maximum text label length
+             $val = @ strlen($this->data[$i][$j++]);
+             $maxt = ($val > $maxt) ? $val : $maxt;
+ 
+ 
+             if ($this->plot_type == 'stackedbars')
+             {
+                 $maxy = $miny = 0;
+             }
+ 
+             switch ($this->data_type)
+             {
+                 case 'text-data':           // Data is passed in as (title, y1, y2, y3, ...)
+                 case 'text-data-single':    // This one is for some pie charts
+                     // $numrecs = @ count($this->data[$i]);
+                     $miny = $maxy = (double) $this->data[$i][$j];
+                     for (; $j < $this->num_recs[$i]; $j++)
+                     {
+                         $val = (double) $this->data[$i][$j];
+                         if ($this->plot_type == 'stackedbars')
+                         {
+                             $maxy += abs($val);      // only positive values for the moment
+                         }
+                         else
+                         {
+                             $maxy = ($val > $maxy) ? $val : $maxy;
+                             $miny = ($val < $miny) ? $val : $miny;
+                         }
+                     }
+                     break;
+                 case 'data-data':           // Data is passed in as (title, x, y, y2, y3, ...)
+                     // X value:
+                     $val = (double) $this->data[$i][$j++];
+                     $maxx = ($val > $maxx) ? $val : $maxx;
+                     $minx = ($val < $minx) ? $val : $minx;
+ 
+                     $miny = $maxy = (double) $this->data[$i][$j];
+                     // $numrecs = @ count($this->data[$i]);
+                     for (; $j < $this->num_recs[$i]; $j++)
+                     {
+                         $val = (double) $this->data[$i][$j];
+                         $maxy = ($val > $maxy) ? $val : $maxy;
+                         $miny = ($val < $miny) ? $val : $miny;
+                     }
+                     break;
+                 case 'data-data-error':     // Data is passed in as (title, x, y, err+, err-, y2, err2+, err2-,...)
+                     // X value:
+                     $val = (double) $this->data[$i][$j++];
+                     $maxx = ($val > $maxx) ? $val : $maxx;
+                     $minx = ($val < $minx) ? $val : $minx;
+ 
+                     $miny = $maxy = (double) $this->data[$i][$j];
+                     // $numrecs = @ count($this->data[$i]);
+                     for (; $j < $this->num_recs[$i];)
+                     {
+                         // Y value:
+                         $val = (double) $this->data[$i][$j++];
+                         $maxy = ($val > $maxy) ? $val : $maxy;
+                         $miny = ($val < $miny) ? $val : $miny;
+                         // Error +:
+                         $val = (double) $this->data[$i][$j++];
+                         $maxe = ($val > $maxe) ? $val : $maxe;
+                         // Error -:
+                         $val = (double) $this->data[$i][$j++];
+                         $mine = ($val > $mine) ? $val : $mine;
+                     }
+                     $maxy = $maxy + $maxe;
+                     $miny = $miny - $mine;      // assume error bars are always > 0
+                     break;
+                 default:
+                     $this->PrintError("FindDataLimits(): Unknown data type '$data_type'.");
+                     break;
+             }
+             $this->data[$i][MINY] = $miny;      // This row's min Y, for DrawXDataLine()
+             $this->data[$i][MAXY] = $maxy;      // This row's max Y, for DrawXDataLine()
+ 
+             $minminy = ($miny < $minminy) ? $miny : $minminy;   // global min
+             $maxmaxy = ($maxy > $maxmaxy) ? $maxy : $maxmaxy;   // global max
+         }
+ 
+         $this->min_x = $minx;
+         $this->max_x = $maxx;
+         $this->min_y = $minminy;
+         $this->max_y = $maxmaxy;
+         $this->max_t = $maxt;
+ 
+         $this->data_limits_done = TRUE;
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Calculates image margins on the fly from title positions and sizes,
+      * and tick labels positions and sizes.
+      *
+      * FIXME: fix x_data_label_pos behaviour. Now we are leaving room for it AND x_tick_label_pos
+      *        maybe it shouldn't be so...
+      *
+      * FIXME: y_data_label_pos is not yet used...
+      *
+      * TODO: add x_tick_label_width and y_tick_label_height and use them to calculate
+      *       max_x_labels and max_y_labels, to be used by drawing functions to avoid overlapping.
+      */
+ 
+     function CalcMargins()
+     {
+         // Temporary variables for label size calculation
+         $xlab = $this->FormatLabel('x', $this->max_x);
+         $ylab = $this->FormatLabel('y', $this->max_y);
+ 
+         // dirty fix:
+         // max_t is the maximum data label length (from column 0 of each data row).
+         if ($this->max_t > strlen($xlab))
+             $xlab = sprintf("%{$this->max_t}s", "_");
+ 
+         //////// Calculate maximum X/Y axis label height and width:
+         // TTFonts:
+         if ($this->use_ttf)
+         {
+             // Maximum X axis label height
+             $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle,
+                             $this->x_label_font['font'], $xlab);
+             $this->x_tick_label_height = $size[1];
+ 
+             // Maximum Y axis label width
+             $size = $this->TTFBBoxSize($this->y_label_font['size'], $this->y_label_angle,
+                             $this->y_label_font['font'], $ylab);
+             $this->y_tick_label_width = $size[0];
+         }
+         // Fixed fonts:
+         else
+         {
+             // Maximum X axis label height
+             if ($this->x_label_angle == 90)
+                 $this->x_tick_label_height = strlen($xlab) * $this->x_label_font['width'];
+             else
+                 $this->x_tick_label_height = $this->x_label_font['height'];
+ 
+             // Maximum Y axis label width
+             $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
+         }
+ 
+ 
+         ///////// Calculate margins:
+         // Upper title, ticks and tick labels, and data labels:
+         $this->y_top_margin = $this->title_height + $this->safe_margin * 2;
+ 
+         if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both')
+             $this->y_top_margin += $this->x_title_height + $this->safe_margin;
+ 
+         if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both')
+             $this->y_top_margin += $this->x_tick_label_height;
+ 
+         if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both')
+             $this->y_top_margin += $this->x_tick_length * 2;
+ 
+         if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
+             $this->y_top_margin += $this->x_tick_label_height;
+ 
+         // Lower title, ticks and tick labels, and data labels:
+         $this->y_bot_margin = $this->safe_margin * 2;
+ 
+         if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both')
+             $this->y_bot_margin += $this->x_title_height;
+ 
+         if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both')
+             $this->y_bot_margin += $this->x_tick_length * 2;
+ 
+         if ($this->x_tick_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
+             $this->y_bot_margin += $this->x_tick_length * 2;
+ 
+         if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both')
+             $this->y_bot_margin += $this->x_tick_label_height;
+ 
+         if ($this->x_tick_label_pos == 'xaxis' && ($this->x_axis_position == '' || $this->x_axis_position == 0))
+             $this->y_bot_margin += $this->x_tick_label_height;
+ 
+         if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
+             $this->y_bot_margin += $this->x_tick_label_height;
+ 
+         // Left title, ticks and tick labels:
+         $this->x_left_margin = $this->safe_margin * 2;
+ 
+         if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both')
+             $this->x_left_margin += $this->y_title_width + $this->safe_margin;
+ 
+         if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both')
+             $this->x_left_margin += $this->y_tick_label_width;
+ 
+         if ($this->y_tick_pos == 'plotleft' || $this->y_tick_pos == 'both')
+             $this->x_left_margin += $this->y_tick_length * 2;
+ 
+         // Right title, ticks and tick labels:
+         $this->x_right_margin = $this->safe_margin * 2;
+ 
+         if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both')
+             $this->x_right_margin += $this->y_title_width + $this->safe_margin;
+ 
+         if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both')
+             $this->x_right_margin += $this->y_tick_label_width;
+ 
+         if ($this->y_tick_pos == 'plotright' || $this->y_tick_pos == 'both')
+             $this->x_right_margin += $this->y_tick_length * 2;
+ 
+ 
+         $this->x_tot_margin = $this->x_left_margin + $this->x_right_margin;
+         $this->y_tot_margin = $this->y_top_margin + $this->y_bot_margin;
+ 
+         return;
+     }
+ 
+     /* !
+      * Set the margins in pixels (left, right, top, bottom)
+      */
+ 
+     function SetMarginsPixels($which_lm, $which_rm, $which_tm, $which_bm)
+     {
+ 
+         $this->x_left_margin = $which_lm;
+         $this->x_right_margin = $which_rm;
+         $this->x_tot_margin = $which_lm + $which_rm;
+ 
+         $this->y_top_margin = $which_tm;
+         $this->y_bot_margin = $which_bm;
+         $this->y_tot_margin = $which_tm + $which_bm;
+ 
+         $this->SetPlotAreaPixels();
+ 
+         return;
+     }
+ 
+     /* !
+      * Sets the limits for the plot area. If no arguments are supplied, uses
+      * values calculated from CalcMargins();
+      * Like in GD, (0,0) is upper left
+      *
+      * This resets the scale if SetPlotAreaWorld() was already called
+      */
+ 
+     function SetPlotAreaPixels($x1=NULL, $y1=NULL, $x2=NULL, $y2=NULL)
+     {
+         if ($x2 && $y2)
+         {
+             $this->plot_area = array($x1, $y1, $x2, $y2);
+         }
+         else
+         {
+             if (!isset($this->x_tot_margin))
+                 $this->CalcMargins();
+ 
+             $this->plot_area = array($this->x_left_margin, $this->y_top_margin,
+                 $this->image_width - $this->x_right_margin,
+                 $this->image_height - $this->y_bot_margin);
+         }
+         $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
+         $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
+ 
+         // Reset the scale with the new plot area.
+         if (isset($this->plot_max_x))
+             $this->CalcTranslation();
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Sets minimum and maximum x and y values in the plot using FindDataLimits()
+      * or from the supplied parameters, if any.
+      *
+      * This resets the scale if SetPlotAreaPixels() was already called
+      */
+ 
+     function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL)
+     {
+         if (!isset($this->data_limits_done))
+         { // For automatic setting of data we need data limits
+             $this->FindDataLimits();
+         }
+ 
+         if ($xmin === NULL || $xmin === '')
+         {
+             if ($this->data_type == 'text-data')  // Valid for data without X values only.
+                 $xmin = 0;
+             else
+                 $xmin = $this->min_x;
+         }
+         if ($xmax === NULL || $xmax === '')
+         {
+             if ($this->data_type == 'text-data')  // Valid for data without X values only.
+                 $xmax = $this->max_x + 1;
+             else
+                 $xmax = $this->max_x;
+         }
+ 
+         // Leave room above and below the highest and lowest data points.
+ 
+         if ($ymin === NULL || $ymin === '')
+         {
+             if ($this->min_y < 0)
+                 $ymin = ceil($this->min_y * 1.1);
+             else
+                 $ymin = floor($this->min_y * 0.9);
+         }
+         if ($ymax === NULL || $ymax === '')
+         {
+             if ($this->max_y < 0)
+                 $ymax = floor($this->max_y * 0.9);
+             else
+                 $ymax = ceil($this->max_y * 1.1);
+         }
+ 
+         // Error checking
+ 
+         if ($ymin == $ymax)     // Minimum height
+             $ymax += 1;
+ 
+         if ($this->yscale_type == 'log')
+         {
+             if ($ymin <= 0)
+             {
+                 $ymin = 1;
+             }
+             if ($ymax <= 0)
+             {
+                 $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0');
+                 return FALSE;
+             }
+         }
+ 
+         if ($ymax <= $ymin)
+         {
+             $this->DrawError('SetPlotAreaWorld(): Error in data - max not greater than min');
+             return FALSE;
+         }
+ 
+ 
+         // Reset (if it was already set) the scale with the new maxs and mins
+ 
+         $this->plot_min_x = $xmin;
+         $this->plot_max_x = $xmax;
+         $this->plot_min_y = $ymin;
+         $this->plot_max_y = $ymax;
+ 
+         if (isset($this->plot_area_width))
+         {
+             $this->CalcTranslation();
+         }
+ 
+         return TRUE;
+     }
+ 
+ //function SetPlotAreaWorld
+ 
+ 
+     /* !
+      * For bar plots, which have equally spaced x variables.
+      */
+ 
+     function CalcBarWidths()
+     {
+         $group_width = ($this->plot_area[2] - $this->plot_area[0]) /
+                 $this->num_data_rows * $this->group_frac_width;
+         if ($this->plot_type == 'bars')
+         {
+             $this->record_bar_width = $group_width / $this->records_per_group;
+         }
+         else if ($this->plot_type == 'stackedbars')
+         {
+             $this->record_bar_width = $group_width;
+         }
+         $this->data_group_space = $group_width / 2;
+         return TRUE;
+     }
+ 
+     /* !
+      * Calculates scaling stuff...
+      */
+ 
+     function CalcTranslation()
+     {
+         if ($this->plot_max_x - $this->plot_min_x == 0)
+         { // Check for div by 0
+             $this->xscale = 0;
+         }
+         else
+         {
+             if ($this->xscale_type == 'log')
+             {
+                 $this->xscale = ($this->plot_area_width) / (log10($this->plot_max_x) - log10($this->plot_min_x));
+             }
+             else
+             {
+                 $this->xscale = ($this->plot_area_width) / ($this->plot_max_x - $this->plot_min_x);
+             }
+         }
+ 
+         if ($this->plot_max_y - $this->plot_min_y == 0)
+         { // Check for div by 0
+             $this->yscale = 0;
+         }
+         else
+         {
+             if ($this->yscale_type == 'log')
+             {
+                 $this->yscale = ($this->plot_area_height) / (log10($this->plot_max_y) - log10($this->plot_min_y));
+             }
+             else
+             {
+                 $this->yscale = ($this->plot_area_height) / ($this->plot_max_y - $this->plot_min_y);
+             }
+         }
+         // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively
+         if ($this->xscale_type == 'log')
+         {
+             $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) );
+         }
+         else
+         {
+             $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x);
+         }
+         if ($this->yscale_type == 'log')
+         {
+             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y));
+         }
+         else
+         {
+             $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y);
+         }
+ 
+         $this->scale_is_set = TRUE;
+ 
+         /*         * ************ FIXME?? ************ */
+         // There should be a better place for this.
+         // User provided y axis position?
+         if ($this->y_axis_position != '')
+         {
+             // Make sure we draw our axis inside the plot
+             $this->y_axis_position = ($this->y_axis_position < $this->plot_min_x) ? $this->plot_min_x : $this->y_axis_position;
+             $this->y_axis_position = ($this->y_axis_position > $this->plot_max_x) ? $this->plot_max_x : $this->y_axis_position;
+             $this->y_axis_x_pixels = $this->xtr($this->y_axis_position);
+         }
+         else
+         {
+             // Default to left axis
+             $this->y_axis_x_pixels = $this->xtr($this->plot_min_x);
+         }
+         // User provided x axis position?
+         if ($this->x_axis_position != '')
+         {
+             // Make sure we draw our axis inside the plot
+             $this->x_axis_position = ($this->x_axis_position < $this->plot_min_y) ? $this->plot_min_y : $this->x_axis_position;
+             $this->x_axis_position = ($this->x_axis_position > $this->plot_max_y) ? $this->plot_max_y : $this->x_axis_position;
+             $this->x_axis_y_pixels = $this->ytr($this->x_axis_position);
+         }
+         else
+         {
+             if ($this->yscale_type == 'log')
+                 $this->x_axis_y_pixels = $this->ytr(1);
+             else
+             // Default to axis at 0 or plot_min_y (should be 0 anyway, from SetPlotAreaWorld())
+                 $this->x_axis_y_pixels = ($this->plot_min_y <= 0) && (0 <= $this->plot_max_y) ? $this->ytr(0) : $this->ytr($this->plot_min_y);
+         }
+     }
+ 
+ // function CalcTranslation()
+ 
+ 
+     /* !
+      * Translate X world coordinate into pixel coordinate
+      * Needs values calculated by _CalcTranslation()
+      */
+ 
+     function xtr($x_world)
+     {
+         //$x_pixels =  $this->x_left_margin + ($this->image_width - $this->x_tot_margin)*
+         //      (($x_world - $this->plot_min_x) / ($this->plot_max_x - $this->plot_min_x)) ;
+         //which with a little bit of math reduces to ...
+         if ($this->xscale_type == 'log')
+         {
+             $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale;
+         }
+         else
+         {
+             $x_pixels = $this->plot_origin_x + $x_world * $this->xscale;
+         }
+         return round($x_pixels);
+     }
+ 
+     /* !
+      * Translate Y world coordinate into pixel coordinate.
+      * Needs values calculated by _CalcTranslation()
+      */
+ 
+     function ytr($y_world)
+     {
+         if ($this->yscale_type == 'log')
+         {
+             //minus because GD defines y = 0 at top. doh!
+             $y_pixels = $this->plot_origin_y - log10($y_world) * $this->yscale;
+         }
+         else
+         {
+             $y_pixels = $this->plot_origin_y - $y_world * $this->yscale;
+         }
+         return round($y_pixels);
+     }
+ 
+     /* !
+      * Formats a tick or data label.
+      *
+      * \note Time formatting suggested by Marlin Viss
+      */
+ 
+     function FormatLabel($which_pos, $which_lab)
+     {
+         switch ($which_pos)
+         {
+             case 'x':
+             case 'plotx':
+                 switch ($this->x_label_type)
+                 {
+                     case 'title':
+                         $lab = @ $this->data[$which_lab][0];
+                         break;
+                     case 'data':
+                         $lab = number_format($which_lab, $this->x_precision, '.', ',') . $this->data_units_text;
+                         break;
+                     case 'time':
+                         $lab = strftime($this->x_time_format, $which_lab);
+                         break;
+                     default:
+                         // Unchanged from whatever format it is passed in
+                         $lab = $which_lab;
+                         break;
+                 }
+                 break;
+             case 'y':
+             case 'ploty':
+                 switch ($this->y_label_type)
+                 {
+                     case 'data':
+                         $lab = number_format($which_lab, $this->y_precision, '.', ',') . $this->data_units_text;
+                         break;
+                     case 'time':
+                         $lab = strftime($this->y_time_format, $which_lab);
+                         break;
+                     default:
+                         // Unchanged from whatever format it is passed in
+                         $lab = $which_lab;
+                         break;
+                 }
+                 break;
+             default:
+                 $this->PrintError("FormatLabel(): Unknown label type $which_type");
+                 return NULL;
+         }
+ 
+         return $lab;
+     }
+ 
+ //function FormatLabel
+ /////////////////////////////////////////////
+ ///////////////                         TICKS
+ /////////////////////////////////////////////
+ 
+     /* !
+      * Use either this or SetNumXTicks() to set where to place x tick marks
+      */
+     function SetXTickIncrement($which_ti=NULL)
+     {
+         if ($which_ti)
+         {
+             $this->x_tick_inc = $which_ti;  //world coordinates
+         }
+         else
+         {
+             if (!isset($this->data_limits_done))
+             {
+                 $this->FindDataLimits();  //Get maxima and minima for scaling
+             }
+             $this->x_tick_inc = ($this->plot_max_x - $this->plot_min_x ) / 10;
+         }
+         $this->num_x_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
+         return TRUE;
+     }
+ 
+     /* !
+      * Use either this or SetNumYTicks() to set where to place y tick marks
+      */
+ 
+     function SetYTickIncrement($which_ti=NULL)
+     {
+         if ($which_ti)
+         {
+             $this->y_tick_inc = $which_ti;  //world coordinates
+         }
+         else
+         {
+             if (!isset($this->data_limits_done))
+             {
+                 $this->FindDataLimits();  //Get maxima and minima for scaling
+             }
+             if (!isset($this->plot_max_y))
+                 $this->SetPlotAreaWorld();
+ 
+             $this->y_tick_inc = ($this->plot_max_y - $this->plot_min_y ) / 10;
+         }
+         $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_inc, not both
+         return TRUE;
+     }
+ 
+     function SetNumXTicks($which_nt)
+     {
+         $this->num_x_ticks = $which_nt;
+         $this->x_tick_inc = '';  //either use num_x_ticks or x_tick_inc, not both
+         return TRUE;
+     }
+ 
+     function SetNumYTicks($which_nt)
+     {
+         $this->num_y_ticks = $which_nt;
+         $this->y_tick_inc = '';  //either use num_y_ticks or y_tick_inc, not both
+         return TRUE;
+     }
+ 
+     /* !
+      *
+      */
+ 
+     function SetYTickPos($which_tp)
+     {
+         $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     /* !
+      *
+      */
+ 
+     function SetXTickPos($which_tp)
+     {
+         $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__);
+         return TRUE;
+     }
+ 
+     /* !
+      * \param skip bool
+      */
+ 
+     function SetSkipTopTick($skip)
+     {
+         $this->skip_top_tick = (bool) $skip;
+         return TRUE;
+     }
+ 
+     /* !
+      * \param skip bool
+      */
+ 
+     function SetSkipBottomTick($skip)
+     {
+         $this->skip_bottom_tick = (bool) $skip;
+         return TRUE;
+     }
+ 
+     /* !
+      * \param skip bool
+      */
+ 
+     function SetSkipLeftTick($skip)
+     {
+         $this->skip_left_tick = (bool) $skip;
+         return TRUE;
+     }
+ 
+     /* !
+      * \param skip bool
+      */
+ 
+     function SetSkipRightTick($skip)
+     {
+         $this->skip_right_tick = (bool) $skip;
+         return TRUE;
+     }
+ 
+     function SetXTickLength($which_xln)
+     {
+         $this->x_tick_length = $which_xln;
+         return TRUE;
+     }
+ 
+     function SetYTickLength($which_yln)
+     {
+         $this->y_tick_length = $which_yln;
+         return TRUE;
+     }
+ 
+     function SetXTickCrossing($which_xc)
+     {
+         $this->x_tick_cross = $which_xc;
+         return TRUE;
+     }
+ 
+     function SetYTickCrossing($which_yc)
+     {
+         $this->y_tick_cross = $which_yc;
+         return TRUE;
+     }
+ 
+ /////////////////////////////////////////////
+ ////////////////////          GENERIC DRAWING
+ /////////////////////////////////////////////
+ 
+     /* !
+      * Fills the background.
+      */
+     function DrawBackground()
+     {
+         // Don't draw this twice if drawing two plots on one image
+         if (!$this->background_done)
+         {
+             if (isset($this->bgimg))
+             {    // If bgimg is defined, use it
+                 $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode);
+             }
+             else
+             {                        // Else use solid color
+                 ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height,
+                         $this->ndx_bg_color);
+             }
+             $this->background_done = TRUE;
+             return TRUE;        // Done
+         }
+         return FALSE;           // Nothing done
+     }
+ 
+     /* !
+      * Fills the plot area background.
+      */
+ 
+     function DrawPlotAreaBackground()
+     {
+         if (isset($this->plotbgimg))
+         {
+             $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1],
+                     $this->plot_area_width, $this->plot_area_height, $this->plotbgmode);
+         }
+         else
+         {
+             if ($this->draw_plot_area_background)
+             {
+                 ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1],
+                         $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color);
+             }
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Tiles an image at some given coordinates.
+      *
+      * \param $file   string Filename of the picture to be used as tile.
+      * \param $xorig  int    X coordinate of the plot where the tile is to begin.
+      * \param $yorig  int    Y coordinate of the plot where the tile is to begin.
+      * \param $width  int    Width of the area to be tiled.
+      * \param $height int    Height of the area to be tiled.
+      * \param $mode   string One of 'centeredtile', 'tile', 'scale'.
+      */
+ 
+     function tile_img($file, $xorig, $yorig, $width, $height, $mode)
+     {
+         $size = getimagesize($file);
+         $input_format = $size[2];
+ 
+         switch ($input_format)
+         {
+             case 1:
+                 $im = @ imagecreatefromGIF($file);
+                 if (!$im)
+                 {
+                     $this->PrintError("tile_img:() Unable to open $file as a GIF.");
+                     return FALSE;
+                 }
+                 break;
+             case 2:
+                 $im = @ imagecreatefromJPEG($file);
+                 if (!$im)
+                 {
+                     $this->PrintError("tile_img(): Unable to open $file as a JPG.");
+                     return FALSE;
+                 }
+                 break;
+             case 3:
+                 $im = @ imagecreatefromPNG($file);
+                 if (!$im)
+                 {
+                     $this->PrintError("tile_img(): Unable to open $file as a PNG.");
+                     return FALSE;
+                 }
+                 break;
+             default:
+                 $this->PrintError('tile_img(): Please select a gif, jpg, or png image.');
+                 return FALSE;
+                 break;
+         }
+ 
+ 
+         if ($mode == 'scale')
+         {
+             imagecopyresized($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, $size[0], $size[1]);
+             return TRUE;
+         }
+         else if ($mode == 'centeredtile')
+         {
+             $x0 = - floor($size[0] / 2);   // Make the tile look better
+             $y0 = - floor($size[1] / 2);
+         }
+         else if ($mode = 'tile')
+         {
+             $x0 = 0;
+             $y0 = 0;
+         }
+ 
+         // Actually draw the tile
+         // But first on a temporal image.
+         $tmp = ImageCreate($width, $height);
+         if (!$tmp)
+             $this->PrintError('tile_img(): Could not create image resource.');
+ 
+         for ($x = $x0; $x < $width; $x += $size[0])
+             for ($y = $y0; $y < $height; $y += $size[1])
+                 imagecopy($tmp, $im, $x, $y, 0, 0, $size[0], $size[1]);
+ 
+         // Copy the temporal image onto the final one.
+         imagecopy($this->img, $tmp, $xorig, $yorig, 0, 0, $width, $height);
+ 
+         // Free resources
+         imagedestroy($tmp);
+         imagedestroy($im);
+ 
+         return TRUE;
+     }
+ 
+ // function tile_img
+ 
+ 
+     /* !
+      * Draws a border around the final image.
+      */
+ 
+     function DrawImageBorder()
+     {
+         switch ($this->image_border_type)
+         {
+             case 'raised':
+                 ImageLine($this->img, 0, 0, $this->image_width - 1, 0, $this->ndx_i_border);
+                 ImageLine($this->img, 1, 1, $this->image_width - 2, 1, $this->ndx_i_border);
+                 ImageLine($this->img, 0, 0, 0, $this->image_height - 1, $this->ndx_i_border);
+                 ImageLine($this->img, 1, 1, 1, $this->image_height - 2, $this->ndx_i_border);
+                 ImageLine($this->img, $this->image_width - 1, 0, $this->image_width - 1,
+                         $this->image_height - 1, $this->ndx_i_border_dark);
+                 ImageLine($this->img, 0, $this->image_height - 1, $this->image_width - 1,
+                         $this->image_height - 1, $this->ndx_i_border_dark);
+                 ImageLine($this->img, $this->image_width - 2, 1, $this->image_width - 2,
+                         $this->image_height - 2, $this->ndx_i_border_dark);
+                 ImageLine($this->img, 1, $this->image_height - 2, $this->image_width - 2,
+                         $this->image_height - 2, $this->ndx_i_border_dark);
+                 break;
+             case 'plain':
+                 ImageLine($this->img, 0, 0, $this->image_width, 0, $this->ndx_i_border_dark);
+                 ImageLine($this->img, $this->image_width - 1, 0, $this->image_width - 1,
+                         $this->image_height, $this->ndx_i_border_dark);
+                 ImageLine($this->img, $this->image_width - 1, $this->image_height - 1, 0, $this->image_height - 1,
+                         $this->ndx_i_border_dark);
+                 ImageLine($this->img, 0, 0, 0, $this->image_height, $this->ndx_i_border_dark);
+                 break;
+             case 'none':
+                 break;
+             default:
+                 $this->DrawError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'");
+                 return FALSE;
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Adds the title to the graph.
+      */
+ 
+     function DrawTitle()
+     {
+         // Center of the plot area
+         //$xpos = ($this->plot_area[0] + $this->plot_area_width )/ 2;
+         // Center of the image:
+         $xpos = $this->image_width / 2;
+ 
+         // Place it at almost at the top
+         $ypos = $this->safe_margin;
+ 
+         $this->DrawText($this->title_font, $this->title_angle, $xpos, $ypos,
+                 $this->ndx_title_color, $this->title_txt, 'center', 'bottom');
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Draws the X-Axis Title
+      */
+ 
+     function DrawXTitle()
+     {
+         if ($this->x_title_pos == 'none')
+             return;
+ 
+         // Center of the plot
+         $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2;
+ 
+         // Upper title
+         if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both')
+         {
+             $ypos = $this->safe_margin + $this->title_height + $this->safe_margin;
+             $this->DrawText($this->x_title_font, $this->x_title_angle,
+                     $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
+         }
+         // Lower title
+         if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both')
+         {
+             $ypos = $this->image_height - $this->x_title_height - $this->safe_margin;
+             $this->DrawText($this->x_title_font, $this->x_title_angle,
+                     $xpos, $ypos, $this->ndx_title_color, $this->x_title_txt, 'center');
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Draws the Y-Axis Title
+      */
+ 
+     function DrawYTitle()
+     {
+         if ($this->y_title_pos == 'none')
+             return;
+ 
+         // Center the title vertically to the plot
+         $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2;
+ 
+         if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both')
+         {
+             $xpos = $this->safe_margin;
+             $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
+                     $this->y_title_txt, 'left', 'center');
+         }
+         if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both')
+         {
+             $xpos = $this->image_width - $this->safe_margin - $this->y_title_width - $this->safe_margin;
+             $this->DrawText($this->y_title_font, 90, $xpos, $ypos, $this->ndx_title_color,
+                     $this->y_title_txt, 'left', 'center');
+         }
+ 
+         return TRUE;
+     }
+ 
+     /*
+      * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis()
+      */
+ 
+     function DrawYAxis()
+     {
+         // Draw ticks, labels and grid, if any
+         $this->DrawYTicks();
+ 
+         // Draw Y axis at X = y_axis_x_pixels
+         ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1],
+                 $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color);
+ 
+         return TRUE;
+     }
+ 
+     /*
+      *
+      */
+ 
+     function DrawXAxis()
+     {
+         // Draw ticks, labels and grid
+         $this->DrawXTicks();
+ 
+         /* This tick and label tend to overlap with regular Y Axis labels,
+          * as Mike Pullen pointed out.
+          *
+           //Draw Tick and Label for X axis
+           if (! $this->skip_bottom_tick) {
+           $ylab =$this->FormatLabel('y', $this->x_axis_position);
+           $this->DrawYTick($ylab, $this->x_axis_y_pixels);
+           }
+          */
+         //Draw X Axis at Y = x_axis_y_pixels
+         ImageLine($this->img, $this->plot_area[0] + 1, $this->x_axis_y_pixels,
+                 $this->plot_area[2] - 1, $this->x_axis_y_pixels, $this->ndx_grid_color);
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * Draw Just one Tick, called from DrawYTicks() and DrawXAxis()
+      * TODO? Move this inside DrawYTicks() and Modify DrawXAxis() ?
+      */
+ 
+     function DrawYTick($which_ylab, $which_ypix)
+     {
+         // Ticks on Y axis
+         if ($this->y_tick_pos == 'yaxis')
+         {
+             ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix,
+                     $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix,
+                     $this->ndx_tick_color);
+         }
+ 
+         // Labels on Y axis
+         if ($this->y_tick_label_pos == 'yaxis')
+         {
+             $this->DrawText($this->y_label_font, $this->y_label_angle,
+                     $this->y_axis_x_pixels - $this->y_tick_length * 1.5, $which_ypix,
+                     $this->ndx_text_color, $which_ylab, 'right', 'center');
+         }
+ 
+         // Ticks to the left of the Plot Area
+         if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both'))
+         {
+             ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length,
+                     $which_ypix, $this->plot_area[0] + $this->y_tick_cross,
+                     $which_ypix, $this->ndx_tick_color);
+         }
+ 
+         // Ticks to the right of the Plot Area
+         if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both'))
+         {
+             ImageLine($this->img, ($this->plot_area[2] + $this->y_tick_length),
+                     $which_ypix, $this->plot_area[2] - $this->y_tick_cross,
+                     $which_ypix, $this->ndx_tick_color);
+         }
+ 
+         // Labels to the left of the plot area
+         if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both')
+         {
+             $this->DrawText($this->y_label_font, $this->y_label_angle,
+                     $this->plot_area[0] - $this->y_tick_length * 1.5, $which_ypix,
+                     $this->ndx_text_color, $which_ylab, 'right', 'center');
+         }
+         // Labels to the right of the plot area
+         if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both')
+         {
+             $this->DrawText($this->y_label_font, $this->y_label_angle,
+                     $this->plot_area[2] + $this->y_tick_length * 1.5, $which_ypix,
+                     $this->ndx_text_color, $which_ylab, 'left', 'center');
+         }
+     }
+ 
+ // Function DrawYTick()
+ 
+ 
+     /* !
+      * Draws Grid, Ticks and Tick Labels along Y-Axis
+      * Ticks and ticklabels can be left of plot only, right of plot only,
+      * both on the left and right of plot, or crossing a user defined Y-axis
+      * TODO: marks at whole numbers (-10, 10, 20, 30 ...) no matter where the plot begins (-3, 4.7, etc.)
+      */
+ 
+     function DrawYTicks()
+     {
+         // Sets the line style for IMG_COLOR_STYLED lines (grid)
+         if ($this->dashed_grid)
+         {
+             $this->SetDashedStyle($this->ndx_light_grid_color);
+             $style = IMG_COLOR_STYLED;
+         }
+         else
+         {
+             $style = $this->ndx_light_grid_color;
+         }
+ 
+         // maxy is always > miny so delta_y is always positive
+         if ($this->y_tick_inc)
+         {
+             $delta_y = $this->y_tick_inc;
+         }
+         elseif ($this->num_y_ticks)
+         {
+             $delta_y = ($this->plot_max_y - $this->plot_min_y) / $this->num_y_ticks;
+         }
+         else
+         {
+             $delta_y = ($this->plot_max_y - $this->plot_min_y) / 10;
+         }
+ 
+         // NOTE: When working with floats, because of approximations when adding $delta_y,
+         // $y_tmp never equals $y_end  at the for loop, so one spurious line would  get drawn where
+         // not for the substraction to $y_end here.
+         $y_tmp = (double) $this->plot_min_y;
+         $y_end = (double) $this->plot_max_y - ($delta_y / 2);
+ 
+         if ($this->skip_bottom_tick)
+             $y_tmp += $delta_y;
+ 
+         if ($this->skip_top_tick)
+             $y_end -= $delta_y;
+ 
+         for (; $y_tmp < $y_end; $y_tmp += $delta_y)
+         {
+             $ylab = $this->FormatLabel('y', $y_tmp);
+             $y_pixels = $this->ytr($y_tmp);
+ 
+             // Horizontal grid line
+             if ($this->draw_y_grid)
+             {
+                 ImageLine($this->img, $this->plot_area[0] + 1, $y_pixels, $this->plot_area[2] - 1, $y_pixels, $style);
+             }
+ 
+             // Draw ticks
+             $this->DrawYTick($ylab, $y_pixels);
+         }
+         return TRUE;
+     }
+ 
+ // function DrawYTicks
+ 
+ 
+     /* !
+      * Draws Grid, Ticks and Tick Labels along X-Axis
+      * Ticks and tick labels can be down of plot only, up of plot only,
+      * both on up and down of plot, or crossing a user defined X-axis
+      *
+      * \note Original vertical code submitted by Marlin Viss
+      */
+ 
+     function DrawXTicks()
+     {
+         // Sets the line style for IMG_COLOR_STYLED lines (grid)
+         if ($this->dashed_grid)
+         {
+             $this->SetDashedStyle($this->ndx_light_grid_color);
+             $style = IMG_COLOR_STYLED;
+         }
+         else
+         {
+             $style = $this->ndx_light_grid_color;
+         }
+ 
+         // Calculate x increment between ticks
+         if ($this->x_tick_inc)
+         {
+             $delta_x = $this->x_tick_inc;
+         }
+         elseif ($this->num_x_ticks)
+         {
+             $delta_x = ($this->plot_max_x - $this->plot_min_x) / $this->num_x_ticks;
+         }
+         else
+         {
+             $delta_x = ($this->plot_max_x - $this->plot_min_x) / 10;
+         }
+ 
+         // NOTE: When working with decimals, because of approximations when adding $delta_x,
+         // $x_tmp never equals $x_end  at the for loop, so one spurious line would  get drawn where
+         // not for the substraction to $x_end here.
+         $x_tmp = (double) $this->plot_min_x;
+         $x_end = (double) $this->plot_max_x - ($delta_x / 2);
+ 
+         // Should the leftmost tick be drawn?
+         if ($this->skip_left_tick)
+             $x_tmp += $delta_x;
+ 
+         // And the rightmost?
+         if (!$this->skip_right_tick)
+             $x_end += $delta_x;
+ 
+         for (; $x_tmp < $x_end; $x_tmp += $delta_x)
+         {
+             $xlab = $this->FormatLabel('x', $x_tmp);
+             $x_pixels = $this->xtr($x_tmp);
+ 
+             // Vertical grid lines
+             if ($this->draw_x_grid)
+             {
+                 ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style);
+             }
+ 
+             // Tick on X Axis
+             if ($this->x_tick_pos == 'xaxis')
+             {
+ 
+                 ImageLine($this->img, $x_pixels, $this->x_axis_y_pixels - $this->x_tick_cross,
+                         $x_pixels, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color);
+             }
+ 
+             // Label on X axis
+             if ($this->x_tick_label_pos == 'xaxis')
+             {
+                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
+                         $this->x_axis_y_pixels + $this->x_tick_length * 1.5, $this->ndx_text_color,
+                         $xlab, 'center', 'bottom');
+             }
+ 
+             // Top of the plot area tick
+             if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both')
+             {
+                 ImageLine($this->img, $x_pixels, $this->plot_area[1] - $this->x_tick_length,
+                         $x_pixels, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color);
+             }
+             // Bottom of the plot area tick
+             if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both')
+             {
+                 ImageLine($this->img, $x_pixels, $this->plot_area[3] + $this->x_tick_length,
+                         $x_pixels, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color);
+             }
+ 
+             // Top of the plot area tick label
+             if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both')
+             {
+                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
+                         $this->plot_area[1] - $this->x_tick_length * 1.5, $this->ndx_text_color,
+                         $xlab, 'center', 'top');
+             }
+ 
+             // Bottom of the plot area tick label
+             if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both')
+             {
+                 $this->DrawText($this->x_label_font, $this->x_label_angle, $x_pixels,
+                         $this->plot_area[3] + $this->x_tick_length * 1.5, $this->ndx_text_color,
+                         $xlab, 'center', 'bottom');
+             }
+         }
+         return;
+     }
+ 
+ // function DrawXTicks
+ 
+ 
+     /* !
+      *
+      */
+ 
+     function DrawPlotBorder()
+     {
+         switch ($this->plot_border_type)
+         {
+             case 'left':    // for past compatibility
+             case 'plotleft':
+                 ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
+                         $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
+                 break;
+             case 'right':
+             case 'plotright':
+                 ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
+                         $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
+                 break;
+             case 'both':
+             case 'sides':
+                 ImageLine($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
+                         $this->plot_area[0], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
+                 ImageLine($this->img, $this->plot_area[2], $this->ytr($this->plot_min_y),
+                         $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
+                 break;
+             case 'none':
+                 //Draw No Border
+                 break;
+             case 'full':
+             default:
+                 ImageRectangle($this->img, $this->plot_area[0], $this->ytr($this->plot_min_y),
+                         $this->plot_area[2], $this->ytr($this->plot_max_y), $this->ndx_grid_color);
+                 break;
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Draws the data label associated with a point in the plot.
+      * This is different from x_labels drawn by DrawXTicks() and care
+      * should be taken not to draw both, as they'd probably overlap.
+      * Calling of this function in DrawLines(), etc is decided after x_data_label_pos value.
+      * Leave the last parameter out, to avoid the drawing of vertical lines, no matter
+      * what the setting is (for plots that need it, like DrawSquared())
+      */
+ 
+     function DrawXDataLabel($xlab, $xpos, $row=FALSE)
+     {
+         // FIXME!! not working...
+         if (($this->_x_label_cnt++ % $this->x_label_inc) != 0)
+             return;
+ 
+         $xlab = $this->FormatLabel('x', $xlab);
+ 
+         // Labels below the plot area
+         if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both')
+             $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos,
+                     $this->plot_area[3] + $this->x_tick_length,
+                     $this->ndx_text_color, $xlab, 'center', 'bottom');
+ 
+         // Labels above the plot area
+         if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both')
+             $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos,
+                     $this->plot_area[1] - $this->x_tick_length,
+                     $this->ndx_text_color, $xlab, 'center', 'top');
+ 
+         if ($row && $this->draw_x_data_label_lines)
+             $this->DrawXDataLine($xpos, $row);
+     }
+ 
+     /* !
+      * Draws Vertical lines from data points up and down.
+      * Which lines are drawn depends on the value of x_data_label_pos,
+      * and whether this is at all done or not, on draw_x_data_label_lines
+      *
+      * \param xpos int position in pixels of the line.
+      * \param row int index of the data row being drawn.
+      */
+ 
+     function DrawXDataLine($xpos, $row)
+     {
+         // Sets the line style for IMG_COLOR_STYLED lines (grid)
+         if ($this->dashed_grid)
+         {
+             $this->SetDashedStyle($this->ndx_light_grid_color);
+             $style = IMG_COLOR_STYLED;
+         }
+         else
+         {
+             $style = $this->ndx_light_grid_color;
+         }
+ 
+         // Lines from the bottom up
+         if ($this->x_data_label_pos == 'both')
+         {
+             ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style);
+         }
+         // Lines coming from the bottom of the plot
+         else if ($this->x_data_label_pos == 'plotdown')
+         {
+             // See FindDataLimits() to see why 'MAXY' index.
+             $ypos = $this->ytr($this->data[$row][MAXY]);
+             ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style);
+         }
+         // Lines coming from the top of the plot
+         else if ($this->x_data_label_pos == 'plotup')
+         {
+             // See FindDataLimits() to see why 'MINY' index.
+             $ypos = $this->ytr($this->data[$row][MINY]);
+             ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style);
+         }
+     }
+ 
+     /*
+       function DrawPlotLabel($xlab, $xpos, $ypos)
+       {
+       $this->DrawText($this->x_label_font, $this->x_label_angle, $xpos, $this
+      */
+ 
+     /* !
+      * Draws the graph legend
+      *
+      * \note Base code submitted by Marlin Viss
+      * FIXME: maximum label length should be calculated more accurately for TT fonts
+      *        Performing a BBox calculation for every legend element, for example.
+      */
+ 
+     function DrawLegend($which_x1, $which_y1, $which_boxtype)
+     {
+         // Find maximum legend label length
+         $max_len = 0;
+         foreach ($this->legend as $leg)
+         {
+             $len = strlen($leg);
+             $max_len = ($len > $max_len) ? $len : $max_len;
+         }
+         $max_len += 5;          // Leave room for the boxes and margins
+         /////// Calculate legend labels sizes:  FIXME - dirty hack - FIXME
+         // TTF:
+         if ($this->use_ttf)
+         {
+             $size = $this->TTFBBoxSize($this->legend_font['size'], 0,
+                             $this->legend_font['font'], '_');
+             $char_w = $size[0];
+ 
+             $size = $this->TTFBBoxSize($this->legend_font['size'], 0,
+                             $this->legend_font['font'], '|');
+             $char_h = $size[1];
+         }
+         // Fixed fonts:
+         else
+         {
+             $char_w = $this->legend_font['width'];
+             $char_h = $this->legend_font['height'];
+         }
+ 
+         $v_margin = $char_h / 2;                         // Between vertical borders and labels
+         $dot_height = $char_h + $this->line_spacing;   // Height of the small colored boxes
+         $width = $char_w * $max_len;
+ 
+         //////// Calculate box size
+         // upper Left
+         if ((!$which_x1) || (!$which_y1))
+         {
+             $box_start_x = $this->plot_area[2] - $width;
+             $box_start_y = $this->plot_area[1] + 5;
+         }
+         else
+         {
+             $box_start_x = $which_x1;
+             $box_start_y = $which_y1;
+         }
+ 
+         // Lower right corner
+         $box_end_y = $box_start_y + $dot_height * (count($this->legend)) + 2 * $v_margin;
+         $box_end_x = $box_start_x + $width - 5;
+ 
+ 
+         // Draw outer box
+         ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_bg_color);
+         ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_grid_color);
+ 
+         $color_index = 0;
+         $max_color_index = count($this->ndx_data_colors) - 1;
+ 
+         $dot_left_x = $box_end_x - $char_w * 2;
+         $dot_right_x = $box_end_x - $char_w;
+         $y_pos = $box_start_y + $v_margin;
+ 
+         foreach ($this->legend as $leg)
+         {
+             // Text right aligned to the little box
+             $this->DrawText($this->legend_font, 0, $dot_left_x - $char_w, $y_pos,
+                     $this->ndx_text_color, $leg, 'right');
+             // Draw a box in the data color
+             ImageFilledRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x,
+                     $y_pos + $dot_height - 1, $this->ndx_data_colors[$color_index]);
+             // Draw a rectangle around the box
+             ImageRectangle($this->img, $dot_left_x, $y_pos + 1, $dot_right_x,
+                     $y_pos + $dot_height - 1, $this->ndx_text_color);
+ 
+             $y_pos += $char_h + $this->line_spacing;
+ 
+             $color_index++;
+             if ($color_index > $max_color_index)
+                 $color_index = 0;
+         }
+     }
+ 
+ // Function DrawLegend()
+ 
+ 
+     /* !
+      * TODO Draws a legend over (or below) an axis of the plot.
+      */
+ 
+     function DrawAxisLegend()
+     {
+         // Calculate available room
+         // Calculate length of all items (boxes included)
+         // Calculate number of lines and room it would take. FIXME: this should be known in CalcMargins()
+         // Draw.
+     }
+ 
+ /////////////////////////////////////////////
+ ////////////////////             PLOT DRAWING
+ /////////////////////////////////////////////
+ 
+ 
+     /* !
+      * Draws a pie chart. Data has to be 'text-data' type.
+      *
+      *  This can work in two ways: the classical, with a column for each sector
+      *  (computes the column totals and draws the pie with that)
+      *  OR
+      *  Takes each row as a sector and uses it's first value. This has the added
+      *  advantage of using the labels provided, which is not the case with the
+      *  former method. This might prove useful for pie charts from GROUP BY sql queries
+      */
+     function DrawPieChart()
+     {
+         $xpos = $this->plot_area[0] + $this->plot_area_width / 2;
+         $ypos = $this->plot_area[1] + $this->plot_area_height / 2;
+         $diameter = min($this->plot_area_width, $this->plot_area_height);
+         $radius = $diameter / 2;
+ 
+         // Get sum of each column? One pie slice per column
+         if ($this->data_type === 'text-data')
+         {
+             for ($i = 0; $i < $this->num_data_rows; $i++)
+             {
+                 for ($j = 1; $j < $this->num_recs[$i]; $j++)
+                 {      // Label ($row[0]) unused in these pie charts
+                     @ $sumarr[$j] += abs($this->data[$i][$j]);      // NOTE!  sum > 0 to make pie charts
+                 }
+             }
+         }
+         // Or only one column per row, one pie slice per row?
+         else if ($this->data_type == 'text-data-single')
+         {
+             for ($i = 0; $i < $this->num_data_rows; $i++)
+             {
+                 $legend[$i] = $this->data[$i][0];                   // Set the legend to column labels
+                 $sumarr[$i] = $this->data[$i][1];
+             }
+         }
+         else if ($this->data_type == 'data-data')
+         {
+             for ($i = 0; $i < $this->num_data_rows; $i++)
+             {
+                 for ($j = 2; $j < $this->num_recs[$i]; $j++)
+                 {
+                     @ $sumarr[$j] += abs($this->data[$i][$j]);
+                 }
+             }
+         }
+         else
+         {
+             $this->DrawError("DrawPieChart(): Data type '$this->data_type' not supported.");
+             return FALSE;
+         }
+ 
+         $total = array_sum($sumarr);
+ 
+         if ($total == 0)
+         {
+             $this->DrawError('DrawPieChart(): Empty data set');
+             return FALSE;
+         }
+ 
+         if ($this->shading)
+         {
+             $diam2 = $diameter / 2;
+         }
+         else
+         {
+             $diam2 = $diameter;
+         }
+         $max_data_colors = count($this->data_colors);
+ 
+         for ($h = $this->shading; $h >= 0; $h--)
+         {
+             $color_index = 0;
+             $start_angle = 0;
+             $end_angle = 0;
+             foreach ($sumarr as $val)
+             {
+                 // For shaded pies: the last one (at the top of the "stack") has a brighter color:
+                 if ($h == 0)
+                     $slicecol = $this->ndx_data_colors[$color_index];
+                 else
+                     $slicecol = $this->ndx_data_dark_colors[$color_index];
+ 
+                 $label_txt = number_format(($val / $total * 100), $this->y_precision, '.', ', ') . '%';
+                 $val = 360 * ($val / $total);
+ 
+                 // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why),
+                 // so the pie chart would start clockwise from 3 o'clock, would it not be
+                 // for the reversal of start and end angles in imagefilledarc()
+                 $start_angle = $end_angle;
+                 $end_angle += $val;
+                 $mid_angle = deg2rad($end_angle - ($val / 2));
+ 
+                 // Draw the slice
+                 ImageFilledArc($this->img, $xpos, $ypos + $h, $diameter, $diam2,
+                         360 - $end_angle, 360 - $start_angle,
+                         $slicecol, IMG_ARC_PIE);
+ 
+                 // Draw the labels only once
+                 if ($h == 0)
+                 {
+                     // Draw the outline
+                     if (!$this->shading)
+                         ImageFilledArc($this->img, $xpos, $ypos + $h, $diameter, $diam2,
+                                 360 - $end_angle, 360 - $start_angle,
+                                 $this->ndx_grid_color, IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL);
+ 
+ 
+                     // The '* 1.2' trick is to get labels out of the pie chart so there are more
+                     // chances they can be seen in small sectors.
+                     $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position;
+                     $label_y = $ypos + $h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position;
+ 
+                     $this->DrawText($this->generic_font, 0, $label_x, $label_y, $this->ndx_grid_color,
+                             $label_txt, 'center', 'center');
+                 }
+                 $color_index++;
+                 $color_index = $color_index % $max_data_colors;
+             }   // end for
+         }   // end for
+     }
+ 
+     /* !
+      * Supported data formats: data-data-error, text-data-error (doesn't exist yet)
+      * ( data comes in as array("title", x, y, error+, error-, y2, error2+, error2-, ...) )
+      */
+ 
+     function DrawDotsError()
+     {
+         $this->CheckOption($this->data_type, 'data-data-error', __FUNCTION__);
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $record = 1;                                // Skip record #0 (title)
+             // Do we have a value for X?
+             if ($this->data_type == 'data-data-error')
+                 $x_now = $this->data[$row][$record++];  // Read it, advance record index
+  else
+                 $x_now = 0.5 + $cnt++;                  // Place text-data at X = 0.5, 1.5, 2.5, etc...
+                 // Draw X Data labels?
+             if ($this->x_data_label_pos != 'none')
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
+ 
+             while ($record < $this->num_recs[$row])
+             {
+                 // Y:
+                 $y_now = $this->data[$row][$record];
+                 $this->DrawDot($x_now, $y_now, $record, $this->ndx_data_colors[$record++]);
+ 
+                 // Error +
+                 $val = $this->data[$row][$record];
+                 $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
+                         $this->ndx_error_bar_colors[$record++]);
+                 // Error -
+                 $val = $this->data[$row][$record];
+                 $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
+                         $this->ndx_error_bar_colors[$record++]);
+             }
+         }
+     }
+ 
+ // function DrawDotsError()
+ 
+ 
+     /*
+      * Supported data types:
+      *  - data-data ("title", x, y1, y2, y3, ...)
+      *  - text-data ("title", y1, y2, y3, ...)
+      */
+ 
+     function DrawDots()
+     {
+         $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $rec = 1;                    // Skip record #0 (data label)
+             // Do we have a value for X?
+             if ($this->data_type == 'data-data')
+                 $x_now = $this->data[$row][$rec++];  // Read it, advance record index
+  else
+                 $x_now = 0.5 + $cnt++;       // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+                 $x_now_pixels = $this->xtr($x_now);
+ 
+             // Draw X Data labels?
+             if ($this->x_data_label_pos != 'none')
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
+ 
+             // Proceed with Y values
+             for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$rec]))
+                 {              // Allow for missing Y data
+                     $this->DrawDot($x_now, $this->data[$row][$rec],
+                             $rec, $this->ndx_data_colors[$idx]);
+                 }
+             }
+         }
+     }
+ 
+ //function DrawDots
+ 
+ 
+     /* !
+      * A clean, fast routine for when you just want charts like stock volume charts
+      */
+ 
+     function DrawThinBarLines()
+     {
+         $this->CheckOption($this->data_type, 'text-data, data-data', __FUNCTION__);
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $rec = 1;                    // Skip record #0 (data label)
+             // Do we have a value for X?
+             if ($this->data_type == 'data-data')
+                 $x_now = $this->data[$row][$rec++];  // Read it, advance record index
+  else
+                 $x_now = 0.5 + $cnt++;       // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+                 $x_now_pixels = $this->xtr($x_now);
+ 
+             // Draw X Data labels?
+             if ($this->x_data_label_pos != 'none')
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+ 
+             // Proceed with Y values
+             for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$rec]))
+                 {              // Allow for missing Y data
+                     ImageSetThickness($this->img, $this->line_widths[$idx]);
+                     // Draws a line from user defined x axis position up to ytr($val)
+                     ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, $x_now_pixels,
+                             $this->ytr($this->data[$row][$rec]), $this->ndx_data_colors[$idx]);
+                 }
+             }
+         }
+ 
+         ImageSetThickness($this->img, 1);
+     }
+ 
+ //function DrawThinBarLines
+ 
+     /* !
+      *
+      */
+ 
+     function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color)
+     {
+         /*
+           // TODO: add a parameter to show datalabels next to error bars?
+           // something like this:
+           if ($this->x_data_label_pos == 'plot') {
+           $this->DrawText($this->error_font, 90, $x1, $y2,
+           $color, $label, 'center', 'top');
+          */
+ 
+         $x1 = $this->xtr($x_world);
+         $y1 = $this->ytr($y_world);
+         $y2 = $this->ytr($y_world + $error_height);
+ 
+         ImageSetThickness($this->img, $this->error_bar_line_width);
+         ImageLine($this->img, $x1, $y1, $x1, $y2, $color);
+ 
+         switch ($error_bar_type)
+         {
+             case 'line':
+                 break;
+             case 'tee':
+                 ImageLine($this->img, $x1 - $this->error_bar_size, $y2, $x1 + $this->error_bar_size, $y2, $color);
+                 break;
+             default:
+                 ImageLine($this->img, $x1 - $this->error_bar_size, $y2, $x1 + $this->error_bar_size, $y2, $color);
+                 break;
+         }
+ 
+         ImageSetThickness($this->img, 1);
+         return TRUE;
+     }
+ 
+     /* !
+      * Draws a styled dot. Uses world coordinates.
+      * Supported types: 'halfline', 'line', 'plus', 'cross', 'rect', 'circle', 'dot',
+      * 'diamond', 'triangle', 'trianglemid'
+      */
+ 
+     function DrawDot($x_world, $y_world, $record, $color)
+     {
+         // TODO: optimize, avoid counting every time we are called.
+         $record = $record % count($this->point_shapes);
+ 
+         $half_point = $this->point_sizes[$record] / 2;
+ 
+         $x_mid = $this->xtr($x_world);
+         $y_mid = $this->ytr($y_world);
+ 
+         $x1 = $x_mid - $half_point;
+         $x2 = $x_mid + $half_point;
+         $y1 = $y_mid - $half_point;
+         $y2 = $y_mid + $half_point;
+ 
+         switch ($this->point_shapes[$record])
+         {
+             case 'halfline':
+                 ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color);
+                 break;
+             case 'line':
+                 ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
+                 break;
+             case 'plus':
+                 ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color);
+                 ImageLine($this->img, $x_mid, $y1, $x_mid, $y2, $color);
+                 break;
+             case 'cross':
+                 ImageLine($this->img, $x1, $y1, $x2, $y2, $color);
+                 ImageLine($this->img, $x1, $y2, $x2, $y1, $color);
+                 break;
+             case 'rect':
+                 ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
+                 break;
+             case 'circle':
+                 ImageArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360, $color);
+                 break;
+             case 'dot':
+                 ImageFilledArc($this->img, $x_mid, $y_mid, $this->point_sizes[$record], $this->point_sizes[$record], 0, 360,
+                         $color, IMG_ARC_PIE);
+                 break;
+             case 'diamond':
+                 $arrpoints = array($x1, $y_mid, $x_mid, $y1, $x2, $y_mid, $x_mid, $y2);
+                 ImageFilledPolygon($this->img, $arrpoints, 4, $color);
+                 break;
+             case 'triangle':
+                 $arrpoints = array($x1, $y_mid, $x2, $y_mid, $x_mid, $y2);
+                 ImageFilledPolygon($this->img, $arrpoints, 3, $color);
+                 break;
+             case 'trianglemid':
+                 $arrpoints = array($x1, $y1, $x2, $y1, $x_mid, $y_mid);
+                 ImageFilledPolygon($this->img, $arrpoints, 3, $color);
+                 break;
+             default:
+                 ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color);
+                 break;
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * Draw an area plot. Supported data types:
+      *      'text-data'
+      *      'data-data'
+      * NOTE: This function used to add first and last data values even on incomplete
+      *       sets. That is not the behaviour now. As for missing data in between,
+      *       there are two posibilities: replace the point with one on the X axis (previous
+      *       way), or forget about it and use the preceding and following ones to draw the polygon.
+      *       There is the possibility to use both, we just need to add the method to set
+      *       it. Something like SetMissingDataBehaviour(), for example.
+      */
+ 
+     function DrawArea()
+     {
+         $incomplete_data_defaults_to_x_axis = FALSE;        // TODO: make this configurable
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $rec = 1;                                       // Skip record #0 (data label)
+ 
+             if ($this->data_type == 'data-data')            // Do we have a value for X?
+                 $x_now = $this->data[$row][$rec++];         // Read it, advance record index
+  else
+                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+                 $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
+ 
+ 
+             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+ 
+             // Proceed with Y values
+             // Create array of points for imagefilledpolygon()
+             for ($idx = 0; $rec < $this->num_recs[$row]; $rec++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$rec]))
+                 {              // Allow for missing Y data
+                     $y_now_pixels = $this->ytr($this->data[$row][$rec]);
+ 
+                     $posarr[$idx][] = $x_now_pixels;
+                     $posarr[$idx][] = $y_now_pixels;
+ 
+                     $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx] + 1 : 1;
+                 }
+                 // If there's missing data...
+                 else
+                 {
+                     if (isset($incomplete_data_defaults_to_x_axis))
+                     {
+                         $posarr[$idx][] = $x_now_pixels;
+                         $posarr[$idx][] = $this->x_axis_y_pixels;
+                         $num_points[$idx] = isset($num_points[$idx]) ? $num_points[$idx] + 1 : 1;
+                     }
+                 }
+             }
+         }   // end for
+ 
+         $end = count($posarr);
+         for ($i = 0; $i < $end; $i++)
+         {
+             // Prepend initial points. X = first point's X, Y = x_axis_y_pixels
+             $x = $posarr[$i][0];
+             array_unshift($posarr[$i], $x, $this->x_axis_y_pixels);
+ 
+             // Append final points. X = last point's X, Y = x_axis_y_pixels
+             $x = $posarr[$i][count($posarr[$i]) - 2];
+             array_push($posarr[$i], $x, $this->x_axis_y_pixels);
+ 
+             $num_points[$i] += 2;
+ 
+             // Draw the poligon
+             ImageFilledPolygon($this->img, $posarr[$i], $num_points[$i], $this->ndx_data_colors[$i]);
+         }
+     }
+ 
+ // function DrawArea()
+ 
+ 
+     /* !
+      * Draw Lines. Supported data-types:
+      *      'data-data',
+      *      'text-data'
+      * NOTE: Please see the note regarding incomplete data sets on DrawArea()
+      */
+ 
+     function DrawLines()
+     {
+         // This will tell us if lines have already begun to be drawn.
+         // It is an array to keep separate information for every line, with a single
+         // variable we would sometimes get "undefined offset" errors and no plot...
+         $start_lines = array_fill(0, $this->records_per_group, FALSE);
+ 
+         if ($this->data_type == 'text-data')
+         {
+             $lastx[0] = $this->xtr(0);
+             $lasty[0] = $this->xtr(0);
+         }
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $record = 1;                                    // Skip record #0 (data label)
+ 
+             if ($this->data_type == 'data-data')            // Do we have a value for X?
+                 $x_now = $this->data[$row][$record++];      // Read it, advance record index
+  else
+                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+                 $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
+ 
+             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
+ 
+             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$record]))
+                 {           //Allow for missing Y data
+                     $y_now_pixels = $this->ytr($this->data[$row][$record]);
+ 
+                     if ($start_lines[$idx] == TRUE)
+                     {
+                         // Set line width, revert it to normal at the end
+                         ImageSetThickness($this->img, $this->line_widths[$idx]);
+ 
+                         if ($this->line_styles[$idx] == 'dashed')
+                         {
+                             $this->SetDashedStyle($this->ndx_data_colors[$idx]);
+                             ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
+                                     IMG_COLOR_STYLED);
+                         }
+                         else
+                         {
+                             ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
+                                     $this->ndx_data_colors[$idx]);
+                         }
+                     }
+                     $lasty[$idx] = $y_now_pixels;
+                     $lastx[$idx] = $x_now_pixels;
+                     $start_lines[$idx] = TRUE;
+                 }
+                 // Y data missing... should we leave a blank or not?
+                 else if ($this->draw_broken_lines)
+                 {
+                     $start_lines[$idx] = FALSE;
+                 }
+             }   // end for
+         }   // end for
+ 
+         ImageSetThickness($this->img, 1);       // Revert to original state for lines to be drawn later.
+     }
+ 
+ // function DrawLines()
+ 
+ 
+     /* !
+      * Draw lines with error bars - data comes in as
+      *      array("label", x, y, error+, error-, y2, error2+, error2-, ...);
+      */
+ 
+     function DrawLinesError()
+     {
+         if ($this->data_type != 'data-data-error')
+         {
+             $this->DrawError("DrawLinesError(): Data type '$this->data_type' not supported.");
+             return FALSE;
+         }
+ 
+         $start_lines = array_fill(0, $this->records_per_group, FALSE);
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $record = 1;                                    // Skip record #0 (data label)
+ 
+             $x_now = $this->data[$row][$record++];          // Read X value, advance record index
+ 
+             $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates.
+ 
+ 
+             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row);
+ 
+             // Now go for Y, E+, E-
+             for ($idx = 0; $record < $this->num_recs[$row]; $idx++)
+             {
+                 // Y
+                 $y_now = $this->data[$row][$record++];
+                 $y_now_pixels = $this->ytr($y_now);
+ 
+                 if ($start_lines[$idx] == TRUE)
+                 {
+                     ImageSetThickness($this->img, $this->line_widths[$idx]);
+ 
+                     if ($this->line_styles[$idx] == 'dashed')
+                     {
+                         $this->SetDashedStyle($this->ndx_data_colors[$idx]);
+                         ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
+                                 IMG_COLOR_STYLED);
+                     }
+                     else
+                     {
+                         ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx],
+                                 $this->ndx_data_colors[$idx]);
+                     }
+                 }
+ 
+                 // Error+
+                 $val = $this->data[$row][$record++];
+                 $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape,
+                         $this->ndx_error_bar_colors[$idx]);
+ 
+                 // Error-
+                 $val = $this->data[$row][$record++];
+                 $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape,
+                         $this->ndx_error_bar_colors[$idx]);
+ 
+                 // Update indexes:
+                 $start_lines[$idx] = TRUE;   // Tells us if we already drew the first column of points,
+                 // thus having $lastx and $lasty ready for the next column.
+                 $lastx[$idx] = $x_now_pixels;
+                 $lasty[$idx] = $y_now_pixels;
+             }   // end while
+         }   // end for
+ 
+         ImageSetThickness($this->img, 1);   // Revert to original state for lines to be drawn later.
+     }
+ 
+ // function DrawLinesError()
+ 
+ 
+ 
+     /* !
+      * This is a mere copy of DrawLines() with one more line drawn for each point
+      */
+ 
+     function DrawSquared()
+     {
+         // This will tell us if lines have already begun to be drawn.
+         // It is an array to keep separate information for every line, for with a single
+         // variable we could sometimes get "undefined offset" errors and no plot...
+         $start_lines = array_fill(0, $this->records_per_group, FALSE);
+ 
+         if ($this->data_type == 'text-data')
+         {
+             $lastx[0] = $this->xtr(0);
+             $lasty[0] = $this->xtr(0);
+         }
+ 
+         for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++)
+         {
+             $record = 1;                                    // Skip record #0 (data label)
+ 
+             if ($this->data_type == 'data-data')            // Do we have a value for X?
+                 $x_now = $this->data[$row][$record++];      // Read it, advance record index
+  else
+                 $x_now = 0.5 + $cnt++;                      // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+                 $x_now_pixels = $this->xtr($x_now);             // Absolute coordinates
+ 
+             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param.
+                 // Draw Lines
+  for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$record]))
+                 {               // Allow for missing Y data
+                     $y_now_pixels = $this->ytr($this->data[$row][$record]);
+ 
+                     if ($start_lines[$idx] == TRUE)
+                     {
+                         // Set line width, revert it to normal at the end
+                         ImageSetThickness($this->img, $this->line_widths[$idx]);
+ 
+                         if ($this->line_styles[$idx] == 'dashed')
+                         {
+                             $this->SetDashedStyle($this->ndx_data_colors[$idx]);
+                             ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
+                                     IMG_COLOR_STYLED);
+                             ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels,
+                                     IMG_COLOR_STYLED);
+                         }
+                         else
+                         {
+                             ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx],
+                                     $this->ndx_data_colors[$idx]);
+                             ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels,
+                                     $this->ndx_data_colors[$idx]);
+                         }
+                     }
+                     $lastx[$idx] = $x_now_pixels;
+                     $lasty[$idx] = $y_now_pixels;
+                     $start_lines[$idx] = TRUE;
+                 }
+                 // Y data missing... should we leave a blank or not?
+                 else if ($this->draw_broken_lines)
+                 {
+                     $start_lines[$idx] = FALSE;
+                 }
+             }
+         }   // end while
+ 
+         ImageSetThickness($this->img, 1);
+     }
+ 
+ // function DrawSquared()
+ 
+ 
+     /* !
+      * Data comes in as array("title", x, y, y2, y3, ...)
+      */
+ 
+     function DrawBars()
+     {
+         if ($this->data_type != 'text-data')
+         {
+             $this->DrawError('DrawBars(): Bar plots must be text-data: use function SetDataType("text-data")');
+             return FALSE;
+         }
+ 
+         for ($row = 0; $row < $this->num_data_rows; $row++)
+         {
+             $record = 1;                                    // Skip record #0 (data label)
+ 
+             $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+             if ($this->x_data_label_pos != 'none')          // Draw X Data labels? TODO:labels on top of bars.
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+ 
+             // Draw the bar
+             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$record]))
+                 {       // Allow for missing Y data
+                     $x1 = $x_now_pixels - $this->data_group_space + ($idx * $this->record_bar_width);
+                     $x2 = $x1 + ($this->bar_width_adjust * $this->record_bar_width);
+ 
+                     if ($this->data[$row][$record] < $this->x_axis_position)
+                     {
+                         $y1 = $this->x_axis_y_pixels;
+                         $y2 = $this->ytr($this->data[$row][$record]);
+                     }
+                     else
+                     {
+                         $y1 = $this->ytr($this->data[$row][$record]);
+                         $y2 = $this->x_axis_y_pixels;
+                     }
+ 
+                     if ($this->shading)
+                     {                           // Draw the shade?
+                         ImageFilledPolygon($this->img, array($x1, $y1,
+                             $x1 + $this->shading, $y1 - $this->shading,
+                             $x2 + $this->shading, $y1 - $this->shading,
+                             $x2 + $this->shading, $y2 - $this->shading,
+                             $x2, $y2,
+                             $x2, $y1),
+                                 6, $this->ndx_data_dark_colors[$idx]);
+                     }
+                     // Or draw a border?
+                     else
+                     {
+                         ImageRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_border_colors[$idx]);
+                     }
+                     // Draw the bar
+                     ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
+                 }
+             }   // end for
+         }   // end for
+     }
+ 
+ //function DrawBars
+ 
+ 
+     /* !
+      * Data comes in as array("title", x, y, y2, y3, ...)
+      * \note Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net >
+      */
+ 
+     function DrawStackedBars()
+     {
+         if ($this->data_type != 'text-data')
+         {
+             $this->DrawError('DrawStackedBars(): Bar plots must be text-data: use SetDataType("text-data")');
+             return FALSE;
+         }
+ 
+         for ($row = 0; $row < $this->num_data_rows; $row++)
+         {
+             $record = 1;                                    // Skip record #0 (data label)
+ 
+             $x_now_pixels = $this->xtr(0.5 + $row);         // Place text-data at X = 0.5, 1.5, 2.5, etc...
+ 
+             if ($this->x_data_label_pos != 'none')          // Draw X Data labels?
+                 $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels);
+ 
+             // Draw the bars
+             $oldv = 0;
+             for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++)
+             {
+                 if (is_numeric($this->data[$row][$record]))
+                 {       // Allow for missing Y data
+                     $x1 = $x_now_pixels - $this->data_group_space;
+                     $x2 = $x_now_pixels + $this->data_group_space;
+ 
+                     $y1 = $this->ytr(abs($this->data[$row][$record]) + $oldv);
+                     $y2 = $this->ytr($this->x_axis_position + $oldv);
+                     $oldv += abs($this->data[$row][$record]);
+ 
+                     if ($this->shading)
+                     {                           // Draw the shade?
+                         ImageFilledPolygon($this->img, array($x1, $y1,
+                             $x1 + $this->shading, $y1 - $this->shading,
+                             $x2 + $this->shading, $y1 - $this->shading,
+                             $x2 + $this->shading, $y2 - $this->shading,
+                             $x2, $y2,
+                             $x2, $y1),
+                                 6, $this->ndx_data_dark_colors[$idx]);
+                     }
+                     // Or draw a border?
+                     else
+                     {
+                         ImageRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_border_colors[$idx]);
+                     }
+                     // Draw the bar
+                     ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]);
+                 }
+             }   // end for
+         }   // end for
+     }
+ 
+ //function DrawStackedBars
+ 
+ 
+     /* !
+      *
+      */
+ 
+     function DrawGraph()
+     {
+         if (!$this->img)
+         {
+             $this->DrawError('DrawGraph(): No image resource allocated');
+             return FALSE;
+         }
+ 
+         if (!is_array($this->data))
+         {
+             $this->DrawError("DrawGraph(): No array of data in \$data");
+             return FALSE;
+         }
+ 
+         if (!isset($this->data_limits_done))
+             $this->FindDataLimits();                // Get maxima and minima for scaling
+ 
+             if ($this->total_records == 0)
+         {            // Check for empty data sets
+             $this->DrawError('Empty data set');
+             return FALSE;
+         }
+ 
+         $this->CalcMargins();                       // Calculate margins
+ 
+         if (!isset($this->plot_area_width))        // Set plot area pixel values (plot_area[])
+             $this->SetPlotAreaPixels();
+ 
+         if (!isset($this->plot_max_y))             // Set plot area world values (plot_max_x, etc.)
+             $this->SetPlotAreaWorld();
+ 
+         if ($this->plot_type == 'bars' || $this->plot_type == 'stackedbars') // Calculate bar widths
+             $this->CalcBarWidths();
+         /* FIXME!!  this sort of thing should not be done without user's consent
+           if ($this->x_data_label_pos != 'none') {    // Default: do not draw tick stuff if
+           $this->x_tick_label_pos = 'none';       // there are data labels.
+           $this->x_tick_pos = 'none';
+           }
+          */
+         $this->PadArrays();                         // Pad color and style arrays to fit records per group.
+ 
+         $this->DrawBackground();
+ 
+         $this->DrawImageBorder();
+ 
+         $this->DrawPlotAreaBackground();
+ 
+         $this->DrawTitle();
+         $this->DrawXTitle();
+         $this->DrawYTitle();
+ 
+         // Pie charts are drawn differently, handle them first
+         if ($this->plot_type == 'pie')
+         {
+             // Pie charts can maximize image space usage.
+             $this->SetPlotAreaPixels($this->safe_margin, $this->title_height,
+                     $this->image_width - $this->safe_margin,
+                     $this->image_height - $this->safe_margin);
+             $this->DrawPieChart();
+ 
+             if ($this->legend)
+                 $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
+ 
+             if ($this->print_image)
+                 $this->PrintImage();
+ 
+             return;
+         }
+ 
+         ////// All other chart types:
+ 
+         if (!$this->grid_at_foreground)
+         {         // Usually one wants grids to go back, but...
+             $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis())
+             $this->DrawXAxis();
+         }
+ 
+         switch ($this->plot_type)
+         {
+             case 'thinbarline':
+                 $this->DrawThinBarLines();
+                 break;
+             case 'area':
+                 $this->DrawArea();
+                 break;
+             case 'squared':
+                 $this->DrawSquared();
+                 break;
+             case 'lines':
+                 if ($this->data_type == 'data-data-error')
+                 {
+                     $this->DrawLinesError();
+                 }
+                 else
+                 {
+                     $this->DrawLines();
+                 }
+                 break;
+             case 'linepoints':          // FIXME !!! DrawXDataLabel gets called in DrawLines() and DrawDots()
+                 if ($this->data_type == 'data-data-error')
+                 {
+                     $this->DrawLinesError();
+                     $this->DrawDotsError();
+                 }
+                 else
+                 {
+                     $this->DrawLines();
+                     $this->DrawDots();
+                 }
+                 break;
+             case 'points';
+                 if ($this->data_type == 'data-data-error')
+                 {
+                     $this->DrawDotsError();
+                 }
+                 else
+                 {
+                     $this->DrawDots();
+                 }
+                 break;
+             case 'stackedbars':
+                 $this->DrawStackedBars();
+                 break;
+             case 'bars':
+                 $this->DrawBars();
+                 break;
+             default:
+                 $this->plot_type = 'bars';  // Set it if it wasn't already set.
+                 $this->DrawBars();
+                 break;
+         }   // end switch
+ 
+         if ($this->grid_at_foreground)
+         {         // Usually one wants grids to go back, but...
+             $this->DrawYAxis();     // Y axis must be drawn before X axis (see DrawYAxis())
+             $this->DrawXAxis();
+         }
+ 
+         $this->DrawPlotBorder();
+ 
+         if ($this->legend)
+             $this->DrawLegend($this->legend_x_pos, $this->legend_y_pos, '');
+ 
+         if ($this->print_image)
+             $this->PrintImage();
+     }
+ 
+ //function DrawGraph()
+ /////////////////////////////////////////////
+ //////////////////         DEPRECATED METHODS
+ /////////////////////////////////////////////
+ 
+     /* !
+      * Deprecated, use SetYTickPos()
+      */
+     function SetDrawVertTicks($which_dvt)
+     {
+         if ($which_dvt != 1)
+             $this->SetYTickPos('none');
+         return TRUE;
+     }
+ 
+     /* !
+      * Deprecated, use SetXTickPos()
+      */
+ 
+     function SetDrawHorizTicks($which_dht)
+     {
+         if ($which_dht != 1)
+             $this->SetXTickPos('none');
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated Use SetNumXTicks()
+      */
+ 
+     function SetNumHorizTicks($n)
+     {
+         return $this->SetNumXTicks($n);
+     }
+ 
+     /* !
+      * \deprecated Use SetNumYTicks()
+      */
+ 
+     function SetNumVertTicks($n)
+     {
+         return $this->SetNumYTicks($n);
+     }
+ 
+     /* !
+      * \deprecated Use SetXTickIncrement()
+      */
+ 
+     function SetHorizTickIncrement($inc)
+     {
+         return $this->SetXTickIncrement($inc);
+     }
+ 
+     /* !
+      * \deprecated Use SetYTickIncrement()
+      */
+ 
+     function SetVertTickIncrement($inc)
+     {
+         return $this->SetYTickIncrement($inc);
+     }
+ 
+     /* !
+      * \deprecated Use SetYTickPos()
+      */
+ 
+     function SetVertTickPosition($which_tp)
+     {
+         return $this->SetYTickPos($which_tp);
+     }
+ 
+     /* !
+      * \deprecated Use SetXTickPos()
+      */
+ 
+     function SetHorizTickPosition($which_tp)
+     {
+         return $this->SetXTickPos($which_tp);
+     }
+ 
+     /* !
+      * \deprecated Use SetFont()
+      */
+ 
+     function SetTitleFontSize($which_size)
+     {
+         return $this->SetFont('title', $which_size);
+     }
+ 
+     /* !
+      * \deprecated Use SetFont()
+      */
+ 
+     function SetAxisFontSize($which_size)
+     {
+         $this->SetFont('x_label', $which_size);
+         $this->SetFont('y_label', $whic_size);
+     }
+ 
+     /* !
+      * \deprecated Use SetFont()
+      */
+ 
+     function SetSmallFontSize($which_size)
+     {
+         return $this->SetFont('generic', $which_size);
+     }
+ 
+     /* !
+      * \deprecated Use SetFont()
+      */
+ 
+     function SetXLabelFontSize($which_size)
+     {
+         return $this->SetFont('x_title', $which_size);
+     }
+ 
+     /* !
+      * \deprecated Use SetFont()
+      */
+ 
+     function SetYLabelFontSize($which_size)
+     {
+         return $this->SetFont('y_title', $which_size);
+     }
+ 
+     /* !
+      * \deprecated Use SetXTitle()
+      */
+ 
+     function SetXLabel($which_xlab)
+     {
+         return $this->SetXTitle($which_xlab);
+     }
+ 
+     /* !
+      * \deprecated Use SetYTitle()
+      */
+ 
+     function SetYLabel($which_ylab)
+     {
+         return $this->SetYTitle($which_ylab);
+     }
+ 
+     /* !
+      * \deprecated This is now an Internal function - please set width and
+      *             height via PHPlot() upon object construction
+      */
+ 
+     function SetImageArea($which_iw, $which_ih)
+     {
+         $this->image_width = $which_iw;
+         $this->image_height = $which_ih;
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated Use SetXTickLength() and SetYTickLength() instead.
+      */
+ 
+     function SetTickLength($which_tl)
+     {
+         $this->SetXTickLength($which_tl);
+         $this->SetYTickLength($which_tl);
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated  Use SetYLabelType()
+      */
+ 
+     function SetYGridLabelType($which_yglt)
+     {
+         return $this->SetYLabelType($which_yglt);
+     }
+ 
+     /* !
+      * \deprecated  Use SetXLabelType()
+      */
+ 
+     function SetXGridLabelType($which_xglt)
+     {
+         return $this->SetXLabelType($which_xglt);
+     }
+ 
+     /* !
+      * \deprecated Use SetYTickLabelPos()
+      */
+ 
+     function SetYGridLabelPos($which_yglp)
+     {
+         return $this->SetYTickLabelPos($which_yglp);
+     }
+ 
+     /* !
+      * \deprecated Use SetXTickLabelPos()
+      */
+ 
+     function SetXGridLabelPos($which_xglp)
+     {
+         return $this->SetXTickLabelPos($which_xglp);
+     }
+ 
+     /* !
+      * \deprecated Use SetXtitle()
+      */
+ 
+     function SetXTitlePos($xpos)
+     {
+         $this->x_title_pos = $xpos;
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated Use SetYTitle()
+      */
+ 
+     function SetYTitlePos($xpos)
+     {
+         $this->y_title_pos = $xpos;
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated  Use DrawDots()
+      */
+ 
+     function DrawDotSeries()
+     {
+         $this->DrawDots();
+     }
+ 
+     /* !
+      * \deprecated Use SetXLabelAngle()
+      */
+ 
+     function SetXDataLabelAngle($which_xdla)
+     {
+         return $this->SetXLabelAngle($which_xdla);
+     }
+ 
+     /* !
+      * Draw Labels (not grid labels) on X Axis, following data points. Default position is
+      * down of plot. Care must be taken not to draw these and x_tick_labels as they'd probably overlap.
+      *
+      * \deprecated Use SetXDataLabelPos()
+      */
+ 
+     function SetDrawXDataLabels($which_dxdl)
+     {
+         if ($which_dxdl == '1')
+             $this->SetXDataLabelPos('plotdown');
+         else
+             $this->SetXDataLabelPos('none');
+     }
+ 
+     /* !
+      * \deprecated This method was intended to improve performance by being specially
+      * written for 'data-data'. However, the improvement didn't pay. Use DrawLines() instead
+      */
+ 
+     function DrawLineSeries()
+     {
+         return $this->DrawLines();
+     }
+ 
+     /* !
+      * \deprecated Calculates maximum X-Axis label height. Now inside CalcMargins()
+      */
+ 
+     function CalcXHeights()
+     {
+         // TTF
+         if ($this->use_ttf)
+         {
+             $xstr = str_repeat('.', $this->max_t);
+             $size = $this->TTFBBoxSize($this->x_label_font['size'], $this->x_label_angle,
+                             $this->x_label_font['font'], $xstr);
+             $this->x_tick_label_height = $size[1];
+         }
+         // Fixed font
+         else
+         { // For Non-TTF fonts we can have only angles 0 or 90
+             if ($this->x_label_angle == 90)
+                 $this->x_tick_label_height = $this->max_t * $this->x_label_font['width'];
+             else
+                 $this->x_tick_label_height = $this->x_label_font['height'];
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated Calculates Maximum Y-Axis tick label width. Now inside CalcMargins()
+      */
+ 
+     function CalcYWidths()
+     {
+         //the "." is for space. It isn't actually printed
+         $ylab = number_format($this->max_y, $this->y_precision, '.', ', ') . $this->data_units_text . '.';
+ 
+         // TTF
+         if ($this->use_ttf)
+         {
+             // Maximum Y tick label width
+             $size = $this->TTFBBoxSize($this->y_label_font['size'], 0, $this->y_label_font['font'], $ylab);
+             $this->y_tick_label_width = $size[0];
+         }
+         // Fixed font
+         else
+         {
+             // Y axis title width
+             $this->y_tick_label_width = strlen($ylab) * $this->y_label_font['width'];
+         }
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated Superfluous.
+      */
+ 
+     function DrawLabels()
+     {
+         $this->DrawTitle();
+         $this->DrawXTitle();
+         $this->DrawYTitle();
+     }
+ 
+     /* !
+      * Set up the image resource 'img'
+      * \deprecated The constructor should init 'img'
+      */
+ 
+     function InitImage()
+     {
+         $this->img = ImageCreate($this->image_width, $this->image_height);
+ 
+         if (!$this->img)
+             $this->PrintError('InitImage(): Could not create image resource');
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated
+      */
+ 
+     function SetNewPlotAreaPixels($x1, $y1, $x2, $y2)
+     {
+         //Like in GD 0, 0 is upper left set via pixel Coordinates
+         $this->plot_area = array($x1, $y1, $x2, $y2);
+         $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0];
+         $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1];
+         $this->y_top_margin = $this->plot_area[1];
+ 
+         if (isset($this->plot_max_x))
+             $this->CalcTranslation();
+ 
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated Use _SetRGBColor()
+      */
+ 
+     function SetColor($which_color)
+     {
+         $this->SetRGBColor($which_color);
+         return TRUE;
+     }
+ 
+     /*
+      * \deprecated Use SetLineWidths().
+      */
+ 
+     function SetLineWidth($which_lw)
+     {
+ 
+         $this->SetLineWidths($which_lw);
+ 
+         if (!$this->error_bar_line_width)
+         {
+             $this->SetErrorBarLineWidth($which_lw);
+         }
+         return TRUE;
+     }
+ 
+     /* !
+      * \deprecated
+      */
+ 
+     function DrawDashedLine($x1, $y1, $x2, $y2, $dash_length, $dash_space, $color)
+     {
+         if ($dash_length)
+             $dashes = array_fill(0, $dash_length, $color);
+         else
+             $dashes = array();
+         if ($dash_space)
+             $spaces = array_fill(0, $dash_space, IMG_COLOR_TRANSPARENT);
+         else
+             $spaces = array();
+ 
+         $style = array_merge($dashes, $spaces);
+         ImageSetStyle($this->img, $style);
+         ImageLine($this->img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
+     }
+ 
+     /* !
+      * \deprecated Selects an input file to be used as background for the whole graph.
+      * This resizes the graph to the image's size.
+      */
+ 
+     function SetInputFile($which_input_file)
+     {
+         $size = GetImageSize($which_input_file);
+         $input_type = $size[2];
+ 
+         switch ($input_type)
+         {
+             case 1:
+                 $im = @ ImageCreateFromGIF($which_input_file);
+                 if (!$im)
+                 { // See if it failed
+                     $this->PrintError("Unable to open $which_input_file as a GIF");
+                     return FALSE;
+                 }
+                 break;
+             case 3:
+                 $im = @ ImageCreateFromPNG($which_input_file);
+                 if (!$im)
+                 { // See if it failed
+                     $this->PrintError("Unable to open $which_input_file as a PNG");
+                     return FALSE;
+                 }
+                 break;
+             case 2:
+                 $im = @ ImageCreateFromJPEG($which_input_file);
+                 if (!$im)
+                 { // See if it failed
+                     $this->PrintError("Unable to open $which_input_file as a JPG");
+                     return FALSE;
+                 }
+                 break;
+             default:
+                 $this->PrintError('SetInputFile(): Please select gif, jpg, or png for image type!');
+                 return FALSE;
+                 break;
+         }
+ 
+         // Set Width and Height of Image
+         $this->image_width = $size[0];
+         $this->image_height = $size[1];
+ 
+         // Deallocate any resources previously allocated
+         if ($this->img)
+             imagedestroy($this->img);
+ 
+         $this->img = $im;
+ 
+         return TRUE;
+     }
+ 
+     /*
+      * \deprecated Use SetPointShapes().
+      */
+ 
+     function SetPointShape($which_pt)
+     {
+         $this->SetPointShapes($which_pt);
+         return TRUE;
+     }
+ 
+     /*
+      * \deprecated Use SetPointSizes().
+      */
+ 
+     function SetPointSize($which_ps)
+     {
+         $this->SetPointSizes($which_ps);
+         return TRUE;
+     }
+ 
+ }
+ 
+ // class PHPlot
+ ////////////////////////
+ 
+ 
+ /* !
+  * Pads an array with another or with itself.
+  *  \param arr array  Original array (reference)
+  *  \param size int   Size of the resulting array.
+  *  \param arr2 array If specified, array to use for padding. If unspecified, pad with $arr.
+  */
+ function array_pad_array(&$arr, $size, $arr2=NULL)
+ {
+     if (!is_array($arr2))
+     {
+         $arr2 = $arr;                           // copy the original array
+     }
+     while (count($arr) < $size)
+         $arr = array_merge_php4($arr, $arr2);        // append until done
+ }
+ 
+ /* !
+  * Fixes problem with array_merge() in PHP5.
+  * \note I simply copied this from a bug report. I am not running php5 yet, so
+  *       I cannot reproduce it, which is why I trust the reporter.
+  */
+ 
+ function array_merge_php4($array1, $array2)
+ {
+     $return = array();
+ 
+     foreach (func_get_args () as $arg)
+     {
+         if (!is_array($arg))
+         {
+             $arg = array($arg);
+         }
+         foreach ($arg as $key => $val)
+         {
+             if (!is_int($key))
+             {
+                 $return[$key] = $val;
+             }
+             else
+             {
+                 $return[] = $val;
+             }
+         }
+     }
+     return $return;
+ }
  
  /**
   * @file
***************
*** 9,397 ****
  /**
   * Implementation of _webform_defaults_component().
   */
! function _webform_defaults_select() {
!   return array(
!     'name' => '',
!     'form_key' => NULL,
!     'mandatory' => 0,
!     'pid' => 0,
!     'weight' => 0,
!     'value' => '',
!     'extra' => array(
!       'items' => '',
!       'multiple' => NULL,
!       'aslist' => NULL,
!       'optrand' => 0,
!       'other_option' => NULL,
!       'other_text' => t('Other...'),
!       'title_display' => 0,
!       'description' => '',
!       'custom_keys' => FALSE,
!       'options_source' => '',
!     ),
!   );
  }
  
  /**
   * Implementation of _webform_theme_component().
   */
! function _webform_theme_select() {
!   return array(
!     'webform_display_select' => array(
!       'arguments' => array('element' => NULL),
!     ),
!   );
  }
  
  /**
   * Implementation of _webform_edit_component().
   */
! function _webform_edit_select($component) {
!   $form = array();
! 
!   drupal_add_js(drupal_get_path('module', 'webform') . '/js/select-admin.js', 'module', 'header', FALSE, TRUE, FALSE);
!   drupal_add_js(array('webform' => array('selectOptionsUrl' => url('webform/ajax/options/' . $component['nid']))), 'setting');
! 
!   $other = array();
!   if ($info = _webform_select_options_info()) {
!     $options = array('' => t('None'));
!     foreach ($info as $name => $source) {
!       $options[$name] = $source['title'];
!     }
! 
!     $other['options_source'] = array(
!       '#title' => t('Load a pre-built option list'),
!       '#type' => 'select',
!       '#options' => $options,
!       '#default_value' => $component['extra']['options_source'],
!       '#weight' => 1,
!       '#description' => t('Use a pre-built list of options rather than entering options manually. Options will not be editable if using pre-built list.'),
!       '#parents' => array('extra', 'options_source'),
!       '#weight' => 5,
!     );
!   }
! 
!   if (module_exists('select_or_other')) {
!     $other['other_option'] = array(
!       '#type' => 'checkbox',
!       '#title' => t('Allow "Other..." option'),
!       '#default_value' => $component['extra']['other_option'],
!       '#description' => t('Check this option if you want to allow users to enter an option not on the list.'),
!       '#parents' => array('extra', 'other_option'),
!       '#weight' => 2,
!     );
!     $other['other_text'] = array(
!       '#type' => 'textfield',
!       '#title' => t('Text for "Other..." option'),
!       '#default_value' => $component['extra']['other_text'],
!       '#description' => t('If allowing other options, enter text to be used for other-enabling option.'),
!       '#parents' => array('extra', 'other_text'),
!       '#weight' => 3,
!     );
!   }
  
!   if (module_exists('options_element')) {
!     $options = _webform_select_options($component, FALSE, FALSE);
  
!     $form['items'] = array(
!       '#type' => 'fieldset',
!       '#title' => t('Options'),
!       '#collapsible' => TRUE,
!       '#attributes' => array('class' => 'webform-options-element'),
!       '#element_validate' => array('_webform_edit_validate_options'),
!       '#weight' => 2,
!     );
  
!     $form['items']['options'] = array(
!       '#type' => 'options',
!       '#limit' => 500,
!       '#optgroups' => $component['extra']['aslist'],
!       '#multiple' => $component['extra']['multiple'],
!       '#multiple_toggle' => t('Multiple'),
!       '#default_value' => $component['value'],
!       '#options' => $options,
!       '#key_type' => 'mixed',
!       '#key_type_toggle' => t('Customize keys (Advanced)'),
!       '#key_type_toggled' => $component['extra']['custom_keys'],
!       '#disabled' => !empty($component['extra']['options_source']),
!       '#weight' => 1,
!     );
  
!     $form['items']['options']['option_settings'] = $other;
!   }
!   else {
!     $form['extra']['items'] = array(
!       '#type' => 'textarea',
!       '#title' => t('Options'),
!       '#default_value' => $component['extra']['items'],
!       '#description' => t('<strong>Key-value pairs MUST be specified as "safe_key|Some readable option"</strong>. Only alphanumeric characters and underscores are allowed as a key. One option per line. Option groups may be specified with &lt;Group Name&gt;. &lt;&gt; can be used to insert items at the root of the menu after specifying a group.') . theme('webform_token_help'),
!       '#cols' => 60,
!       '#rows' => 5,
!       '#weight' => 0,
!       '#required' => TRUE,
!       '#wysiwyg' => FALSE,
!       '#element_validate' => array('_webform_edit_validate_select'),
!     );
  
!     if (!empty($component['extra']['options_source'])) {
!       $form['extra']['items']['#attributes'] = array('readonly' => 'readonly');
      }
  
!     $form['extra'] = array_merge($form['extra'], $other);
!     $form['value'] = array(
!       '#type' => 'textfield',
!       '#title' => t('Default value'),
!       '#default_value' => $component['value'],
!       '#description' => t('The default value of the field. For multiple selects use commas to separate multiple defaults.') . theme('webform_token_help'),
!       '#size' => 60,
!       '#maxlength' => 256,
!       '#weight' => 0,
      );
!     $form['extra']['multiple'] = array(
!       '#type' => 'checkbox',
!       '#title' => t('Multiple'),
!       '#default_value' => $component['extra']['multiple'],
!       '#description' => t('Check this option if the user should be allowed to choose multiple values.'),
!       '#weight' => 0,
      );
-   }
- 
-   $form['display']['aslist'] = array(
-     '#type' => 'checkbox',
-     '#title' => t('Listbox'),
-     '#default_value' => $component['extra']['aslist'],
-     '#description' => t('Check this option if you want the select component to be of listbox type instead of radio buttons or checkboxes.'),
-     '#parents' => array('extra', 'aslist'),
-   );
-   $form['display']['optrand'] = array(
-     '#type' => 'checkbox',
-     '#title' => t('Randomize options'),
-     '#default_value' => $component['extra']['optrand'],
-     '#description' => t('Randomizes the order of the options when they are displayed in the form.'),
-     '#parents' => array('extra', 'optrand'),
-   );
  
!   return $form;
  }
  
  /**
   * Element validation callback. Ensure keys are not duplicated.
   */
! function _webform_edit_validate_select($element, &$form_state) {
!   // Check for duplicate key values to prevent unexpected data loss. Require
!   // all options to include a safe_key.
!   if (!empty($element['#value'])) {
!     $lines = explode("\n", trim($element['#value']));
!     $existing_keys = array();
!     $duplicate_keys = array();
!     $missing_keys = array();
!     $long_keys = array();
!     $group = '';
!     foreach ($lines as $line) {
!       $matches = array();
!       $line = trim($line);
!       if (preg_match('/^\<([^>]*)\>$/', $line, $matches)) {
!         $group = $matches[1];
!         $key = NULL; // No need to store group names.
!       }
!       elseif (preg_match('/^([^|]*)\|(.*)$/', $line, $matches)) {
!         $key = $matches[1];
!         if (strlen($key) > 128) {
!           $long_keys[] = $key;
!         }
!       }
!       else {
!         $missing_keys[] = $line;
!       }
! 
!       if (isset($key)) {
!         if (isset($existing_keys[$group][$key])) {
!           $duplicate_keys[$key] = $key;
!         }
!         else {
!           $existing_keys[$group][$key] = $key;
!         }
!       }
!     }
! 
!     if (!empty($missing_keys)) {
!       form_error($element, t('Every option must have a key specified. Specify each option as "safe_key|Some readable option".'));
!     }
! 
!     if (!empty($long_keys)) {
!       form_error($element, t('Option keys must be less than 128 characters. The following keys exceed this limit:') . theme('item_list', $long_keys));
!     }
  
!     if (!empty($duplicate_keys)) {
!       form_error($element, t('Options within the select list must be unique. The following keys have been used multiple times:') . theme('item_list', $duplicate_keys));
!     }
  
!   }
  
!   return TRUE;
  }
  
  /**
   * Set the appropriate webform values when using the options element module.
   */
! function _webform_edit_validate_options($element, &$form_state) {
!   $key = end($element['#parents']);
!   $element_options = $form_state['values'][$key]['options'];
!   unset($form_state['values'][$key]);
! 
!   $form_state['values']['extra'][$key] = form_options_to_text($element_options['options'], 'custom');
! 
!   // Options saved for select components.
!   if ($key == 'items') {
!     $form_state['values']['extra']['multiple'] = $element_options['multiple'];
!     $form_state['values']['extra']['custom_keys'] = $element_options['custom_keys'];
!     $form_state['values']['value'] = is_array($element_options['default_value']) ? implode(', ', $element_options['default_value']) : $element_options['default_value'];
!   }
!   // Options saved for grid components.
!   else {
!     $form_state['values']['extra']['custom_' . rtrim($key, 's') . '_keys'] = $element_options['custom_keys'];
!   }
  }
  
  /**
   * Implementation of _webform_render_component().
   */
! function _webform_render_select($component, $value = NULL, $filter = TRUE) {
!   $element = array(
!     '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
!     '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
!     '#required' => $component['mandatory'],
!     '#weight' => $component['weight'],
!     '#description' => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
!     '#theme_wrappers' => array('webform_element_wrapper'),
!     '#pre_render' => array('webform_element_title_display'),
!     '#post_render' => array('webform_element_wrapper'),
!     '#webform_component' => $component,
!   );
! 
!   // Convert the user-entered options list into an array.
!   $default_value = $filter ? _webform_filter_values($component['value'], NULL, NULL, NULL, FALSE) : $component['value'];
!   $options = _webform_select_options($component, !$component['extra']['aslist'], $filter);
! 
!   if ($component['extra']['optrand']) {
!     _webform_shuffle_options($options);
!   }
! 
!   if ($component['extra']['aslist'] && !$component['extra']['multiple'] && !($component['mandatory'] && $default_value !== '')) {
!     $options = array('' => t('select...')) + $options;
!   }
! 
!   // Set the component options.
!   $element['#options'] = $options;
! 
!   // Set the default value.
!   if (isset($value)) {
!     if ($component['extra']['multiple']) {
!       // Set the value as an array.
!       $element['#default_value'] = array();
!       foreach ((array) $value as $key => $option_value) {
!         $element['#default_value'][] = $option_value;
!       }
!     }
!     else {
!       // Set the value as a single string.
!       $element['#default_value'] = '';
!       foreach ((array) $value as $option_value) {
!         $element['#default_value'] = $option_value;
!       }
!     }
!   }
!   elseif ($default_value !== '') {
!     // Convert default value to a list if necessary.
!     if ($component['extra']['multiple']) {
!       $varray = explode(',', $default_value);
!       foreach ($varray as $key => $v) {
!         $v = trim($v);
!         if ($v !== '') {
!           $element['#default_value'][] = $v;
!         }
!       }
!     }
!     else {
!       $element['#default_value'] = $default_value;
!     }
!   }
!   elseif ($component['extra']['multiple']) {
!     $element['#default_value'] = array();
!   }
! 
!   if ($component['extra']['other_option'] && module_exists('select_or_other')) {
!     // Set display as a select list:
!     $element['#type'] = 'select_or_other';
!     $element['#other'] = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
!     $element['#other_unknown_defaults'] = 'other';
!     $element['#other_delimiter'] = ', ';
!     if ($component['extra']['multiple']) {
!       $element['#multiple'] = TRUE;
!       $element['#select_type'] = 'checkboxes';
!     }
!     else {
!       $element['#multiple'] = FALSE;
!       $element['#select_type'] = 'radios';
!     }
!     if ($component['extra']['aslist']) {
!       $element['#select_type'] = 'select';
!     }
!   }
!   elseif ($component['extra']['aslist']) {
!     // Set display as a select list:
!     $element['#type'] = 'select';
!     if ($component['extra']['multiple']) {
!       $element['#size'] = 4;
!       $element['#multiple'] = TRUE;
!     }
!   }
!   else {
!     if ($component['extra']['multiple']) {
!       // Set display as a checkbox set.
!       $element['#type'] = 'checkboxes';
!       // Drupal 6 hack to properly render on multipage forms.
!       $element['#process'] = array('webform_expand_checkboxes', 'webform_expand_select_ids');
!     }
!     else {
!       // Set display as a radio set.
!       $element['#type'] = 'radios';
!       $element['#process'] = array('expand_radios', 'webform_expand_select_ids');
      }
-   }
  
!   return $element;
  }
  
  /**
   * Drupal 6 hack that properly *renders* checkboxes in multistep forms. This is
   * different than the value hack needed in Drupal 5, which is no longer needed.
   */
! function webform_expand_checkboxes($element) {
!   // Elements that have a value set are already in the form structure cause
!   // them not to be written when the expand_checkboxes function is called.
!   $default_value = array();
!   foreach (element_children($element) as $key) {
!     if (isset($element[$key]['#default_value'])) {
!       $default_value[$key] = $element[$key]['#default_value'];
!       unset($element[$key]);
!     }
!   }
! 
!   $element = expand_checkboxes($element);
! 
!   // Escape the values of checkboxes.
!   foreach (element_children($element) as $key) {
!     $element[$key]['#return_value'] = check_plain($element[$key]['#return_value']);
!     $element[$key]['#name'] = $element['#name'] . '[' . $element[$key]['#return_value'] . ']';
!     $element[$key]['#value_callback'] = 'webform_checkbox_value';
!     $element[$key]['#pre_render'][] = 'webform_checkbox_prerender';
!   }
! 
!   foreach ($default_value as $key => $val) {
!     $element[$key]['#default_value'] = $val;
!   }
!   return $element;
  }
  
  /**
--- 5648,6088 ----
  /**
   * Implementation of _webform_defaults_component().
   */
! function _webform_defaults_select()
! {
!     return array(
!         'name' => '',
!         'form_key' => NULL,
!         'mandatory' => 0,
!         'pid' => 0,
!         'weight' => 0,
!         'value' => '',
!         'extra' => array(
!             'items' => '',
!             'multiple' => NULL,
!             'aslist' => NULL,
!             'optrand' => 0,
!             'other_option' => NULL,
!             'other_text' => t('Other...'),
!             'title_display' => 0,
!             'description' => '',
!             'custom_keys' => FALSE,
!             'options_source' => '',
!         ),
!     );
  }
  
  /**
   * Implementation of _webform_theme_component().
   */
! function _webform_theme_select()
! {
!     return array(
!         'webform_display_select' => array(
!             'arguments' => array('element' => NULL),
!         ),
!     );
  }
  
  /**
   * Implementation of _webform_edit_component().
   */
! function _webform_edit_select($component)
! {
!     $form = array();
! 
!     drupal_add_js(drupal_get_path('module', 'webform') . '/js/select-admin.js', 'module', 'header', FALSE, TRUE, FALSE);
!     drupal_add_js(array('webform' => array('selectOptionsUrl' => url('webform/ajax/options/' . $component['nid']))), 'setting');
! 
!     $other = array();
!     if ($info = _webform_select_options_info())
!     {
!         $options = array('' => t('None'));
!         foreach ($info as $name => $source)
!         {
!             $options[$name] = $source['title'];
!         }
  
!         $other['options_source'] = array(
!             '#title' => t('Load a pre-built option list'),
!             '#type' => 'select',
!             '#options' => $options,
!             '#default_value' => $component['extra']['options_source'],
!             '#weight' => 1,
!             '#description' => t('Use a pre-built list of options rather than entering options manually. Options will not be editable if using pre-built list.'),
!             '#parents' => array('extra', 'options_source'),
!             '#weight' => 5,
!         );
!     }
  
!     if (module_exists('select_or_other'))
!     {
!         $other['other_option'] = array(
!             '#type' => 'checkbox',
!             '#title' => t('Allow "Other..." option'),
!             '#default_value' => $component['extra']['other_option'],
!             '#description' => t('Check this option if you want to allow users to enter an option not on the list.'),
!             '#parents' => array('extra', 'other_option'),
!             '#weight' => 2,
!         );
!         $other['other_text'] = array(
!             '#type' => 'textfield',
!             '#title' => t('Text for "Other..." option'),
!             '#default_value' => $component['extra']['other_text'],
!             '#description' => t('If allowing other options, enter text to be used for other-enabling option.'),
!             '#parents' => array('extra', 'other_text'),
!             '#weight' => 3,
!         );
!     }
  
!     if (module_exists('options_element'))
!     {
!         $options = _webform_select_options($component, FALSE, FALSE);
! 
!         $form['items'] = array(
!             '#type' => 'fieldset',
!             '#title' => t('Options'),
!             '#collapsible' => TRUE,
!             '#attributes' => array('class' => 'webform-options-element'),
!             '#element_validate' => array('_webform_edit_validate_options'),
!             '#weight' => 2,
!         );
! 
!         $form['items']['options'] = array(
!             '#type' => 'options',
!             '#limit' => 500,
!             '#optgroups' => $component['extra']['aslist'],
!             '#multiple' => $component['extra']['multiple'],
!             '#multiple_toggle' => t('Multiple'),
!             '#default_value' => $component['value'],
!             '#options' => $options,
!             '#key_type' => 'mixed',
!             '#key_type_toggle' => t('Customize keys (Advanced)'),
!             '#key_type_toggled' => $component['extra']['custom_keys'],
!             '#disabled' => !empty($component['extra']['options_source']),
!             '#weight' => 1,
!         );
  
!         $form['items']['options']['option_settings'] = $other;
!     }
!     else
!     {
!         $form['extra']['items'] = array(
!             '#type' => 'textarea',
!             '#title' => t('Options'),
!             '#default_value' => $component['extra']['items'],
!             '#description' => t('<strong>Key-value pairs MUST be specified as "safe_key|Some readable option"</strong>. Only alphanumeric characters and underscores are allowed as a key. One option per line. Option groups may be specified with &lt;Group Name&gt;. &lt;&gt; can be used to insert items at the root of the menu after specifying a group.') . theme('webform_token_help'),
!             '#cols' => 60,
!             '#rows' => 5,
!             '#weight' => 0,
!             '#required' => TRUE,
!             '#wysiwyg' => FALSE,
!             '#element_validate' => array('_webform_edit_validate_select'),
!         );
! 
!         if (!empty($component['extra']['options_source']))
!         {
!             $form['extra']['items']['#attributes'] = array('readonly' => 'readonly');
!         }
  
!         $form['extra'] = array_merge($form['extra'], $other);
!         $form['value'] = array(
!             '#type' => 'textfield',
!             '#title' => t('Default value'),
!             '#default_value' => $component['value'],
!             '#description' => t('The default value of the field. For multiple selects use commas to separate multiple defaults.') . theme('webform_token_help'),
!             '#size' => 60,
!             '#maxlength' => 256,
!             '#weight' => 0,
!         );
!         $form['extra']['multiple'] = array(
!             '#type' => 'checkbox',
!             '#title' => t('Multiple'),
!             '#default_value' => $component['extra']['multiple'],
!             '#description' => t('Check this option if the user should be allowed to choose multiple values.'),
!             '#weight' => 0,
!         );
      }
  
!     $form['display']['aslist'] = array(
!         '#type' => 'checkbox',
!         '#title' => t('Listbox'),
!         '#default_value' => $component['extra']['aslist'],
!         '#description' => t('Check this option if you want the select component to be of listbox type instead of radio buttons or checkboxes.'),
!         '#parents' => array('extra', 'aslist'),
      );
!     $form['display']['optrand'] = array(
!         '#type' => 'checkbox',
!         '#title' => t('Randomize options'),
!         '#default_value' => $component['extra']['optrand'],
!         '#description' => t('Randomizes the order of the options when they are displayed in the form.'),
!         '#parents' => array('extra', 'optrand'),
      );
  
!     return $form;
  }
  
  /**
   * Element validation callback. Ensure keys are not duplicated.
   */
! function _webform_edit_validate_select($element, &$form_state)
! {
!     // Check for duplicate key values to prevent unexpected data loss. Require
!     // all options to include a safe_key.
!     if (!empty($element['#value']))
!     {
!         $lines = explode("\n", trim($element['#value']));
!         $existing_keys = array();
!         $duplicate_keys = array();
!         $missing_keys = array();
!         $long_keys = array();
!         $group = '';
!         foreach ($lines as $line)
!         {
!             $matches = array();
!             $line = trim($line);
!             if (preg_match('/^\<([^>]*)\>$/', $line, $matches))
!             {
!                 $group = $matches[1];
!                 $key = NULL; // No need to store group names.
!             }
!             elseif (preg_match('/^([^|]*)\|(.*)$/', $line, $matches))
!             {
!                 $key = $matches[1];
!                 if (strlen($key) > 128)
!                 {
!                     $long_keys[] = $key;
!                 }
!             }
!             else
!             {
!                 $missing_keys[] = $line;
!             }
! 
!             if (isset($key))
!             {
!                 if (isset($existing_keys[$group][$key]))
!                 {
!                     $duplicate_keys[$key] = $key;
!                 }
!                 else
!                 {
!                     $existing_keys[$group][$key] = $key;
!                 }
!             }
!         }
  
!         if (!empty($missing_keys))
!         {
!             form_error($element, t('Every option must have a key specified. Specify each option as "safe_key|Some readable option".'));
!         }
! 
!         if (!empty($long_keys))
!         {
!             form_error($element, t('Option keys must be less than 128 characters. The following keys exceed this limit:') . theme('item_list', $long_keys));
!         }
  
!         if (!empty($duplicate_keys))
!         {
!             form_error($element, t('Options within the select list must be unique. The following keys have been used multiple times:') . theme('item_list', $duplicate_keys));
!         }
!     }
  
!     return TRUE;
  }
  
  /**
   * Set the appropriate webform values when using the options element module.
   */
! function _webform_edit_validate_options($element, &$form_state)
! {
!     $key = end($element['#parents']);
!     $element_options = $form_state['values'][$key]['options'];
!     unset($form_state['values'][$key]);
! 
!     $form_state['values']['extra'][$key] = form_options_to_text($element_options['options'], 'custom');
! 
!     // Options saved for select components.
!     if ($key == 'items')
!     {
!         $form_state['values']['extra']['multiple'] = $element_options['multiple'];
!         $form_state['values']['extra']['custom_keys'] = $element_options['custom_keys'];
!         $form_state['values']['value'] = is_array($element_options['default_value']) ? implode(', ', $element_options['default_value']) : $element_options['default_value'];
!     }
!     // Options saved for grid components.
!     else
!     {
!         $form_state['values']['extra']['custom_' . rtrim($key, 's') . '_keys'] = $element_options['custom_keys'];
!     }
  }
  
  /**
   * Implementation of _webform_render_component().
   */
! function _webform_render_select($component, $value = NULL, $filter = TRUE)
! {
!     $element = array(
!         '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'],
!         '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
!         '#required' => $component['mandatory'],
!         '#weight' => $component['weight'],
!         '#description' => $filter ? _webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
!         '#theme_wrappers' => array('webform_element_wrapper'),
!         '#pre_render' => array('webform_element_title_display'),
!         '#post_render' => array('webform_element_wrapper'),
!         '#webform_component' => $component,
!     );
! 
!     // Convert the user-entered options list into an array.
!     $default_value = $filter ? _webform_filter_values($component['value'], NULL, NULL, NULL, FALSE) : $component['value'];
!     $options = _webform_select_options($component, !$component['extra']['aslist'], $filter);
! 
!     if ($component['extra']['optrand'])
!     {
!         _webform_shuffle_options($options);
!     }
! 
!     if ($component['extra']['aslist'] && !$component['extra']['multiple'] && !($component['mandatory'] && $default_value !== ''))
!     {
!         $options = array('' => t('select...')) + $options;
!     }
! 
!     // Set the component options.
!     $element['#options'] = $options;
! 
!     // Set the default value.
!     if (isset($value))
!     {
!         if ($component['extra']['multiple'])
!         {
!             // Set the value as an array.
!             $element['#default_value'] = array();
!             foreach ((array) $value as $key => $option_value)
!             {
!                 $element['#default_value'][] = $option_value;
!             }
!         }
!         else
!         {
!             // Set the value as a single string.
!             $element['#default_value'] = '';
!             foreach ((array) $value as $option_value)
!             {
!                 $element['#default_value'] = $option_value;
!             }
!         }
!     }
!     elseif ($default_value !== '')
!     {
!         // Convert default value to a list if necessary.
!         if ($component['extra']['multiple'])
!         {
!             $varray = explode(',', $default_value);
!             foreach ($varray as $key => $v)
!             {
!                 $v = trim($v);
!                 if ($v !== '')
!                 {
!                     $element['#default_value'][] = $v;
!                 }
!             }
!         }
!         else
!         {
!             $element['#default_value'] = $default_value;
!         }
!     }
!     elseif ($component['extra']['multiple'])
!     {
!         $element['#default_value'] = array();
!     }
! 
!     if ($component['extra']['other_option'] && module_exists('select_or_other'))
!     {
!         // Set display as a select list:
!         $element['#type'] = 'select_or_other';
!         $element['#other'] = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
!         $element['#other_unknown_defaults'] = 'other';
!         $element['#other_delimiter'] = ', ';
!         if ($component['extra']['multiple'])
!         {
!             $element['#multiple'] = TRUE;
!             $element['#select_type'] = 'checkboxes';
!         }
!         else
!         {
!             $element['#multiple'] = FALSE;
!             $element['#select_type'] = 'radios';
!         }
!         if ($component['extra']['aslist'])
!         {
!             $element['#select_type'] = 'select';
!         }
!     }
!     elseif ($component['extra']['aslist'])
!     {
!         // Set display as a select list:
!         $element['#type'] = 'select';
!         if ($component['extra']['multiple'])
!         {
!             $element['#size'] = 4;
!             $element['#multiple'] = TRUE;
!         }
!     }
!     else
!     {
!         if ($component['extra']['multiple'])
!         {
!             // Set display as a checkbox set.
!             $element['#type'] = 'checkboxes';
!             // Drupal 6 hack to properly render on multipage forms.
!             $element['#process'] = array('webform_expand_checkboxes', 'webform_expand_select_ids');
!         }
!         else
!         {
!             // Set display as a radio set.
!             $element['#type'] = 'radios';
!             $element['#process'] = array('expand_radios', 'webform_expand_select_ids');
!         }
      }
  
!     return $element;
  }
  
  /**
   * Drupal 6 hack that properly *renders* checkboxes in multistep forms. This is
   * different than the value hack needed in Drupal 5, which is no longer needed.
   */
! function webform_expand_checkboxes($element)
! {
!     // Elements that have a value set are already in the form structure cause
!     // them not to be written when the expand_checkboxes function is called.
!     $default_value = array();
!     foreach (element_children($element) as $key)
!     {
!         if (isset($element[$key]['#default_value']))
!         {
!             $default_value[$key] = $element[$key]['#default_value'];
!             unset($element[$key]);
!         }
!     }
! 
!     $element = expand_checkboxes($element);
! 
!     // Escape the values of checkboxes.
!     foreach (element_children($element) as $key)
!     {
!         $element[$key]['#return_value'] = check_plain($element[$key]['#return_value']);
!         $element[$key]['#name'] = $element['#name'] . '[' . $element[$key]['#return_value'] . ']';
!         $element[$key]['#value_callback'] = 'webform_checkbox_value';
!         $element[$key]['#pre_render'][] = 'webform_checkbox_prerender';
!     }
! 
!     foreach ($default_value as $key => $val)
!     {
!         $element[$key]['#default_value'] = $val;
!     }
!     return $element;
  }
  
  /**
***************
*** 403,413 ****
   *
   * @see theme_checkbox()
   */
! function webform_checkbox_prerender($element) {
!   if ($element['#value'] === '0') {
!     $element['#value'] = TRUE;
!   }
!   return $element;
  }
  
  /**
--- 6094,6106 ----
   *
   * @see theme_checkbox()
   */
! function webform_checkbox_prerender($element)
! {
!     if ($element['#value'] === '0')
!     {
!         $element['#value'] = TRUE;
!     }
!     return $element;
  }
  
  /**
***************
*** 419,466 ****
   *
   * @see form_type_checkbox_value()
   */
! function webform_checkbox_value($form, $edit = FALSE) {
!   if ($edit !== FALSE) {
!     if (empty($form['#disabled'])) {
!       return $edit !== NULL ? $form['#return_value'] : NULL; // 0 in core.
!     }
!     else {
!       return $form['#default_value'];
      }
-   }
  }
  
  /**
   * FAPI process function to rename IDs attached to checkboxes and radios.
   */
! function webform_expand_select_ids($element) {
!   $id = $element['#id'] = str_replace('_', '-', _webform_safe_name(strip_tags($element['#id'])));
!   $delta = 0;
!   foreach (element_children($element) as $key) {
!     $delta++;
!     // Convert the #id for each child to a safe name, regardless of key.
!     $element[$key]['#id'] = $id . '-' . $delta;
! 
!     // Prevent scripts or CSS in the labels for each checkbox or radio.
!     $element[$key]['#title'] = _webform_filter_xss($element[$key]['#title']);
!   }
!   return $element;
  }
  
  /**
   * Implementation of _webform_display_component().
   */
! function _webform_display_select($component, $value, $format = 'html') {
!   return array(
!     '#title' => $component['name'],
!     '#weight' => $component['weight'],
!     '#theme' => 'webform_display_select',
!     '#theme_wrappers' => $format == 'html' ? array('webform_element', 'webform_element_wrapper') : array('webform_element_text'),
!     '#post_render' => array('webform_element_wrapper'),
!     '#format' => $format,
!     '#value' => (array) $value,
!     '#webform_component' => $component,
!   );
  }
  
  /**
--- 6112,6166 ----
   *
   * @see form_type_checkbox_value()
   */
! function webform_checkbox_value($form, $edit = FALSE)
! {
!     if ($edit !== FALSE)
!     {
!         if (empty($form['#disabled']))
!         {
!             return $edit !== NULL ? $form['#return_value'] : NULL; // 0 in core.
!         }
!         else
!         {
!             return $form['#default_value'];
!         }
      }
  }
  
  /**
   * FAPI process function to rename IDs attached to checkboxes and radios.
   */
! function webform_expand_select_ids($element)
! {
!     $id = $element['#id'] = str_replace('_', '-', _webform_safe_name(strip_tags($element['#id'])));
!     $delta = 0;
!     foreach (element_children($element) as $key)
!     {
!         $delta++;
!         // Convert the #id for each child to a safe name, regardless of key.
!         $element[$key]['#id'] = $id . '-' . $delta;
! 
!         // Prevent scripts or CSS in the labels for each checkbox or radio.
!         $element[$key]['#title'] = _webform_filter_xss($element[$key]['#title']);
!     }
!     return $element;
  }
  
  /**
   * Implementation of _webform_display_component().
   */
! function _webform_display_select($component, $value, $format = 'html')
! {
!     return array(
!         '#title' => $component['name'],
!         '#weight' => $component['weight'],
!         '#theme' => 'webform_display_select',
!         '#theme_wrappers' => $format == 'html' ? array('webform_element', 'webform_element_wrapper') : array('webform_element_text'),
!         '#post_render' => array('webform_element_wrapper'),
!         '#format' => $format,
!         '#value' => (array) $value,
!         '#webform_component' => $component,
!     );
  }
  
  /**
***************
*** 468,747 ****
   *
   * Convert FAPI 0/1 values into something saveable.
   */
! function _webform_submit_select($component, $value) {
!   // Build a list of all valid keys expected to be submitted.
!   $options = _webform_select_options($component, TRUE);
! 
!   $return = NULL;
!   if (is_array($value)) {
!     $return = array();
!     foreach ($value as $key => $option_value) {
!       // Handle options that are specified options.
!       if ($option_value !== '' && isset($options[$option_value])) {
!         // Checkboxes submit a value of FALSE when unchecked. A checkbox with
!         // a value of '0' is valid, so we can't use empty() here.
!         if ($option_value === FALSE && !$component['extra']['aslist'] && $component['extra']['multiple']) {
!           unset($value[$option_value]);
!         }
!         else {
!           $return[] = $option_value;
!         }
!       }
!       // Handle options that are added through the "other" field.
!       elseif ($component['extra']['other_option'] && module_exists('select_or_other')) {
!         $return[] = $option_value;
!       }
!     }
!   }
!   elseif (is_string($value)) {
!     $return = $value;
!   }
  
!   return $return;
  }
  
  /**
   * Format the text output for this component.
   */
! function theme_webform_display_select($element) {
!   $component = $element['#webform_component'];
! 
!   // Convert submitted 'safe' values to un-edited, original form.
!   $options = _webform_select_options($component, TRUE);
  
!   $items = array();
!   if ($component['extra']['multiple']) {
!     foreach ((array) $element['#value'] as $option_value) {
!       if ($option_value !== '') {
!         // Administer provided values.
!         if (isset($options[$option_value])) {
!           $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$option_value]) : $options[$option_value];
!         }
!         // User-specified in the "other" field.
!         else {
!           $items[] = $element['#format'] == 'html' ? check_plain($option_value) : $option_value;
!         }
!       }
!     }
!   }
!   else {
!     if (isset($element['#value'][0]) && $element['#value'][0] !== '') {
!       // Administer provided values.
!       if (isset($options[$element['#value'][0]])) {
!         $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$element['#value'][0]]) : $options[$element['#value'][0]];
!       }
!       // User-specified in the "other" field.
!       else {
!         $items[] = $element['#format'] == 'html' ? check_plain($element['#value'][0]) : $element['#value'][0];
!       }
!     }
!   }
! 
!   if ($element['#format'] == 'html') {
!     $output = count($items) > 1 ? theme('item_list', $items) : (isset($items[0]) ? $items[0] : ' ');
!   }
!   else {
!     if (count($items) > 1) {
!       foreach ($items as $key => $item) {
!         $items[$key] = ' - ' . $item;
!       }
!       $output = implode("\n", $items);
      }
!     else {
!       $output = isset($items[0]) ? $items[0] : ' ';
      }
-   }
  
!   return $output;
  }
  
  /**
   * Implementation of _webform_analysis_component().
   */
! function _webform_analysis_select($component, $sids = array(), $single = FALSE) {
!   $options = _webform_select_options($component, TRUE);
!   $show_other_results = $single;
! 
!   $sid_placeholders = count($sids) ? array_fill(0, count($sids), "'%s'") : array();
!   $sid_filter = count($sids) ? " AND sid IN (" . implode(",", $sid_placeholders) . ")" : "";
! 
!   $option_operator = $show_other_results ? 'NOT IN' : 'IN';
!   $placeholders = count($options) ? array_fill(0, count($options), "'%s'") : array();
!   $query = 'SELECT data, count(data) as datacount ' .
!     ' FROM {webform_submitted_data} ' .
!     ' WHERE nid = %d ' .
!     ' AND cid = %d ' .
!     " AND data != ''" . $sid_filter .
!     ($placeholders ? ' AND data ' . $option_operator . ' (' . implode(',', $placeholders) . ')' : '') .
!     ' GROUP BY data ';
! 
!   $count_query = 'SELECT count(*) as datacount ' .
!     ' FROM {webform_submitted_data} ' .
!     ' WHERE nid = %d ' .
!     ' AND cid = %d ' .
!     " AND data != ''" . $sid_filter;
! 
!   $result = db_query($query, array_merge(array($component['nid'], $component['cid']), $sids, array_keys($options)));
!   $rows = array();
!   $normal_count = 0;
!   while ($data = db_fetch_array($result)) {
!     $display_option = $single ? $data['data'] : $options[$data['data']];
!     $rows[$data['data']] = array(_webform_filter_xss($display_option), $data['datacount']);
!     $normal_count += $data['datacount'];
!   }
! 
!   if (!$show_other_results) {
!     // Order the results according to the normal options array.
!     $ordered_rows = array();
!     foreach (array_intersect_key($options, $rows) as $key => $label) {
!       $ordered_rows[] = $rows[$key];
!     }
! 
!     // Add a row for any unknown or user-entered values.
!     if ($component['extra']['other_option']) {
!       $full_count = db_result(db_query($count_query, array_merge(array($component['nid'], $component['cid']), $sids)));
!       $other_count = $full_count - $normal_count;
!       $display_option = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
!       $other_text = $other_count ? $other_count . ' (' . l(t('view'), 'node/' . $component['nid'] . '/webform-results/analysis/' . $component['cid']) . ')' : $other_count;
!       $ordered_rows[] = array($display_option, $other_text);
      }
  
-     $rows = $ordered_rows;
-   }
  
!   return $rows;
  }
  
  /**
   * Implementation of _webform_table_component().
   */
! function _webform_table_select($component, $value) {
!   // Convert submitted 'safe' values to un-edited, original form.
!   $options = _webform_select_options($component, TRUE);
! 
!   $value = (array) $value;
!   $items = array();
!   // Set the value as a single string.
!   foreach ($value as $option_value) {
!     if ($option_value !== '') {
!       if (isset($options[$option_value])) {
!         $items[] = _webform_filter_xss($options[$option_value]);
!       }
!       else {
!         $items[] = check_plain($option_value);
!       }
      }
-   }
  
!   return implode('<br />', $items);
  }
  
  /**
   * Implementation of _webform_csv_headers_component().
   */
! function _webform_csv_headers_select($component, $export_options) {
!   $headers = array(
!     0 => array(),
!     1 => array(),
!     2 => array(),
!   );
! 
!   if ($component['extra']['multiple'] && $export_options['select_format'] == 'separate') {
!     $headers[0][] = '';
!     $headers[1][] = $component['name'];
!     $items = _webform_select_options($component, TRUE, FALSE);
!     if ($component['extra']['other_option']) {
!       $other_label = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
!       $items[$other_label] = $other_label;
!     }
!     $count = 0;
!     foreach ($items as $key => $item) {
!       // Empty column per sub-field in main header.
!       if ($count != 0) {
          $headers[0][] = '';
          $headers[1][] = '';
!       }
!       $headers[2][] = $key;
!       $count++;
!     }
!   }
!   else {
!     $headers[0][] = '';
!     $headers[1][] = '';
!     $headers[2][] = $component['name'];
!   }
!   return $headers;
  }
  
  /**
   * Implementation of _webform_csv_data_component().
   */
! function _webform_csv_data_select($component, $export_options, $value) {
!   $options = _webform_select_options($component, TRUE, FALSE);
!   $return = array();
! 
!   if ($component['extra']['multiple']) {
!     foreach ($options as $key => $item) {
!       $index = array_search($key, (array) $value);
!       if ($index !== FALSE) {
!         if ($export_options['select_format'] == 'separate') {
!           $return[] = 'X';
!         }
!         else {
!           $return[] = $export_options['select_keys'] ? $key : $item;
!         }
!         unset($value[$index]);
!       }
!       elseif ($export_options['select_format'] == 'separate') {
!         $return[] = '';
!       }
!     }
! 
!     // Any remaining items in the $value array will be user-added options.
!     if ($component['extra']['other_option']) {
!       $return[] = count($value) ? implode(',', $value) : '';
!     }
!   }
!   else {
!     $key = $value[0];
!     if ($export_options['select_keys']){
!       $return = $key;
!     }
!     else {
!       $return = isset($options[$key]) ? $options[$key] : $key;
!     }
!   }
! 
!   if ($component['extra']['multiple'] && $export_options['select_format'] == 'compact') {
!     $return = implode(',', (array) $return);
!   }
  
!   return $return;
  }
  
  /**
   * Menu callback; Return a predefined list of select options as JSON.
   */
! function webform_select_options_ajax($source_name = '') {
!   $info = _webform_select_options_info();
  
!   $component['extra']['options_source'] = $source_name;
!   if ($source_name && isset($info[$source_name])) {
!     $options = _webform_select_options_to_text(_webform_select_options($component, !$component['extra']['aslist'], FALSE));
!   }
!   else {
!     $options = '';
!   }
! 
!   $return = array(
!     'elementId' => module_exists('options_element') ? 'edit-items-options-options-field-widget' : 'edit-extra-items',
!     'options' => $options,
!   );
  
!   drupal_json($return);
  }
  
- 
  /**
   * Generate a list of options for a select list.
   */
--- 6168,6570 ----
   *
   * Convert FAPI 0/1 values into something saveable.
   */
! function _webform_submit_select($component, $value)
! {
!     // Build a list of all valid keys expected to be submitted.
!     $options = _webform_select_options($component, TRUE);
! 
!     $return = NULL;
!     if (is_array($value))
!     {
!         $return = array();
!         foreach ($value as $key => $option_value)
!         {
!             // Handle options that are specified options.
!             if ($option_value !== '' && isset($options[$option_value]))
!             {
!                 // Checkboxes submit a value of FALSE when unchecked. A checkbox with
!                 // a value of '0' is valid, so we can't use empty() here.
!                 if ($option_value === FALSE && !$component['extra']['aslist'] && $component['extra']['multiple'])
!                 {
!                     unset($value[$option_value]);
!                 }
!                 else
!                 {
!                     $return[] = $option_value;
!                 }
!             }
!             // Handle options that are added through the "other" field.
!             elseif ($component['extra']['other_option'] && module_exists('select_or_other'))
!             {
!                 $return[] = $option_value;
!             }
!         }
!     }
!     elseif (is_string($value))
!     {
!         $return = $value;
!     }
  
!     return $return;
  }
  
  /**
   * Format the text output for this component.
   */
! function theme_webform_display_select($element)
! {
!     $component = $element['#webform_component'];
! 
!     // Convert submitted 'safe' values to un-edited, original form.
!     $options = _webform_select_options($component, TRUE);
! 
!     $items = array();
!     if ($component['extra']['multiple'])
!     {
!         foreach ((array) $element['#value'] as $option_value)
!         {
!             if ($option_value !== '')
!             {
!                 // Administer provided values.
!                 if (isset($options[$option_value]))
!                 {
!                     $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$option_value]) : $options[$option_value];
!                 }
!                 // User-specified in the "other" field.
!                 else
!                 {
!                     $items[] = $element['#format'] == 'html' ? check_plain($option_value) : $option_value;
!                 }
!             }
!         }
!     }
!     else
!     {
!         if (isset($element['#value'][0]) && $element['#value'][0] !== '')
!         {
!             // Administer provided values.
!             if (isset($options[$element['#value'][0]]))
!             {
!                 $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$element['#value'][0]]) : $options[$element['#value'][0]];
!             }
!             // User-specified in the "other" field.
!             else
!             {
!                 $items[] = $element['#format'] == 'html' ? check_plain($element['#value'][0]) : $element['#value'][0];
!             }
!         }
!     }
  
!     if ($element['#format'] == 'html')
!     {
!         $output = count($items) > 1 ? theme('item_list', $items) : (isset($items[0]) ? $items[0] : ' ');
      }
!     else
!     {
!         if (count($items) > 1)
!         {
!             foreach ($items as $key => $item)
!             {
!                 $items[$key] = ' - ' . $item;
!             }
!             $output = implode("\n", $items);
!         }
!         else
!         {
!             $output = isset($items[0]) ? $items[0] : ' ';
!         }
      }
  
!     return $output;
  }
  
  /**
   * Implementation of _webform_analysis_component().
   */
! function _webform_analysis_select($component, $sids = array(), $single = FALSE)
! {
!     $options = _webform_select_options($component, TRUE);
!     $show_other_results = $single;
! 
!     $sid_placeholders = count($sids) ? array_fill(0, count($sids), "'%s'") : array();
!     $sid_filter = count($sids) ? " AND sid IN (" . implode(",", $sid_placeholders) . ")" : "";
! 
!     $option_operator = $show_other_results ? 'NOT IN' : 'IN';
!     $placeholders = count($options) ? array_fill(0, count($options), "'%s'") : array();
!     $query = 'SELECT data, count(data) as datacount ' .
!             ' FROM {webform_submitted_data} ' .
!             ' WHERE nid = %d ' .
!             ' AND cid = %d ' .
!             " AND data != ''" . $sid_filter .
!             ($placeholders ? ' AND data ' . $option_operator . ' (' . implode(',', $placeholders) . ')' : '') .
!             ' GROUP BY data ';
! 
!     $count_query = 'SELECT count(*) as datacount ' .
!             ' FROM {webform_submitted_data} ' .
!             ' WHERE nid = %d ' .
!             ' AND cid = %d ' .
!             " AND data != ''" . $sid_filter;
! 
!     $result = db_query($query, array_merge(array($component['nid'], $component['cid']), $sids, array_keys($options)));
!     $rows = array();
!     $normal_count = 0;
! 
!     while ($data = db_fetch_array($result))
!     {
!         $display_option = $single ? $data['data'] : $options[$data['data']];
!         $rows[$data['data']] = array(_webform_filter_xss($display_option), $data['datacount']);
!         $normal_count += $data['datacount'];
      }
  
  
!     if (!$show_other_results)
!     {
!         // Order the results according to the normal options array.
!         $ordered_rows = array();
!         foreach (array_intersect_key($options, $rows) as $key => $label)
!         {
!             $ordered_rows[] = $rows[$key];
!         }
! 
!         // Add a row for any unknown or user-entered values.
!         if ($component['extra']['other_option'])
!         {
!             $full_count = db_result(db_query($count_query, array_merge(array($component['nid'], $component['cid']), $sids)));
!             $other_count = $full_count - $normal_count;
!             $display_option = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
!             $other_text = $other_count ? $other_count . ' (' . l(t('view'), 'node/' . $component['nid'] . '/webform-results/analysis/' . $component['cid']) . ')' : $other_count;
!             $ordered_rows[] = array($display_option, $other_text);
!         }
! 
!         //Draw the graph here
!         //Get random file name
!         $characters = array(
!             "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M",
!             "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
!             "a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "l", "m",
!             "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
!             "1", "2", "3", "4", "5", "6", "7", "8", "9", "0");
! 
!         //make an "empty container" or array for our keys
!         $keys = array();
! 
!         $character_count = 10;
! 
!         while (count($keys) < $character_count)
!         {
!             //"0" because we use this to FIND ARRAY KEYS which has a 0 value
!             //"-1" because were only concerned of number of keys which is 32 not 33
!             //count($characters) = 33
!             $x = mt_rand(0, count($characters) - 1);
!             if (!in_array($x, $keys))
!             {
!                 $keys[] = $x;
!             }
!         }
! 
!         foreach ($keys as $key)
!         {
!             $random_chars .= $characters[$key];
!         }
! 
!         $module_path = drupal_get_path('module', 'webform');
!         $directory = "$module_path/analysis_images/";
! 
!         //Create the directory if it does not exist create it
!         if (!file_exists($directory))
!         {
!             mkdir($directory);
!         }
! 
!         $temp_png = "$directory/$random_chars.png";
! 
!         $graph = new PHPlot(600, 450);
!         $graph->SetDataValues(array_values($rows));
! 
!         $graph->SetPlotType('pie');
!         $graph->SetDataType('text-data-single');
! 
!         $graph->SetXTickPos('none');
!         $graph->SetXTickLabelPos('none');
!         $legend = array_keys($rows);
!         $graph->SetLegend($legend);
!         //Specify this random file to be the output file of the image
!         $graph->SetOutputfile($temp_png);
!         $graph->SetTitle("Results Chart");
!         $graph->SetFileFormat("png"); // is default anyway
!         //Don't draw the image yet
!         $graph->SetPrintImage(0);
!         //Don't output the file headers - we're inline!
!         $graph->SetIsInline("1");
!         //Draw the chart
!         $graph->DrawGraph();
!         //Now, the image is ready - print it to the file!
!         $graph->PrintImage();
! 
!         global $base_url;
!         $ordered_rows[] = array("<img src='$base_url/$temp_png'>", "");
! 
!         $rows = $ordered_rows;
!     }
! 
!     return $rows;
  }
  
  /**
   * Implementation of _webform_table_component().
   */
! function _webform_table_select($component, $value)
! {
!     // Convert submitted 'safe' values to un-edited, original form.
!     $options = _webform_select_options($component, TRUE);
! 
!     $value = (array) $value;
!     $items = array();
!     // Set the value as a single string.
!     foreach ($value as $option_value)
!     {
!         if ($option_value !== '')
!         {
!             if (isset($options[$option_value]))
!             {
!                 $items[] = _webform_filter_xss($options[$option_value]);
!             }
!             else
!             {
!                 $items[] = check_plain($option_value);
!             }
!         }
      }
  
!     return implode('<br />', $items);
  }
  
  /**
   * Implementation of _webform_csv_headers_component().
   */
! function _webform_csv_headers_select($component, $export_options)
! {
!     $headers = array(
!         0 => array(),
!         1 => array(),
!         2 => array(),
!     );
! 
!     if ($component['extra']['multiple'] && $export_options['select_format'] == 'separate')
!     {
!         $headers[0][] = '';
!         $headers[1][] = $component['name'];
!         $items = _webform_select_options($component, TRUE, FALSE);
!         if ($component['extra']['other_option'])
!         {
!             $other_label = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...');
!             $items[$other_label] = $other_label;
!         }
!         $count = 0;
!         foreach ($items as $key => $item)
!         {
!             // Empty column per sub-field in main header.
!             if ($count != 0)
!             {
!                 $headers[0][] = '';
!                 $headers[1][] = '';
!             }
!             $headers[2][] = $key;
!             $count++;
!         }
!     }
!     else
!     {
          $headers[0][] = '';
          $headers[1][] = '';
!         $headers[2][] = $component['name'];
!     }
!     return $headers;
  }
  
  /**
   * Implementation of _webform_csv_data_component().
   */
! function _webform_csv_data_select($component, $export_options, $value)
! {
!     $options = _webform_select_options($component, TRUE, FALSE);
!     $return = array();
! 
!     if ($component['extra']['multiple'])
!     {
!         foreach ($options as $key => $item)
!         {
!             $index = array_search($key, (array) $value);
!             if ($index !== FALSE)
!             {
!                 if ($export_options['select_format'] == 'separate')
!                 {
!                     $return[] = 'X';
!                 }
!                 else
!                 {
!                     $return[] = $export_options['select_keys'] ? $key : $item;
!                 }
!                 unset($value[$index]);
!             }
!             elseif ($export_options['select_format'] == 'separate')
!             {
!                 $return[] = '';
!             }
!         }
! 
!         // Any remaining items in the $value array will be user-added options.
!         if ($component['extra']['other_option'])
!         {
!             $return[] = count($value) ? implode(',', $value) : '';
!         }
!     }
!     else
!     {
!         $key = $value[0];
!         if ($export_options['select_keys'])
!         {
!             $return = $key;
!         }
!         else
!         {
!             $return = isset($options[$key]) ? $options[$key] : $key;
!         }
!     }
  
!     if ($component['extra']['multiple'] && $export_options['select_format'] == 'compact')
!     {
!         $return = implode(',', (array) $return);
!     }
! 
!     return $return;
  }
  
  /**
   * Menu callback; Return a predefined list of select options as JSON.
   */
! function webform_select_options_ajax($source_name = '')
! {
!     $info = _webform_select_options_info();
! 
!     $component['extra']['options_source'] = $source_name;
!     if ($source_name && isset($info[$source_name]))
!     {
!         $options = _webform_select_options_to_text(_webform_select_options($component, !$component['extra']['aslist'], FALSE));
!     }
!     else
!     {
!         $options = '';
!     }
  
!     $return = array(
!         'elementId' => module_exists('options_element') ? 'edit-items-options-options-field-widget' : 'edit-extra-items',
!         'options' => $options,
!     );
  
!     drupal_json($return);
  }
  
  /**
   * Generate a list of options for a select list.
   */
***************
*** 745,779 ****
  /**
   * Generate a list of options for a select list.
   */
! function _webform_select_options($component, $flat = FALSE, $filter = TRUE) {
!   if ($component['extra']['options_source']) {
!     $options = _webform_select_options_callback($component['extra']['options_source'], $component, $flat, $filter);
!   }
!   else {
!     $options = _webform_select_options_from_text($component['extra']['items'], $flat, $filter);
!   }
  
!   return isset($options) ? $options : array();
  }
  
  /**
   * Load Webform select option info from 3rd party modules.
   */
! function _webform_select_options_info() {
!   static $info;
!   if (!isset($info)) {
!     $info = array();
! 
!     foreach (module_implements('webform_select_options_info') as $module) {
!       $additions = module_invoke($module, 'webform_select_options_info');
!       foreach ($additions as $key => $addition) {
!         $additions[$key]['module'] = $module;
!       }
!       $info = array_merge($info, $additions);
!     }
!     drupal_alter('webform_select_options_info', $info);
!   }
!   return $info;
  }
  
  /**
--- 6568,6609 ----
  /**
   * Generate a list of options for a select list.
   */
! function _webform_select_options($component, $flat = FALSE, $filter = TRUE)
! {
!     if ($component['extra']['options_source'])
!     {
!         $options = _webform_select_options_callback($component['extra']['options_source'], $component, $flat, $filter);
!     }
!     else
!     {
!         $options = _webform_select_options_from_text($component['extra']['items'], $flat, $filter);
!     }
  
!     return isset($options) ? $options : array();
  }
  
  /**
   * Load Webform select option info from 3rd party modules.
   */
! function _webform_select_options_info()
! {
!     static $info;
!     if (!isset($info))
!     {
!         $info = array();
! 
!         foreach (module_implements('webform_select_options_info') as $module)
!         {
!             $additions = module_invoke($module, 'webform_select_options_info');
!             foreach ($additions as $key => $addition)
!             {
!                 $additions[$key]['module'] = $module;
!             }
!             $info = array_merge($info, $additions);
!         }
!         drupal_alter('webform_select_options_info', $info);
!     }
!     return $info;
  }
  
  /**
***************
*** 788,814 ****
   * @param $filter
   *   Whether information returned should be sanitized. Defaults to TRUE.
   */
! function _webform_select_options_callback($name, $component, $flat = FALSE, $filter = TRUE) {
!   $info = _webform_select_options_info();
! 
!   // Include any necessary files.
!   if (isset($info[$name]['file'])) {
!     $pathinfo = pathinfo($info[$name]['file']);
!     $path = ($pathinfo['dirname'] ? $pathinfo['dirname'] . '/' : '') . basename($pathinfo['basename'], '.' . $pathinfo['extension']);
!     module_load_include($pathinfo['extension'], $info[$name]['module'], $path);
!   }
! 
!   // Execute the callback function.
!   if (isset($info[$name]['options callback']) && function_exists($info[$name]['options callback'])) {
!     $function = $info[$name]['options callback'];
! 
!     $options_args = array();
!     if (isset($info[$name]['options arguments'])) {
!       $arguments = $info[$name]['options arguments'];
      }
  
!     return $function($component, $flat, $filter, $arguments);
!   }
  }
  
  /**
--- 6618,6648 ----
   * @param $filter
   *   Whether information returned should be sanitized. Defaults to TRUE.
   */
! function _webform_select_options_callback($name, $component, $flat = FALSE, $filter = TRUE)
! {
!     $info = _webform_select_options_info();
! 
!     // Include any necessary files.
!     if (isset($info[$name]['file']))
!     {
!         $pathinfo = pathinfo($info[$name]['file']);
!         $path = ($pathinfo['dirname'] ? $pathinfo['dirname'] . '/' : '') . basename($pathinfo['basename'], '.' . $pathinfo['extension']);
!         module_load_include($pathinfo['extension'], $info[$name]['module'], $path);
      }
  
!     // Execute the callback function.
!     if (isset($info[$name]['options callback']) && function_exists($info[$name]['options callback']))
!     {
!         $function = $info[$name]['options callback'];
! 
!         $options_args = array();
!         if (isset($info[$name]['options arguments']))
!         {
!             $arguments = $info[$name]['options arguments'];
!         }
! 
!         return $function($component, $flat, $filter, $arguments);
!     }
  }
  
  /**
***************
*** 822,877 ****
   * @param $filter
   *   Optional. Whether or not to filter returned values.
   */
! function _webform_select_options_from_text($text, $flat = FALSE, $filter = TRUE) {
!   static $option_cache = array();
  
!   // Keep each processed option block in an array indexed by the MD5 hash of
!   // the option text and the value of the $flat variable.
!   $md5 = md5($text);
! 
!   // Check if this option block has been previously processed.
!   if (!isset($option_cache[$flat][$md5])) {
!     $options = array();
!     $rows = array_filter(explode("\n", trim($text)));
!     $group = NULL;
!     foreach ($rows as $option) {
!       $option = trim($option);
!       /**
!        * If the Key of the option is within < >, treat as an optgroup
!        *
!        * <Group 1>
!        *   creates an optgroup with the label "Group 1"
!        *
!        * <>
!        *   Unsets the current group, allowing items to be inserted at the root element.
!        */
!       if (preg_match('/^\<([^>]*)\>$/', $option, $matches)) {
!         if (empty($matches[1])) {
!           unset($group);
!         }
!         elseif (!$flat) {
!           $group = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1];
!         }
!       }
!       elseif (preg_match('/^([^|]+)\|(.*)$/', $option, $matches)) {
!         $key = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1];
!         $value = $filter ? _webform_filter_values($matches[2], NULL, NULL, NULL, FALSE) : $matches[2];
!         isset($group) ? $options[$group][$key] = $value : $options[$key] = $value;
!       }
!       else {
!         $filtered_option = $filter ? _webform_filter_values($option, NULL, NULL, NULL, FALSE) : $option;
!         isset($group) ? $options[$group][$filtered_option] = $filtered_option : $options[$filtered_option] = $filtered_option;
!       }
      }
  
!     $option_cache[$flat][$md5] = $options;
!   }
! 
!   // Return our options from the option_cache array.
!   return $option_cache[$flat][$md5];
  }
  
- 
  /**
   * Convert an array of options into text.
   */
--- 6656,6718 ----
   * @param $filter
   *   Optional. Whether or not to filter returned values.
   */
! function _webform_select_options_from_text($text, $flat = FALSE, $filter = TRUE)
! {
!     static $option_cache = array();
! 
!     // Keep each processed option block in an array indexed by the MD5 hash of
!     // the option text and the value of the $flat variable.
!     $md5 = md5($text);
! 
!     // Check if this option block has been previously processed.
!     if (!isset($option_cache[$flat][$md5]))
!     {
!         $options = array();
!         $rows = array_filter(explode("\n", trim($text)));
!         $group = NULL;
!         foreach ($rows as $option)
!         {
!             $option = trim($option);
!             /**
!              * If the Key of the option is within < >, treat as an optgroup
!              *
!              * <Group 1>
!              *   creates an optgroup with the label "Group 1"
!              *
!              * <>
!              *   Unsets the current group, allowing items to be inserted at the root element.
!              */
!             if (preg_match('/^\<([^>]*)\>$/', $option, $matches))
!             {
!                 if (empty($matches[1]))
!                 {
!                     unset($group);
!                 }
!                 elseif (!$flat)
!                 {
!                     $group = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1];
!                 }
!             }
!             elseif (preg_match('/^([^|]+)\|(.*)$/', $option, $matches))
!             {
!                 $key = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1];
!                 $value = $filter ? _webform_filter_values($matches[2], NULL, NULL, NULL, FALSE) : $matches[2];
!                 isset($group) ? $options[$group][$key] = $value : $options[$key] = $value;
!             }
!             else
!             {
!                 $filtered_option = $filter ? _webform_filter_values($option, NULL, NULL, NULL, FALSE) : $option;
!                 isset($group) ? $options[$group][$filtered_option] = $filtered_option : $options[$filtered_option] = $filtered_option;
!             }
!         }
  
!         $option_cache[$flat][$md5] = $options;
      }
  
!     // Return our options from the option_cache array.
!     return $option_cache[$flat][$md5];
  }
  
  /**
   * Convert an array of options into text.
   */
***************
*** 875,921 ****
  /**
   * Convert an array of options into text.
   */
! function _webform_select_options_to_text($options) {
!   $output = '';
!   $previous_key = false;
! 
!   foreach ($options as $key => $value) {
!     // Convert groups.
!     if (is_array($value)) {
!       $output .= '<' . $key . '>' . "\n";
!       foreach ($value as $subkey => $subvalue) {
!         $output .= $subkey . '|' . $subvalue . "\n";
!       }
!       $previous_key = $key;
!     }
!     // Typical key|value pairs.
!     else {
!       // Exit out of any groups.
!       if (isset($options[$previous_key]) && is_array($options[$previous_key])) {
!         $output .= "<>\n";
!       }
!       // Skip empty rows.
!       if ($options[$key] !== '') {
!         $output .= $key . '|' . $value . "\n";
!       }
!       $previous_key = $key;
      }
-   }
  
!   return $output;
  }
  
  /**
   * Utility function to shuffle an array while preserving key-value pairs.
   */
! function _webform_shuffle_options(&$array) {
!   // First shuffle the array keys, then use them as the basis for ordering
!   // the options.
!   $aux = array();
!   $keys = array_keys($array);
!   shuffle($keys);
!   foreach ($keys as $key) {
!     $aux[$key] = $array[$key];
!   }
!   $array = $aux;
  }
--- 6716,6771 ----
  /**
   * Convert an array of options into text.
   */
! function _webform_select_options_to_text($options)
! {
!     $output = '';
!     $previous_key = false;
! 
!     foreach ($options as $key => $value)
!     {
!         // Convert groups.
!         if (is_array($value))
!         {
!             $output .= '<' . $key . '>' . "\n";
!             foreach ($value as $subkey => $subvalue)
!             {
!                 $output .= $subkey . '|' . $subvalue . "\n";
!             }
!             $previous_key = $key;
!         }
!         // Typical key|value pairs.
!         else
!         {
!             // Exit out of any groups.
!             if (isset($options[$previous_key]) && is_array($options[$previous_key]))
!             {
!                 $output .= "<>\n";
!             }
!             // Skip empty rows.
!             if ($options[$key] !== '')
!             {
!                 $output .= $key . '|' . $value . "\n";
!             }
!             $previous_key = $key;
!         }
      }
  
!     return $output;
  }
  
  /**
   * Utility function to shuffle an array while preserving key-value pairs.
   */
! function _webform_shuffle_options(&$array)
! {
!     // First shuffle the array keys, then use them as the basis for ordering
!     // the options.
!     $aux = array();
!     $keys = array_keys($array);
!     shuffle($keys);
!     foreach ($keys as $key)
!     {
!         $aux[$key] = $array[$key];
!     }
!     $array = $aux;
  }
Only in webform_arthur/images: Thumbs.db
diff -crB webform/includes/webform.report.inc webform_arthur/includes/webform.report.inc
*** webform/includes/webform.report.inc	2010-09-29 21:38:19.000000000 +0300
--- webform_arthur/includes/webform.report.inc	2010-11-12 09:12:02.000000000 +0300
***************
*** 14,93 ****
  /**
   * Retrieve lists of submissions for a given webform.
   */
! function webform_results_submissions($node, $user_filter, $pager_count) {
!   global $user;
  
!   if (isset($_GET['results']) && is_numeric($_GET['results'])) {
!     $pager_count = $_GET['results'];
!   }
! 
!   $header = theme('webform_results_submissions_header', $node);
!   if ($user_filter) {
!     if ($user->uid) {
!       drupal_set_title(t('Submissions for %user', array('%user' => $user->name)));
!     }
!     else {
!       drupal_set_title(t('Your submissions'));
!       webform_disable_page_cache();
!     }
!     webform_set_breadcrumb($node);
!     $submissions = webform_get_submissions(array('nid' => $node->nid, 'uid' => $user->uid), $header, $pager_count);
!     $count = webform_get_submission_count($node->nid, $user->uid);
!   }
!   else {
!     $submissions = webform_get_submissions($node->nid, $header, $pager_count);
!     $count = webform_get_submission_count($node->nid);
!   }
  
!   $operation_column = end($header);
!   $operation_total = $operation_column['colspan'];
  
!   $rows = array();
!   foreach ($submissions as $sid => $submission) {
!     $row = array(
!       $submission->is_draft ? t('@sid (draft)', array('@sid' => $sid)) : $sid,
!       format_date($submission->submitted, 'small'),
!     );
!     if (webform_results_access($node, $user)) {
!       $row[] = theme('username', $submission);
!       $row[] = $submission->remote_addr;
!     }
!     $row[] = l(t('View'), "node/$node->nid/submission/$sid");
!     $operation_count = 1;
!     if (module_exists('print_pdf') && user_access('access PDF version')) {
!       $row[] = l(t('PDF'), "printpdf/$node->nid/submission/$sid", array('query' => drupal_get_destination()));
!       $operation_count++;
!     }
!     if (module_exists('print') && user_access('access print')) {
!       $row[] = l(t('Print'), "print/$node->nid/submission/$sid");
!       $operation_count++;
!     }
!     if (webform_submission_access($node, $submission, 'edit', $user)) {
!       $row[] = l(t('Edit'), "node/$node->nid/submission/$sid/edit", array('query' => drupal_get_destination()));
!       $operation_count++;
!     }
!     if (webform_submission_access($node, $submission, 'delete', $user)) {
!       $row[] = l(t('Delete'), "node/$node->nid/submission/$sid/delete", array('query' => drupal_get_destination()));
!       $operation_count++;
!     }
!     if ($operation_count < $operation_total) {
!       $row[count($row) - 1] = array('data' => $row[count($row) - 1], 'colspan' => $operation_total - $operation_count + 1);
!     }
!     $rows[] = $row;
!   }
! 
!   $element['#theme'] = 'webform_results_submissions';
!   $element['#node'] = $node;
!   $element['#submissions'] = $submissions;
!   $element['#total_count'] = $count;
!   $element['#pager_count'] = $pager_count;
! 
!   $element['table']['#theme'] = 'table';
!   $element['table']['#header'] = $header;
!   $element['table']['#rows'] = $rows;
!   $element['table']['#operation_total'] = $operation_total;
  
!   return drupal_render($element);
  }
  
  /**
--- 14,106 ----
  /**
   * Retrieve lists of submissions for a given webform.
   */
! function webform_results_submissions($node, $user_filter, $pager_count)
! {
!     global $user;
! 
!     if (isset($_GET['results']) && is_numeric($_GET['results']))
!     {
!         $pager_count = $_GET['results'];
!     }
  
!     $header = theme('webform_results_submissions_header', $node);
!     if ($user_filter)
!     {
!         if ($user->uid)
!         {
!             drupal_set_title(t('Submissions for %user', array('%user' => $user->name)));
!         }
!         else
!         {
!             drupal_set_title(t('Your submissions'));
!             webform_disable_page_cache();
!         }
!         webform_set_breadcrumb($node);
!         $submissions = webform_get_submissions(array('nid' => $node->nid, 'uid' => $user->uid), $header, $pager_count);
!         $count = webform_get_submission_count($node->nid, $user->uid);
!     }
!     else
!     {
!         $submissions = webform_get_submissions($node->nid, $header, $pager_count);
!         $count = webform_get_submission_count($node->nid);
!     }
! 
!     $operation_column = end($header);
!     $operation_total = $operation_column['colspan'];
! 
!     $rows = array();
!     foreach ($submissions as $sid => $submission)
!     {
!         $row = array(
!             $submission->is_draft ? t('@sid (draft)', array('@sid' => $sid)) : $sid,
!             format_date($submission->submitted, 'small'),
!         );
!         if (webform_results_access($node, $user))
!         {
!             $row[] = theme('username', $submission);
!             $row[] = $submission->remote_addr;
!         }
!         $row[] = l(t('View'), "node/$node->nid/submission/$sid");
!         $operation_count = 1;
!         if (module_exists('print_pdf') && user_access('access PDF version'))
!         {
!             $row[] = l(t('PDF'), "printpdf/$node->nid/submission/$sid", array('query' => drupal_get_destination()));
!             $operation_count++;
!         }
!         if (module_exists('print') && user_access('access print'))
!         {
!             $row[] = l(t('Print'), "print/$node->nid/submission/$sid");
!             $operation_count++;
!         }
!         if (webform_submission_access($node, $submission, 'edit', $user))
!         {
!             $row[] = l(t('Edit'), "node/$node->nid/submission/$sid/edit", array('query' => drupal_get_destination()));
!             $operation_count++;
!         }
!         if (webform_submission_access($node, $submission, 'delete', $user))
!         {
!             $row[] = l(t('Delete'), "node/$node->nid/submission/$sid/delete", array('query' => drupal_get_destination()));
!             $operation_count++;
!         }
!         if ($operation_count < $operation_total)
!         {
!             $row[count($row) - 1] = array('data' => $row[count($row) - 1], 'colspan' => $operation_total - $operation_count + 1);
!         }
!         $rows[] = $row;
!     }
  
!     $element['#theme'] = 'webform_results_submissions';
!     $element['#node'] = $node;
!     $element['#submissions'] = $submissions;
!     $element['#total_count'] = $count;
!     $element['#pager_count'] = $pager_count;
  
!     $element['table']['#theme'] = 'table';
!     $element['table']['#header'] = $header;
!     $element['table']['#rows'] = $rows;
!     $element['table']['#operation_total'] = $operation_total;
  
!     return drupal_render($element);
  }
  
  /**
***************
*** 98,138 ****
   * @param $pager_count
   *   The current number of results displayed per page.
   */
! function theme_webform_results_per_page($total_count, $pager_count) {
!   $output = '';
  
!   // Create a list of results-per-page options.
!   $counts = array(
!     '20' => '20',
!     '50' => '50',
!     '100' => '100',
!     '200' => '200',
!     '500' => '500',
!     '1000' => '1000',
!     '0' => t('All'),
!   );
! 
!   $count_links = array();
! 
!   foreach ($counts as $number => $text) {
!     if ($number < $total_count) {
!       $count_links[] = l($text, $_GET['q'], array('query' => 'results=' . $number, 'attributes' => array('class' => $pager_count == $number ? 'selected' : '')));
!     }
!   }
! 
!   $output .= '<div class="webform-results-per-page">';
!   if (count($count_links) > 1) {
!     $output .= t('Show !count results per page.', array('!count' => implode(' | ', $count_links)));
!   }
!   else {
!     $output .= t('Showing all results.');
!   }
!   if ($total_count > 1) {
!     $output .= ' ' . t('@total results total.', array('@total' => $total_count));
!   }
!   $output .= '</div>';
  
!   return $output;
  }
  
  /**
--- 111,157 ----
   * @param $pager_count
   *   The current number of results displayed per page.
   */
! function theme_webform_results_per_page($total_count, $pager_count)
! {
!     $output = '';
! 
!     // Create a list of results-per-page options.
!     $counts = array(
!         '20' => '20',
!         '50' => '50',
!         '100' => '100',
!         '200' => '200',
!         '500' => '500',
!         '1000' => '1000',
!         '0' => t('All'),
!     );
! 
!     $count_links = array();
! 
!     foreach ($counts as $number => $text)
!     {
!         if ($number < $total_count)
!         {
!             $count_links[] = l($text, $_GET['q'], array('query' => 'results=' . $number, 'attributes' => array('class' => $pager_count == $number ? 'selected' : '')));
!         }
!     }
  
!     $output .= '<div class="webform-results-per-page">';
!     if (count($count_links) > 1)
!     {
!         $output .= t('Show !count results per page.', array('!count' => implode(' | ', $count_links)));
!     }
!     else
!     {
!         $output .= t('Showing all results.');
!     }
!     if ($total_count > 1)
!     {
!         $output .= ' ' . t('@total results total.', array('@total' => $total_count));
!     }
!     $output .= '</div>';
  
!     return $output;
  }
  
  /**
***************
*** 141,201 ****
   * This is done in it's own function so that webform can retrieve the header and
   * use it for sorting the results.
   */
! function theme_webform_results_submissions_header($node) {
!   $columns = array(
!     array('data' => t('#'), 'field' => 'sid', 'sort' => 'asc'),
!     array('data' => t('Submitted'), 'field' => 'submitted'),
!   );
!   if (webform_results_access($node)) {
!     $columns[] = array('data' => t('User'), 'field' => 'name');
!     $columns[] = array('data' => t('IP Address'), 'field' => 'remote_addr');
!   }
!   $columns[] = array('data' => t('Operations'), 'colspan' => module_exists('print') ? 5 : 3);
  
!   return $columns;
  }
  
  /**
   * Preprocess function for webform-results-submissions.tpl.php
   */
! function template_preprocess_webform_results_submissions(&$vars) {
!   $vars['node'] = $vars['element']['#node'];
!   $vars['submissions'] = $vars['element']['#submissions'];
!   $vars['table'] = $vars['element']['table'];
!   $vars['total_count'] = $vars['element']['#total_count'];
!   $vars['pager_count'] = $vars['element']['#pager_count'];
!   $vars['is_submissions'] = (arg(2) == 'submissions')? 1 : 0;
  
!   unset($vars['element']);
  }
  
  /**
   * Create a table containing all submitted values for a webform node.
   */
! function webform_results_table($node, $pager_count = 0) {
!   if (isset($_GET['results']) && is_numeric($_GET['results'])) {
!     $pager_count = $_GET['results'];
!   }
! 
!   // Get all the submissions for the node.
!   $header = theme('webform_results_table_header', $node);
!   $submissions = webform_get_submissions($node->nid, $header, $pager_count);
!   $total_count = webform_get_submission_count($node->nid);
! 
!   $output = theme('webform_results_table', $node, $node->webform['components'], $submissions, $total_count, $pager_count);
!   if ($pager_count) {
!     $output .= theme('pager', NULL, $pager_count, 0);
!   }
!   return $output;
! }
! 
! function theme_webform_results_table_header($node) {
!   return array(
!     array('data' => t('#'), 'field' => 'sid', 'sort' => 'asc'),
!     array('data' => t('Submitted'), 'field' => 'submitted'),
!     array('data' => t('User'), 'field' => 'name'),
!     array('data' => t('IP Address'), 'field' => 'remote_addr'),
!   );
  }
  
  /**
--- 160,227 ----
   * This is done in it's own function so that webform can retrieve the header and
   * use it for sorting the results.
   */
! function theme_webform_results_submissions_header($node)
! {
!     $columns = array(
!         array('data' => t('#'), 'field' => 'sid', 'sort' => 'asc'),
!         array('data' => t('Submitted'), 'field' => 'submitted'),
!     );
!     if (webform_results_access($node))
!     {
!         $columns[] = array('data' => t('User'), 'field' => 'name');
!         $columns[] = array('data' => t('IP Address'), 'field' => 'remote_addr');
!     }
!     $columns[] = array('data' => t('Operations'), 'colspan' => module_exists('print') ? 5 : 3);
  
!     return $columns;
  }
  
  /**
   * Preprocess function for webform-results-submissions.tpl.php
   */
! function template_preprocess_webform_results_submissions(&$vars)
! {
!     $vars['node'] = $vars['element']['#node'];
!     $vars['submissions'] = $vars['element']['#submissions'];
!     $vars['table'] = $vars['element']['table'];
!     $vars['total_count'] = $vars['element']['#total_count'];
!     $vars['pager_count'] = $vars['element']['#pager_count'];
!     $vars['is_submissions'] = (arg(2) == 'submissions') ? 1 : 0;
  
!     unset($vars['element']);
  }
  
  /**
   * Create a table containing all submitted values for a webform node.
   */
! function webform_results_table($node, $pager_count = 0)
! {
!     if (isset($_GET['results']) && is_numeric($_GET['results']))
!     {
!         $pager_count = $_GET['results'];
!     }
! 
!     // Get all the submissions for the node.
!     $header = theme('webform_results_table_header', $node);
!     $submissions = webform_get_submissions($node->nid, $header, $pager_count);
!     $total_count = webform_get_submission_count($node->nid);
! 
!     $output = theme('webform_results_table', $node, $node->webform['components'], $submissions, $total_count, $pager_count);
!     if ($pager_count)
!     {
!         $output .= theme('pager', NULL, $pager_count, 0);
!     }
!     return $output;
! }
! 
! function theme_webform_results_table_header($node)
! {
!     return array(
!         array('data' => t('#'), 'field' => 'sid', 'sort' => 'asc'),
!         array('data' => t('Submitted'), 'field' => 'submitted'),
!         array('data' => t('User'), 'field' => 'name'),
!         array('data' => t('IP Address'), 'field' => 'remote_addr'),
!     );
  }
  
  /**
***************
*** 212,262 ****
   * @param $pager_count
   *   The number of results to be shown per page.
   */
! function theme_webform_results_table($node, $components, $submissions, $total_count, $pager_count) {
!   drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
  
!   $header = array();
!   $rows = array();
!   $cell = array();
! 
!   // This header has to be generated seperately so we can add the SQL necessary.
!   // to sort the results.
!   $header = theme('webform_results_table_header', $node);
! 
!   // Generate a row for each submission.
!   foreach ($submissions as $sid => $submission) {
!     $cell[] = l($sid, 'node/' . $node->nid . '/submission/' . $sid);
!     $cell[] = format_date($submission->submitted, 'small');
!     $cell[] = theme('username', $submission);
!     $cell[] = $submission->remote_addr;
!     $component_headers = array();
! 
!     // Generate a cell for each component.
!     foreach ($node->webform['components'] as $component) {
!       $data = isset($submission->data[$component['cid']]['value']) ? $submission->data[$component['cid']]['value'] : NULL;
!       $submission_output = webform_component_invoke($component['type'], 'table', $component, $data);
!       if ($submission_output !== NULL) {
!         $component_headers[] = $component['name'];
!         $cell[] = $submission_output;
!       }
!     }
! 
!     $rows[] = $cell;
!     unset($cell);
!   }
!   if (!empty($component_headers)) {
!     $header = array_merge($header, $component_headers);
!   }
! 
!   if (count($rows) == 0) {
!     $rows[] = array(array('data' => t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))), 'colspan' => 4));
!   }
! 
! 
!   $output = '';
!   $output .= theme('webform_results_per_page', $total_count, $pager_count);
!   $output .= theme('table', $header, $rows);
!   return $output;
  }
  
  /**
--- 238,294 ----
   * @param $pager_count
   *   The number of results to be shown per page.
   */
! function theme_webform_results_table($node, $components, $submissions, $total_count, $pager_count)
! {
!     drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
! 
!     $header = array();
!     $rows = array();
!     $cell = array();
! 
!     // This header has to be generated seperately so we can add the SQL necessary.
!     // to sort the results.
!     $header = theme('webform_results_table_header', $node);
! 
!     // Generate a row for each submission.
!     foreach ($submissions as $sid => $submission)
!     {
!         $cell[] = l($sid, 'node/' . $node->nid . '/submission/' . $sid);
!         $cell[] = format_date($submission->submitted, 'small');
!         $cell[] = theme('username', $submission);
!         $cell[] = $submission->remote_addr;
!         $component_headers = array();
! 
!         // Generate a cell for each component.
!         foreach ($node->webform['components'] as $component)
!         {
!             $data = isset($submission->data[$component['cid']]['value']) ? $submission->data[$component['cid']]['value'] : NULL;
!             $submission_output = webform_component_invoke($component['type'], 'table', $component, $data);
!             if ($submission_output !== NULL)
!             {
!                 $component_headers[] = $component['name'];
!                 $cell[] = $submission_output;
!             }
!         }
! 
!         $rows[] = $cell;
!         unset($cell);
!     }
!     if (!empty($component_headers))
!     {
!         $header = array_merge($header, $component_headers);
!     }
  
!     if (count($rows) == 0)
!     {
!         $rows[] = array(array('data' => t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))), 'colspan' => 4));
!     }
! 
! 
!     $output = '';
!     $output .= theme('webform_results_per_page', $total_count, $pager_count);
!     $output .= theme('table', $header, $rows);
!     return $output;
  }
  
  /**
***************
*** 265,276 ****
   * @param $nid
   *   The node id whose submissions will be deleted.
   */
! function webform_results_clear($nid) {
!   $node = node_load($nid);
!   $submissions = webform_get_submissions($nid);
!   foreach ($submissions as $submission) {
!     webform_submission_delete($node, $submission);
!   }
  }
  
  /**
--- 297,310 ----
   * @param $nid
   *   The node id whose submissions will be deleted.
   */
! function webform_results_clear($nid)
! {
!     $node = node_load($nid);
!     $submissions = webform_get_submissions($nid);
!     foreach ($submissions as $submission)
!     {
!         webform_submission_delete($node, $submission);
!     }
  }
  
  /**
***************
*** 279,457 ****
   * @param $nid
   *   ID of node for which to clear submissions.
   */
! function webform_results_clear_form($form_state, $node) {
!   drupal_set_title(t('Clear Form Submissions'));
  
!   $form = array();
!   $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
!   $question = t('Are you sure you want to delete all submissions for this form?');
  
!   return confirm_form($form, $question, 'node/' . $node->nid . '/webform-results', NULL, t('Clear'), t('Cancel'));
! }
  
! function webform_results_clear_form_submit($form, &$form_state) {
!   webform_results_clear($form_state['values']['nid']);
!   $node = node_load($form_state['values']['nid']);
!   $title = $node->title;
  
!   $message = t('Webform %title entries cleared.', array('%title' => $title));
!   drupal_set_message($message);
!   watchdog('webform', $message);
!   $form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/webform-results';
  }
  
! /**
!  * Form to configure the download of CSV files.
!  */
! function webform_results_download_form(&$form_state, $node) {
!   module_load_include('inc', 'webform', 'includes/webform.export');
!   module_load_include('inc', 'webform', 'includes/webform.components');
! 
!   $form = array();
! 
!   $form['node'] = array(
!     '#type' => 'value',
!     '#value' => $node,
!   );
! 
!   $form['format'] = array(
!     '#type' => 'radios',
!     '#title' => t('Export format'),
!     '#options' => webform_export_list(),
!     '#default_value' => variable_get('webform_export_format', 'delimited'),
!   );
! 
!   $form['delimiter'] = array(
!     '#type' => 'select',
!     '#title' => t('Delimited text format'),
!     '#description' => t('This is the delimiter used in the CSV/TSV file when downloading Webform results. Using tabs in the export is the most reliable method for preserving non-latin characters. You may want to change this to another character depending on the program with which you anticipate importing results.'),
!     '#default_value' => variable_get('webform_csv_delimiter', '\t'),
!     '#options' => array(
!       ','  => t('Comma (,)'),
!       '\t' => t('Tab (\t)'),
!       ';'  => t('Semicolon (;)'),
!       ':'  => t('Colon (:)'),
!       '|'  => t('Pipe (|)'),
!       '.'  => t('Period (.)'),
!       ' '  => t('Space ( )'),
!     ),
!   );
! 
!   $form['select_options'] = array(
!     '#type' => 'fieldset',
!     '#title' => t('Select list options'),
!     '#collapsible' => TRUE,
!     '#collapsed' => TRUE,
!   );
! 
!   $form['select_options']['select_keys'] = array(
!     '#type' => 'radios',
!     '#title' => t('Select keys'),
!     '#options' => array(
!       0 => t('Full, human-readable options (values)'),
!       1 => t('Short, raw options (keys)'),
!     ),
!     '#default_value' => 0,
!     '#description' => t('Choose which part of options should be displayed from key|value pairs.'),
!   );
! 
!   $form['select_options']['select_format'] = array(
!     '#type' => 'radios',
!     '#title' => t('Select list format'),
!     '#options' => array(
!       'separate' => t('Separate'),
!       'compact' => t('Compact'),
!     ),
!     '#default_value' => 'separate',
!     '#attributes' => array('class' => 'webform-select-list-format'),
!     '#theme' => 'webform_results_download_select_format',
!   );
! 
!   $csv_components = array(
!     'info' => t('Submission information'),
!     'serial' => '-' . t('Submission Number'),
!     'sid' => '-' . t('Submission ID'),
!     'time' => '-' . t('Time'),
!     'draft' => '-' . t('Draft'),
!     'ip_address' => '-' . t('IP Address'),
!     'uid' => '-' . t('User ID'),
!     'username' => '-' . t('Username'),
!   );
!   $csv_components += webform_component_list($node, 'csv', TRUE);
! 
!   $form['components'] = array(
!     '#type' => 'select',
!     '#title' => t('Included export components'),
!     '#options' => $csv_components,
!     '#default_value' => array_keys($csv_components),
!     '#multiple' => TRUE,
!     '#size' => 10,
!     '#description' => t('The selected components will be included in the export.'),
!     '#process' => array('webform_component_select'),
!   );
! 
!   $form['submit'] = array(
!     '#type' => 'submit',
!     '#value' => t('Download'),
!   );
! 
!   return $form;
! }
! 
! function webform_results_download_form_submit(&$form, &$form_state) {
!   $options = array(
!     'delimiter' => $form_state['values']['delimiter'],
!     'components' => array_keys(array_filter($form_state['values']['components'])),
!     'select_keys' => $form_state['values']['select_keys'],
!     'select_format' => $form_state['values']['select_format'],
!   );
!   webform_results_download($form_state['values']['node'], $form_state['values']['format'], $options);
  }
  
  /**
   * Theme the output of the export form.
   */
! function theme_webform_results_download_form($form) {
!   drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
  
!   return drupal_render($form);
  }
  
  /**
   * Theme the output of the select list format radio buttons.
   */
! function theme_webform_results_download_select_format($element) {
!   drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
  
!   $output = '';
  
!   // Build an example table for the separate option.
!   $header = array(t('Option A'), t('Option B'), t('Option C'));
!   $rows = array(
!     array('X', '', ''),
!     array('X', '', 'X'),
!     array('', 'X', 'X'),
!   );
! 
!   $element['separate']['#attributes']['class'] = '';
!   $element['separate']['#description'] = theme('table', $header, $rows);
!   $element['separate']['#description'] .= t('Separate options are more suitable for building reports, graphs, and statistics in a spreadsheet application.');
!   $output .= drupal_render($element['separate']);
! 
!   // Build an example table for the compact option.
!   $header = array(t('My select list'));
!   $rows = array(
!     array('Option A'),
!     array('Option A,Option C'),
!     array('Option B,Option C'),
!   );
! 
!   $element['separate']['#attributes']['class'] = '';
!   $element['compact']['#description'] = theme('table', $header, $rows);
!   $element['compact']['#description'] .= t('Compact options are more suitable for importing data into other systems.');
!   $output .= drupal_render($element['compact']);
  
!   return $output;
  }
  
  /**
--- 313,497 ----
   * @param $nid
   *   ID of node for which to clear submissions.
   */
! function webform_results_clear_form($form_state, $node)
! {
!     drupal_set_title(t('Clear Form Submissions'));
! 
!     $form = array();
!     $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
!     $question = t('Are you sure you want to delete all submissions for this form?');
! 
!     return confirm_form($form, $question, 'node/' . $node->nid . '/webform-results', NULL, t('Clear'), t('Cancel'));
! }
! 
! function webform_results_clear_form_submit($form, &$form_state)
! {
!     webform_results_clear($form_state['values']['nid']);
!     $node = node_load($form_state['values']['nid']);
!     $title = $node->title;
! 
!     $message = t('Webform %title entries cleared.', array('%title' => $title));
!     drupal_set_message($message);
!     watchdog('webform', $message);
!     $form_state['redirect'] = 'node/' . $form_state['values']['nid'] . '/webform-results';
! }
  
! /**
!  * Form to configure the download of CSV files.
!  */
! function webform_results_download_form(&$form_state, $node)
! {
!     module_load_include('inc', 'webform', 'includes/webform.export');
!     module_load_include('inc', 'webform', 'includes/webform.components');
! 
!     $form = array();
! 
!     $form['node'] = array(
!         '#type' => 'value',
!         '#value' => $node,
!     );
  
!     $form['format'] = array(
!         '#type' => 'radios',
!         '#title' => t('Export format'),
!         '#options' => webform_export_list(),
!         '#default_value' => variable_get('webform_export_format', 'delimited'),
!     );
! 
!     $form['delimiter'] = array(
!         '#type' => 'select',
!         '#title' => t('Delimited text format'),
!         '#description' => t('This is the delimiter used in the CSV/TSV file when downloading Webform results. Using tabs in the export is the most reliable method for preserving non-latin characters. You may want to change this to another character depending on the program with which you anticipate importing results.'),
!         '#default_value' => variable_get('webform_csv_delimiter', '\t'),
!         '#options' => array(
!             ',' => t('Comma (,)'),
!             '\t' => t('Tab (\t)'),
!             ';' => t('Semicolon (;)'),
!             ':' => t('Colon (:)'),
!             '|' => t('Pipe (|)'),
!             '.' => t('Period (.)'),
!             ' ' => t('Space ( )'),
!         ),
!     );
! 
!     $form['select_options'] = array(
!         '#type' => 'fieldset',
!         '#title' => t('Select list options'),
!         '#collapsible' => TRUE,
!         '#collapsed' => TRUE,
!     );
! 
!     $form['select_options']['select_keys'] = array(
!         '#type' => 'radios',
!         '#title' => t('Select keys'),
!         '#options' => array(
!             0 => t('Full, human-readable options (values)'),
!             1 => t('Short, raw options (keys)'),
!         ),
!         '#default_value' => 0,
!         '#description' => t('Choose which part of options should be displayed from key|value pairs.'),
!     );
  
!     $form['select_options']['select_format'] = array(
!         '#type' => 'radios',
!         '#title' => t('Select list format'),
!         '#options' => array(
!             'separate' => t('Separate'),
!             'compact' => t('Compact'),
!         ),
!         '#default_value' => 'separate',
!         '#attributes' => array('class' => 'webform-select-list-format'),
!         '#theme' => 'webform_results_download_select_format',
!     );
! 
!     $csv_components = array(
!         'info' => t('Submission information'),
!         'serial' => '-' . t('Submission Number'),
!         'sid' => '-' . t('Submission ID'),
!         'time' => '-' . t('Time'),
!         'draft' => '-' . t('Draft'),
!         'ip_address' => '-' . t('IP Address'),
!         'uid' => '-' . t('User ID'),
!         'username' => '-' . t('Username'),
!     );
!     $csv_components += webform_component_list($node, 'csv', TRUE);
! 
!     $form['components'] = array(
!         '#type' => 'select',
!         '#title' => t('Included export components'),
!         '#options' => $csv_components,
!         '#default_value' => array_keys($csv_components),
!         '#multiple' => TRUE,
!         '#size' => 10,
!         '#description' => t('The selected components will be included in the export.'),
!         '#process' => array('webform_component_select'),
!     );
! 
!     $form['submit'] = array(
!         '#type' => 'submit',
!         '#value' => t('Download'),
!     );
  
!     return $form;
  }
  
! function webform_results_download_form_submit(&$form, &$form_state)
! {
!     $options = array(
!         'delimiter' => $form_state['values']['delimiter'],
!         'components' => array_keys(array_filter($form_state['values']['components'])),
!         'select_keys' => $form_state['values']['select_keys'],
!         'select_format' => $form_state['values']['select_format'],
!     );
!     webform_results_download($form_state['values']['node'], $form_state['values']['format'], $options);
  }
  
  /**
   * Theme the output of the export form.
   */
! function theme_webform_results_download_form($form)
! {
!     drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
  
!     return drupal_render($form);
  }
  
  /**
   * Theme the output of the select list format radio buttons.
   */
! function theme_webform_results_download_select_format($element)
! {
!     drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform-admin.css', 'theme', 'all', FALSE);
! 
!     $output = '';
! 
!     // Build an example table for the separate option.
!     $header = array(t('Option A'), t('Option B'), t('Option C'));
!     $rows = array(
!         array('X', '', ''),
!         array('X', '', 'X'),
!         array('', 'X', 'X'),
!     );
  
!     $element['separate']['#attributes']['class'] = '';
!     $element['separate']['#description'] = theme('table', $header, $rows);
!     $element['separate']['#description'] .= t('Separate options are more suitable for building reports, graphs, and statistics in a spreadsheet application.');
!     $output .= drupal_render($element['separate']);
! 
!     // Build an example table for the compact option.
!     $header = array(t('My select list'));
!     $rows = array(
!         array('Option A'),
!         array('Option A,Option C'),
!         array('Option B,Option C'),
!     );
  
!     $element['separate']['#attributes']['class'] = '';
!     $element['compact']['#description'] = theme('table', $header, $rows);
!     $element['compact']['#description'] .= t('Compact options are more suitable for importing data into other systems.');
!     $output .= drupal_render($element['compact']);
  
!     return $output;
  }
  
  /**
***************
*** 486,621 ****
   *   27 Feb 2005       ,1035,56 ,212   ,X         ,        ,        ,..,       ,X          ,X       ,X       ,..,X       ,How is this?
   *
   */
! function webform_results_download($node, $format = 'delimiter', $options = array()) {
!   module_load_include('inc', 'webform', 'includes/webform.export');
!   module_load_include('inc', 'webform', 'includes/webform.components');
! 
!   $submission_information = array(
!     'serial' => t('Serial'),
!     'sid' => t('SID'),
!     'time' => t('Time'),
!     'draft' => t('Draft'),
!     'ip_address' => t('IP Address'),
!     'uid' => t('UID'),
!     'username' => t('Username'),
!   );
  
!   if (empty($options)) {
!     $options = array(
!       'delimiter' => variable_get('webform_csv_delimiter', '\t'),
!       'components' => array_keys($submission_information) + array_keys(webform_component_list($node, 'csv', TRUE)),
!       'select_display' => 'value',
!       'select_format' => 'separate',
!     );
!   }
!   else {
!     foreach ($submission_information as $key => $label) {
!       if (!in_array($key, $options['components'])) {
!         unset($submission_information[$key]);
!       }
!     }
!   }
! 
!   // Open a new Webform exporter object.
!   $exporter = webform_export_create_handler($format, $options);
! 
!   $file_name = tempnam(variable_get('file_directory_temp', file_directory_temp()), 'webform');
!   $handle = @fopen($file_name, 'w'); // The @ suppresses errors.
!   $exporter->bof($handle);
! 
!   // Fill in the header for the submission information (if any).
!   $header[2] = $header[1] = $header[0] = count($submission_information) ? array_fill(0, count($submission_information), '') : array();
!   if (count($submission_information)) {
!     $header[0][0] = $node->title;
!     $header[1][0] = t('Submission Details');
!     foreach (array_values($submission_information) as $column => $label) {
!       $header[2][$column] = $label;
!     }
!   }
! 
!   // Compile header information for components.
!   foreach ($options['components'] as $cid) {
!     if (isset($node->webform['components'][$cid])) {
!       $component = $node->webform['components'][$cid];
! 
!       // Let each component determine its headers.
!       if (webform_component_feature($component['type'], 'csv')) {
!         $component_header = (array) webform_component_invoke($component['type'], 'csv_headers', $component, $options);
!         $header[0] = array_merge($header[0], (array) $component_header[0]);
!         $header[1] = array_merge($header[1], (array) $component_header[1]);
!         $header[2] = array_merge($header[2], (array) $component_header[2]);
!       }
!     }
!   }
! 
!   // Add headers to the file.
!   foreach ($header as $row) {
!     $exporter->add_row($handle, $row);
!   }
! 
!   // Get all the submissions for the node.
!   $submissions = webform_get_submissions($node->nid);
! 
!   // Generate a row for each submission.
!   $row_count = 0;
!   foreach ($submissions as $sid => $submission) {
!     $row_count++;
! 
!     $row = array();
!     if (isset($submission_information['serial'])) {
!       $row[] = $row_count;
!     }
!     if (isset($submission_information['sid'])) {
!       $row[] = $sid;
!     }
!     if (isset($submission_information['time'])) {
!       $row[] = format_date($submission->submitted, 'small');
!     }
!     if (isset($submission_information['draft'])) {
!       $row[] = $submission->is_draft;
!     }
!     if (isset($submission_information['ip_address'])) {
!       $row[] =  $submission->remote_addr;
!     }
!     if (isset($submission_information['uid'])) {
!       $row[] = $submission->uid;
!     }
!     if (isset($submission_information['username'])) {
!       $row[] = $submission->name;
!     }
! 
!     foreach ($options['components'] as $cid) {
!       if (isset($node->webform['components'][$cid])) {
!         $component = $node->webform['components'][$cid];
!         // Let each component add its data.
!         $raw_data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
!         if (webform_component_feature($component['type'], 'csv')) {
!           $data = webform_component_invoke($component['type'], 'csv_data', $component, $options, $raw_data);
!           if (is_array($data)) {
!             $row = array_merge($row, array_values($data));
!           }
!           else {
!             $row[] = empty($data) ? '' : $data;
!           }
!         }
!       }
!     }
! 
!     // Write data from submissions.
!     $data = $exporter->add_row($handle, $row);
!   }
! 
!   // Add the closing bytes.
!   $exporter->eof($handle);
! 
!   // Close the file.
!   @fclose($handle);
! 
!   $export_name = _webform_safe_name($node->title);
!   $exporter->set_headers($export_name);
!   @readfile($file_name);  // The @ makes it silent.
!   @unlink($file_name);  // Clean up, the @ makes it silent.
!   exit();
  }
  
  /**
--- 526,685 ----
   *   27 Feb 2005       ,1035,56 ,212   ,X         ,        ,        ,..,       ,X          ,X       ,X       ,..,X       ,How is this?
   *
   */
! function webform_results_download($node, $format = 'delimiter', $options = array())
! {
!     module_load_include('inc', 'webform', 'includes/webform.export');
!     module_load_include('inc', 'webform', 'includes/webform.components');
! 
!     $submission_information = array(
!         'serial' => t('Serial'),
!         'sid' => t('SID'),
!         'time' => t('Time'),
!         'draft' => t('Draft'),
!         'ip_address' => t('IP Address'),
!         'uid' => t('UID'),
!         'username' => t('Username'),
!     );
  
!     if (empty($options))
!     {
!         $options = array(
!             'delimiter' => variable_get('webform_csv_delimiter', '\t'),
!             'components' => array_keys($submission_information) + array_keys(webform_component_list($node, 'csv', TRUE)),
!             'select_display' => 'value',
!             'select_format' => 'separate',
!         );
!     }
!     else
!     {
!         foreach ($submission_information as $key => $label)
!         {
!             if (!in_array($key, $options['components']))
!             {
!                 unset($submission_information[$key]);
!             }
!         }
!     }
! 
!     // Open a new Webform exporter object.
!     $exporter = webform_export_create_handler($format, $options);
! 
!     $file_name = tempnam(variable_get('file_directory_temp', file_directory_temp()), 'webform');
!     $handle = @fopen($file_name, 'w'); // The @ suppresses errors.
!     $exporter->bof($handle);
! 
!     // Fill in the header for the submission information (if any).
!     $header[2] = $header[1] = $header[0] = count($submission_information) ? array_fill(0, count($submission_information), '') : array();
!     if (count($submission_information))
!     {
!         $header[0][0] = $node->title;
!         $header[1][0] = t('Submission Details');
!         foreach (array_values($submission_information) as $column => $label)
!         {
!             $header[2][$column] = $label;
!         }
!     }
! 
!     // Compile header information for components.
!     foreach ($options['components'] as $cid)
!     {
!         if (isset($node->webform['components'][$cid]))
!         {
!             $component = $node->webform['components'][$cid];
! 
!             // Let each component determine its headers.
!             if (webform_component_feature($component['type'], 'csv'))
!             {
!                 $component_header = (array) webform_component_invoke($component['type'], 'csv_headers', $component, $options);
!                 $header[0] = array_merge($header[0], (array) $component_header[0]);
!                 $header[1] = array_merge($header[1], (array) $component_header[1]);
!                 $header[2] = array_merge($header[2], (array) $component_header[2]);
!             }
!         }
!     }
! 
!     // Add headers to the file.
!     foreach ($header as $row)
!     {
!         $exporter->add_row($handle, $row);
!     }
! 
!     // Get all the submissions for the node.
!     $submissions = webform_get_submissions($node->nid);
! 
!     // Generate a row for each submission.
!     $row_count = 0;
!     foreach ($submissions as $sid => $submission)
!     {
!         $row_count++;
! 
!         $row = array();
!         if (isset($submission_information['serial']))
!         {
!             $row[] = $row_count;
!         }
!         if (isset($submission_information['sid']))
!         {
!             $row[] = $sid;
!         }
!         if (isset($submission_information['time']))
!         {
!             $row[] = format_date($submission->submitted, 'small');
!         }
!         if (isset($submission_information['draft']))
!         {
!             $row[] = $submission->is_draft;
!         }
!         if (isset($submission_information['ip_address']))
!         {
!             $row[] = $submission->remote_addr;
!         }
!         if (isset($submission_information['uid']))
!         {
!             $row[] = $submission->uid;
!         }
!         if (isset($submission_information['username']))
!         {
!             $row[] = $submission->name;
!         }
! 
!         foreach ($options['components'] as $cid)
!         {
!             if (isset($node->webform['components'][$cid]))
!             {
!                 $component = $node->webform['components'][$cid];
!                 // Let each component add its data.
!                 $raw_data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
!                 if (webform_component_feature($component['type'], 'csv'))
!                 {
!                     $data = webform_component_invoke($component['type'], 'csv_data', $component, $options, $raw_data);
!                     if (is_array($data))
!                     {
!                         $row = array_merge($row, array_values($data));
!                     }
!                     else
!                     {
!                         $row[] = empty($data) ? '' : $data;
!                     }
!                 }
!             }
!         }
! 
!         // Write data from submissions.
!         $data = $exporter->add_row($handle, $row);
!     }
! 
!     // Add the closing bytes.
!     $exporter->eof($handle);
! 
!     // Close the file.
!     @fclose($handle);
! 
!     $export_name = _webform_safe_name($node->title);
!     $exporter->set_headers($export_name);
!     @readfile($file_name);  // The @ makes it silent.
!     @unlink($file_name);  // Clean up, the @ makes it silent.
!     exit();
  }
  
  /**
***************
*** 631,657 ****
   *   relating specifically to that component's analysis, such as a list of
   *   "Other" values within a select list.
   */
! function webform_results_analysis($node, $sids = array(), $analysis_component = NULL) {
!   if (!is_array($sids)) {
!     $sids = array();
!   }
! 
!   // If showing a component's details, we don't want to loose the menu tabs.
!   if ($analysis_component) {
!     $item = menu_get_item('node/' . $node->nid . '/webform-results/analysis');
!     menu_set_item(NULL, $item);
!   }
! 
!   $components = isset($analysis_component) ? array($analysis_component['cid'] => $analysis_component) : $node->webform['components'];
!   $data = array();
!   foreach ($components as $cid => $component) {
!     // Do component specific call.
!     if ($row_data = webform_component_invoke($component['type'], 'analysis', $component, $sids, isset($analysis_component))) {
!       $data[$cid] = $row_data;
      }
-   }
  
!   return theme('webform_results_analysis', $node, $data, $sids, $analysis_component);
  }
  
  /**
--- 695,726 ----
   *   relating specifically to that component's analysis, such as a list of
   *   "Other" values within a select list.
   */
! function webform_results_analysis($node, $sids = array(), $analysis_component = NULL)
! {
!     if (!is_array($sids))
!     {
!         $sids = array();
!     }
! 
!     // If showing a component's details, we don't want to loose the menu tabs.
!     if ($analysis_component)
!     {
!         $item = menu_get_item('node/' . $node->nid . '/webform-results/analysis');
!         menu_set_item(NULL, $item);
!     }
! 
!     $components = isset($analysis_component) ? array($analysis_component['cid'] => $analysis_component) : $node->webform['components'];
!     $data = array();
!     foreach ($components as $cid => $component)
!     {
!         // Do component specific call.
!         if ($row_data = webform_component_invoke($component['type'], 'analysis', $component, $sids, isset($analysis_component)))
!         {
!             $data[$cid] = $row_data;
!         }
      }
  
!     return theme('webform_results_analysis', $node, $data, $sids, $analysis_component);
  }
  
  /**
***************
*** 659,691 ****
   *
   * @see webform_results_analysis()
   */
! function theme_webform_results_analysis($node, $data, $sids = array(), $analysis_component = NULL) {
  
!   $rows = array();
!   $question_number = 0;
!   $single = isset($analysis_component);
! 
!   $header = array(
!     $single ? $analysis_component['name'] : t('Q'),
!     array('data' => $single ? '&nbsp' : t('responses'), 'colspan' => '10')
!   );
! 
!   foreach ($data as $cid => $row_data) {
!     $question_number++;
! 
!     if (is_array($row_data)) {
!       $row = array();
!       if (!$single) {
!         $row[] = array('data' => '<strong>' . $question_number . '</strong>', 'rowspan' => count($row_data) + 1, 'valign' => 'top');
!         $row[] = array('data' => '<strong>' . check_plain($node->webform['components'][$cid]['name']) . '</strong>', 'colspan' => '10');
!       }
!       $rows = array_merge($rows, array_merge(array($row), $row_data));
!     }
!   }
! 
!   if (count($rows) == 0) {
!     $rows[] = array(array('data' => t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))), 'colspan' => 20));
!   }
  
!   return theme('table', $header, $rows);
  }
--- 728,765 ----
   *
   * @see webform_results_analysis()
   */
! function theme_webform_results_analysis($node, $data, $sids = array(), $analysis_component = NULL)
! {
  
!     $rows = array();
!     $question_number = 0;
!     $single = isset($analysis_component);
! 
!     $header = array(
!         $single ? $analysis_component['name'] : t('Q'),
!         array('data' => $single ? '&nbsp' : t('responses'), 'colspan' => '10')
!     );
! 
!     foreach ($data as $cid => $row_data)
!     {
!         $question_number++;
! 
!         if (is_array($row_data))
!         {
!             $row = array();
!             if (!$single)
!             {
!                 $row[] = array('data' => '<strong>' . $question_number . '</strong>', 'rowspan' => count($row_data) + 1, 'valign' => 'top');
!                 $row[] = array('data' => '<strong>' . check_plain($node->webform['components'][$cid]['name']) . '</strong>', 'colspan' => '10');
!             }
!             $rows = array_merge($rows, array_merge(array($row), $row_data));
!         }
!     }
! 
!     if (count($rows) == 0)
!     {
!         $rows[] = array(array('data' => t('There are no submissions for this form. <a href="!url">View this form</a>.', array('!url' => url('node/' . $node->nid))), 'colspan' => 20));
!     }
  
!     return theme('table', $header, $rows);
  }
diff -crB webform/webform.module webform_arthur/webform.module
*** webform/webform.module	2010-10-18 10:38:44.000000000 +0300
--- webform_arthur/webform.module	2010-11-19 10:19:48.000000000 +0300
***************
*** 14,47 ****
  /**
   * Implementation of hook_help().
   */
! function webform_help($section = 'admin/help#webform', $arg = NULL) {
!   $output = '';
!   switch ($section) {
!     case 'admin/settings/webform':
!       $type_list = webform_admin_type_list();
!       $output = t('Webform enables nodes to have attached forms and questionnaires.');
!       if ($type_list) {
!         $output .= ' ' . t('To add one, create a !types piece of content.', array('!types' => $type_list));
!       }
!       else {
!         $output .= ' <strong>' . t('Webform is currently not enabled on any content types.') . '</strong> ' . t('To use Webform, please enable it on at least one content type on this page.');
!       }
!       $output = '<p>' . $output . '</p>';
!       break;
!     case 'admin/content/webform':
!       $output = '<p>' . t('This page lists all of the content on the site that may have a webform attached to it.') . '</p>';
!       break;
!     case 'admin/help#webform':
!       module_load_include('inc', 'webform', 'includes/webform.admin');
!       $types = webform_admin_type_list();
!       if (empty($types)) {
!         $types = t('Webform-enabled piece of content');
!         $types_message = t('Webform is currently not enabled on any content types.') . ' ' . t('Visit the <a href="!url">Webform settings</a> page and enable Webform on at least one content type.', array('!url' => url('admin/settings/webform')));
!       }
!       else {
!         $types_message = t('Optional: Enable Webform on multiple types by visiting the <a href="!url">Webform settings</a> page.', array('!url' => url('admin/settings/webform')));
!       }
!       $output = t("<p>This module lets you create forms or questionnaires and define their content. Submissions from these forms are stored in the database and optionally also sent by e-mail to a predefined address.</p>
        <p>Here is how to create one:</p>
        <ul>
          <li>!webform-types-message</li>
--- 15,54 ----
  /**
   * Implementation of hook_help().
   */
! function webform_help($section = 'admin/help#webform', $arg = NULL)
! {
!     $output = '';
!     switch ($section)
!     {
!         case 'admin/settings/webform':
!             $type_list = webform_admin_type_list();
!             $output = t('Webform enables nodes to have attached forms and questionnaires.');
!             if ($type_list)
!             {
!                 $output .= ' ' . t('To add one, create a !types piece of content.', array('!types' => $type_list));
!             }
!             else
!             {
!                 $output .= ' <strong>' . t('Webform is currently not enabled on any content types.') . '</strong> ' . t('To use Webform, please enable it on at least one content type on this page.');
!             }
!             $output = '<p>' . $output . '</p>';
!             break;
!         case 'admin/content/webform':
!             $output = '<p>' . t('This page lists all of the content on the site that may have a webform attached to it.') . '</p>';
!             break;
!         case 'admin/help#webform':
!             module_load_include('inc', 'webform', 'includes/webform.admin');
!             $types = webform_admin_type_list();
!             if (empty($types))
!             {
!                 $types = t('Webform-enabled piece of content');
!                 $types_message = t('Webform is currently not enabled on any content types.') . ' ' . t('Visit the <a href="!url">Webform settings</a> page and enable Webform on at least one content type.', array('!url' => url('admin/settings/webform')));
!             }
!             else
!             {
!                 $types_message = t('Optional: Enable Webform on multiple types by visiting the <a href="!url">Webform settings</a> page.', array('!url' => url('admin/settings/webform')));
!             }
!             $output = t("<p>This module lets you create forms or questionnaires and define their content. Submissions from these forms are stored in the database and optionally also sent by e-mail to a predefined address.</p>
        <p>Here is how to create one:</p>
        <ul>
          <li>!webform-types-message</li>
***************
*** 60,383 ****
        </ul>
        <p>Help on adding and configuring the components will be shown after you add your first component.</p>
        ", array('!webform-types-message' => $types_message, '!create-content' => url('node/add'), '!types' => $types));
!       break;
!     case 'node/%/webform/components':
!       $output .= '<p>' . t('This page displays all the components currently configured for this webform node. You may add any number of components to the form, even multiple of the same type. To add a new component, fill in a name and select a type from the fields at the bottom of the table. Submit the form to create the new component or update any changed form values.') . '</p>';
!       $output .= '<p>' . t('Click on any existing component\'s name to edit its settings.') . '</p>';
!       break;
!   }
  
!   return $output;
  }
  
  /**
   * Implementation of hook_menu().
   */
! function webform_menu() {
!   $items = array();
  
!   // Submissions listing.
!   $items['admin/content/webform'] = array(
!     'title' => 'Webforms',
!     'page callback' => 'webform_admin_content',
!     'access callback' => 'user_access',
!     'access arguments' => array('access all webform results'),
!     'description' => 'View and edit all the available webforms on your site.',
!     'file' => 'includes/webform.admin.inc',
!     'type' => MENU_NORMAL_ITEM,
!   );
! 
!   // Admin Settings.
!   $items['admin/settings/webform'] = array(
!     'title' => 'Webform settings',
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_admin_settings'),
!     'access callback' => 'user_access',
!     'access arguments' => array('administer site configuration'),
!     'description' => 'Global configuration of webform functionality.',
!     'file' => 'includes/webform.admin.inc',
!     'type' => MENU_NORMAL_ITEM,
!   );
! 
!   // Node page tabs.
!   $items['node/%webform_menu/done'] = array(
!     'title' => 'Webform confirmation',
!     'page callback' => '_webform_confirmation',
!     'page arguments' => array(1),
!     'access callback' => 'node_access',
!     'access arguments' => array('view', 1),
!     'type' => MENU_CALLBACK,
!   );
!   $items['node/%webform_menu/webform'] = array(
!     'title' => 'Webform',
!     'page callback' => 'webform_components_page',
!     'page arguments' => array(1),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'file' => 'includes/webform.components.inc',
!     'weight' => 1,
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform/components'] = array(
!     'title' => 'Form components',
!     'page callback' => 'webform_components_page',
!     'page arguments' => array(1),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'file' => 'includes/webform.components.inc',
!     'weight' => 0,
!     'type' => MENU_DEFAULT_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform/configure'] = array(
!     'title' => 'Form settings',
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_configure_form', 1),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'file' => 'includes/webform.pages.inc',
!     'weight' => 2,
!     'type' => MENU_LOCAL_TASK,
!   );
! 
!   // Node e-mail forms.
!   $items['node/%webform_menu/webform/emails'] = array(
!     'title' => 'E-mails',
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_emails_form', 1),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'file' => 'includes/webform.emails.inc',
!     'weight' => 1,
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform/emails/%webform_menu_email'] = array(
!     'title' => 'Edit e-mail settings',
!     'load arguments' => array(1),
!     'page arguments' => array('webform_email_edit_form', 1, 4),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'file' => 'includes/webform.emails.inc',
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform/emails/%webform_menu_email/delete'] = array(
!     'title' => 'Delete e-mail settings',
!     'load arguments' => array(1),
!     'page arguments' => array('webform_email_delete_form', 1, 4),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'type' => MENU_LOCAL_TASK,
!   );
! 
!   // Node component forms.
!   $items['node/%webform_menu/webform/components/%webform_menu_component'] = array(
!     'load arguments' => array(1, 5),
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_component_edit_form', 1, 4, FALSE),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform/components/%webform_menu_component/clone'] = array(
!     'load arguments' => array(1, 5),
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_component_edit_form', 1, 4, TRUE),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform/components/%webform_menu_component/delete'] = array(
!     'load arguments' => array(1, 5),
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_component_delete_form', 1, 4),
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 1),
!     'type' => MENU_LOCAL_TASK,
!   );
! 
!   // AJAX callback for loading select list options.
!   $items['webform/ajax/options/%webform_menu'] = array(
!     'load arguments' => array(3),
!     'page callback' => 'webform_select_options_ajax',
!     'access callback' => 'node_access',
!     'access arguments' => array('update', 3),
!     'file' => 'components/select.inc',
!     'type' => MENU_CALLBACK,
!   );
! 
!   // Node webform results.
!   $items['node/%webform_menu/webform-results'] = array(
!     'title' => 'Results',
!     'page callback' => 'webform_results_submissions',
!     'page arguments' => array(1, FALSE, '50'),
!     'access callback' => 'webform_results_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'weight' => 2,
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform-results/submissions'] = array(
!     'title' => 'Submissions',
!     'page callback' => 'webform_results_submissions',
!     'page arguments' => array(1, FALSE, '50'),
!     'access callback' => 'webform_results_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'weight' => 4,
!     'type' => MENU_DEFAULT_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform-results/analysis'] = array(
!     'title' => 'Analysis',
!     'page callback' => 'webform_results_analysis',
!     'page arguments' => array(1),
!     'access callback' => 'webform_results_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'weight' => 5,
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform-results/analysis/%webform_menu_component'] = array(
!     'title' => 'Analysis',
!     'load arguments' => array(1, 4),
!     'page callback' => 'webform_results_analysis',
!     'page arguments' => array(1, array(), 4),
!     'access callback' => 'webform_results_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'type' => MENU_CALLBACK,
!   );
!   $items['node/%webform_menu/webform-results/table'] = array(
!     'title' => 'Table',
!     'page callback' => 'webform_results_table',
!     'page arguments' => array(1, '50'),
!     'access callback' => 'webform_results_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'weight' => 6,
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform-results/download'] = array(
!     'title' => 'Download',
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_results_download_form', 1),
!     'access callback' => 'webform_results_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'weight' => 7,
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/webform-results/clear'] = array(
!     'title' => 'Clear',
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_results_clear_form', 1),
!     'access callback' => 'webform_results_clear_access',
!     'access arguments' => array(1),
!     'file' => 'includes/webform.report.inc',
!     'weight' => 8,
!     'type' => MENU_LOCAL_TASK,
!   );
! 
!   // Node submissions.
!   $items['node/%webform_menu/submissions'] = array(
!     'title' => 'Submissions',
!     'page callback' => 'webform_results_submissions',
!     'page arguments' => array(1, TRUE, '50'),
!     'access callback' => 'webform_submission_access',
!     'access arguments' => array(1, NULL, 'list'),
!     'file' => 'includes/webform.report.inc',
!     'type' => MENU_CALLBACK,
!   );
!   $items['node/%webform_menu/submission/%webform_menu_submission'] = array(
!     'title' => 'Webform submission',
!     'load arguments' => array(1),
!     'page callback' => 'webform_submission_page',
!     'page arguments' => array(1, 3, 'html'),
!     'title callback' => 'webform_submission_title',
!     'title arguments' => array(1, 3),
!     'access callback' => 'webform_submission_access',
!     'access arguments' => array(1, 3, 'view'),
!     'file' => 'includes/webform.submissions.inc',
!     'type' => MENU_CALLBACK,
!   );
!   $items['node/%webform_menu/submission/%webform_menu_submission/view'] = array(
!     'title' => 'View',
!     'load arguments' => array(1),
!     'page callback' => 'webform_submission_page',
!     'page arguments' => array(1, 3, 'html'),
!     'access callback' => 'webform_submission_access',
!     'access arguments' => array(1, 3, 'view'),
!     'weight' => 0,
!     'file' => 'includes/webform.submissions.inc',
!     'type' => MENU_DEFAULT_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/submission/%webform_menu_submission/edit'] = array(
!     'title' => 'Edit',
!     'load arguments' => array(1),
!     'page callback' => 'webform_submission_page',
!     'page arguments' => array(1, 3, 'form'),
!     'access callback' => 'webform_submission_access',
!     'access arguments' => array(1, 3, 'edit'),
!     'weight' => 1,
!     'file' => 'includes/webform.submissions.inc',
!     'type' => MENU_LOCAL_TASK,
!   );
!   $items['node/%webform_menu/submission/%webform_menu_submission/delete'] = array(
!     'title' => 'Delete',
!     'load arguments' => array(1),
!     'page callback' => 'drupal_get_form',
!     'page arguments' => array('webform_submission_delete_form', 1, 3),
!     'access callback' => 'webform_submission_access',
!     'access arguments' => array(1, 3, 'delete'),
!     'weight' => 2,
!     'file' => 'includes/webform.submissions.inc',
!     'type' => MENU_LOCAL_TASK,
!   );
  
!   return $items;
  }
  
  /**
   * Menu loader callback. Load a webform node if the given nid is a webform.
   */
! function webform_menu_load($nid) {
!   if (!is_numeric($nid)) {
!     return FALSE;
!   }
!   $node = node_load($nid);
!   if (!isset($node->type) || !in_array($node->type, webform_variable_get('webform_node_types'))) {
!     return FALSE;
!   }
!   return $node;
  }
  
  /**
   * Menu loader callback. Load a webform submission if the given sid is a valid.
   */
! function webform_menu_submission_load($sid, $nid) {
!   module_load_include('inc', 'webform', 'includes/webform.submissions');
!   $submission = webform_get_submission($nid, $sid);
!   return empty($submission) ? FALSE : $submission;
  }
  
  /**
   * Menu loader callback. Load a webform component if the given cid is a valid.
   */
! function webform_menu_component_load($cid, $nid, $type) {
!   module_load_include('inc', 'webform', 'includes/webform.components');
!   if ($cid == 'new') {
!     $components = webform_components();
!     $component = in_array($type, array_keys($components)) ? array('type' => $type, 'nid' => $nid, 'name' => $_GET['name'], 'mandatory' => $_GET['mandatory'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE;
!   }
!   else {
!     $node = node_load($nid);
!     $component = isset($node->webform['components'][$cid]) ? $node->webform['components'][$cid] : FALSE;
!   }
!   if ($component) {
!     webform_component_defaults($component);
!   }
!   return $component;
  }
  
- 
  /**
   * Menu loader callback. Load a webform e-mail if the given eid is a valid.
   */
--- 67,398 ----
        </ul>
        <p>Help on adding and configuring the components will be shown after you add your first component.</p>
        ", array('!webform-types-message' => $types_message, '!create-content' => url('node/add'), '!types' => $types));
!             break;
!         case 'node/%/webform/components':
!             $output .= '<p>' . t('This page displays all the components currently configured for this webform node. You may add any number of components to the form, even multiple of the same type. To add a new component, fill in a name and select a type from the fields at the bottom of the table. Submit the form to create the new component or update any changed form values.') . '</p>';
!             $output .= '<p>' . t('Click on any existing component\'s name to edit its settings.') . '</p>';
!             break;
!     }
  
!     return $output;
  }
  
  /**
   * Implementation of hook_menu().
   */
! function webform_menu()
! {
!     $items = array();
! 
!     // Submissions listing.
!     $items['admin/content/webform'] = array(
!         'title' => 'Webforms',
!         'page callback' => 'webform_admin_content',
!         'access callback' => 'user_access',
!         'access arguments' => array('access all webform results'),
!         'description' => 'View and edit all the available webforms on your site.',
!         'file' => 'includes/webform.admin.inc',
!         'type' => MENU_NORMAL_ITEM,
!     );
! 
!     // Admin Settings.
!     $items['admin/settings/webform'] = array(
!         'title' => 'Webform settings',
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_admin_settings'),
!         'access callback' => 'user_access',
!         'access arguments' => array('administer site configuration'),
!         'description' => 'Global configuration of webform functionality.',
!         'file' => 'includes/webform.admin.inc',
!         'type' => MENU_NORMAL_ITEM,
!     );
! 
!     // Node page tabs.
!     $items['node/%webform_menu/done'] = array(
!         'title' => 'Webform confirmation',
!         'page callback' => '_webform_confirmation',
!         'page arguments' => array(1),
!         'access callback' => 'node_access',
!         'access arguments' => array('view', 1),
!         'type' => MENU_CALLBACK,
!     );
!     $items['node/%webform_menu/webform'] = array(
!         'title' => 'Webform',
!         'page callback' => 'webform_components_page',
!         'page arguments' => array(1),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'file' => 'includes/webform.components.inc',
!         'weight' => 1,
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform/components'] = array(
!         'title' => 'Form components',
!         'page callback' => 'webform_components_page',
!         'page arguments' => array(1),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'file' => 'includes/webform.components.inc',
!         'weight' => 0,
!         'type' => MENU_DEFAULT_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform/configure'] = array(
!         'title' => 'Form settings',
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_configure_form', 1),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'file' => 'includes/webform.pages.inc',
!         'weight' => 2,
!         'type' => MENU_LOCAL_TASK,
!     );
! 
!     // Node e-mail forms.
!     $items['node/%webform_menu/webform/emails'] = array(
!         'title' => 'E-mails',
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_emails_form', 1),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'file' => 'includes/webform.emails.inc',
!         'weight' => 1,
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform/emails/%webform_menu_email'] = array(
!         'title' => 'Edit e-mail settings',
!         'load arguments' => array(1),
!         'page arguments' => array('webform_email_edit_form', 1, 4),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'file' => 'includes/webform.emails.inc',
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform/emails/%webform_menu_email/delete'] = array(
!         'title' => 'Delete e-mail settings',
!         'load arguments' => array(1),
!         'page arguments' => array('webform_email_delete_form', 1, 4),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'type' => MENU_LOCAL_TASK,
!     );
! 
!     // Node component forms.
!     $items['node/%webform_menu/webform/components/%webform_menu_component'] = array(
!         'load arguments' => array(1, 5),
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_component_edit_form', 1, 4, FALSE),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform/components/%webform_menu_component/clone'] = array(
!         'load arguments' => array(1, 5),
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_component_edit_form', 1, 4, TRUE),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform/components/%webform_menu_component/delete'] = array(
!         'load arguments' => array(1, 5),
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_component_delete_form', 1, 4),
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 1),
!         'type' => MENU_LOCAL_TASK,
!     );
! 
!     // AJAX callback for loading select list options.
!     $items['webform/ajax/options/%webform_menu'] = array(
!         'load arguments' => array(3),
!         'page callback' => 'webform_select_options_ajax',
!         'access callback' => 'node_access',
!         'access arguments' => array('update', 3),
!         'file' => 'components/select.inc',
!         'type' => MENU_CALLBACK,
!     );
! 
!     // Node webform results.
!     $items['node/%webform_menu/webform-results'] = array(
!         'title' => 'Results',
!         'page callback' => 'webform_results_submissions',
!         'page arguments' => array(1, FALSE, '50'),
!         'access callback' => 'webform_results_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'weight' => 2,
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform-results/submissions'] = array(
!         'title' => 'Submissions',
!         'page callback' => 'webform_results_submissions',
!         'page arguments' => array(1, FALSE, '50'),
!         'access callback' => 'webform_results_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'weight' => 4,
!         'type' => MENU_DEFAULT_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform-results/analysis'] = array(
!         'title' => 'Analysis',
!         'page callback' => 'webform_results_analysis',
!         'page arguments' => array(1),
!         'access callback' => 'webform_results_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'weight' => 5,
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform-results/analysis/%webform_menu_component'] = array(
!         'title' => 'Analysis',
!         'load arguments' => array(1, 4),
!         'page callback' => 'webform_results_analysis',
!         'page arguments' => array(1, array(), 4),
!         'access callback' => 'webform_results_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'type' => MENU_CALLBACK,
!     );
!     $items['node/%webform_menu/webform-results/table'] = array(
!         'title' => 'Table',
!         'page callback' => 'webform_results_table',
!         'page arguments' => array(1, '50'),
!         'access callback' => 'webform_results_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'weight' => 6,
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform-results/download'] = array(
!         'title' => 'Download',
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_results_download_form', 1),
!         'access callback' => 'webform_results_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'weight' => 7,
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/webform-results/clear'] = array(
!         'title' => 'Clear',
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_results_clear_form', 1),
!         'access callback' => 'webform_results_clear_access',
!         'access arguments' => array(1),
!         'file' => 'includes/webform.report.inc',
!         'weight' => 8,
!         'type' => MENU_LOCAL_TASK,
!     );
  
!     // Node submissions.
!     $items['node/%webform_menu/submissions'] = array(
!         'title' => 'Submissions',
!         'page callback' => 'webform_results_submissions',
!         'page arguments' => array(1, TRUE, '50'),
!         'access callback' => 'webform_submission_access',
!         'access arguments' => array(1, NULL, 'list'),
!         'file' => 'includes/webform.report.inc',
!         'type' => MENU_CALLBACK,
!     );
!     $items['node/%webform_menu/submission/%webform_menu_submission'] = array(
!         'title' => 'Webform submission',
!         'load arguments' => array(1),
!         'page callback' => 'webform_submission_page',
!         'page arguments' => array(1, 3, 'html'),
!         'title callback' => 'webform_submission_title',
!         'title arguments' => array(1, 3),
!         'access callback' => 'webform_submission_access',
!         'access arguments' => array(1, 3, 'view'),
!         'file' => 'includes/webform.submissions.inc',
!         'type' => MENU_CALLBACK,
!     );
!     $items['node/%webform_menu/submission/%webform_menu_submission/view'] = array(
!         'title' => 'View',
!         'load arguments' => array(1),
!         'page callback' => 'webform_submission_page',
!         'page arguments' => array(1, 3, 'html'),
!         'access callback' => 'webform_submission_access',
!         'access arguments' => array(1, 3, 'view'),
!         'weight' => 0,
!         'file' => 'includes/webform.submissions.inc',
!         'type' => MENU_DEFAULT_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/submission/%webform_menu_submission/edit'] = array(
!         'title' => 'Edit',
!         'load arguments' => array(1),
!         'page callback' => 'webform_submission_page',
!         'page arguments' => array(1, 3, 'form'),
!         'access callback' => 'webform_submission_access',
!         'access arguments' => array(1, 3, 'edit'),
!         'weight' => 1,
!         'file' => 'includes/webform.submissions.inc',
!         'type' => MENU_LOCAL_TASK,
!     );
!     $items['node/%webform_menu/submission/%webform_menu_submission/delete'] = array(
!         'title' => 'Delete',
!         'load arguments' => array(1),
!         'page callback' => 'drupal_get_form',
!         'page arguments' => array('webform_submission_delete_form', 1, 3),
!         'access callback' => 'webform_submission_access',
!         'access arguments' => array(1, 3, 'delete'),
!         'weight' => 2,
!         'file' => 'includes/webform.submissions.inc',
!         'type' => MENU_LOCAL_TASK,
!     );
  
!     return $items;
  }
  
  /**
   * Menu loader callback. Load a webform node if the given nid is a webform.
   */
! function webform_menu_load($nid)
! {
!     if (!is_numeric($nid))
!     {
!         return FALSE;
!     }
!     $node = node_load($nid);
!     if (!isset($node->type) || !in_array($node->type, webform_variable_get('webform_node_types')))
!     {
!         return FALSE;
!     }
!     return $node;
  }
  
  /**
   * Menu loader callback. Load a webform submission if the given sid is a valid.
   */
! function webform_menu_submission_load($sid, $nid)
! {
!     module_load_include('inc', 'webform', 'includes/webform.submissions');
!     $submission = webform_get_submission($nid, $sid);
!     return empty($submission) ? FALSE : $submission;
  }
  
  /**
   * Menu loader callback. Load a webform component if the given cid is a valid.
   */
! function webform_menu_component_load($cid, $nid, $type)
! {
!     module_load_include('inc', 'webform', 'includes/webform.components');
!     if ($cid == 'new')
!     {
!         $components = webform_components();
!         $component = in_array($type, array_keys($components)) ? array('type' => $type, 'nid' => $nid, 'name' => $_GET['name'], 'mandatory' => $_GET['mandatory'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE;
!     }
!     else
!     {
!         $node = node_load($nid);
!         $component = isset($node->webform['components'][$cid]) ? $node->webform['components'][$cid] : FALSE;
!     }
!     if ($component)
!     {
!         webform_component_defaults($component);
!     }
!     return $component;
  }
  
  /**
   * Menu loader callback. Load a webform e-mail if the given eid is a valid.
   */
***************
*** 381,759 ****
  /**
   * Menu loader callback. Load a webform e-mail if the given eid is a valid.
   */
! function webform_menu_email_load($eid, $nid) {
!   module_load_include('inc', 'webform', 'includes/webform.emails');
!   $node = node_load($nid);
!   $email = webform_email_load($eid, $nid);
!   if ($eid == 'new') {
!     if (isset($_GET['option']) && isset($_GET['email'])) {
!       $type = $_GET['option'];
!       if ($type == 'custom') {
!         $email['email'] = $_GET['email'];
!       }
!       elseif ($type == 'component' && isset($node->webform['components'][$_GET['email']])) {
!         $email['email'] = $_GET['email'];
!       }
!     }
!   }
! 
!   return $email;
! }
! 
! function webform_submission_access($node, $submission, $op = 'view', $account = NULL) {
!   global $user;
!   $account = isset($account) ? $account : $user;
! 
!   $access_all = user_access('access all webform results', $account);
!   $access_own_submission = isset($submission) && user_access('access own webform submissions', $account) && (($account->uid && $account->uid == $submission->uid) || isset($_SESSION['webform_submission'][$submission->sid]));
!   $access_node_submissions = user_access('access own webform results', $account) && $account->uid == $node->uid;
! 
!   $general_access = $access_all || $access_own_submission || $access_node_submissions;
! 
!   // Disable the page cache for anonymous users in this access callback,
!   // otherwise the "Access denied" page gets cached.
!   if (!$account->uid && user_access('access own webform submissions', $account)) {
!     webform_disable_page_cache();
!   }
! 
!   $module_access = count(array_filter(module_invoke_all('webform_submission_access', $node, $submission, $op, $account))) > 0;
! 
!   switch ($op) {
!     case 'view':
!       return $module_access || $general_access;
!     case 'edit':
!       return $module_access || ($general_access && (user_access('edit all webform submissions', $account) || (user_access('edit own webform submissions', $account) && $account->uid == $submission->uid)));
!     case 'delete':
!       return $module_access || ($general_access && (user_access('delete all webform submissions', $account) || (user_access('delete own webform submissions', $account) && $account->uid == $submission->uid)));
!     case 'list':
!       return $module_access || user_access('access all webform results', $account) || (user_access('access own webform submissions', $account) && ($account->uid || isset($_SESSION['webform_submission']))) || (user_access('access own webform results', $account) && $account->uid == $node->uid);
!   }
  }
  
  /**
   * Menu access callback. Ensure a user both access and node 'view' permission.
   */
! function webform_results_access($node, $account = NULL) {
!   global $user;
!   $account = isset($account) ? $account : $user;
  
!   $module_access = count(array_filter(module_invoke_all('webform_results_access', $node, $account))) > 0;
  
!   return node_access('view', $node, $account) && ($module_access || user_access('access all webform results', $account) || (user_access('access own webform results', $account) && $account->uid == $node->uid));
  }
  
! function webform_results_clear_access($node, $account = NULL) {
!   global $user;
!   $account = isset($account) ? $account : $user;
  
!   $module_access = count(array_filter(module_invoke_all('webform_results_clear_access', $node, $account))) > 0;
  
!   return webform_results_access($node, $account) && ($module_access || user_access('delete all webform submissions', $account));
  }
  
  /**
   * Implementation of hook_init().
   */
! function webform_init() {
!   // Use the administrative theme if set to use on content editing pages.
!   // See system_init().
!   if (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(2) == 'webform' || arg(2) == 'webform-results')) {
!     global $custom_theme;
!     $custom_theme = variable_get('admin_theme', '0');
!     drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', 'module');
! 
!     // Support for Admin module (1.x).
!     if (function_exists('_admin_init_theme') && empty($custom_theme)) {
!       _admin_init_theme();
      }
-   }
  }
  
  /**
   * Implementation of hook_perm().
   */
! function webform_perm() {
!   return array(
!     'access all webform results',
!     'access own webform results',
!     'edit all webform submissions',
!     'delete all webform submissions',
!     'access own webform submissions',
!     'edit own webform submissions',
!     'delete own webform submissions',
!   );
  }
  
  /**
   * Implementation of hook_theme().
   */
! function webform_theme() {
!   $theme = array(
!     // webform.module.
!     'webform_view' => array(
!       'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'form' => NULL, 'enabled' => NULL),
!     ),
!     'webform_view_messages' => array(
!       'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'limit_exceeded' => NULL, 'allowed_roles' => NULL),
!     ),
!     'webform_form' => array(
!       'arguments' => array('form' => NULL),
!       'template' => 'templates/webform-form',
!       'pattern' => 'webform_form_[0-9]+',
!     ),
!     'webform_confirmation' => array(
!       'arguments' => array('node' => NULL, 'sid' => NULL),
!       'template' => 'templates/webform-confirmation',
!       'pattern' => 'webform_confirmation_[0-9]+',
!     ),
!     'webform_element' => array(
!       'arguments' => array('element' => NULL, 'value' => NULL),
!     ),
!     'webform_element_wrapper' => array(
!       'arguments' => array('element' => NULL, 'content' => NULL),
!     ),
!     'webform_element_text' => array(
!       'arguments' => array('element' => NULL, 'value' => NULL),
!     ),
!     'webform_mail_message' => array(
!       'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
!       'template' => 'templates/webform-mail',
!       'pattern' => 'webform_mail(_[0-9]+)?',
!     ),
!     'webform_mail_headers' => array(
!       'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
!       'pattern' => 'webform_mail_headers_[0-9]+',
!     ),
!     'webform_token_help' => array(
!       'arguments' => array(),
!     ),
!     // webform.admin.inc.
!     'webform_admin_settings' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.admin.inc',
!     ),
!     'webform_admin_content' => array(
!       'arguments' => array('nodes' => NULL),
!       'file' => 'includes/webform.admin.inc',
!     ),
!     // webform.emails.inc.
!     'webform_emails_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.emails.inc',
!     ),
!     'webform_email_add_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.emails.inc',
!     ),
!     'webform_email_edit_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.emails.inc',
!     ),
!     // webform.components.inc.
!     'webform_components_page' => array(
!       'arguments' => array('node' => NULL, 'form' => NULL),
!       'file' => 'includes/webform.components.inc',
!     ),
!     'webform_components_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.components.inc',
!     ),
!     'webform_component_select' => array(
!       'arguments' => array('element' => NULL),
!       'file' => 'includes/webform.components.inc',
!     ),
!     // webform.pages.inc.
!     'webform_advanced_redirection_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.pages.inc',
!     ),
!     'webform_advanced_submit_limit_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.pages.inc',
!     ),
!     // webform.report.inc.
!     'webform_results_per_page' => array(
!       'arguments' => array('total_count' => NULL, 'pager_count' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_submissions_header' => array(
!       'arguments' => array('node' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_submissions' => array(
!       'arguments' => array('element' => NULL),
!       'template' => 'templates/webform-results-submissions',
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_table_header' => array(
!       'arguments' => array('node' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_table' => array(
!       'arguments' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'node' => NULL, 'total_count' => NULL, 'pager_count' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_download_form' => array(
!       'arguments' => array('form' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_download_select_format' => array(
!       'arguments' => array('element' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     'webform_results_analysis' => array(
!       'arguments' => array('node' => NULL, 'data' => NULL, 'sids' => array(), 'component' => NULL),
!       'file' => 'includes/webform.report.inc',
!     ),
!     // webform.submissions.inc
!     'webform_submission' => array(
!       'arguments' => array('renderable' => NULL),
!       'template' => 'templates/webform-submission',
!       'pattern' => 'webform_submission_[0-9]+',
!       'file' => 'includes/webform.submissions.inc',
!     ),
!     'webform_submission_page' => array(
!       'arguments' => array('node' => NULL, 'submission' => NULL, 'submission_content' => NULL, 'submission_navigation' => NULL, 'submission_information' => NULL),
!       'template' => 'templates/webform-submission-page',
!       'file' => 'includes/webform.submissions.inc',
!     ),
!     'webform_submission_information' => array(
!       'arguments' => array('node' => NULL, 'submission' => NULL),
!       'template' => 'templates/webform-submission-information',
!       'file' => 'includes/webform.submissions.inc',
!     ),
!     'webform_submission_navigation' => array(
!       'arguments' => array('node' => NULL, 'submission' => NULL, 'mode' => NULL),
!       'template' => 'templates/webform-submission-navigation',
!       'file' => 'includes/webform.submissions.inc',
!     ),
!   );
! 
!   // Theme functions in all components.
!   $components = webform_components(TRUE);
!   foreach ($components as $type => $component) {
!     if ($theme_additions = webform_component_invoke($type, 'theme')) {
!       $theme = array_merge($theme, $theme_additions);
      }
!   }
!   return $theme;
  }
  
  /**
   * Implementation of hook_webform_component_info().
   */
! function webform_webform_component_info() {
!   return array(
!     'date' => array(
!       'label' => t('Date'),
!       'description' => t('Presents month, day, and year fields.'),
!       'features' => array(
!         'conditional' => FALSE,
!       ),
!       'file' => 'components/date.inc',
!     ),
!     'email' => array(
!       'label' => t('E-mail'),
!       'description' => t('A special textfield that accepts e-mail addresses.'),
!       'file' => 'components/email.inc',
!       'features' => array(
!         'email_address' => TRUE,
!         'spam_analysis' => TRUE,
!       ),
!     ),
!     'fieldset' => array(
!       'label' => t('Fieldset'),
!       'description' => t('Fieldsets allow you to organize multiple fields into groups.'),
!       'features' => array(
!         'csv' => FALSE,
!         'required' => FALSE,
!         'conditional' => FALSE,
!         'group' => TRUE,
!       ),
!       'file' => 'components/fieldset.inc',
!     ),
!     'file' => array(
!       'label' => t('File'),
!       'description' => t('Allow users to upload files of configurable types.'),
!       'features' => array(
!         'conditional' => FALSE,
!         'attachment' => TRUE,
!       ),
!       'file' => 'components/file.inc',
!     ),
!     'grid' => array(
!       'label' => t('Grid'),
!       'description' => t('Allows creation of grid questions, denoted by radio buttons.'),
!       'features' => array(
!         'conditional' => FALSE,
!       ),
!       'file' => 'components/grid.inc',
!     ),
!     'hidden' => array(
!       'label' => t('Hidden'),
!       'description' => t('A field which is not visible to the user, but is recorded with the submission.'),
!       'file' => 'components/hidden.inc',
!       'features' => array(
!         'required' => FALSE,
!         'email_address' => TRUE,
!         'email_name' => TRUE,
!       ),
!     ),
!     'markup' => array(
!       'label' => t('Markup'),
!       'description' => t('Displays text as HTML in the form; does not render a field.'),
!       'features' => array(
!         'csv' => FALSE,
!         'email' => FALSE,
!         'required' => FALSE,
!         'conditional' => FALSE,
!       ),
!       'file' => 'components/markup.inc',
!     ),
!     'pagebreak' => array(
!       'label' => t('Page break'),
!       'description' => t('Organize forms into multiple pages.'),
!       'features' => array(
!         'csv' => FALSE,
!         'required' => FALSE,
!       ),
!       'file' => 'components/pagebreak.inc',
!     ),
!     'select' => array(
!       'label' => t('Select options'),
!       'description' => t('Allows creation of checkboxes, radio buttons, or select menus.'),
!       'file' => 'components/select.inc',
!       'features' => array(
!         'email_address' => TRUE,
!         'email_name' => TRUE,
!       ),
!     ),
!     'textarea' => array(
!       'label' => t('Textarea'),
!       'description' => t('A large text area that allows for multiple lines of input.'),
!       'file' => 'components/textarea.inc',
!       'features' => array(
!         'spam_analysis' => TRUE,
!       ),
!     ),
!     'textfield' => array(
!       'label' => t('Textfield'),
!       'description' => t('Basic textfield type.'),
!       'file' => 'components/textfield.inc',
!       'features' => array(
!         'email_name' => TRUE,
!         'spam_analysis' => TRUE,
!       ),
!     ),
!     'time' => array(
!       'label' => t('Time'),
!       'description' => t('Presents the user with hour and minute fields. Optional am/pm fields.'),
!       'features' => array(
!         'conditional' => FALSE,
!       ),
!       'file' => 'components/time.inc',
!     ),
!   );
  }
  
  /**
--- 396,792 ----
  /**
   * Menu loader callback. Load a webform e-mail if the given eid is a valid.
   */
! function webform_menu_email_load($eid, $nid)
! {
!     module_load_include('inc', 'webform', 'includes/webform.emails');
!     $node = node_load($nid);
!     $email = webform_email_load($eid, $nid);
!     if ($eid == 'new')
!     {
!         if (isset($_GET['option']) && isset($_GET['email']))
!         {
!             $type = $_GET['option'];
!             if ($type == 'custom')
!             {
!                 $email['email'] = $_GET['email'];
!             }
!             elseif ($type == 'component' && isset($node->webform['components'][$_GET['email']]))
!             {
!                 $email['email'] = $_GET['email'];
!             }
!         }
!     }
! 
!     return $email;
! }
! 
! function webform_submission_access($node, $submission, $op = 'view', $account = NULL)
! {
!     global $user;
!     $account = isset($account) ? $account : $user;
! 
!     $access_all = user_access('access all webform results', $account);
!     $access_own_submission = isset($submission) && user_access('access own webform submissions', $account) && (($account->uid && $account->uid == $submission->uid) || isset($_SESSION['webform_submission'][$submission->sid]));
!     $access_node_submissions = user_access('access own webform results', $account) && $account->uid == $node->uid;
! 
!     $general_access = $access_all || $access_own_submission || $access_node_submissions;
! 
!     // Disable the page cache for anonymous users in this access callback,
!     // otherwise the "Access denied" page gets cached.
!     if (!$account->uid && user_access('access own webform submissions', $account))
!     {
!         webform_disable_page_cache();
!     }
! 
!     $module_access = count(array_filter(module_invoke_all('webform_submission_access', $node, $submission, $op, $account))) > 0;
! 
!     switch ($op)
!     {
!         case 'view':
!             return $module_access || $general_access;
!         case 'edit':
!             return $module_access || ($general_access && (user_access('edit all webform submissions', $account) || (user_access('edit own webform submissions', $account) && $account->uid == $submission->uid)));
!         case 'delete':
!             return $module_access || ($general_access && (user_access('delete all webform submissions', $account) || (user_access('delete own webform submissions', $account) && $account->uid == $submission->uid)));
!         case 'list':
!             return $module_access || user_access('access all webform results', $account) || (user_access('access own webform submissions', $account) && ($account->uid || isset($_SESSION['webform_submission']))) || (user_access('access own webform results', $account) && $account->uid == $node->uid);
!     }
  }
  
  /**
   * Menu access callback. Ensure a user both access and node 'view' permission.
   */
! function webform_results_access($node, $account = NULL)
! {
!     global $user;
!     $account = isset($account) ? $account : $user;
  
!     $module_access = count(array_filter(module_invoke_all('webform_results_access', $node, $account))) > 0;
  
!     return node_access('view', $node, $account) && ($module_access || user_access('access all webform results', $account) || (user_access('access own webform results', $account) && $account->uid == $node->uid));
  }
  
! function webform_results_clear_access($node, $account = NULL)
! {
!     global $user;
!     $account = isset($account) ? $account : $user;
  
!     $module_access = count(array_filter(module_invoke_all('webform_results_clear_access', $node, $account))) > 0;
  
!     return webform_results_access($node, $account) && ($module_access || user_access('delete all webform submissions', $account));
  }
  
  /**
   * Implementation of hook_init().
   */
! function webform_init()
! {
!     // Use the administrative theme if set to use on content editing pages.
!     // See system_init().
!     if (variable_get('node_admin_theme', '0') && arg(0) == 'node' && (arg(2) == 'webform' || arg(2) == 'webform-results'))
!     {
!         global $custom_theme;
!         $custom_theme = variable_get('admin_theme', '0');
!         drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', 'module');
! 
!         // Support for Admin module (1.x).
!         if (function_exists('_admin_init_theme') && empty($custom_theme))
!         {
!             _admin_init_theme();
!         }
      }
  }
  
  /**
   * Implementation of hook_perm().
   */
! function webform_perm()
! {
!     return array(
!         'access all webform results',
!         'access own webform results',
!         'edit all webform submissions',
!         'delete all webform submissions',
!         'access own webform submissions',
!         'edit own webform submissions',
!         'delete own webform submissions',
!     );
  }
  
  /**
   * Implementation of hook_theme().
   */
! function webform_theme()
! {
!     $theme = array(
!         // webform.module.
!         'webform_view' => array(
!             'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'form' => NULL, 'enabled' => NULL),
!         ),
!         'webform_view_messages' => array(
!             'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'limit_exceeded' => NULL, 'allowed_roles' => NULL),
!         ),
!         'webform_form' => array(
!             'arguments' => array('form' => NULL),
!             'template' => 'templates/webform-form',
!             'pattern' => 'webform_form_[0-9]+',
!         ),
!         'webform_confirmation' => array(
!             'arguments' => array('node' => NULL, 'sid' => NULL),
!             'template' => 'templates/webform-confirmation',
!             'pattern' => 'webform_confirmation_[0-9]+',
!         ),
!         'webform_element' => array(
!             'arguments' => array('element' => NULL, 'value' => NULL),
!         ),
!         'webform_element_wrapper' => array(
!             'arguments' => array('element' => NULL, 'content' => NULL),
!         ),
!         'webform_element_text' => array(
!             'arguments' => array('element' => NULL, 'value' => NULL),
!         ),
!         'webform_mail_message' => array(
!             'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
!             'template' => 'templates/webform-mail',
!             'pattern' => 'webform_mail(_[0-9]+)?',
!         ),
!         'webform_mail_headers' => array(
!             'arguments' => array('node' => NULL, 'submission' => NULL, 'email' => NULL),
!             'pattern' => 'webform_mail_headers_[0-9]+',
!         ),
!         'webform_token_help' => array(
!             'arguments' => array(),
!         ),
!         // webform.admin.inc.
!         'webform_admin_settings' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.admin.inc',
!         ),
!         'webform_admin_content' => array(
!             'arguments' => array('nodes' => NULL),
!             'file' => 'includes/webform.admin.inc',
!         ),
!         // webform.emails.inc.
!         'webform_emails_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.emails.inc',
!         ),
!         'webform_email_add_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.emails.inc',
!         ),
!         'webform_email_edit_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.emails.inc',
!         ),
!         // webform.components.inc.
!         'webform_components_page' => array(
!             'arguments' => array('node' => NULL, 'form' => NULL),
!             'file' => 'includes/webform.components.inc',
!         ),
!         'webform_components_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.components.inc',
!         ),
!         'webform_component_select' => array(
!             'arguments' => array('element' => NULL),
!             'file' => 'includes/webform.components.inc',
!         ),
!         // webform.pages.inc.
!         'webform_advanced_redirection_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.pages.inc',
!         ),
!         'webform_advanced_submit_limit_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.pages.inc',
!         ),
!         // webform.report.inc.
!         'webform_results_per_page' => array(
!             'arguments' => array('total_count' => NULL, 'pager_count' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_submissions_header' => array(
!             'arguments' => array('node' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_submissions' => array(
!             'arguments' => array('element' => NULL),
!             'template' => 'templates/webform-results-submissions',
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_table_header' => array(
!             'arguments' => array('node' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_table' => array(
!             'arguments' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'node' => NULL, 'total_count' => NULL, 'pager_count' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_download_form' => array(
!             'arguments' => array('form' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_download_select_format' => array(
!             'arguments' => array('element' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         'webform_results_analysis' => array(
!             'arguments' => array('node' => NULL, 'data' => NULL, 'sids' => array(), 'component' => NULL),
!             'file' => 'includes/webform.report.inc',
!         ),
!         // webform.submissions.inc
!         'webform_submission' => array(
!             'arguments' => array('renderable' => NULL),
!             'template' => 'templates/webform-submission',
!             'pattern' => 'webform_submission_[0-9]+',
!             'file' => 'includes/webform.submissions.inc',
!         ),
!         'webform_submission_page' => array(
!             'arguments' => array('node' => NULL, 'submission' => NULL, 'submission_content' => NULL, 'submission_navigation' => NULL, 'submission_information' => NULL),
!             'template' => 'templates/webform-submission-page',
!             'file' => 'includes/webform.submissions.inc',
!         ),
!         'webform_submission_information' => array(
!             'arguments' => array('node' => NULL, 'submission' => NULL),
!             'template' => 'templates/webform-submission-information',
!             'file' => 'includes/webform.submissions.inc',
!         ),
!         'webform_submission_navigation' => array(
!             'arguments' => array('node' => NULL, 'submission' => NULL, 'mode' => NULL),
!             'template' => 'templates/webform-submission-navigation',
!             'file' => 'includes/webform.submissions.inc',
!         ),
!     );
! 
!     // Theme functions in all components.
!     $components = webform_components(TRUE);
!     foreach ($components as $type => $component)
!     {
!         if ($theme_additions = webform_component_invoke($type, 'theme'))
!         {
!             $theme = array_merge($theme, $theme_additions);
!         }
      }
!     return $theme;
  }
  
  /**
   * Implementation of hook_webform_component_info().
   */
! function webform_webform_component_info()
! {
!     return array(
!         'date' => array(
!             'label' => t('Date'),
!             'description' => t('Presents month, day, and year fields.'),
!             'features' => array(
!                 'conditional' => FALSE,
!             ),
!             'file' => 'components/date.inc',
!         ),
!         'email' => array(
!             'label' => t('E-mail'),
!             'description' => t('A special textfield that accepts e-mail addresses.'),
!             'file' => 'components/email.inc',
!             'features' => array(
!                 'email_address' => TRUE,
!                 'spam_analysis' => TRUE,
!             ),
!         ),
!         'fieldset' => array(
!             'label' => t('Fieldset'),
!             'description' => t('Fieldsets allow you to organize multiple fields into groups.'),
!             'features' => array(
!                 'csv' => FALSE,
!                 'required' => FALSE,
!                 'conditional' => FALSE,
!                 'group' => TRUE,
!             ),
!             'file' => 'components/fieldset.inc',
!         ),
!         'file' => array(
!             'label' => t('File'),
!             'description' => t('Allow users to upload files of configurable types.'),
!             'features' => array(
!                 'conditional' => FALSE,
!                 'attachment' => TRUE,
!             ),
!             'file' => 'components/file.inc',
!         ),
!         'grid' => array(
!             'label' => t('Grid'),
!             'description' => t('Allows creation of grid questions, denoted by radio buttons.'),
!             'features' => array(
!                 'conditional' => FALSE,
!             ),
!             'file' => 'components/grid.inc',
!         ),
!         'hidden' => array(
!             'label' => t('Hidden'),
!             'description' => t('A field which is not visible to the user, but is recorded with the submission.'),
!             'file' => 'components/hidden.inc',
!             'features' => array(
!                 'required' => FALSE,
!                 'email_address' => TRUE,
!                 'email_name' => TRUE,
!             ),
!         ),
!         'markup' => array(
!             'label' => t('Markup'),
!             'description' => t('Displays text as HTML in the form; does not render a field.'),
!             'features' => array(
!                 'csv' => FALSE,
!                 'email' => FALSE,
!                 'required' => FALSE,
!                 'conditional' => FALSE,
!             ),
!             'file' => 'components/markup.inc',
!         ),
!         'pagebreak' => array(
!             'label' => t('Page break'),
!             'description' => t('Organize forms into multiple pages.'),
!             'features' => array(
!                 'csv' => FALSE,
!                 'required' => FALSE,
!             ),
!             'file' => 'components/pagebreak.inc',
!         ),
!         'select' => array(
!             'label' => t('Select options'),
!             'description' => t('Allows creation of checkboxes, radio buttons, or select menus.'),
!             'file' => 'components/select.inc',
!             'features' => array(
!                 'email_address' => TRUE,
!                 'email_name' => TRUE,
!             ),
!         ),
!         'textarea' => array(
!             'label' => t('Textarea'),
!             'description' => t('A large text area that allows for multiple lines of input.'),
!             'file' => 'components/textarea.inc',
!             'features' => array(
!                 'spam_analysis' => TRUE,
!             ),
!         ),
!         'textfield' => array(
!             'label' => t('Textfield'),
!             'description' => t('Basic textfield type.'),
!             'file' => 'components/textfield.inc',
!             'features' => array(
!                 'email_name' => TRUE,
!                 'spam_analysis' => TRUE,
!             ),
!         ),
!         'time' => array(
!             'label' => t('Time'),
!             'description' => t('Presents the user with hour and minute fields. Optional am/pm fields.'),
!             'features' => array(
!                 'conditional' => FALSE,
!             ),
!             'file' => 'components/time.inc',
!         ),
!     );
  }
  
  /**
***************
*** 761,780 ****
   *
   * All webform_client_form forms share the same form handler
   */
! function webform_forms($form_id) {
!   $forms = array();
!   if (strpos($form_id, 'webform_client_form_') === 0) {
!     $forms[$form_id]['callback'] = 'webform_client_form';
!   }
!   return $forms;
  }
  
  /**
   * Implementation of hook_webform_select_options_info().
   */
! function webform_webform_select_options_info() {
!   module_load_include('inc', 'webform', 'includes/webform.options');
!   return _webform_options_info();
  }
  
  /**
--- 794,816 ----
   *
   * All webform_client_form forms share the same form handler
   */
! function webform_forms($form_id)
! {
!     $forms = array();
!     if (strpos($form_id, 'webform_client_form_') === 0)
!     {
!         $forms[$form_id]['callback'] = 'webform_client_form';
!     }
!     return $forms;
  }
  
  /**
   * Implementation of hook_webform_select_options_info().
   */
! function webform_webform_select_options_info()
! {
!     module_load_include('inc', 'webform', 'includes/webform.options');
!     return _webform_options_info();
  }
  
  /**
***************
*** 782,1148 ****
   *
   * Only allow users with view webform submissions to download files.
   */
! function webform_file_download($file) {
!   global $user;
! 
!   // If the Webform directory doesn't exist, don't attempt to deliver a file.
!   $webform_directory = file_directory_path() . '/webform/';
!   if (!is_dir($webform_directory)) {
!     return;
!   }
! 
!   $file = file_check_location(file_directory_path() . '/' . $file, $webform_directory);
!   if ($file && (user_access('access all webform results') || user_access('access own webform results'))) {
!     $info = image_get_info(file_create_path($file));
!     if (isset($info['mime_type'])) {
!       $headers = array('Content-type: ' . $info['mime_type']);
!     }
!     else {
!       $headers = array(
!         'Content-type: force-download',
!         'Content-disposition: attachment',
!       );
      }
-     return $headers;
-   }
  }
  
  /**
   * Implementation of hook_node_type().
   */
! function webform_node_type($op, $info) {
!   $webform_types = webform_variable_get('webform_node_types');
!   $affected_type = isset($info->old_type) ? $info->old_type : $info->type;
!   $key = array_search($affected_type, $webform_types);
!   if ($key !== FALSE) {
!     if ($op == 'update') {
!       $webform_types[$key] = $info->type;
!     }
!     if ($op == 'delete') {
!       unset($webform_types[$key]);
      }
-     variable_set('webform_node_types', $webform_types);
-   }
  }
  
  /**
   * Implementation of hook_nodeapi().
   */
! function webform_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
!   if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
!     return;
!   }
! 
!   switch ($op) {
!     case 'insert':
!       webform_node_insert($node);
!       break;
!     case 'update':
!       webform_node_update($node);
!       break;
!     case 'delete':
!       webform_node_delete($node);
!       break;
!     case 'prepare':
!       webform_node_prepare($node);
!       break;
!     case 'prepare translation':
!       webform_node_prepare_translation($node);
!       break;
!     case 'load':
!       return webform_node_load($node);
!     case 'view':
!       return webform_node_view($node, $teaser, $page);
!   }
! }
  
! /**
!  * Implementation of hook_node_insert().
!  */
! function webform_node_insert($node) {
!   if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
!     return;
!   }
! 
!   module_load_include('inc', 'webform', 'includes/webform.components');
!   module_load_include('inc', 'webform', 'includes/webform.emails');
! 
!   // Insert the webform.
!   $node->webform['nid'] = $node->nid;
!   drupal_write_record('webform', $node->webform);
! 
!   // Insert the components into the database. Used with clone.module.
!   if (isset($node->webform['components']) && !empty($node->webform['components'])) {
!     foreach ($node->webform['components'] as $cid => $component) {
!       $component['nid'] = $node->nid; // Required for clone.module.
!       webform_component_insert($component);
!     }
!   }
! 
!   // Insert emails. Also used with clone.module.
!   if (isset($node->webform['emails']) && !empty($node->webform['emails'])) {
!     foreach ($node->webform['emails'] as $eid => $email) {
!       $email['nid'] = $node->nid;
!       webform_email_insert($email);
!     }
!   }
! 
!   // Set the per-role submission access control.
!   foreach (array_filter($node->webform['roles']) as $rid) {
!     db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
!   }
  }
  
  /**
!  * Implementation of hook_node_update().
   */
! function webform_node_update($node) {
!   if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
!     return;
!   }
! 
!   // Check if there is an existing entry at all for this node.
!   $exists = db_result(db_query('SELECT nid FROM {webform} WHERE nid = %d', $node->nid));
! 
!   // If a webform row doesn't even exist, we can assume it needs to be inserted.
!   if (!$exists) {
!     webform_node_insert($node);
!     return;
!   }
! 
!   // Update the webform entry.
!   $node->webform['nid'] = $node->nid;
!   drupal_write_record('webform', $node->webform, array('nid'));
! 
!   // Compare the webform components and don't do anything if it's not needed.
!   $original = node_load($node->nid);
  
-   if ($original->webform['components'] != $node->webform['components']) {
      module_load_include('inc', 'webform', 'includes/webform.components');
  
!     $original_cids = array_keys($original->webform['components']);
!     $current_cids = array_keys($node->webform['components']);
  
!     $all_cids = $original_cids + $current_cids;
!     $deleted_cids = array_diff($original_cids, $current_cids);
!     $inserted_cids = array_diff($current_cids, $original_cids);
! 
!     foreach ($all_cids as $cid) {
!       if (in_array($cid, $inserted_cids)) {
!         webform_component_insert($node->webform['components'][$cid]);
!       }
!       elseif (in_array($cid, $deleted_cids)) {
!         webform_component_delete($node, $original->webform['components'][$cid]);
!       }
!       elseif ($node->webform['components'][$cid] != $original->webform['components'][$cid]) {
!         $node->webform['components'][$cid]['nid'] = $node->nid;
!         webform_component_update($node->webform['components'][$cid]);
!       }
      }
-   }
  
!   // Compare the webform e-mails and don't do anything if it's not needed.
!   if ($original->webform['emails'] != $node->webform['emails']) {
!     module_load_include('inc', 'webform', 'includes/webform.emails');
  
!     $original_eids = array_keys($original->webform['emails']);
!     $current_eids = array_keys($node->webform['emails']);
  
!     $all_eids = $original_eids + $current_eids;
!     $deleted_eids = array_diff($original_eids, $current_eids);
!     $inserted_eids = array_diff($current_eids, $original_eids);
! 
!     foreach ($all_eids as $eid) {
!       if (in_array($eid, $inserted_eids)) {
!         webform_email_insert($node->webform['emails'][$eid]);
!       }
!       elseif (in_array($eid, $deleted_eids)) {
!         webform_email_delete($node, $original->webform['emails'][$eid]);
!       }
!       elseif ($node->webform['emails'][$eid] != $original->webform['emails'][$eid]) {
!         $node->webform['emails'][$eid]['nid'] = $node->nid;
!         webform_email_update($node->webform['emails'][$eid]);
!       }
      }
-   }
  
!   // Just delete and re-insert roles if they've changed.
!   if ($original->webform['roles'] != $node->webform['roles']) {
!     db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
!     foreach (array_filter($node->webform['roles']) as $rid) {
!       db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
      }
-   }
  }
  
  /**
   * Implementation of hook_delete().
   */
! function webform_node_delete($node) {
!   if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
!     return;
!   }
! 
!   // Allow components clean up extra data, such as uploaded files.
!   module_load_include('inc', 'webform', 'includes/webform.components');
!   foreach ($node->webform['components'] as $cid => $component) {
!     webform_component_delete($node, $component);
!   }
! 
!   // Remove any trace of webform data from the database.
!   db_query('DELETE FROM {webform} WHERE nid = %d', $node->nid);
!   db_query('DELETE FROM {webform_component} WHERE nid = %d', $node->nid);
!   db_query('DELETE FROM {webform_emails} WHERE nid = %d', $node->nid);
!   db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
!   db_query('DELETE FROM {webform_submissions} WHERE nid = %d', $node->nid);
!   db_query('DELETE FROM {webform_submitted_data} WHERE nid = %d', $node->nid);
  }
  
  /**
   * Default settings for a newly created webform node.
   */
! function webform_node_defaults() {
!   return array(
!     'confirmation' => '',
!     'confirmation_format' => FILTER_FORMAT_DEFAULT,
!     'redirect_url' => '<confirmation>',
!     'teaser' => 0,
!     'block' => 0,
!     'allow_draft' => 0,
!     'submit_notice' => 1,
!     'submit_text' => '',
!     'submit_limit' => -1,
!     'submit_interval' => -1,
!     'roles' => array(1, 2),
!     'emails' => array(),
!     'components' => array(),
!   );
  }
  
  /**
   * Implementation of hook_node_prepare().
   */
! function webform_node_prepare(&$node) {
!   if (!isset($node->webform)) {
!     $node->webform = webform_node_defaults();
!   }
  }
  
  /**
   * Implementation of hook_node_prepare_translation().
   */
! function webform_node_prepare_translation(&$node) {
!   // Copy all Webform settings over to translated versions of this node.
!   if (isset($node->translation_source)) {
!     $source_node = node_load($node->translation_source->nid);
!     $node->webform = $source_node->webform;
!   }
  }
  
  /**
   * Implementation of hook_node_load().
   */
! function webform_node_load($node) {
!   module_load_include('inc', 'webform', 'includes/webform.components');
!   $additions = array();
! 
!   if (isset($node->nid)) {
!     $webform = db_fetch_array(db_query('SELECT * FROM {webform} WHERE nid = %d', $node->nid));
  
!     // If a webform record doesn't exist, just return the defaults.
!     if (!$webform) {
!       $additions['webform'] = webform_node_defaults();
!       return $additions;
!     }
  
!     $additions['webform'] = $webform;
!     $additions['webform']['roles'] = array();
!     $result = db_query('SELECT rid FROM {webform_roles} WHERE nid = %d', $node->nid);
!     while ($role = db_fetch_object($result)) {
!       $additions['webform']['roles'][] = $role->rid;
!     }
  
!     $additions['webform']['emails'] = array();
!     $result = db_query('SELECT * FROM {webform_emails} WHERE nid = %d', $node->nid);
!     while ($email = db_fetch_array($result)) {
!       $additions['webform']['emails'][$email['eid']] = $email;
!       $additions['webform']['emails'][$email['eid']]['excluded_components'] = array_filter(explode(',', $email['excluded_components']));
!       if (variable_get('webform_format_override', 0)) {
!         $additions['webform']['emails'][$email['eid']]['html'] = variable_get('webform_default_format', 0);
!       }
      }
-   }
  
!   $additions['webform']['components'] = array();
  
!   // If we don't have a NID yet, no point in doing additional queries.
!   if (!isset($node->nid)) {
      return $additions;
-   }
- 
-   $result = db_query('SELECT * FROM {webform_component} WHERE nid = %d ORDER BY weight, name', $node->nid);
-   while ($c = db_fetch_array($result)) {
-     $component =& $additions['webform']['components'][$c['cid']];
-     $component['nid'] = $node->nid;
-     $component['cid'] = $c['cid'];
-     $component['form_key'] = $c['form_key'] ? $c['form_key'] : $c['cid'];
-     $component['name'] = t($c['name']);
-     $component['type'] = $c['type'];
-     $component['value'] = $c['value'];
-     $component['extra'] = unserialize($c['extra']);
-     $component['mandatory'] = $c['mandatory'];
-     $component['pid'] = $c['pid'];
-     $component['weight'] = $c['weight'];
- 
-     webform_component_defaults($component);
-   }
- 
-   // Organize the components into a fieldset-based order.
-   if (!empty($additions['webform']['components'])) {
-     $component_tree = array();
-     $page_count = 1;
-     _webform_components_tree_build($additions['webform']['components'], $component_tree, 0, $page_count);
-     $additions['webform']['components'] = _webform_components_tree_flatten($component_tree['children']);
-   }
-   return $additions;
  }
  
  /**
   * Implementation of hook_link().
   * Always add a "view form" link.
   */
! function webform_link($type, $node = NULL, $teaser = FALSE) {
!   $links = array();
!   if (isset($node->type) && $node->type === 'webform') {
!     if ($teaser && !$node->webform['teaser']) {
!       $links['webform_goto'] = array(
!         'title' => t('Go to form'),
!         'href' => 'node/' . $node->nid,
!         'attributes' => array('title' => t('View this form.'), 'class' => 'read-more')
!       );
      }
!   }
!   return $links;
  }
  
  /**
   * Implementation of hook_form_alter().
   */
! function webform_form_alter(&$form, $form_state, $form_id) {
!   $matches = array();
!   if (isset($form['#node']->type) && $form_id == $form['#node']->type . '_node_form' && in_array($form['#node']->type, webform_variable_get('webform_node_types'))) {
!     $node = $form['#node'];
!     // Preserve all Webform options currently set on the node.
!     $form['webform'] = array(
!       '#type' => 'value',
!       '#value' => $node->webform,
!     );
  
!     // If a new node, redirect the user to the components form after save.
!     if (empty($node->nid) && in_array($node->type, webform_variable_get('webform_node_types_redirect'))) {
!       $form['buttons']['submit']['#submit'][] = 'webform_form_submit';
      }
-   }
  }
  
  /**
--- 818,1241 ----
   *
   * Only allow users with view webform submissions to download files.
   */
! function webform_file_download($file)
! {
!     global $user;
! 
!     // If the Webform directory doesn't exist, don't attempt to deliver a file.
!     $webform_directory = file_directory_path() . '/webform/';
!     if (!is_dir($webform_directory))
!     {
!         return;
!     }
! 
!     $file = file_check_location(file_directory_path() . '/' . $file, $webform_directory);
!     if ($file && (user_access('access all webform results') || user_access('access own webform results')))
!     {
!         $info = image_get_info(file_create_path($file));
!         if (isset($info['mime_type']))
!         {
!             $headers = array('Content-type: ' . $info['mime_type']);
!         }
!         else
!         {
!             $headers = array(
!                 'Content-type: force-download',
!                 'Content-disposition: attachment',
!             );
!         }
!         return $headers;
      }
  }
  
  /**
   * Implementation of hook_node_type().
   */
! function webform_node_type($op, $info)
! {
!     $webform_types = webform_variable_get('webform_node_types');
!     $affected_type = isset($info->old_type) ? $info->old_type : $info->type;
!     $key = array_search($affected_type, $webform_types);
!     if ($key !== FALSE)
!     {
!         if ($op == 'update')
!         {
!             $webform_types[$key] = $info->type;
!         }
!         if ($op == 'delete')
!         {
!             unset($webform_types[$key]);
!         }
!         variable_set('webform_node_types', $webform_types);
      }
  }
  
  /**
   * Implementation of hook_nodeapi().
   */
! function webform_nodeapi(&$node, $op, $teaser = NULL, $page = NULL)
! {
!     if (!in_array($node->type, webform_variable_get('webform_node_types')))
!     {
!         return;
!     }
  
!     switch ($op)
!     {
!         case 'insert':
!             webform_node_insert($node);
!             break;
!         case 'update':
!             webform_node_update($node);
!             break;
!         case 'delete':
!             webform_node_delete($node);
!             break;
!         case 'prepare':
!             webform_node_prepare($node);
!             break;
!         case 'prepare translation':
!             webform_node_prepare_translation($node);
!             break;
!         case 'load':
!             return webform_node_load($node);
!         case 'view':
!             return webform_node_view($node, $teaser, $page);
!     }
  }
  
  /**
!  * Implementation of hook_node_insert().
   */
! function webform_node_insert($node)
! {
!     if (!in_array($node->type, webform_variable_get('webform_node_types')))
!     {
!         return;
!     }
  
      module_load_include('inc', 'webform', 'includes/webform.components');
+     module_load_include('inc', 'webform', 'includes/webform.emails');
  
!     // Insert the webform.
!     $node->webform['nid'] = $node->nid;
!     drupal_write_record('webform', $node->webform);
! 
!     // Insert the components into the database. Used with clone.module.
!     if (isset($node->webform['components']) && !empty($node->webform['components']))
!     {
!         foreach ($node->webform['components'] as $cid => $component)
!         {
!             $component['nid'] = $node->nid; // Required for clone.module.
!             webform_component_insert($component);
!         }
!     }
  
!     // Insert emails. Also used with clone.module.
!     if (isset($node->webform['emails']) && !empty($node->webform['emails']))
!     {
!         foreach ($node->webform['emails'] as $eid => $email)
!         {
!             $email['nid'] = $node->nid;
!             webform_email_insert($email);
!         }
      }
  
!     // Set the per-role submission access control.
!     foreach (array_filter($node->webform['roles']) as $rid)
!     {
!         db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
!     }
! }
  
! /**
!  * Implementation of hook_node_update().
!  */
! function webform_node_update($node)
! {
!     if (!in_array($node->type, webform_variable_get('webform_node_types')))
!     {
!         return;
!     }
! 
!     // Check if there is an existing entry at all for this node.
!     $exists = db_result(db_query('SELECT nid FROM {webform} WHERE nid = %d', $node->nid));
! 
!     // If a webform row doesn't even exist, we can assume it needs to be inserted.
!     if (!$exists)
!     {
!         webform_node_insert($node);
!         return;
!     }
! 
!     // Update the webform entry.
!     $node->webform['nid'] = $node->nid;
!     drupal_write_record('webform', $node->webform, array('nid'));
! 
!     // Compare the webform components and don't do anything if it's not needed.
!     $original = node_load($node->nid);
! 
!     if ($original->webform['components'] != $node->webform['components'])
!     {
!         module_load_include('inc', 'webform', 'includes/webform.components');
! 
!         $original_cids = array_keys($original->webform['components']);
!         $current_cids = array_keys($node->webform['components']);
! 
!         $all_cids = $original_cids + $current_cids;
!         $deleted_cids = array_diff($original_cids, $current_cids);
!         $inserted_cids = array_diff($current_cids, $original_cids);
! 
!         foreach ($all_cids as $cid)
!         {
!             if (in_array($cid, $inserted_cids))
!             {
!                 webform_component_insert($node->webform['components'][$cid]);
!             }
!             elseif (in_array($cid, $deleted_cids))
!             {
!                 webform_component_delete($node, $original->webform['components'][$cid]);
!             }
!             elseif ($node->webform['components'][$cid] != $original->webform['components'][$cid])
!             {
!                 $node->webform['components'][$cid]['nid'] = $node->nid;
!                 webform_component_update($node->webform['components'][$cid]);
!             }
!         }
!     }
  
!     // Compare the webform e-mails and don't do anything if it's not needed.
!     if ($original->webform['emails'] != $node->webform['emails'])
!     {
!         module_load_include('inc', 'webform', 'includes/webform.emails');
! 
!         $original_eids = array_keys($original->webform['emails']);
!         $current_eids = array_keys($node->webform['emails']);
! 
!         $all_eids = $original_eids + $current_eids;
!         $deleted_eids = array_diff($original_eids, $current_eids);
!         $inserted_eids = array_diff($current_eids, $original_eids);
! 
!         foreach ($all_eids as $eid)
!         {
!             if (in_array($eid, $inserted_eids))
!             {
!                 webform_email_insert($node->webform['emails'][$eid]);
!             }
!             elseif (in_array($eid, $deleted_eids))
!             {
!                 webform_email_delete($node, $original->webform['emails'][$eid]);
!             }
!             elseif ($node->webform['emails'][$eid] != $original->webform['emails'][$eid])
!             {
!                 $node->webform['emails'][$eid]['nid'] = $node->nid;
!                 webform_email_update($node->webform['emails'][$eid]);
!             }
!         }
      }
  
!     // Just delete and re-insert roles if they've changed.
!     if ($original->webform['roles'] != $node->webform['roles'])
!     {
!         db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
!         foreach (array_filter($node->webform['roles']) as $rid)
!         {
!             db_query('INSERT INTO {webform_roles} (nid, rid) VALUES (%d, %d)', $node->nid, $rid);
!         }
      }
  }
  
  /**
   * Implementation of hook_delete().
   */
! function webform_node_delete($node)
! {
!     if (!in_array($node->type, webform_variable_get('webform_node_types')))
!     {
!         return;
!     }
! 
!     // Allow components clean up extra data, such as uploaded files.
!     module_load_include('inc', 'webform', 'includes/webform.components');
!     foreach ($node->webform['components'] as $cid => $component)
!     {
!         webform_component_delete($node, $component);
!     }
! 
!     // Remove any trace of webform data from the database.
!     db_query('DELETE FROM {webform} WHERE nid = %d', $node->nid);
!     db_query('DELETE FROM {webform_component} WHERE nid = %d', $node->nid);
!     db_query('DELETE FROM {webform_emails} WHERE nid = %d', $node->nid);
!     db_query('DELETE FROM {webform_roles} WHERE nid = %d', $node->nid);
!     db_query('DELETE FROM {webform_submissions} WHERE nid = %d', $node->nid);
!     db_query('DELETE FROM {webform_submitted_data} WHERE nid = %d', $node->nid);
  }
  
  /**
   * Default settings for a newly created webform node.
   */
! function webform_node_defaults()
! {
!     return array(
!         'confirmation' => '',
!         'confirmation_format' => FILTER_FORMAT_DEFAULT,
!         'redirect_url' => '<confirmation>',
!         'teaser' => 0,
!         'block' => 0,
!         'allow_draft' => 0,
!         'submit_notice' => 1,
!         'submit_text' => '',
!         'submit_limit' => -1,
!         'submit_interval' => -1,
!         'roles' => array(1, 2),
!         'emails' => array(),
!         'components' => array(),
!     );
  }
  
  /**
   * Implementation of hook_node_prepare().
   */
! function webform_node_prepare(&$node)
! {
!     if (!isset($node->webform))
!     {
!         $node->webform = webform_node_defaults();
!     }
  }
  
  /**
   * Implementation of hook_node_prepare_translation().
   */
! function webform_node_prepare_translation(&$node)
! {
!     // Copy all Webform settings over to translated versions of this node.
!     if (isset($node->translation_source))
!     {
!         $source_node = node_load($node->translation_source->nid);
!         $node->webform = $source_node->webform;
!     }
  }
  
  /**
   * Implementation of hook_node_load().
   */
! function webform_node_load($node)
! {
!     module_load_include('inc', 'webform', 'includes/webform.components');
!     $additions = array();
  
!     if (isset($node->nid))
!     {
!         $webform = db_fetch_array(db_query('SELECT * FROM {webform} WHERE nid = %d', $node->nid));
! 
!         // If a webform record doesn't exist, just return the defaults.
!         if (!$webform)
!         {
!             $additions['webform'] = webform_node_defaults();
!             return $additions;
!         }
  
!         $additions['webform'] = $webform;
!         $additions['webform']['roles'] = array();
!         $result = db_query('SELECT rid FROM {webform_roles} WHERE nid = %d', $node->nid);
!         while ($role = db_fetch_object($result))
!         {
!             $additions['webform']['roles'][] = $role->rid;
!         }
  
!         $additions['webform']['emails'] = array();
!         $result = db_query('SELECT * FROM {webform_emails} WHERE nid = %d', $node->nid);
!         while ($email = db_fetch_array($result))
!         {
!             $additions['webform']['emails'][$email['eid']] = $email;
!             $additions['webform']['emails'][$email['eid']]['excluded_components'] = array_filter(explode(',', $email['excluded_components']));
!             if (variable_get('webform_format_override', 0))
!             {
!                 $additions['webform']['emails'][$email['eid']]['html'] = variable_get('webform_default_format', 0);
!             }
!         }
      }
  
!     $additions['webform']['components'] = array();
  
!     // If we don't have a NID yet, no point in doing additional queries.
!     if (!isset($node->nid))
!     {
!         return $additions;
!     }
! 
!     $result = db_query('SELECT * FROM {webform_component} WHERE nid = %d ORDER BY weight, name', $node->nid);
!     while ($c = db_fetch_array($result))
!     {
!         $component = & $additions['webform']['components'][$c['cid']];
!         $component['nid'] = $node->nid;
!         $component['cid'] = $c['cid'];
!         $component['form_key'] = $c['form_key'] ? $c['form_key'] : $c['cid'];
!         $component['name'] = t($c['name']);
!         $component['type'] = $c['type'];
!         $component['value'] = $c['value'];
!         $component['extra'] = unserialize($c['extra']);
!         $component['mandatory'] = $c['mandatory'];
!         $component['pid'] = $c['pid'];
!         $component['weight'] = $c['weight'];
! 
!         webform_component_defaults($component);
!     }
! 
!     // Organize the components into a fieldset-based order.
!     if (!empty($additions['webform']['components']))
!     {
!         $component_tree = array();
!         $page_count = 1;
!         _webform_components_tree_build($additions['webform']['components'], $component_tree, 0, $page_count);
!         $additions['webform']['components'] = _webform_components_tree_flatten($component_tree['children']);
!     }
      return $additions;
  }
  
  /**
   * Implementation of hook_link().
   * Always add a "view form" link.
   */
! function webform_link($type, $node = NULL, $teaser = FALSE)
! {
!     $links = array();
!     if (isset($node->type) && $node->type === 'webform')
!     {
!         if ($teaser && !$node->webform['teaser'])
!         {
!             $links['webform_goto'] = array(
!                 'title' => t('Go to form'),
!                 'href' => 'node/' . $node->nid,
!                 'attributes' => array('title' => t('View this form.'), 'class' => 'read-more')
!             );
!         }
      }
!     return $links;
  }
  
  /**
   * Implementation of hook_form_alter().
   */
! function webform_form_alter(&$form, $form_state, $form_id)
! {
!     $matches = array();
!     if (isset($form['#node']->type) && $form_id == $form['#node']->type . '_node_form' && in_array($form['#node']->type, webform_variable_get('webform_node_types')))
!     {
!         $node = $form['#node'];
!         // Preserve all Webform options currently set on the node.
!         $form['webform'] = array(
!             '#type' => 'value',
!             '#value' => $node->webform,
!         );
  
!         // If a new node, redirect the user to the components form after save.
!         if (empty($node->nid) && in_array($node->type, webform_variable_get('webform_node_types_redirect')))
!         {
!             $form['buttons']['submit']['#submit'][] = 'webform_form_submit';
!         }
      }
  }
  
  /**
***************
*** 1151,1248 ****
   * Redirect the user to the components form on new node inserts. Note that this
   * fires after the hook_submit() function above.
   */
! function webform_form_submit($form, &$form_state) {
!   drupal_set_message(t('The new webform %title has been created. Add new fields to your webform with the form below.', array('%title' => $form_state['values']['title'])));
!   $form_state['redirect'] = 'node/' . $form_state['nid'] . '/webform/components';
  }
  
  /**
   * Implementation of hook_node_view().
   */
! function webform_node_view(&$node, $teaser, $page) {
!   global $user;
!   // If empty, a teaser, or a new node (during preview) do not display.
!   if (empty($node->webform['components']) || ($teaser && !$node->webform['teaser']) || empty($node->nid)) {
!     return;
!   }
! 
!   $info = array();
!   $submission = array();
!   $submission_count = 0;
!   $enabled = TRUE;
!   $logging_in = FALSE;
!   $limit_exceeded = FALSE;
! 
!   // When logging in using a form on the same page as a webform node, surpress
!   // output messages so that they don't show up after the user has logged in.
!   // See http://drupal.org/node/239343.
!   if (isset($_POST['op']) && isset($_POST['name']) && isset($_POST['pass'])) {
!     $logging_in = TRUE;
!   }
! 
!   // Check if the user's role can submit this webform.
!   if (variable_get('webform_submission_access_control', 1)) {
!     $allowed_roles = array();
!     foreach ($node->webform['roles'] as $rid) {
!       $allowed_roles[$rid] = isset($user->roles[$rid]) ? TRUE : FALSE;
!     }
!     if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
!       $enabled = FALSE;
!     }
!   }
!   else {
!     // If not using Webform submission access control, allow for all roles.
!     $allowed_roles = array_keys(user_roles());
!   }
  
!   // Check if the user can add another submission.
!   if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
!     module_load_include('inc', 'webform', 'includes/webform.submissions');
  
!     // Disable the form if the limit is exceeded and page cache is not active.
!     if (($limit_exceeded = _webform_submission_limit_check($node)) && ($user->uid != 0 || variable_get('cache', 0) == 0)) {
!       $enabled = FALSE;
      }
-   }
  
!   // Get a count of previous submissions by this user.
!   if ($page && webform_submission_access($node, NULL, 'list')) {
!     module_load_include('inc', 'webform', 'includes/webform.submissions');
!     $submission_count = webform_get_submission_count($node->nid, $user->uid);
!   }
  
!   // Check if this user has a draft for this webform.
!   $is_draft = FALSE;
!   if ($node->webform['allow_draft'] && $user->uid != 0) {
!     // Draft found - display form with draft data for further editing.
!     if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
!       module_load_include('inc', 'webform', 'includes/webform.submissions');
!       $submission = webform_get_submission($node->nid, $draft_sid);
!       $enabled = TRUE;
!       $is_draft = TRUE;
!     }
!   }
! 
!   // Render the form and generate the output.
!   $form = !empty($node->webform['components']) ? drupal_get_form('webform_client_form_' . $node->nid, $node, $submission, $is_draft) : '';
!   $output = theme('webform_view', $node, $teaser, $page, $form, $enabled);
! 
!   // Remove the surrounding <form> tag if this is a preview.
!   if ($node->build_mode == NODE_BUILD_PREVIEW) {
!     $output = preg_replace('/<\/?form[^>]*>/', '', $output);
!   }
! 
!   // Print out messages for the webform.
!   if ($node->build_mode != NODE_BUILD_PREVIEW && !isset($node->webform_block) && !$logging_in) {
!     theme('webform_view_messages', $node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles);
!   }
! 
!   if (isset($output)) {
!     if (module_exists('content')) {
!       $weight = content_extra_field_weight($node->type, 'webform');
      }
-     $node->content['webform'] = array('#value' => $output, '#weight' => isset($weight) ? $weight : 10);
-   }
  }
  
  /**
--- 1244,1386 ----
   * Redirect the user to the components form on new node inserts. Note that this
   * fires after the hook_submit() function above.
   */
! function webform_form_submit($form, &$form_state)
! {
!     drupal_set_message(t('The new webform %title has been created. Add new fields to your webform with the form below.', array('%title' => $form_state['values']['title'])));
!     $form_state['redirect'] = 'node/' . $form_state['nid'] . '/webform/components';
  }
  
  /**
   * Implementation of hook_node_view().
   */
! function webform_node_view(&$node, $teaser, $page)
! {
!     global $user;
!     // If empty, a teaser, or a new node (during preview) do not display.
!     if (empty($node->webform['components']) || ($teaser && !$node->webform['teaser']) || empty($node->nid))
!     {
!         return;
!     }
! 
!     $info = array();
!     $submission = array();
!     $submission_count = 0;
!     $enabled = TRUE;
!     $logging_in = FALSE;
!     $limit_exceeded = FALSE;
! 
!     // When logging in using a form on the same page as a webform node, surpress
!     // output messages so that they don't show up after the user has logged in.
!     // See http://drupal.org/node/239343.
!     if (isset($_POST['op']) && isset($_POST['name']) && isset($_POST['pass']))
!     {
!         $logging_in = TRUE;
!     }
! 
!     // Check if the user's role can submit this webform.
!     if (variable_get('webform_submission_access_control', 1))
!     {
!         $allowed_roles = array();
!         foreach ($node->webform['roles'] as $rid)
!         {
!             $allowed_roles[$rid] = isset($user->roles[$rid]) ? TRUE : FALSE;
!         }
!         if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1)
!         {
!             $enabled = FALSE;
!         }
!     }
!     else
!     {
!         // If not using Webform submission access control, allow for all roles.
!         $allowed_roles = array_keys(user_roles());
!     }
  
!     // Check if the user can add another submission.
!     if ($node->webform['submit_limit'] != -1)
!     { // -1: Submissions are never throttled.
!         module_load_include('inc', 'webform', 'includes/webform.submissions');
! 
!         // Disable the form if the limit is exceeded and page cache is not active.
!         if (($limit_exceeded = _webform_submission_limit_check($node)) && ($user->uid != 0 || variable_get('cache', 0) == 0))
!         {
!             $enabled = FALSE;
!         }
!     }
  
!     // Get a count of previous submissions by this user.
!     if ($page && webform_submission_access($node, NULL, 'list'))
!     {
!         module_load_include('inc', 'webform', 'includes/webform.submissions');
!         $submission_count = webform_get_submission_count($node->nid, $user->uid);
!     }
! 
!     // Check if this user has a draft for this webform.
!     $is_draft = FALSE;
!     if ($node->webform['allow_draft'] && $user->uid != 0)
!     {
!         // Draft found - display form with draft data for further editing.
!         if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid))
!         {
!             module_load_include('inc', 'webform', 'includes/webform.submissions');
!             $submission = webform_get_submission($node->nid, $draft_sid);
!             $enabled = TRUE;
!             $is_draft = TRUE;
!         }
      }
  
!     // Render the form and generate the output.
!     $form = !empty($node->webform['components']) ? drupal_get_form('webform_client_form_' . $node->nid, $node, $submission, $is_draft) : '';
!     $output = theme('webform_view', $node, $teaser, $page, $form, $enabled);
! 
!     // Remove the surrounding <form> tag if this is a preview.
!     if ($node->build_mode == NODE_BUILD_PREVIEW)
!     {
!         $output = preg_replace('/<\/?form[^>]*>/', '', $output);
!     }
! 
!     // Print out messages for the webform.
!     if ($node->build_mode != NODE_BUILD_PREVIEW && !isset($node->webform_block) && !$logging_in)
!     {
!         theme('webform_view_messages', $node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles);
!     }
! 
!     if (isset($output))
!     {
!         if (module_exists('content'))
!         {
!             $weight = content_extra_field_weight($node->type, 'webform');
!         }
  
!         //Get the expiry date
! //        $query = "SELECT field_expiry_value FROM content_type_webform WHERE nid=%d";
! //        $results = db_query($query, $node->nid);
! //        $expiry_date = "";
! //        while ($fields = db_fetch_array($results))
! //        {
! //            $expiry_date = $fields["field_expiry_value"];
! //        }
! //        //Get only the date by removing the time segment
! //        $pos = strrpos($expiry_date, "T");
! //        $expiry_date = substr($expiry_date, 0, $pos);
! //
! //        $date_array = explode("-", $expiry_date);
! //        $year = $date_array[0];
! //        $month = $date_array[1];
! //        $day = $date_array[2];
! //
! //        if (time() > mktime(0, 0, 0, $month, $day, $year))
! //        {
! //            //Form has expired
! //            $message = "Submissions for this form are closed.";
! //            $type = "warning";
! //            drupal_set_message($message, $type);
! //        }
! //        else
! //        {
!             $node->content['webform'] = array('#value' => $output, '#weight' => isset($weight) ? $weight : 10);
! //        }
      }
  }
  
  /**
***************
*** 1259,1269 ****
   * @param $enabled
   *   If the form allowed to be completed by the current user.
   */
! function theme_webform_view($node, $teaser, $page, $form, $enabled) {
!   // Only show the form if this user is allowed access.
!   if ($enabled) {
!     return $form;
!   }
  }
  
  /**
--- 1397,1409 ----
   * @param $enabled
   *   If the form allowed to be completed by the current user.
   */
! function theme_webform_view($node, $teaser, $page, $form, $enabled)
! {
!     // Only show the form if this user is allowed access.
!     if ($enabled)
!     {
!         return $form;
!     }
  }
  
  /**
***************
*** 1283,1493 ****
   * @param $allowed_roles
   *   A list of user roles that are allowed to submit this webform.
   */
! function theme_webform_view_messages($node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles) {
!   global $user;
  
!   $type = 'notice';
!   $cached = $user->uid == 0 && variable_get('cache', 0);
  
!   // If not allowed to submit the form, give an explanation.
!   if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
!     if (empty($allowed_roles)) {
!       // No roles are allowed to submit the form.
!       $message = t('Submissions for this form are closed.');
!     }
!     elseif (isset($allowed_roles[2])) {
!       // The "authenticated user" role is allowed to submit and the user is currently logged-out.
!       $login = url('user/login', array('query' => drupal_get_destination()));
!       $register = url('user/register', array('query' => drupal_get_destination()));
!       if (variable_get('user_register', 1) == 0) {
!         $message = t('You must <a href="!login">login</a> to view this form.', array('!login' => $login));
!       }
!       else {
!         $message = t('You must <a href="!login">login</a> or <a href="!register">register</a> to view this form.', array('!login' => $login, '!register' => $register));
!       }
!     }
!     else {
!       // The user must be some other role to submit.
!       $message = t('You do not have permission to view this form.');
!     }
!   }
! 
!   // If the user has exceeded the limit of submissions, explain the limit.
!   elseif ($limit_exceeded && !$cached) {
!     if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] > 1) {
!       $message = t('You have submitted this form the maximum number of times (@count).', array('@count' => $node->webform['submit_limit']));
!     }
!     elseif ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1) {
!       $message = t('You have already submitted this form.');
!     }
!     else {
!       $message = t('You may not submit another entry at this time.');
!     }
!     $type = 'error';
!   }
! 
!   // If the user has submitted before, give them a link to their submissions.
!   if ($submission_count > 0 && $node->webform['submit_notice'] == 1 && !$cached) {
!     if (empty($message)) {
!       $message = t('You have already submitted this form.') . ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
!     }
!     else {
!       $message .= ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
!     }
!   }
! 
!   if ($page && isset($message)) {
!     drupal_set_message($message, $type);
!   }
  }
  
  /**
   * Implementation of hook_mail().
   */
! function webform_mail($key, &$message, $params) {
!   $message['headers'] = array_merge($message['headers'], $params['headers']);
!   $message['subject'] = $params['subject'];
!   $message['body'][] = $params['message'];
  }
  
  /**
   * Implementation of hook_block().
   */
! function webform_block($op = 'list', $delta = 0, $edit = array()) {
!   // Get the node ID from delta.
!   $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
!   // The result will be FALSE if this is not a webform node block.
!   if ($op != 'list' && !db_result(db_query("SELECT block FROM {webform} WHERE nid = %d", $nid))) {
!     return;
!   }
! 
!   switch ($op) {
!     case 'list':
!       return webform_block_info();
!     case 'view':
!       return webform_block_view($delta);
!     case 'configure':
!       return webform_block_configure($delta);
!     case 'save':
!       webform_block_save($delta, $edit);
!       break;
!   }
  }
  
  /**
   * Implementation of hook_block_info().
   */
! function webform_block_info() {
!   $blocks = array();
!   $webform_node_types = webform_variable_get('webform_node_types');
!   if (!empty($webform_node_types)) {
!     $placeholders = db_placeholders($webform_node_types);
!     $result = db_query("SELECT n.title, n.nid FROM {webform} w LEFT JOIN {node} n ON w.nid = n.nid WHERE w.block = 1 AND n.type IN ($placeholders)", $webform_node_types);
!     while ($data = db_fetch_object($result)) {
!       $blocks['client-block-'. $data->nid] = array(
!         'info' => t('Webform: !title', array('!title' => $data->title)),
!         'cache' => BLOCK_NO_CACHE,
!       );
      }
!   }
!   return $blocks;
  }
  
  /**
   * Implementation of hook_block_view().
   */
! function webform_block_view($delta = '') {
!   global $user;
  
!   // Load the block-specific configuration settings.
!   $webform_blocks = variable_get('webform_blocks', array());
!   $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
!   $settings += array(
!     'display' => 'form',
!   );
! 
!   // Get the node ID from delta.
!   $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
! 
!   // Load node in current language.
!   if (module_exists('translation')) {
!     global $language;
!     if (($translations = translation_node_get_translations($nid)) && (isset($translations[$language->language]))) {
!       $nid = $translations[$language->language]->nid;
!     }
!   }
! 
!   // The webform node to display in the block.
!   $node = node_load($nid);
! 
!   // Return if user has no access to the webform node.
!   if (!node_access('view', $node)) {
!     return;
!   }
! 
!   // This is a webform node block.
!   $node->webform_block = TRUE;
! 
!   // Use the node title for the block title.
!   $subject = $node->title;
! 
!   // Generate the content of the block based on display settings.
!   if ($settings['display'] == 'form') {
!     webform_node_view($node, FALSE, TRUE, FALSE);
!     $content = $node->content['webform']['#value'];
!   }
!   else {
!     $teaser = ($settings['display'] == 'teaser') ? TRUE : FALSE;
!     $content = node_view($node, $teaser, TRUE, FALSE);
!   }
! 
!   // Create the block.
!   $block = array(
!     'subject' => $subject,
!     'content' => $content,
!   );
!   return $block;
  }
  
  /**
   * Implementation of hook_block_configure().
   */
! function webform_block_configure($delta = '') {
!   // Load the block-specific configuration settings.
!   $webform_blocks = variable_get('webform_blocks', array());
!   $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
!   $settings += array(
!     'display' => 'form',
!   );
! 
!   $form = array();
!   $form['display'] = array(
!     '#type' => 'radios',
!     '#title' => t('Display mode'),
!     '#default_value' => $settings['display'],
!     '#options' => array(
!       'form' => t('Form only'),
!       'full' => t('Full node'),
!       'teaser' => t('Teaser'),
!     ),
!     '#description' => t('The display mode determines how much of the webform to show within the block.'),
!   );
!   return $form;
  }
  
  /**
   * Implementation of hook_block_save().
   */
! function webform_block_save($delta = '', $edit = array()) {
!   // Load the previously defined block-specific configuration settings.
!   $settings = variable_get('webform_blocks', array());
!   // Build the settings array.
!   $new_settings[$delta] = array(
!     'display' => $edit['display'],
!   );
!   // We store settings for multiple blocks in just one variable
!   // so we merge the existing settings with the new ones before save.
!   variable_set('webform_blocks', array_merge($settings, $new_settings));
  }
  
  /**
--- 1423,1664 ----
   * @param $allowed_roles
   *   A list of user roles that are allowed to submit this webform.
   */
! function theme_webform_view_messages($node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles)
! {
!     global $user;
! 
!     $type = 'notice';
!     $cached = $user->uid == 0 && variable_get('cache', 0);
! 
!     // If not allowed to submit the form, give an explanation.
!     if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1)
!     {
!         if (empty($allowed_roles))
!         {
!             // No roles are allowed to submit the form.
!             $message = t('Submissions for this form are closed.');
!         }
!         elseif (isset($allowed_roles[2]))
!         {
!             // The "authenticated user" role is allowed to submit and the user is currently logged-out.
!             $login = url('user/login', array('query' => drupal_get_destination()));
!             $register = url('user/register', array('query' => drupal_get_destination()));
!             if (variable_get('user_register', 1) == 0)
!             {
!                 $message = t('You must <a href="!login">login</a> to view this form.', array('!login' => $login));
!             }
!             else
!             {
!                 $message = t('You must <a href="!login">login</a> or <a href="!register">register</a> to view this form.', array('!login' => $login, '!register' => $register));
!             }
!         }
!         else
!         {
!             // The user must be some other role to submit.
!             $message = t('You do not have permission to view this form.');
!         }
!     }
! 
!     // If the user has exceeded the limit of submissions, explain the limit.
!     elseif ($limit_exceeded && !$cached)
!     {
!         if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] > 1)
!         {
!             $message = t('You have submitted this form the maximum number of times (@count).', array('@count' => $node->webform['submit_limit']));
!         }
!         elseif ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1)
!         {
!             $message = t('You have already submitted this form.');
!         }
!         else
!         {
!             $message = t('You may not submit another entry at this time.');
!         }
!         $type = 'error';
!     }
  
!     // If the user has submitted before, give them a link to their submissions.
!     if ($submission_count > 0 && $node->webform['submit_notice'] == 1 && !$cached)
!     {
!         if (empty($message))
!         {
!             $message = t('You have already submitted this form.') . ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
!         }
!         else
!         {
!             $message .= ' ' . t('<a href="!url">View your previous submissions</a>.', array('!url' => url('node/' . $node->nid . '/submissions')));
!         }
!     }
  
!     if ($page && isset($message))
!     {
!         drupal_set_message($message, $type);
!     }
  }
  
  /**
   * Implementation of hook_mail().
   */
! function webform_mail($key, &$message, $params)
! {
!     $message['headers'] = array_merge($message['headers'], $params['headers']);
!     $message['subject'] = $params['subject'];
!     $message['body'][] = $params['message'];
  }
  
  /**
   * Implementation of hook_block().
   */
! function webform_block($op = 'list', $delta = 0, $edit = array())
! {
!     // Get the node ID from delta.
!     $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
!     // The result will be FALSE if this is not a webform node block.
!     if ($op != 'list' && !db_result(db_query("SELECT block FROM {webform} WHERE nid = %d", $nid)))
!     {
!         return;
!     }
! 
!     switch ($op)
!     {
!         case 'list':
!             return webform_block_info();
!         case 'view':
!             return webform_block_view($delta);
!         case 'configure':
!             return webform_block_configure($delta);
!         case 'save':
!             webform_block_save($delta, $edit);
!             break;
!     }
  }
  
  /**
   * Implementation of hook_block_info().
   */
! function webform_block_info()
! {
!     $blocks = array();
!     $webform_node_types = webform_variable_get('webform_node_types');
!     if (!empty($webform_node_types))
!     {
!         $placeholders = db_placeholders($webform_node_types);
!         $result = db_query("SELECT n.title, n.nid FROM {webform} w LEFT JOIN {node} n ON w.nid = n.nid WHERE w.block = 1 AND n.type IN ($placeholders)", $webform_node_types);
!         while ($data = db_fetch_object($result))
!         {
!             $blocks['client-block-' . $data->nid] = array(
!                 'info' => t('Webform: !title', array('!title' => $data->title)),
!                 'cache' => BLOCK_NO_CACHE,
!             );
!         }
      }
!     return $blocks;
  }
  
  /**
   * Implementation of hook_block_view().
   */
! function webform_block_view($delta = '')
! {
!     global $user;
! 
!     // Load the block-specific configuration settings.
!     $webform_blocks = variable_get('webform_blocks', array());
!     $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
!     $settings += array(
!         'display' => 'form',
!     );
! 
!     // Get the node ID from delta.
!     $nid = drupal_substr($delta, strrpos($delta, '-') + 1);
! 
!     // Load node in current language.
!     if (module_exists('translation'))
!     {
!         global $language;
!         if (($translations = translation_node_get_translations($nid)) && (isset($translations[$language->language])))
!         {
!             $nid = $translations[$language->language]->nid;
!         }
!     }
  
!     // The webform node to display in the block.
!     $node = node_load($nid);
! 
!     // Return if user has no access to the webform node.
!     if (!node_access('view', $node))
!     {
!         return;
!     }
! 
!     // This is a webform node block.
!     $node->webform_block = TRUE;
! 
!     // Use the node title for the block title.
!     $subject = $node->title;
! 
!     // Generate the content of the block based on display settings.
!     if ($settings['display'] == 'form')
!     {
!         webform_node_view($node, FALSE, TRUE, FALSE);
!         $content = $node->content['webform']['#value'];
!     }
!     else
!     {
!         $teaser = ($settings['display'] == 'teaser') ? TRUE : FALSE;
!         $content = node_view($node, $teaser, TRUE, FALSE);
!     }
! 
!     // Create the block.
!     $block = array(
!         'subject' => $subject,
!         'content' => $content,
!     );
! 
!     return $block;
  }
  
  /**
   * Implementation of hook_block_configure().
   */
! function webform_block_configure($delta = '')
! {
!     // Load the block-specific configuration settings.
!     $webform_blocks = variable_get('webform_blocks', array());
!     $settings = isset($webform_blocks[$delta]) ? $webform_blocks[$delta] : array();
!     $settings += array(
!         'display' => 'form',
!     );
! 
!     $form = array();
!     $form['display'] = array(
!         '#type' => 'radios',
!         '#title' => t('Display mode'),
!         '#default_value' => $settings['display'],
!         '#options' => array(
!             'form' => t('Form only'),
!             'full' => t('Full node'),
!             'teaser' => t('Teaser'),
!         ),
!         '#description' => t('The display mode determines how much of the webform to show within the block.'),
!     );
!     return $form;
  }
  
  /**
   * Implementation of hook_block_save().
   */
! function webform_block_save($delta = '', $edit = array())
! {
!     // Load the previously defined block-specific configuration settings.
!     $settings = variable_get('webform_blocks', array());
!     // Build the settings array.
!     $new_settings[$delta] = array(
!         'display' => $edit['display'],
!     );
!     // We store settings for multiple blocks in just one variable
!     // so we merge the existing settings with the new ones before save.
!     variable_set('webform_blocks', array_merge($settings, $new_settings));
  }
  
  /**
***************
*** 1509,1727 ****
   *   building the form. Values need to be unfiltered to be editable by
   *   Form Builder.
   */
! function webform_client_form(&$form_state, $node, $submission, $is_draft = FALSE, $filter = TRUE) {
!   global $user;
  
!   module_load_include('inc', 'webform', 'includes/webform.components');
  
!   // Bind arguments to $form to make them available in theming and form_alter.
!   $form['#node'] = $node;
!   $form['#submission'] = $submission;
!   $form['#is_draft'] = $is_draft;
!   $form['#filter'] = $filter;
! 
!   // Add a theme function for this form.
!   $form['#theme'] = array('webform_form_' . $node->nid, 'webform_form');
! 
!   // Add a css class for all client forms.
!   $form['#attributes'] = array('class' => 'webform-client-form');
! 
!   // Set the encoding type (necessary for file uploads).
!   $form['#attributes']['enctype'] = 'multipart/form-data';
! 
!   // Set the form action to the node ID in case this is being displayed on the
!   // teaser, subsequent pages should be on the node page directly.
!   if (!isset($node->webform_block) && empty($submission)) {
!     $form['#action'] = url('node/' . $node->nid);
!   }
! 
!   $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit');
!   $form['#validate'] = array('webform_client_form_validate');
! 
!   if (is_array($node->webform['components']) && !empty($node->webform['components'])) {
!     // Prepare a new form array.
!     $form['submitted'] = array(
!       '#tree' => TRUE
!     );
!     $form['details'] = array(
!       '#tree' => TRUE,
!     );
! 
!     // Put the components into a tree structure.
!     if (!isset($form_state['storage']['component_tree'])) {
!       $form_state['webform']['component_tree'] = array();
!       $form_state['webform']['page_count'] = 1;
!       $form_state['webform']['page_num'] = 1;
!       _webform_components_tree_build($node->webform['components'], $form_state['webform']['component_tree'], 0, $form_state['webform']['page_count']);
!     }
!     else {
!       $form_state['webform']['component_tree'] = $form_state['storage']['component_tree'];
!       $form_state['webform']['page_count'] = $form_state['storage']['page_count'];
!       $form_state['webform']['page_num'] = $form_state['storage']['page_num'];
!     }
! 
!     // Shorten up our variable names.
!     $component_tree = $form_state['webform']['component_tree'];
!     $page_count = $form_state['webform']['page_count'];
!     $page_num = $form_state['webform']['page_num'];
! 
!     // Recursively add components to the form.
!     foreach ($component_tree['children'] as $cid => $component) {
!       $component_value = isset($form_state['values']['submitted'][$component['form_key']]) ? $form_state['values']['submitted'][$component['form_key']] : NULL;
!       if (_webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
!         _webform_client_form_add_component($node, $component, $component_value, $form['submitted'], $form, $form_state, $submission, 'form', $page_num, $filter);
!       }
!     }
! 
!     // These form details help managing data upon submission.
!     $form['details']['nid'] = array(
!       '#type' => 'value',
!       '#value' => $node->nid,
!     );
!     $form['details']['sid'] = array(
!       '#type' => 'hidden',
!       '#value' => isset($submission->sid) ? $submission->sid : '',
!     );
!     $form['details']['uid'] = array(
!       '#type' => 'value',
!       '#value' => isset($submission->uid) ? $submission->uid : $user->uid,
!     );
!     $form['details']['page_num'] = array(
!       '#type'  => 'hidden',
!       '#value' => $page_num,
!     );
!     $form['details']['page_count'] = array(
!       '#type'  => 'hidden',
!       '#value' => $page_count,
!     );
!     $form['details']['finished'] = array(
!       '#type' => 'hidden',
!       '#value' => isset($submission->is_draft) ? (!$submission->is_draft) : 0,
!     );
! 
!     // Add buttons for pages, drafts, and submissions.
!     $form['actions'] = array(
!       '#tree' => FALSE,
!       '#weight' => 1000,
!       '#prefix' => '<div id="edit-actions" class="form-actions form-wrapper">',
!       '#suffix' => '</div>',
!     );
! 
!     if ($page_count > 1) {
!       $next_page = t('Next Page >');
!       $prev_page = t('< Previous Page');
! 
!       // Add the submit button(s).
!      if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
!         $form['actions']['draft'] = array(
!           '#type' => 'submit',
!           '#value' => t('Save Draft'),
!           '#weight' => -2,
!           '#validate' => array(),
          );
!       }
!       if ($page_num > 1) {
!         $form['actions']['previous'] = array(
!           '#type' => 'submit',
!           '#value' => $prev_page,
!           '#weight' => 5,
!           '#validate' => array(),
          );
!       }
!       if ($page_num == $page_count) {
!         $form['actions']['submit'] = array(
!           '#type' => 'submit',
!           '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
!           '#weight' => 10,
          );
!       }
!       elseif ($page_num < $page_count) {
!         $form['actions']['next'] = array(
!           '#type' => 'submit',
!           '#value' => $next_page,
!           '#weight' => 10,
          );
!       }
!     }
!     else {
!       // Add the submit button.
!       $form['actions']['submit'] = array(
!         '#type' => 'submit',
!         '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
!         '#weight' => 10,
!       );
      }
-   }
  
!   return $form;
  }
  
  /**
   * Check if a component should be displayed on the current page.
   */
! function _webform_client_form_rule_check($node, $component, $page_num, $form_state = NULL, $submission = NULL) {
!   $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL;
!   $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL;
! 
!   // Check the rules for this entire page. Note individual page breaks are
!   // checked down below in the individual component rule checks.
!   $show_page = TRUE;
!   if ($component['page_num'] > 1 && $component['type'] != 'pagebreak') {
!     foreach ($node->webform['components'] as $cid => $page_component) {
!       if ($page_component['type'] == 'pagebreak' && $page_component['page_num'] == $page_num) {
!         $show_page = _webform_client_form_rule_check($node, $page_component, $page_num, $form_state, $submission);
!         break;
!       }
!     }
!   }
! 
!   // Check any parents' visibility rules.
!   $show_parent = $show_page;
!   if ($show_parent && $component['pid'] && isset($node->webform['components'][$component['pid']])) {
!     $parent_component = $node->webform['components'][$component['pid']];
!     $show_parent = _webform_client_form_rule_check($node, $parent_component, $page_num, $form_state, $submission);
!   }
! 
!   // Check the individual component rules.
!   $show_component = $show_parent;
!   if ($show_component && ($page_num == 0 || $component['page_num'] == $page_num) && $conditional_component && strlen(trim($conditional_values))) {
!     $input_values = array();
!     if (isset($form_state)) {
!       $parents = webform_component_parent_keys($node, $conditional_component);
!       $input_value = isset($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
!       foreach ($parents as $parent) {
!         if (isset($input_value[$parent])) {
!           $input_value = $input_value[$parent];
!         }
!         else {
!           $input_value = NULL;
!           break;
!         }
!       }
!       $input_values = is_array($input_value) ? $input_value : array($input_value);
!     }
!     elseif (isset($submission)) {
!       $input_values = $submission->data[$conditional_component['cid']]['value'];
!     }
! 
!     $test_values = array_map('trim', explode("\n", $conditional_values));
!     if (empty($input_values) && !empty($test_values)) {
!       $show_component = FALSE;
!     }
!     else {
!       foreach ($input_values as $input_value) {
!         if ($show_component = in_array($input_value, $test_values)) {
!           break;
!         }
!       }
      }
  
!     if ($component['extra']['conditional_operator'] == '!=') {
!       $show_component = !$show_component;
      }
-   }
  
!   return $show_component;
  }
  
  /**
--- 1680,1927 ----
   *   building the form. Values need to be unfiltered to be editable by
   *   Form Builder.
   */
! function webform_client_form(&$form_state, $node, $submission, $is_draft = FALSE, $filter = TRUE)
! {
!     global $user;
  
!     module_load_include('inc', 'webform', 'includes/webform.components');
  
!     // Bind arguments to $form to make them available in theming and form_alter.
!     $form['#node'] = $node;
!     $form['#submission'] = $submission;
!     $form['#is_draft'] = $is_draft;
!     $form['#filter'] = $filter;
! 
!     // Add a theme function for this form.
!     $form['#theme'] = array('webform_form_' . $node->nid, 'webform_form');
! 
!     // Add a css class for all client forms.
!     $form['#attributes'] = array('class' => 'webform-client-form');
! 
!     // Set the encoding type (necessary for file uploads).
!     $form['#attributes']['enctype'] = 'multipart/form-data';
! 
!     // Set the form action to the node ID in case this is being displayed on the
!     // teaser, subsequent pages should be on the node page directly.
!     if (!isset($node->webform_block) && empty($submission))
!     {
!         $form['#action'] = url('node/' . $node->nid);
!     }
! 
!     $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit');
!     $form['#validate'] = array('webform_client_form_validate');
! 
!     if (is_array($node->webform['components']) && !empty($node->webform['components']))
!     {
!         // Prepare a new form array.
!         $form['submitted'] = array(
!             '#tree' => TRUE
          );
!         $form['details'] = array(
!             '#tree' => TRUE,
          );
! 
!         // Put the components into a tree structure.
!         if (!isset($form_state['storage']['component_tree']))
!         {
!             $form_state['webform']['component_tree'] = array();
!             $form_state['webform']['page_count'] = 1;
!             $form_state['webform']['page_num'] = 1;
!             _webform_components_tree_build($node->webform['components'], $form_state['webform']['component_tree'], 0, $form_state['webform']['page_count']);
!         }
!         else
!         {
!             $form_state['webform']['component_tree'] = $form_state['storage']['component_tree'];
!             $form_state['webform']['page_count'] = $form_state['storage']['page_count'];
!             $form_state['webform']['page_num'] = $form_state['storage']['page_num'];
!         }
! 
!         // Shorten up our variable names.
!         $component_tree = $form_state['webform']['component_tree'];
!         $page_count = $form_state['webform']['page_count'];
!         $page_num = $form_state['webform']['page_num'];
! 
!         // Recursively add components to the form.
!         foreach ($component_tree['children'] as $cid => $component)
!         {
!             $component_value = isset($form_state['values']['submitted'][$component['form_key']]) ? $form_state['values']['submitted'][$component['form_key']] : NULL;
!             if (_webform_client_form_rule_check($node, $component, $page_num, $form_state))
!             {
!                 _webform_client_form_add_component($node, $component, $component_value, $form['submitted'], $form, $form_state, $submission, 'form', $page_num, $filter);
!             }
!         }
! 
!         // These form details help managing data upon submission.
!         $form['details']['nid'] = array(
!             '#type' => 'value',
!             '#value' => $node->nid,
          );
!         $form['details']['sid'] = array(
!             '#type' => 'hidden',
!             '#value' => isset($submission->sid) ? $submission->sid : '',
          );
!         $form['details']['uid'] = array(
!             '#type' => 'value',
!             '#value' => isset($submission->uid) ? $submission->uid : $user->uid,
!         );
!         $form['details']['page_num'] = array(
!             '#type' => 'hidden',
!             '#value' => $page_num,
!         );
!         $form['details']['page_count'] = array(
!             '#type' => 'hidden',
!             '#value' => $page_count,
!         );
!         $form['details']['finished'] = array(
!             '#type' => 'hidden',
!             '#value' => isset($submission->is_draft) ? (!$submission->is_draft) : 0,
!         );
! 
!         // Add buttons for pages, drafts, and submissions.
!         $form['actions'] = array(
!             '#tree' => FALSE,
!             '#weight' => 1000,
!             '#prefix' => '<div id="edit-actions" class="form-actions form-wrapper">',
!             '#suffix' => '</div>',
!         );
! 
!         if ($page_count > 1)
!         {
!             $next_page = t('Next Page >');
!             $prev_page = t('< Previous Page');
! 
!             // Add the submit button(s).
!             if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0)
!             {
!                 $form['actions']['draft'] = array(
!                     '#type' => 'submit',
!                     '#value' => t('Save Draft'),
!                     '#weight' => -2,
!                     '#validate' => array(),
!                 );
!             }
!             if ($page_num > 1)
!             {
!                 $form['actions']['previous'] = array(
!                     '#type' => 'submit',
!                     '#value' => $prev_page,
!                     '#weight' => 5,
!                     '#validate' => array(),
!                 );
!             }
!             if ($page_num == $page_count)
!             {
!                 $form['actions']['submit'] = array(
!                     '#type' => 'submit',
!                     '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
!                     '#weight' => 10,
!                 );
!             }
!             elseif ($page_num < $page_count)
!             {
!                 $form['actions']['next'] = array(
!                     '#type' => 'submit',
!                     '#value' => $next_page,
!                     '#weight' => 10,
!                 );
!             }
!         }
!         else
!         {
!             // Add the submit button.
!             $form['actions']['submit'] = array(
!                 '#type' => 'submit',
!                 '#value' => empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']),
!                 '#weight' => 10,
!             );
!         }
      }
  
!     return $form;
  }
  
  /**
   * Check if a component should be displayed on the current page.
   */
! function _webform_client_form_rule_check($node, $component, $page_num, $form_state = NULL, $submission = NULL)
! {
!     $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL;
!     $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL;
! 
!     // Check the rules for this entire page. Note individual page breaks are
!     // checked down below in the individual component rule checks.
!     $show_page = TRUE;
!     if ($component['page_num'] > 1 && $component['type'] != 'pagebreak')
!     {
!         foreach ($node->webform['components'] as $cid => $page_component)
!         {
!             if ($page_component['type'] == 'pagebreak' && $page_component['page_num'] == $page_num)
!             {
!                 $show_page = _webform_client_form_rule_check($node, $page_component, $page_num, $form_state, $submission);
!                 break;
!             }
!         }
      }
  
!     // Check any parents' visibility rules.
!     $show_parent = $show_page;
!     if ($show_parent && $component['pid'] && isset($node->webform['components'][$component['pid']]))
!     {
!         $parent_component = $node->webform['components'][$component['pid']];
!         $show_parent = _webform_client_form_rule_check($node, $parent_component, $page_num, $form_state, $submission);
!     }
! 
!     // Check the individual component rules.
!     $show_component = $show_parent;
!     if ($show_component && ($page_num == 0 || $component['page_num'] == $page_num) && $conditional_component && strlen(trim($conditional_values)))
!     {
!         $input_values = array();
!         if (isset($form_state))
!         {
!             $parents = webform_component_parent_keys($node, $conditional_component);
!             $input_value = isset($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
!             foreach ($parents as $parent)
!             {
!                 if (isset($input_value[$parent]))
!                 {
!                     $input_value = $input_value[$parent];
!                 }
!                 else
!                 {
!                     $input_value = NULL;
!                     break;
!                 }
!             }
!             $input_values = is_array($input_value) ? $input_value : array($input_value);
!         }
!         elseif (isset($submission))
!         {
!             $input_values = $submission->data[$conditional_component['cid']]['value'];
!         }
! 
!         $test_values = array_map('trim', explode("\n", $conditional_values));
!         if (empty($input_values) && !empty($test_values))
!         {
!             $show_component = FALSE;
!         }
!         else
!         {
!             foreach ($input_values as $input_value)
!             {
!                 if ($show_component = in_array($input_value, $test_values))
!                 {
!                     break;
!                 }
!             }
!         }
! 
!         if ($component['extra']['conditional_operator'] == '!=')
!         {
!             $show_component = !$show_component;
!         }
      }
  
!     return $show_component;
  }
  
  /**
***************
*** 1754,1840 ****
   * @see webform_client_form
   * @see webform_submission_render
   */
! function _webform_client_form_add_component($node, $component, $component_value, &$parent_fieldset, &$form, $form_state, $submission, $format = 'form', $page_num = 0, $filter = TRUE) {
!   $cid = $component['cid'];
  
!   // Load with submission information if necessary.
!   if ($format != 'form') {
!     // This component is display only.
!     $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value'];
!     if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format)) {
!       // The form_builder() function usually adds #parents and #id for us, but
!       // because these are not marked for #input, we need to add them manually.
!       if (!isset($display_element['#parents'])) {
!         $parents = isset($parent_fieldset['#parents']) ? $parent_fieldset['#parents'] : array('submitted');
!         $parents[] = $component['form_key'];
!         $display_element['#parents'] = $parents;
!       }
!       if (!isset($display_element['#id'])) {
!         $display_element['#id'] = form_clean_id('edit-' . implode('-', $display_element['#parents']));
!       }
!       $parent_fieldset[$component['form_key']] = $display_element;
!     }
!   }
!   elseif ($component['page_num'] == $page_num) {
!     // Add this user-defined field to the form (with all the values that are always available).
!     $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
!     if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) {
!       $parent_fieldset[$component['form_key']] = $element;
! 
!       // Override the value if one already exists in the form state.
!       if (isset($component_value)) {
!         $parent_fieldset[$component['form_key']]['#default_value'] = $component_value;
!         if (is_array($component_value)) {
!           foreach ($component_value as $key => $value) {
!             if (isset($parent_fieldset[$component['form_key']][$key])) {
!               $parent_fieldset[$component['form_key']][$key]['#default_value'] = $value;
!             }
!           }
!         }
!       }
!     }
!     else {
!       drupal_set_message(t('The webform component @type is not able to be displayed', array('@type' => $component['type'])));
!     }
!   }
! 
!   // Disable validation initially on all elements. We manually validate
!   // all webform elements in webform_client_form_validate().
!   if (isset($parent_fieldset[$component['form_key']])) {
!     $parent_fieldset[$component['form_key']]['#validated'] = TRUE;
!     $parent_fieldset[$component['form_key']]['#webform_validated'] = FALSE;
!   }
! 
!   if (isset($component['children']) && is_array($component['children'])) {
!     foreach ($component['children'] as $scid => $subcomponent) {
!       $subcomponent_value = isset($component_value[$subcomponent['form_key']]) ? $component_value[$subcomponent['form_key']] : NULL;
!       if (_webform_client_form_rule_check($node, $subcomponent, $page_num, $form_state, $submission)) {
!         _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $form_state, $submission, $format, $page_num, $filter);
!       }
!     }
!   }
! }
! 
! function webform_client_form_validate($form, &$form_state) {
!   $node = node_load($form_state['values']['details']['nid']);
!   $finished = $form_state['values']['details']['finished'];
! 
!   // Check that the user has not exceeded the submission limit.
!   // This usually will only apply to anonymous users when the page cache is
!   // enabled, because they may submit the form even if they do not have access.
!   if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled.
!     module_load_include('inc', 'webform', 'includes/webform.submissions');
  
!     if (!$finished && $limit_exceeded = _webform_submission_limit_check($node)) {
!       $error = theme('webform_view_messages', $node, 0, 1, 0, $limit_exceeded, array_keys(user_roles()));
!       form_set_error('', $error);
!       return;
!     }
!   }
! 
!   // Run all #element_validate and #required checks. These are skipped initially
!   // by setting #validated = TRUE on all components when they are added.
!   _webform_client_form_validate($form, $form_state);
  }
  
  /**
--- 1954,2059 ----
   * @see webform_client_form
   * @see webform_submission_render
   */
! function _webform_client_form_add_component($node, $component, $component_value, &$parent_fieldset, &$form, $form_state, $submission, $format = 'form', $page_num = 0, $filter = TRUE)
! {
!     $cid = $component['cid'];
! 
!     // Load with submission information if necessary.
!     if ($format != 'form')
!     {
!         // This component is display only.
!         $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value'];
!         if ($display_element = webform_component_invoke($component['type'], 'display', $component, $data, $format))
!         {
!             // The form_builder() function usually adds #parents and #id for us, but
!             // because these are not marked for #input, we need to add them manually.
!             if (!isset($display_element['#parents']))
!             {
!                 $parents = isset($parent_fieldset['#parents']) ? $parent_fieldset['#parents'] : array('submitted');
!                 $parents[] = $component['form_key'];
!                 $display_element['#parents'] = $parents;
!             }
!             if (!isset($display_element['#id']))
!             {
!                 $display_element['#id'] = form_clean_id('edit-' . implode('-', $display_element['#parents']));
!             }
!             $parent_fieldset[$component['form_key']] = $display_element;
!         }
!     }
!     elseif ($component['page_num'] == $page_num)
!     {
!         // Add this user-defined field to the form (with all the values that are always available).
!         $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL;
!         if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter))
!         {
!             $parent_fieldset[$component['form_key']] = $element;
! 
!             // Override the value if one already exists in the form state.
!             if (isset($component_value))
!             {
!                 $parent_fieldset[$component['form_key']]['#default_value'] = $component_value;
!                 if (is_array($component_value))
!                 {
!                     foreach ($component_value as $key => $value)
!                     {
!                         if (isset($parent_fieldset[$component['form_key']][$key]))
!                         {
!                             $parent_fieldset[$component['form_key']][$key]['#default_value'] = $value;
!                         }
!                     }
!                 }
!             }
!         }
!         else
!         {
!             drupal_set_message(t('The webform component @type is not able to be displayed', array('@type' => $component['type'])));
!         }
!     }
  
!     // Disable validation initially on all elements. We manually validate
!     // all webform elements in webform_client_form_validate().
!     if (isset($parent_fieldset[$component['form_key']]))
!     {
!         $parent_fieldset[$component['form_key']]['#validated'] = TRUE;
!         $parent_fieldset[$component['form_key']]['#webform_validated'] = FALSE;
!     }
! 
!     if (isset($component['children']) && is_array($component['children']))
!     {
!         foreach ($component['children'] as $scid => $subcomponent)
!         {
!             $subcomponent_value = isset($component_value[$subcomponent['form_key']]) ? $component_value[$subcomponent['form_key']] : NULL;
!             if (_webform_client_form_rule_check($node, $subcomponent, $page_num, $form_state, $submission))
!             {
!                 _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $form_state, $submission, $format, $page_num, $filter);
!             }
!         }
!     }
! }
! 
! function webform_client_form_validate($form, &$form_state)
! {
!     $node = node_load($form_state['values']['details']['nid']);
!     $finished = $form_state['values']['details']['finished'];
! 
!     // Check that the user has not exceeded the submission limit.
!     // This usually will only apply to anonymous users when the page cache is
!     // enabled, because they may submit the form even if they do not have access.
!     if ($node->webform['submit_limit'] != -1)
!     { // -1: Submissions are never throttled.
!         module_load_include('inc', 'webform', 'includes/webform.submissions');
! 
!         if (!$finished && $limit_exceeded = _webform_submission_limit_check($node))
!         {
!             $error = theme('webform_view_messages', $node, 0, 1, 0, $limit_exceeded, array_keys(user_roles()));
!             form_set_error('', $error);
!             return;
!         }
!     }
  
!     // Run all #element_validate and #required checks. These are skipped initially
!     // by setting #validated = TRUE on all components when they are added.
!     _webform_client_form_validate($form, $form_state);
  }
  
  /**
***************
*** 1843,2236 ****
   * This function imitates _form_validate in Drupal's form.inc, only it sets
   * a different property to ensure that validation has occurred.
   */
! function _webform_client_form_validate($elements, &$form_state, $first_run = TRUE) {
!   static $form;
!   if ($first_run) {
!     $form = $elements;
!   }
! 
!   // Recurse through all children.
!   foreach (element_children($elements) as $key) {
!     if (isset($elements[$key]) && $elements[$key]) {
!       _webform_client_form_validate($elements[$key], $form_state, FALSE);
!     }
!   }
!   // Validate the current input.
!   if (isset($elements['#webform_validated']) && $elements['#webform_validated'] == FALSE) {
!     if (isset($elements['#needs_validation'])) {
!       // Make sure a value is passed when the field is required.
!       // A simple call to empty() will not cut it here as some fields, like
!       // checkboxes, can return a valid value of '0'. Instead, check the
!       // length if it's a string, and the item count if it's an array.
!       if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) {
!         form_error($elements, t('!name field is required.', array('!name' => $elements['#title'])));
!       }
! 
!       // Verify that the value is not longer than #maxlength.
!       if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength']) {
!         form_error($elements, t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
!       }
! 
!       if (isset($elements['#options']) && isset($elements['#value'])) {
!         if ($elements['#type'] == 'select') {
!           $options = form_options_flatten($elements['#options']);
!         }
!         else {
!           $options = $elements['#options'];
!         }
!         if (is_array($elements['#value'])) {
!           $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
!           foreach ($value as $v) {
!             if (!isset($options[$v])) {
!               form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
!               watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
!             }
!           }
!         }
!         elseif ($elements['#value'] !== '' && !isset($options[$elements['#value']])) {
!           form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
!           watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
!         }
!       }
!     }
! 
!     // Call any element-specific validators. These must act on the element
!     // #value data.
!     if (isset($elements['#element_validate'])) {
!       foreach ($elements['#element_validate'] as $function) {
!         if (function_exists($function))  {
!           $function($elements, $form_state, $form);
          }
-       }
      }
!     $elements['#webform_validated'] = TRUE;
!   }
  }
  
  /**
   * Handle the processing of pages and conditional logic.
   */
! function webform_client_form_pages($form, &$form_state) {
!   $node = node_load($form_state['values']['details']['nid']);
  
!   // Move special settings to storage.
!   if (isset($form_state['webform']['component_tree'])) {
!     $form_state['storage']['component_tree'] = $form_state['webform']['component_tree'];
!     $form_state['storage']['page_count'] = $form_state['webform']['page_count'];
!     $form_state['storage']['page_num'] = $form_state['webform']['page_num'];
!   }
! 
!   // Check for a multi-page form that is not yet complete.
!   $submit_op = empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']);
!   $draft_op = t('Save Draft');
!   if (!in_array($form_state['values']['op'], array($submit_op, $draft_op))) {
!     // Checkboxes need post-processing to maintain their values.
!     _webform_client_form_submit_process($node, $form_state['values']['submitted'], array('select', 'grid'));
! 
!     // Store values from the current page in the form state storage.
!     if (is_array($form_state['values']['submitted'])) {
!       foreach ($form_state['values']['submitted'] as $key => $val) {
!         $form_state['storage']['submitted'][$key] = $val;
!       }
!     }
! 
!     // Update form state values with those from storage.
!     if (isset($form_state['storage']['submitted'])) {
!       foreach ($form_state['storage']['submitted'] as $key => $val) {
!         $form_state['values']['submitted'][$key] = $val;
!       }
!     }
! 
!     // Set the page number.
!     if (!isset($form_state['storage']['page_num'])) {
!       $form_state['storage']['page_num'] = 1;
!     }
!     if (end($form_state['clicked_button']['#parents']) == 'next') {
!       $direction = 1;
!     }
!     else {
!       $direction = 0;
!     }
! 
!     // If the next page has no components that need to be displayed, skip it.
!     if (isset($direction)) {
!       $components = $direction ? $node->webform['components'] : array_reverse($node->webform['components'], TRUE);
!       $last_component = end($node->webform['components']);
!       foreach ($components as $component) {
!         if ($component['type'] == 'pagebreak' && (
!             $direction == 1 && $component['page_num'] > $form_state['storage']['page_num'] ||
!             $direction == 0 && $component['page_num'] <= $form_state['storage']['page_num'])) {
!           $previous_pagebreak = $component;
!           continue;
!         }
!         if (isset($previous_pagebreak)) {
!           $page_num = $previous_pagebreak['page_num'] + $direction - 1;
!           // If we've found an component on this page, advance to that page.
!           if ($component['page_num'] == $page_num && _webform_client_form_rule_check($node, $component, $page_num, $form_state)) {
!             $form_state['storage']['page_num'] = $page_num;
!             break;
!           }
!           // If we've gotten to the end of the form without finding any more
!           // components, set the page number more than the max, ending the form.
!           elseif ($direction && $component['cid'] == $last_component['cid']) {
!             $form_state['storage']['page_num'] = $page_num + 1;
!           }
          }
-       }
-     }
  
!     // Rebuild the form and display the next page.
!     if ($form_state['storage']['page_num'] <= $form_state['storage']['page_count']) {
!       $form_state['rebuild'] = TRUE;
!       return;
!     }
!   }
  
!   if (isset($form_state['storage']['submitted'])) {
!     // Merge any stored submission data for multistep forms.
!     $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
!     unset($form_state['values']['submitted']);
  
!     foreach ($form_state['storage']['submitted'] as $key => $val) {
!       $form_state['values']['submitted'][$key] = $val;
!     }
!     foreach ($original_values as $key => $val) {
!       $form_state['values']['submitted'][$key] = $val;
      }
  
!     // Remove the variable so it doesn't show up in the additional processing.
!     unset($original_values);
!   }
  
!   // Remove the form state storage now that we're done with the pages.
!   unset($form_state['rebuild']);
!   unset($form_state['storage']);
  
!   // Perform post processing by components.
!   _webform_client_form_submit_process($node, $form_state['values']['submitted']);
  
!   // Flatten trees within the submission.
!   $form_state['values']['submitted_tree'] = $form_state['values']['submitted'];
!   $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']);
  
!   // Set a flag indicating processing should continue and be saved.
!   $form_state['webform_completed'] = TRUE;
  }
  
  /**
   * Submit handler for saving the form values and sending e-mails.
   */
! function webform_client_form_submit($form, &$form_state) {
!   module_load_include('inc', 'webform', 'includes/webform.submissions');
!   module_load_include('inc', 'webform', 'includes/webform.components');
!   global $user;
! 
!   if (empty($form_state['webform_completed'])) {
!     return;
!   }
! 
!   $node = $form['#node'];
! 
!   // Check if user is submitting as a draft.
!   $is_draft = $form_state['values']['op'] == t('Save Draft');
! 
!   // Create a submission object.
!   $submission = (object) array(
!     'nid' => $node->nid,
!     'uid' => $user->uid,
!     'submitted' => time(),
!     'remote_addr' => ip_address(),
!     'is_draft' => $is_draft,
!     'data' => webform_submission_data($node, $form_state['values']['submitted']),
!   );
! 
!   // Save the submission to the database.
!   if (empty($form_state['values']['details']['sid'])) {
!     // No sid was found thus insert it in the dataabase.
!     $form_state['values']['details']['sid'] = webform_submission_insert($node, $submission);
!     $form_state['values']['details']['is_new'] = TRUE;
! 
!     // Set a cookie including the server's submission time.
!     // The cookie expires in the length of the interval plus a day to compensate for different timezones.
!     if (variable_get('webform_use_cookies', 0)) {
!       $cookie_name = 'webform-' . $node->nid;
!       $time = time();
!       setcookie($cookie_name . '[' . $time . ']', $time, $time + $node->webform['submit_interval'] + 86400);
!     }
! 
!     // Save session information about this submission for anonymous users,
!     // allowing them to access or edit their submissions.
!     if (!$user->uid && user_access('access own webform submissions')) {
!       $_SESSION['webform_submission'][$form_state['values']['details']['sid']] = $node->nid;
!     }
!   }
!   else {
!     // Sid was found thus update the existing sid in the database.
!     $submission->sid = $form_state['values']['details']['sid'];
!     webform_submission_update($node, $submission);
!     $form_state['values']['details']['is_new'] = FALSE;
!   }
! 
!   $sid = $form_state['values']['details']['sid'];
! 
!   // Check if this form is sending an email.
!   if (!$is_draft && !$form_state['values']['details']['finished']) {
!     $submission = webform_get_submission($node->nid, $sid, TRUE);
! 
!     // Create a themed message for mailing.
!     foreach ($node->webform['emails'] as $eid => $email) {
!       // Pass through the theme layer if using the default template.
!       if ($email['template'] == 'default') {
!         $email['message'] = theme(array('webform_mail_' . $node->nid, 'webform_mail', 'webform_mail_message'), $node, $submission, $email);
!       }
!       else {
!         $email['message'] = $email['template'];
!       }
! 
!       // Replace tokens in the message.
!       $email['html'] = ($email['html'] && module_exists('mimemail'));
!       $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE, TRUE);
! 
!       // Build the e-mail headers.
!       $email['headers'] = theme(array('webform_mail_headers_' . $node->nid, 'webform_mail_headers'), $node, $submission, $email);
! 
!       // Assemble the FROM string.
!       if (isset($email['headers']['From'])) {
!         // If a header From is already set, don't override it.
!         $email['from'] = $email['headers']['From'];
!         unset($email['headers']['From']);
!       }
!       else {
!         $email['from'] = webform_format_email_address($email['from_address'], $email['from_name'], $node, $submission);
!       }
! 
!       // Update the subject if set in the themed headers.
!       if (isset($email['headers']['Subject'])) {
!         $email['headers']['subject'] = $email['headers']['Subject'];
!         unset($email['headers']['Subject']);
!       }
!       else {
!         $email['subject'] = webform_format_email_subject($email['subject'], $node, $submission);
!       }
! 
!       // Update the to e-mail if set in the themed headers.
!       if (isset($email['headers']['To'])) {
!         $email['email'] = $email['headers']['To'];
!         unset($email['headers']['To']);
!       }
! 
!       // Generate the list of addresses that this e-mail will be sent to.
!       $addresses = array_filter(explode(',', $email['email']));
!       $addresses_final = array();
!       foreach ($addresses as $address) {
!         $address = trim($address);
! 
!         // After filtering e-mail addresses with component values, a single value
!         // might contain multiple addresses (such as from checkboxes or selects).
!         $address = webform_format_email_address($address, NULL, $node, $submission, TRUE, FALSE, 'short');
! 
!         if (is_array($address)) {
!           foreach ($address as $new_address) {
!             $new_address = trim($new_address);
!             if (valid_email_address($new_address)) {
!               $addresses_final[] = $new_address;
!             }
!           }
!         }
!         elseif (valid_email_address($address)) {
!           $addresses_final[] = $address;
!         }
!       }
! 
!       // Mail the webform results.
!       foreach ($addresses_final as $address) {
!         // Verify that this submission is not attempting to send any spam hacks.
!         if (_webform_submission_spam_check($address, $email['subject'], $email['from'], $email['headers'])) {
!           watchdog('webform', 'Possible spam attempt from @remote_addr' . "<br />\n" . nl2br(htmlentities($email['message'])), array('@remote_add' => ip_address()));
!           drupal_set_message(t('Illegal information. Data not submitted.'), 'error');
!           return FALSE;
!         }
! 
!         $language = $user->uid ? user_preferred_language($user) : language_default();
!         $mail_params = array(
!           'message' => $email['message'],
!           'subject' => $email['subject'],
!           'headers' => $email['headers'],
!           'node' => $node,
!           'submission' => $submission,
!         );
  
!         if (module_exists('mimemail')) {
!           // Load attachments for the e-mail.
!           $attachments = array();
!           if ($email['attachments']) {
!             webform_component_include('file');
!             foreach ($node->webform['components'] as $component) {
!               if (webform_component_feature($component['type'], 'attachment') && !empty($submission->data[$component['cid']]['value'][0])) {
!                 $file = webform_get_file($submission->data[$component['cid']]['value'][0]);
!                 if ($file) {
!                   $file->list = 1; // Needed to include in attachments.
!                   $attachments[] = $file;
!                 }
!               }
!             }
!           }
  
!           // Send the e-mail via MIME mail.
!           mimemail($email['from'], $address, $email['subject'], $email['message'], !$email['html'], $email['headers'], $email['html'] ? NULL : $email['message'], $attachments, 'webform');
          }
!         else {
!           // Normal Drupal mailer.
!           drupal_mail('webform', 'submission', $address, $language, $mail_params, $email['from']);
          }
!       }
  
      }
-   }
  
!   // Strip out empty tags added by WYSIWYG editors if needed.
!   $confirmation = strlen(trim(strip_tags($node->webform['confirmation']))) ? $node->webform['confirmation'] : '';
!   $redirect_url = trim($node->webform['redirect_url']);
  
!   // Remove the domain name from the redirect.
!   $redirect_url = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $redirect_url);
  
!   // Check confirmation and redirect_url fields.
!   $message = NULL;
!   $redirect = NULL;
!   $external_url = FALSE;
!   if ($is_draft) {
!     $message = t('Draft saved');
!   }
!   elseif (!empty($form_state['values']['details']['finished'])) {
!     $message = t('Submission updated.');
!   }
!   elseif ($redirect_url == '<none>') {
      $redirect = NULL;
!   }
!   elseif ($redirect_url == '<confirmation>') {
!     $redirect = array('node/' . $node->nid . '/done', 'sid=' . $sid);
!   }
!   elseif (valid_url($redirect_url, TRUE)) {
!     $redirect = $redirect_url;
!     $external_url = TRUE;
!   }
!   elseif ($redirect_url && strpos($redirect_url, 'http') !== 0) {
!     $parts = parse_url($redirect_url);
!     $query = $parts['query'] ? ($parts['query'] . '&sid=' . $sid) : ('sid=' . $sid);
!     $redirect = array($parts['path'], $query, $parts['fragment']);
!   }
! 
!   // Show a message if manually set.
!   if (isset($message)) {
!     drupal_set_message($message);
!   }
!   // If redirecting and we have a confirmation message, show it as a message.
!   elseif (!$external_url && (!empty($redirect_url) && $redirect_url != '<confirmation>') && !empty($confirmation)) {
!     drupal_set_message(check_markup($confirmation, $node->webform['confirmation_format'], FALSE));
!   }
  
!   $form_state['redirect'] = $redirect;
  }
  
  /**
--- 2062,2528 ----
   * This function imitates _form_validate in Drupal's form.inc, only it sets
   * a different property to ensure that validation has occurred.
   */
! function _webform_client_form_validate($elements, &$form_state, $first_run = TRUE)
! {
!     static $form;
!     if ($first_run)
!     {
!         $form = $elements;
!     }
! 
!     // Recurse through all children.
!     foreach (element_children($elements) as $key)
!     {
!         if (isset($elements[$key]) && $elements[$key])
!         {
!             _webform_client_form_validate($elements[$key], $form_state, FALSE);
          }
      }
!     // Validate the current input.
!     if (isset($elements['#webform_validated']) && $elements['#webform_validated'] == FALSE)
!     {
!         if (isset($elements['#needs_validation']))
!         {
!             // Make sure a value is passed when the field is required.
!             // A simple call to empty() will not cut it here as some fields, like
!             // checkboxes, can return a valid value of '0'. Instead, check the
!             // length if it's a string, and the item count if it's an array.
!             if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0)))
!             {
!                 form_error($elements, t('!name field is required.', array('!name' => $elements['#title'])));
!             }
! 
!             // Verify that the value is not longer than #maxlength.
!             if (isset($elements['#maxlength']) && drupal_strlen($elements['#value']) > $elements['#maxlength'])
!             {
!                 form_error($elements, t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value']))));
!             }
! 
!             if (isset($elements['#options']) && isset($elements['#value']))
!             {
!                 if ($elements['#type'] == 'select')
!                 {
!                     $options = form_options_flatten($elements['#options']);
!                 }
!                 else
!                 {
!                     $options = $elements['#options'];
!                 }
!                 if (is_array($elements['#value']))
!                 {
!                     $value = $elements['#type'] == 'checkboxes' ? array_keys(array_filter($elements['#value'])) : $elements['#value'];
!                     foreach ($value as $v)
!                     {
!                         if (!isset($options[$v]))
!                         {
!                             form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
!                             watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
!                         }
!                     }
!                 }
!                 elseif ($elements['#value'] !== '' && !isset($options[$elements['#value']]))
!                 {
!                     form_error($elements, t('An illegal choice has been detected. Please contact the site administrator.'));
!                     watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
!                 }
!             }
!         }
! 
!         // Call any element-specific validators. These must act on the element
!         // #value data.
!         if (isset($elements['#element_validate']))
!         {
!             foreach ($elements['#element_validate'] as $function)
!             {
!                 if (function_exists($function))
!                 {
!                     $function($elements, $form_state, $form);
!                 }
!             }
!         }
!         $elements['#webform_validated'] = TRUE;
!     }
  }
  
  /**
   * Handle the processing of pages and conditional logic.
   */
! function webform_client_form_pages($form, &$form_state)
! {
!     $node = node_load($form_state['values']['details']['nid']);
! 
!     // Move special settings to storage.
!     if (isset($form_state['webform']['component_tree']))
!     {
!         $form_state['storage']['component_tree'] = $form_state['webform']['component_tree'];
!         $form_state['storage']['page_count'] = $form_state['webform']['page_count'];
!         $form_state['storage']['page_num'] = $form_state['webform']['page_num'];
!     }
! 
!     // Check for a multi-page form that is not yet complete.
!     $submit_op = empty($node->webform['submit_text']) ? t('Submit') : t($node->webform['submit_text']);
!     $draft_op = t('Save Draft');
!     if (!in_array($form_state['values']['op'], array($submit_op, $draft_op)))
!     {
!         // Checkboxes need post-processing to maintain their values.
!         _webform_client_form_submit_process($node, $form_state['values']['submitted'], array('select', 'grid'));
! 
!         // Store values from the current page in the form state storage.
!         if (is_array($form_state['values']['submitted']))
!         {
!             foreach ($form_state['values']['submitted'] as $key => $val)
!             {
!                 $form_state['storage']['submitted'][$key] = $val;
!             }
!         }
  
!         // Update form state values with those from storage.
!         if (isset($form_state['storage']['submitted']))
!         {
!             foreach ($form_state['storage']['submitted'] as $key => $val)
!             {
!                 $form_state['values']['submitted'][$key] = $val;
!             }
          }
  
!         // Set the page number.
!         if (!isset($form_state['storage']['page_num']))
!         {
!             $form_state['storage']['page_num'] = 1;
!         }
!         if (end($form_state['clicked_button']['#parents']) == 'next')
!         {
!             $direction = 1;
!         }
!         else
!         {
!             $direction = 0;
!         }
  
!         // If the next page has no components that need to be displayed, skip it.
!         if (isset($direction))
!         {
!             $components = $direction ? $node->webform['components'] : array_reverse($node->webform['components'], TRUE);
!             $last_component = end($node->webform['components']);
!             foreach ($components as $component)
!             {
!                 if ($component['type'] == 'pagebreak' && (
!                         $direction == 1 && $component['page_num'] > $form_state['storage']['page_num'] ||
!                         $direction == 0 && $component['page_num'] <= $form_state['storage']['page_num']))
!                 {
!                     $previous_pagebreak = $component;
!                     continue;
!                 }
!                 if (isset($previous_pagebreak))
!                 {
!                     $page_num = $previous_pagebreak['page_num'] + $direction - 1;
!                     // If we've found an component on this page, advance to that page.
!                     if ($component['page_num'] == $page_num && _webform_client_form_rule_check($node, $component, $page_num, $form_state))
!                     {
!                         $form_state['storage']['page_num'] = $page_num;
!                         break;
!                     }
!                     // If we've gotten to the end of the form without finding any more
!                     // components, set the page number more than the max, ending the form.
!                     elseif ($direction && $component['cid'] == $last_component['cid'])
!                     {
!                         $form_state['storage']['page_num'] = $page_num + 1;
!                     }
!                 }
!             }
!         }
  
!         // Rebuild the form and display the next page.
!         if ($form_state['storage']['page_num'] <= $form_state['storage']['page_count'])
!         {
!             $form_state['rebuild'] = TRUE;
!             return;
!         }
      }
  
!     if (isset($form_state['storage']['submitted']))
!     {
!         // Merge any stored submission data for multistep forms.
!         $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
!         unset($form_state['values']['submitted']);
! 
!         foreach ($form_state['storage']['submitted'] as $key => $val)
!         {
!             $form_state['values']['submitted'][$key] = $val;
!         }
!         foreach ($original_values as $key => $val)
!         {
!             $form_state['values']['submitted'][$key] = $val;
!         }
  
!         // Remove the variable so it doesn't show up in the additional processing.
!         unset($original_values);
!     }
! 
!     // Remove the form state storage now that we're done with the pages.
!     unset($form_state['rebuild']);
!     unset($form_state['storage']);
  
!     // Perform post processing by components.
!     _webform_client_form_submit_process($node, $form_state['values']['submitted']);
  
!     // Flatten trees within the submission.
!     $form_state['values']['submitted_tree'] = $form_state['values']['submitted'];
!     $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']);
  
!     // Set a flag indicating processing should continue and be saved.
!     $form_state['webform_completed'] = TRUE;
  }
  
  /**
   * Submit handler for saving the form values and sending e-mails.
   */
! function webform_client_form_submit($form, &$form_state)
! {
!     module_load_include('inc', 'webform', 'includes/webform.submissions');
!     module_load_include('inc', 'webform', 'includes/webform.components');
!     global $user;
  
!     if (empty($form_state['webform_completed']))
!     {
!         return;
!     }
! 
!     $node = $form['#node'];
  
!     // Check if user is submitting as a draft.
!     $is_draft = $form_state['values']['op'] == t('Save Draft');
! 
!     // Create a submission object.
!     $submission = (object) array(
!                 'nid' => $node->nid,
!                 'uid' => $user->uid,
!                 'submitted' => time(),
!                 'remote_addr' => ip_address(),
!                 'is_draft' => $is_draft,
!                 'data' => webform_submission_data($node, $form_state['values']['submitted']),
!     );
! 
!     // Save the submission to the database.
!     if (empty($form_state['values']['details']['sid']))
!     {
!         // No sid was found thus insert it in the dataabase.
!         $form_state['values']['details']['sid'] = webform_submission_insert($node, $submission);
!         $form_state['values']['details']['is_new'] = TRUE;
! 
!         // Set a cookie including the server's submission time.
!         // The cookie expires in the length of the interval plus a day to compensate for different timezones.
!         if (variable_get('webform_use_cookies', 0))
!         {
!             $cookie_name = 'webform-' . $node->nid;
!             $time = time();
!             setcookie($cookie_name . '[' . $time . ']', $time, $time + $node->webform['submit_interval'] + 86400);
          }
! 
!         // Save session information about this submission for anonymous users,
!         // allowing them to access or edit their submissions.
!         if (!$user->uid && user_access('access own webform submissions'))
!         {
!             $_SESSION['webform_submission'][$form_state['values']['details']['sid']] = $node->nid;
          }
!     }
!     else
!     {
!         // Sid was found thus update the existing sid in the database.
!         $submission->sid = $form_state['values']['details']['sid'];
!         webform_submission_update($node, $submission);
!         $form_state['values']['details']['is_new'] = FALSE;
!     }
! 
!     $sid = $form_state['values']['details']['sid'];
! 
!     // Check if this form is sending an email.
!     if (!$is_draft && !$form_state['values']['details']['finished'])
!     {
!         $submission = webform_get_submission($node->nid, $sid, TRUE);
! 
!         // Create a themed message for mailing.
!         foreach ($node->webform['emails'] as $eid => $email)
!         {
!             // Pass through the theme layer if using the default template.
!             if ($email['template'] == 'default')
!             {
!                 $email['message'] = theme(array('webform_mail_' . $node->nid, 'webform_mail', 'webform_mail_message'), $node, $submission, $email);
!             }
!             else
!             {
!                 $email['message'] = $email['template'];
!             }
  
+             // Replace tokens in the message.
+             $email['html'] = ($email['html'] && module_exists('mimemail'));
+             $email['message'] = _webform_filter_values($email['message'], $node, $submission, $email, FALSE, TRUE);
+ 
+             // Build the e-mail headers.
+             $email['headers'] = theme(array('webform_mail_headers_' . $node->nid, 'webform_mail_headers'), $node, $submission, $email);
+ 
+             // Assemble the FROM string.
+             if (isset($email['headers']['From']))
+             {
+                 // If a header From is already set, don't override it.
+                 $email['from'] = $email['headers']['From'];
+                 unset($email['headers']['From']);
+             }
+             else
+             {
+                 $email['from'] = webform_format_email_address($email['from_address'], $email['from_name'], $node, $submission);
+             }
+ 
+             // Update the subject if set in the themed headers.
+             if (isset($email['headers']['Subject']))
+             {
+                 $email['headers']['subject'] = $email['headers']['Subject'];
+                 unset($email['headers']['Subject']);
+             }
+             else
+             {
+                 $email['subject'] = webform_format_email_subject($email['subject'], $node, $submission);
+             }
+ 
+             // Update the to e-mail if set in the themed headers.
+             if (isset($email['headers']['To']))
+             {
+                 $email['email'] = $email['headers']['To'];
+                 unset($email['headers']['To']);
+             }
+ 
+             // Generate the list of addresses that this e-mail will be sent to.
+             $addresses = array_filter(explode(',', $email['email']));
+             $addresses_final = array();
+             foreach ($addresses as $address)
+             {
+                 $address = trim($address);
+ 
+                 // After filtering e-mail addresses with component values, a single value
+                 // might contain multiple addresses (such as from checkboxes or selects).
+                 $address = webform_format_email_address($address, NULL, $node, $submission, TRUE, FALSE, 'short');
+ 
+                 if (is_array($address))
+                 {
+                     foreach ($address as $new_address)
+                     {
+                         $new_address = trim($new_address);
+                         if (valid_email_address($new_address))
+                         {
+                             $addresses_final[] = $new_address;
+                         }
+                     }
+                 }
+                 elseif (valid_email_address($address))
+                 {
+                     $addresses_final[] = $address;
+                 }
+             }
+ 
+             // Mail the webform results.
+             foreach ($addresses_final as $address)
+             {
+                 // Verify that this submission is not attempting to send any spam hacks.
+                 if (_webform_submission_spam_check($address, $email['subject'], $email['from'], $email['headers']))
+                 {
+                     watchdog('webform', 'Possible spam attempt from @remote_addr' . "<br />\n" . nl2br(htmlentities($email['message'])), array('@remote_add' => ip_address()));
+                     drupal_set_message(t('Illegal information. Data not submitted.'), 'error');
+                     return FALSE;
+                 }
+ 
+                 $language = $user->uid ? user_preferred_language($user) : language_default();
+                 $mail_params = array(
+                     'message' => $email['message'],
+                     'subject' => $email['subject'],
+                     'headers' => $email['headers'],
+                     'node' => $node,
+                     'submission' => $submission,
+                 );
+ 
+                 if (module_exists('mimemail'))
+                 {
+                     // Load attachments for the e-mail.
+                     $attachments = array();
+                     if ($email['attachments'])
+                     {
+                         webform_component_include('file');
+                         foreach ($node->webform['components'] as $component)
+                         {
+                             if (webform_component_feature($component['type'], 'attachment') && !empty($submission->data[$component['cid']]['value'][0]))
+                             {
+                                 $file = webform_get_file($submission->data[$component['cid']]['value'][0]);
+                                 if ($file)
+                                 {
+                                     $file->list = 1; // Needed to include in attachments.
+                                     $attachments[] = $file;
+                                 }
+                             }
+                         }
+                     }
+ 
+                     // Send the e-mail via MIME mail.
+                     mimemail($email['from'], $address, $email['subject'], $email['message'], !$email['html'], $email['headers'], $email['html'] ? NULL : $email['message'], $attachments, 'webform');
+                 }
+                 else
+                 {
+                     // Normal Drupal mailer.
+                     drupal_mail('webform', 'submission', $address, $language, $mail_params, $email['from']);
+                 }
+             }
+         }
      }
  
!     // Strip out empty tags added by WYSIWYG editors if needed.
!     $confirmation = strlen(trim(strip_tags($node->webform['confirmation']))) ? $node->webform['confirmation'] : '';
!     $redirect_url = trim($node->webform['redirect_url']);
  
!     // Remove the domain name from the redirect.
!     $redirect_url = preg_replace('/^' . preg_quote($GLOBALS['base_url'], '/') . '\//', '', $redirect_url);
  
!     // Check confirmation and redirect_url fields.
!     $message = NULL;
      $redirect = NULL;
!     $external_url = FALSE;
!     if ($is_draft)
!     {
!         $message = t('Draft saved');
!     }
!     elseif (!empty($form_state['values']['details']['finished']))
!     {
!         $message = t('Submission updated.');
!     }
!     elseif ($redirect_url == '<none>')
!     {
!         $redirect = NULL;
!     }
!     elseif ($redirect_url == '<confirmation>')
!     {
!         $redirect = array('node/' . $node->nid . '/done', 'sid=' . $sid);
!     }
!     elseif (valid_url($redirect_url, TRUE))
!     {
!         $redirect = $redirect_url;
!         $external_url = TRUE;
!     }
!     elseif ($redirect_url && strpos($redirect_url, 'http') !== 0)
!     {
!         $parts = parse_url($redirect_url);
!         $query = $parts['query'] ? ($parts['query'] . '&sid=' . $sid) : ('sid=' . $sid);
!         $redirect = array($parts['path'], $query, $parts['fragment']);
!     }
! 
!     // Show a message if manually set.
!     if (isset($message))
!     {
!         drupal_set_message($message);
!     }
!     // If redirecting and we have a confirmation message, show it as a message.
!     elseif (!$external_url && (!empty($redirect_url) && $redirect_url != '<confirmation>') && !empty($confirmation))
!     {
!         drupal_set_message(check_markup($confirmation, $node->webform['confirmation_format'], FALSE));
!     }
  
!     $form_state['redirect'] = $redirect;
  }
  
  /**
***************
*** 2245,2338 ****
   * @param $parent
   *   Internal use. The current parent CID whose children are being processed.
   */
! function _webform_client_form_submit_process($node, &$form_values, $types = NULL, $parent = 0) {
!   if (is_array($form_values)) {
!     foreach ($form_values as $form_key => $value) {
!       $cid = webform_get_cid($node, $form_key, $parent);
!       if (is_array($value) && isset($node->webform['components'][$cid]['type']) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
!         _webform_client_form_submit_process($node, $form_values[$form_key], $types, $cid);
!       }
! 
!       if (isset($node->webform['components'][$cid])) {
!         // Call the component process submission function.
!         $component = $node->webform['components'][$cid];
!         if ((!isset($types) || in_array($component['type'], $types))) {
!           $new_value = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
!           if ($new_value !== NULL) {
!             $form_values[$component['form_key']] = $new_value;
!           }
          }
-       }
      }
-   }
  }
  
  /**
   * Flattens a submitted form back into a single array representation (rather than nested fields)
   */
! function _webform_client_form_submit_flatten($node, $fieldset, $parent = 0) {
!   $values = array();
! 
!   if (is_array($fieldset)) {
!     foreach ($fieldset as $form_key => $value) {
!       $cid = webform_get_cid($node, $form_key, $parent);
! 
!       if (is_array($value) && webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
!         $values += _webform_client_form_submit_flatten($node, $value, $cid);
!       }
!       else {
!         $values[$cid] = $value;
!       }
      }
-   }
  
!   return $values;
  }
  
  /**
   * Prints the confirmation message after a successful submission.
   */
! function _webform_confirmation($node) {
!   drupal_set_title(check_plain($node->title));
!   webform_set_breadcrumb($node);
!   if (empty($output)) {
!     $output = theme(array('webform_confirmation_' . $node->nid, 'webform_confirmation'), $node, $_GET['sid']);
!   }
!   return $output;
  }
  
  /**
   * Prepare for theming of the webform form.
   */
! function template_preprocess_webform_form(&$vars) {
!   drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform.css');
!   drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform.js');
! 
!   if (isset($vars['form']['details']['nid']['#value'])) {
!     $vars['nid'] = $vars['form']['details']['nid']['#value'];
!   }
!   elseif (isset($vars['form']['submission']['#value'])) {
!     $vars['nid'] = $vars['form']['submission']['#value']->nid;
!   }
  }
  
  /**
   * Prepare for theming of the webform submission confirmation.
   */
! function template_preprocess_webform_confirmation(&$vars) {
!   $confirmation = check_markup($vars['node']->webform['confirmation'], $vars['node']->webform['confirmation_format'], FALSE);
!   // Strip out empty tags added by WYSIWYG editors if needed.
!   $vars['confirmation_message'] = strlen(trim(strip_tags($confirmation))) ? $confirmation : '';
  }
  
  /**
   * Prepare to theme the contents of e-mails sent by webform.
   */
! function template_preprocess_webform_mail_message(&$vars) {
!   global $user;
  
!   $vars['user'] = $user;
!   $vars['ip_address'] = ip_address();
  }
  
  /**
--- 2537,2649 ----
   * @param $parent
   *   Internal use. The current parent CID whose children are being processed.
   */
! function _webform_client_form_submit_process($node, &$form_values, $types = NULL, $parent = 0)
! {
!     if (is_array($form_values))
!     {
!         foreach ($form_values as $form_key => $value)
!         {
!             $cid = webform_get_cid($node, $form_key, $parent);
!             if (is_array($value) && isset($node->webform['components'][$cid]['type']) && webform_component_feature($node->webform['components'][$cid]['type'], 'group'))
!             {
!                 _webform_client_form_submit_process($node, $form_values[$form_key], $types, $cid);
!             }
! 
!             if (isset($node->webform['components'][$cid]))
!             {
!                 // Call the component process submission function.
!                 $component = $node->webform['components'][$cid];
!                 if ((!isset($types) || in_array($component['type'], $types)))
!                 {
!                     $new_value = webform_component_invoke($component['type'], 'submit', $component, $form_values[$component['form_key']]);
!                     if ($new_value !== NULL)
!                     {
!                         $form_values[$component['form_key']] = $new_value;
!                     }
!                 }
!             }
          }
      }
  }
  
  /**
   * Flattens a submitted form back into a single array representation (rather than nested fields)
   */
! function _webform_client_form_submit_flatten($node, $fieldset, $parent = 0)
! {
!     $values = array();
! 
!     if (is_array($fieldset))
!     {
!         foreach ($fieldset as $form_key => $value)
!         {
!             $cid = webform_get_cid($node, $form_key, $parent);
! 
!             if (is_array($value) && webform_component_feature($node->webform['components'][$cid]['type'], 'group'))
!             {
!                 $values += _webform_client_form_submit_flatten($node, $value, $cid);
!             }
!             else
!             {
!                 $values[$cid] = $value;
!             }
!         }
      }
  
!     return $values;
  }
  
  /**
   * Prints the confirmation message after a successful submission.
   */
! function _webform_confirmation($node)
! {
!     drupal_set_title(check_plain($node->title));
!     webform_set_breadcrumb($node);
!     if (empty($output))
!     {
!         $output = theme(array('webform_confirmation_' . $node->nid, 'webform_confirmation'), $node, $_GET['sid']);
!     }
!     return $output;
  }
  
  /**
   * Prepare for theming of the webform form.
   */
! function template_preprocess_webform_form(&$vars)
! {
!     drupal_add_css(drupal_get_path('module', 'webform') . '/css/webform.css');
!     drupal_add_js(drupal_get_path('module', 'webform') . '/js/webform.js');
! 
!     if (isset($vars['form']['details']['nid']['#value']))
!     {
!         $vars['nid'] = $vars['form']['details']['nid']['#value'];
!     }
!     elseif (isset($vars['form']['submission']['#value']))
!     {
!         $vars['nid'] = $vars['form']['submission']['#value']->nid;
!     }
  }
  
  /**
   * Prepare for theming of the webform submission confirmation.
   */
! function template_preprocess_webform_confirmation(&$vars)
! {
!     $confirmation = check_markup($vars['node']->webform['confirmation'], $vars['node']->webform['confirmation_format'], FALSE);
!     // Strip out empty tags added by WYSIWYG editors if needed.
!     $vars['confirmation_message'] = strlen(trim(strip_tags($confirmation))) ? $confirmation : '';
  }
  
  /**
   * Prepare to theme the contents of e-mails sent by webform.
   */
! function template_preprocess_webform_mail_message(&$vars)
! {
!     global $user;
  
!     $vars['user'] = $user;
!     $vars['ip_address'] = ip_address();
  }
  
  /**
***************
*** 2341,2351 ****
   * Note: this entire function may be removed in Drupal 7, which supports
   * #title_display natively.
   */
! function webform_element_title_display($element) {
!   if (isset($element['#title_display']) && $element['#title_display'] == 'none') {
!     $element['#title'] = NULL;
!   }
!   return $element;
  }
  
  /**
--- 2652,2664 ----
   * Note: this entire function may be removed in Drupal 7, which supports
   * #title_display natively.
   */
! function webform_element_title_display($element)
! {
!     if (isset($element['#title_display']) && $element['#title_display'] == 'none')
!     {
!         $element['#title'] = NULL;
!     }
!     return $element;
  }
  
  /**
***************
*** 2354,2392 ****
   * Note: this entire function may be removed in Drupal 7, which supports
   * #theme_wrappers natively.
   */
! function webform_element_wrapper($content, $elements) {
!   if (isset($elements['#theme_wrappers'])) {
!     foreach ($elements['#theme_wrappers'] as $theme_wrapper) {
!       $content = theme($theme_wrapper, $elements, $content);
      }
!   }
!   return $content;
  }
  
  /**
   * Replacement for theme_form_element().
   */
! function theme_webform_element($element, $value) {
!   $wrapper_classes = array(
!    'form-item',
!   );
!   $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
!   $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';
! 
!   if (!empty($element['#title'])) {
!     $title = $element['#title'];
!     $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
!   }
! 
!   $output .= '<div id="' . $element['#id'] . '">' . $value . '</div>' . "\n";
! 
!   if (!empty($element['#description'])) {
!     $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
!   }
  
!   $output .= "</div>\n";
  
!   return $output;
  }
  
  /**
--- 2667,2711 ----
   * Note: this entire function may be removed in Drupal 7, which supports
   * #theme_wrappers natively.
   */
! function webform_element_wrapper($content, $elements)
! {
!     if (isset($elements['#theme_wrappers']))
!     {
!         foreach ($elements['#theme_wrappers'] as $theme_wrapper)
!         {
!             $content = theme($theme_wrapper, $elements, $content);
!         }
      }
!     return $content;
  }
  
  /**
   * Replacement for theme_form_element().
   */
! function theme_webform_element($element, $value)
! {
!     $wrapper_classes = array(
!         'form-item',
!     );
!     $output = '<div class="' . implode(' ', $wrapper_classes) . '" id="' . $element['#id'] . '-wrapper">' . "\n";
!     $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required.') . '">*</span>' : '';
! 
!     if (!empty($element['#title']))
!     {
!         $title = $element['#title'];
!         $output .= ' <label for="' . $element['#id'] . '">' . t('!title: !required', array('!title' => filter_xss_admin($title), '!required' => $required)) . "</label>\n";
!     }
! 
!     $output .= '<div id="' . $element['#id'] . '">' . $value . '</div>' . "\n";
  
!     if (!empty($element['#description']))
!     {
!         $output .= ' <div class="description">' . $element['#description'] . "</div>\n";
!     }
! 
!     $output .= "</div>\n";
  
!     return $output;
  }
  
  /**
***************
*** 2395,2471 ****
   * This is a temporary work-around until we can use theme_webform_element() for
   * all webform form elements in Drupal 7.
   */
! function theme_webform_element_wrapper($element, $content) {
!   // All elements using this for display only are given the "display" type.
!   if (isset($element['#format']) && $element['#format'] == 'html') {
!     $type = 'display';
!   }
!   else {
!     $type = (isset($element['#type']) && !in_array($element['#type'], array('markup', 'textfield'))) ? $element['#type'] : $element['#webform_component']['type'];
!   }
! 
!   $parents = str_replace('_', '-', implode('--', array_slice($element['#parents'], 1)));
! 
!   $output = '';
!   $output .= '<div class="webform-component webform-component-' . $type . '" id="webform-component-' . $parents . '">';
!   $output .= $content;
!   $output .= '</div>';
!   return $output;
  }
  
  /**
   * Output a form element in plain text format.
   */
! function theme_webform_element_text($element, $value) {
!   $output = '';
!   $is_group = webform_component_feature($element['#webform_component']['type'], 'group');
! 
!   // Output the element title.
!   if (isset($element['#title'])) {
!     if ($is_group) {
!       $output .= '--' . $element['#title'] . '--';
!     }
!     elseif (!in_array(substr($element['#title'], -1), array('?', ':', '!', '%', ';', '@'))) {
!       $output .= $element['#title'] . ':';
!     }
!     else {
!       $output .= $element['#title'];
!     }
!   }
! 
!   // Wrap long values at 65 characters, allowing for a few fieldset indents.
!   // It's common courtesy to wrap at 75 characters in e-mails.
!   if ($is_group && strlen($value) > 65) {
!     $value = wordwrap($value, 65, "\n");
!     $lines = explode("\n", $value);
!     foreach ($lines as $key => $line) {
!       $lines[$key] = '  ' . $line;
!     }
!     $value = implode("\n", $lines);
!   }
! 
!   // Add the value to the output.
!   if ($value) {
!     $output .= (strpos($value, "\n") === FALSE ? ' ' : "\n") . $value;
!   }
! 
!   // Indent fieldsets.
!   if ($is_group) {
!     $lines = explode("\n", $output);
!     foreach ($lines as $number => $line) {
!       if (strlen($line)) {
!         $lines[$number] = '  ' . $line;
!       }
!     }
!     $output = implode("\n", $lines);
!     $output .= "\n";
!   }
! 
!   if ($output) {
!     $output .= "\n";
!   }
  
!   return $output;
  }
  
  /**
--- 2714,2805 ----
   * This is a temporary work-around until we can use theme_webform_element() for
   * all webform form elements in Drupal 7.
   */
! function theme_webform_element_wrapper($element, $content)
! {
!     // All elements using this for display only are given the "display" type.
!     if (isset($element['#format']) && $element['#format'] == 'html')
!     {
!         $type = 'display';
!     }
!     else
!     {
!         $type = (isset($element['#type']) && !in_array($element['#type'], array('markup', 'textfield'))) ? $element['#type'] : $element['#webform_component']['type'];
!     }
! 
!     $parents = str_replace('_', '-', implode('--', array_slice($element['#parents'], 1)));
! 
!     $output = '';
!     $output .= '<div class="webform-component webform-component-' . $type . '" id="webform-component-' . $parents . '">';
!     $output .= $content;
!     $output .= '</div>';
!     return $output;
  }
  
  /**
   * Output a form element in plain text format.
   */
! function theme_webform_element_text($element, $value)
! {
!     $output = '';
!     $is_group = webform_component_feature($element['#webform_component']['type'], 'group');
! 
!     // Output the element title.
!     if (isset($element['#title']))
!     {
!         if ($is_group)
!         {
!             $output .= '--' . $element['#title'] . '--';
!         }
!         elseif (!in_array(substr($element['#title'], -1), array('?', ':', '!', '%', ';', '@')))
!         {
!             $output .= $element['#title'] . ':';
!         }
!         else
!         {
!             $output .= $element['#title'];
!         }
!     }
! 
!     // Wrap long values at 65 characters, allowing for a few fieldset indents.
!     // It's common courtesy to wrap at 75 characters in e-mails.
!     if ($is_group && strlen($value) > 65)
!     {
!         $value = wordwrap($value, 65, "\n");
!         $lines = explode("\n", $value);
!         foreach ($lines as $key => $line)
!         {
!             $lines[$key] = '  ' . $line;
!         }
!         $value = implode("\n", $lines);
!     }
! 
!     // Add the value to the output.
!     if ($value)
!     {
!         $output .= ( strpos($value, "\n") === FALSE ? ' ' : "\n") . $value;
!     }
! 
!     // Indent fieldsets.
!     if ($is_group)
!     {
!         $lines = explode("\n", $output);
!         foreach ($lines as $number => $line)
!         {
!             if (strlen($line))
!             {
!                 $lines[$number] = '  ' . $line;
!             }
!         }
!         $output = implode("\n", $lines);
!         $output .= "\n";
!     }
! 
!     if ($output)
!     {
!         $output .= "\n";
!     }
  
!     return $output;
  }
  
  /**
***************
*** 2485,2507 ****
   *   for "From", "To", or "Subject" are set, they will take precedence over
   *   the values set in the webform configuration.
   */
! function theme_webform_mail_headers($node, $submission, $email) {
!   $headers = array(
!     'X-Mailer' => 'Drupal Webform (PHP/' . phpversion() . ')',
!   );
!   return $headers;
  }
  
  /**
   * Check if current user has a draft of this webform, and return the sid.
   */
! function _webform_fetch_draft_sid($nid, $uid) {
!   $result = db_query("SELECT * FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1 ORDER BY submitted DESC", $nid, $uid);
!   $row = db_fetch_array($result);
!   if (isset($row['sid'])) {
!     return (int) $row['sid'];
!   }
!   return FALSE;
  }
  
  /**
--- 2819,2844 ----
   *   for "From", "To", or "Subject" are set, they will take precedence over
   *   the values set in the webform configuration.
   */
! function theme_webform_mail_headers($node, $submission, $email)
! {
!     $headers = array(
!         'X-Mailer' => 'Drupal Webform (PHP/' . phpversion() . ')',
!     );
!     return $headers;
  }
  
  /**
   * Check if current user has a draft of this webform, and return the sid.
   */
! function _webform_fetch_draft_sid($nid, $uid)
! {
!     $result = db_query("SELECT * FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1 ORDER BY submitted DESC", $nid, $uid);
!     $row = db_fetch_array($result);
!     if (isset($row['sid']))
!     {
!         return (int) $row['sid'];
!     }
!     return FALSE;
  }
  
  /**
***************
*** 2528,2685 ****
   *   preserved in the anonymous page cache and should only be used in
   *   non-cached situations, such as e-mails.
   */
! function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE, $allow_anonymous = FALSE) {
!   global $user;
!   static $replacements;
! 
!   // Don't do any filtering if the string is empty.
!   if (strlen(trim($string)) == 0) {
!     return $string;
!   }
! 
!   // Setup default token replacements.
!   if (!isset($replacements)) {
!     $replacements['unsafe'] = array();
!     $replacements['safe']['%site'] = variable_get('site_name', 'drupal');
!     $replacements['safe']['%date'] = format_date(time(), 'large');
!   }
! 
!   // Node replacements.
!   if (isset($node) && !array_key_exists('%title', $replacements)) {
!     $replacements['safe']['%title'] = $node->title;
!   }
! 
!   // Determine the display format.
!   $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
! 
!   // Submission replacements.
!   if (isset($submission) && !isset($replacements['email'][$format])) {
!     module_load_include('inc', 'webform', 'includes/webform.components.inc');
! 
!     // E-mails may be sent in two formats, keep tokens separate for each one.
!     $replacements['email'][$format] = array();
! 
!     foreach ($submission->data as $cid => $value) {
!       $component = $node->webform['components'][$cid];
! 
!       // Find by form key.
!       $parents = webform_component_parent_keys($node, $component);
!       $form_key = implode('][', $parents);
!       $display_element = webform_component_invoke($component['type'], 'display', $component, $value['value'], $format);
!       $replacements['email'][$format]['%email[' . $form_key . ']'] = drupal_render($display_element);
!       $replacements['email'][$format]['%value[' . $form_key . ']'] = isset($display_element['#children']) ? $display_element['#children'] : '';
!     }
! 
!     // Submission edit URL.
!     $replacements['unsafe']['%submission_url'] = url('node/' . $node->nid . '/submission/' . $submission->sid, array('absolute' => TRUE));
!   }
! 
!   // Token for the entire form tree for e-mails.
!   if (isset($submission) && isset($email) && !isset($replacements['email'][$format]['%email_values'])) {
!     $replacements['email'][$format]['%email_values'] = webform_submission_render($node, $submission, $email, $format);
!   }
! 
!   // Provide a list of candidates for token replacement.
!   $special_tokens = array(
!     'safe' => array(
!       '%get' => $_GET,
!       '%post' => $_POST,
!     ),
!     'unsafe' => array(
!       '%cookie' => $_COOKIE,
!       '%session' => isset($_SESSION) ? $_SESSION : array(),
!       '%request' => $_REQUEST,
!       '%server' => $_SERVER,
!       '%profile' => (array) $user,
!     ),
!   );
! 
!   // Replacements of global variable tokens.
!   if (!isset($replacements['specials_set'])) {
!     $replacements['specials_set'] = TRUE;
! 
!     // Load profile information if available.
!     if ($user->uid) {
!       $account = user_load($user->uid);
!       $special_tokens['unsafe']['%profile'] = (array) $account;
!     }
! 
!     // User replacements.
!     if (!array_key_exists('%username', $replacements['unsafe'])) {
!       $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : '';
!       $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : '';
!       $replacements['unsafe']['%ip_address'] = ip_address();
!     }
! 
!     // Populate the replacements array with special variables.
!     foreach ($special_tokens as $safe_state => $tokens) {
!       foreach ($tokens as $token => $variable) {
!         // Safety check in case $_POST or some other global has been removed
!         // by a naughty module, in which case $variable may be NULL.
!         if (!is_array($variable)) {
!           continue;
!         }
! 
!         foreach ($variable as $key => $value) {
!           // This special case for profile module dates.
!           if ($token == '%profile' && is_array($value) && isset($value['year'])) {
!             $replacement = format_date(strtotime($value['month'] . '/' . $value['day'] . '/' . $value['year']), 'custom', 'F j, Y', '0');
!           }
!           else {
!             $replacement = (!is_array($value) && !is_object($value)) ? $value : '';
!           }
!           $replacements[$safe_state][$token . '[' . $key . ']'] = $replacement;
!         }
!       }
!     }
!   }
! 
!   // Make a copy of the replacements so we don't affect the static version.
!   $safe_replacements = $replacements['safe'];
! 
!   // Restrict replacements for anonymous users. Not all tokens can be used
!   // because they may expose session or other private data to other users when
!   // anonymous page caching is enabled.
!   if ($user->uid || $allow_anonymous) {
!     $safe_replacements += $replacements['unsafe'];
!     if (isset($replacements['email'][$format])) {
!       $safe_replacements += $replacements['email'][$format];
!     }
!   }
!   else {
!     foreach ($replacements['unsafe'] as $key => $value) {
!       $safe_replacements[$key] = '';
!     }
!   }
! 
!   $find = array_keys($safe_replacements);
!   $replace = array_values($safe_replacements);
!   $string = str_replace($find, $replace, $string);
! 
!   // Clean up any unused tokens.
!   foreach ($special_tokens as $safe_state => $tokens) {
!     foreach (array_keys($tokens) as $token) {
!       $string = preg_replace('/\\' . $token . '\[\w+\]/', '', $string);
      }
-   }
  
!   return $strict ? filter_xss($string) : $string;
  }
  
  /**
   * Filters all special tokens provided by webform, and allows basic layout in descriptions.
   */
! function _webform_filter_descriptions($string, $node = NULL, $submission = NULL) {
!   return strlen($string) == 0 ? '' : check_markup(_webform_filter_values($string, $node, $submission, NULL, FALSE));
  }
  
  /**
   * Filter labels for display by running through XSS checks.
   */
! function _webform_filter_xss($string) {
!   static $allowed_tags;
!   $allowed_tags = isset($allowed_tags) ? $allowed_tags : webform_variable_get('webform_allowed_tags');
!   return filter_xss($string, $allowed_tags);
  }
  
  /**
--- 2865,3046 ----
   *   preserved in the anonymous page cache and should only be used in
   *   non-cached situations, such as e-mails.
   */
! function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE, $allow_anonymous = FALSE)
! {
!     global $user;
!     static $replacements;
! 
!     // Don't do any filtering if the string is empty.
!     if (strlen(trim($string)) == 0)
!     {
!         return $string;
!     }
! 
!     // Setup default token replacements.
!     if (!isset($replacements))
!     {
!         $replacements['unsafe'] = array();
!         $replacements['safe']['%site'] = variable_get('site_name', 'drupal');
!         $replacements['safe']['%date'] = format_date(time(), 'large');
!     }
! 
!     // Node replacements.
!     if (isset($node) && !array_key_exists('%title', $replacements))
!     {
!         $replacements['safe']['%title'] = $node->title;
!     }
! 
!     // Determine the display format.
!     $format = isset($email['html']) && $email['html'] ? 'html' : 'text';
! 
!     // Submission replacements.
!     if (isset($submission) && !isset($replacements['email'][$format]))
!     {
!         module_load_include('inc', 'webform', 'includes/webform.components.inc');
! 
!         // E-mails may be sent in two formats, keep tokens separate for each one.
!         $replacements['email'][$format] = array();
! 
!         foreach ($submission->data as $cid => $value)
!         {
!             $component = $node->webform['components'][$cid];
! 
!             // Find by form key.
!             $parents = webform_component_parent_keys($node, $component);
!             $form_key = implode('][', $parents);
!             $display_element = webform_component_invoke($component['type'], 'display', $component, $value['value'], $format);
!             $replacements['email'][$format]['%email[' . $form_key . ']'] = drupal_render($display_element);
!             $replacements['email'][$format]['%value[' . $form_key . ']'] = isset($display_element['#children']) ? $display_element['#children'] : '';
!         }
! 
!         // Submission edit URL.
!         $replacements['unsafe']['%submission_url'] = url('node/' . $node->nid . '/submission/' . $submission->sid, array('absolute' => TRUE));
      }
  
!     // Token for the entire form tree for e-mails.
!     if (isset($submission) && isset($email) && !isset($replacements['email'][$format]['%email_values']))
!     {
!         $replacements['email'][$format]['%email_values'] = webform_submission_render($node, $submission, $email, $format);
!     }
! 
!     // Provide a list of candidates for token replacement.
!     $special_tokens = array(
!         'safe' => array(
!             '%get' => $_GET,
!             '%post' => $_POST,
!         ),
!         'unsafe' => array(
!             '%cookie' => $_COOKIE,
!             '%session' => isset($_SESSION) ? $_SESSION : array(),
!             '%request' => $_REQUEST,
!             '%server' => $_SERVER,
!             '%profile' => (array) $user,
!         ),
!     );
! 
!     // Replacements of global variable tokens.
!     if (!isset($replacements['specials_set']))
!     {
!         $replacements['specials_set'] = TRUE;
! 
!         // Load profile information if available.
!         if ($user->uid)
!         {
!             $account = user_load($user->uid);
!             $special_tokens['unsafe']['%profile'] = (array) $account;
!         }
! 
!         // User replacements.
!         if (!array_key_exists('%username', $replacements['unsafe']))
!         {
!             $replacements['unsafe']['%username'] = isset($user->name) ? $user->name : '';
!             $replacements['unsafe']['%useremail'] = isset($user->mail) ? $user->mail : '';
!             $replacements['unsafe']['%ip_address'] = ip_address();
!         }
! 
!         // Populate the replacements array with special variables.
!         foreach ($special_tokens as $safe_state => $tokens)
!         {
!             foreach ($tokens as $token => $variable)
!             {
!                 // Safety check in case $_POST or some other global has been removed
!                 // by a naughty module, in which case $variable may be NULL.
!                 if (!is_array($variable))
!                 {
!                     continue;
!                 }
! 
!                 foreach ($variable as $key => $value)
!                 {
!                     // This special case for profile module dates.
!                     if ($token == '%profile' && is_array($value) && isset($value['year']))
!                     {
!                         $replacement = format_date(strtotime($value['month'] . '/' . $value['day'] . '/' . $value['year']), 'custom', 'F j, Y', '0');
!                     }
!                     else
!                     {
!                         $replacement = (!is_array($value) && !is_object($value)) ? $value : '';
!                     }
!                     $replacements[$safe_state][$token . '[' . $key . ']'] = $replacement;
!                 }
!             }
!         }
!     }
! 
!     // Make a copy of the replacements so we don't affect the static version.
!     $safe_replacements = $replacements['safe'];
! 
!     // Restrict replacements for anonymous users. Not all tokens can be used
!     // because they may expose session or other private data to other users when
!     // anonymous page caching is enabled.
!     if ($user->uid || $allow_anonymous)
!     {
!         $safe_replacements += $replacements['unsafe'];
!         if (isset($replacements['email'][$format]))
!         {
!             $safe_replacements += $replacements['email'][$format];
!         }
!     }
!     else
!     {
!         foreach ($replacements['unsafe'] as $key => $value)
!         {
!             $safe_replacements[$key] = '';
!         }
!     }
! 
!     $find = array_keys($safe_replacements);
!     $replace = array_values($safe_replacements);
!     $string = str_replace($find, $replace, $string);
! 
!     // Clean up any unused tokens.
!     foreach ($special_tokens as $safe_state => $tokens)
!     {
!         foreach (array_keys($tokens) as $token)
!         {
!             $string = preg_replace('/\\' . $token . '\[\w+\]/', '', $string);
!         }
!     }
! 
!     return $strict ? filter_xss($string) : $string;
  }
  
  /**
   * Filters all special tokens provided by webform, and allows basic layout in descriptions.
   */
! function _webform_filter_descriptions($string, $node = NULL, $submission = NULL)
! {
!     return strlen($string) == 0 ? '' : check_markup(_webform_filter_values($string, $node, $submission, NULL, FALSE));
  }
  
  /**
   * Filter labels for display by running through XSS checks.
   */
! function _webform_filter_xss($string)
! {
!     static $allowed_tags;
!     $allowed_tags = isset($allowed_tags) ? $allowed_tags : webform_variable_get('webform_allowed_tags');
!     return filter_xss($string, $allowed_tags);
  }
  
  /**
***************
*** 2692,2801 ****
   * @param $parent
   *   The cid of the parent component.
   */
! function webform_get_cid(&$node, $form_key, $pid) {
!   foreach ($node->webform['components'] as $cid => $component) {
!     if ($component['form_key'] == $form_key && $component['pid'] == $pid) {
!       return $cid;
      }
-   }
  }
  
  /**
   * Retreive a Drupal variable with the appropriate default value.
   */
! function webform_variable_get($variable) {
!   switch ($variable) {
!     case 'webform_allowed_tags':
!       $result = array('a', 'em', 'strong', 'code', 'img');
!       break;
!     case 'webform_default_from_name':
!       $result = variable_get('webform_default_from_name', variable_get('site_name', ''));
!       break;
!     case 'webform_default_from_address':
!       $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from')));
!       break;
!     case 'webform_default_subject':
!       $result = variable_get('webform_default_subject', t('Form submission from: %title'));
!       break;
!     case 'webform_node_types':
!       $result = variable_get('webform_node_types', array('webform'));
!       break;
!     case 'webform_node_types_redirect':
!       $result = variable_get('webform_node_types_redirect', array('webform'));
!       break;
!   }
!   return $result;
! }
! 
! function theme_webform_token_help($node = NULL) {
!   $basic_tokens = array(
!     '%username',
!     '%useremail',
!     '%ip_address',
!     '%site',
!     '%date',
!   );
! 
!   $special_tokens = array(
!     '%profile[' . t('key') . ']',
!     '%server[' . t('key') . ']',
!     '%session[' . t('key') . ']',
!     '%get[' . t('key') . ']',
!     '%post[' . t('key') . ']',
!     '%request[' . t('key') . ']',
!   );
! 
!   if (isset($node)) {
!     $submission_tokens = array(
!       t('@submission_url - The URL for viewing the completed submission.', array('@submission_url' => '%submission_url')),
!       t('@email_values - All included components in a hierarchical structure.', array('@email_values' => '%email_values')),
!       t('@email_key - A formatted value and field label. Elements may be accessed such as <em>%email[fieldset_a][key_b]</em>. Do not include quotes.', array('@email_key' => '%email[key]')),
!       t('@value_key - A value without additional formatting. Elements may be accessed such as <em>%value[fieldset_a][key_b]</em>. Do not include quotes.', array('@value_key' => '%value[key]')),
!     );
!   }
! 
!   $output = '';
!   $output .= '<p>' . t('You may use special tokens in this field that will be replaced with dynamic values.') . '</p>';
! 
!   if (!empty($submission_tokens)) {
!     $output .= theme('item_list', $submission_tokens, t('Component variables'));
!   }
! 
!   $output .= theme('item_list', $basic_tokens, t('Basic variables'));
!   $output .= theme('item_list', $special_tokens, t('Special variables'));
!   $output .= '<p>' . t('You can use %server[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.server">$_SERVER</a> variables, %session[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.session">$_SESSION</a> variables and %get[key] to create prefilled forms from the <a href="http://www.php.net/reserved.variables#reserved.variables.get">URL</a>. %cookie, %request and %post also work with their respective PHP variables. For example %server[HTTP_USER_AGENT], %session[id], or %get[q].') . '</p>';
!   if (module_exists('profile')) {
!     $output .= '<p>' . t('If you are using the Profile module, you can also access all profile data using the syntax %profile[form_name]. If you for example have a profile value named profile_city, add the variable %profile[profile_city].') . '</p>';
!   }
! 
!   $fieldset = array(
!     '#title' => t('Token values'),
!     '#type' => 'fieldset',
!     '#collapsible' => TRUE,
!     '#collapsed' => TRUE,
!     '#children' => '<div>' . $output . '</div>',
!   );
!   return theme('fieldset', $fieldset);
! }
! 
! function _webform_safe_name($name) {
!   $new = trim($name);
! 
!   // If transliteration is available, use it to convert names to ASCII.
!   if (function_exists('transliteration_get')) {
!     $new = transliteration_get($new, '');
!     $new = str_replace(array(' ', '-', '/'), array('_', '_', '_'), $new);
!   }
!   else {
!     $new = str_replace(
!       array(' ', '-', '/', '€', 'ƒ', 'Š', 'Ž', 'š', 'ž', 'Ÿ', '¢', '¥', 'µ', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'à', 'á', 'â', 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Œ',  'œ',  'Æ',  'Ð',  'Þ',  'ß',  'æ',  'ð',  'þ'),
!       array('_', '_', '_', 'E', 'f', 'S', 'Z', 's', 'z', 'Y', 'c', 'Y', 'u', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'),
!       $new);
!   }
! 
!   $new = drupal_strtolower($new);
!   $new = preg_replace('/[^a-z0-9_]/', '', $new);
!   return $new;
  }
  
  /**
--- 3053,3174 ----
   * @param $parent
   *   The cid of the parent component.
   */
! function webform_get_cid(&$node, $form_key, $pid)
! {
!     foreach ($node->webform['components'] as $cid => $component)
!     {
!         if ($component['form_key'] == $form_key && $component['pid'] == $pid)
!         {
!             return $cid;
!         }
      }
  }
  
  /**
   * Retreive a Drupal variable with the appropriate default value.
   */
! function webform_variable_get($variable)
! {
!     switch ($variable)
!     {
!         case 'webform_allowed_tags':
!             $result = array('a', 'em', 'strong', 'code', 'img');
!             break;
!         case 'webform_default_from_name':
!             $result = variable_get('webform_default_from_name', variable_get('site_name', ''));
!             break;
!         case 'webform_default_from_address':
!             $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from')));
!             break;
!         case 'webform_default_subject':
!             $result = variable_get('webform_default_subject', t('Form submission from: %title'));
!             break;
!         case 'webform_node_types':
!             $result = variable_get('webform_node_types', array('webform'));
!             break;
!         case 'webform_node_types_redirect':
!             $result = variable_get('webform_node_types_redirect', array('webform'));
!             break;
!     }
!     return $result;
! }
! 
! function theme_webform_token_help($node = NULL)
! {
!     $basic_tokens = array(
!         '%username',
!         '%useremail',
!         '%ip_address',
!         '%site',
!         '%date',
!     );
! 
!     $special_tokens = array(
!         '%profile[' . t('key') . ']',
!         '%server[' . t('key') . ']',
!         '%session[' . t('key') . ']',
!         '%get[' . t('key') . ']',
!         '%post[' . t('key') . ']',
!         '%request[' . t('key') . ']',
!     );
! 
!     if (isset($node))
!     {
!         $submission_tokens = array(
!             t('@submission_url - The URL for viewing the completed submission.', array('@submission_url' => '%submission_url')),
!             t('@email_values - All included components in a hierarchical structure.', array('@email_values' => '%email_values')),
!             t('@email_key - A formatted value and field label. Elements may be accessed such as <em>%email[fieldset_a][key_b]</em>. Do not include quotes.', array('@email_key' => '%email[key]')),
!             t('@value_key - A value without additional formatting. Elements may be accessed such as <em>%value[fieldset_a][key_b]</em>. Do not include quotes.', array('@value_key' => '%value[key]')),
!         );
!     }
! 
!     $output = '';
!     $output .= '<p>' . t('You may use special tokens in this field that will be replaced with dynamic values.') . '</p>';
! 
!     if (!empty($submission_tokens))
!     {
!         $output .= theme('item_list', $submission_tokens, t('Component variables'));
!     }
! 
!     $output .= theme('item_list', $basic_tokens, t('Basic variables'));
!     $output .= theme('item_list', $special_tokens, t('Special variables'));
!     $output .= '<p>' . t('You can use %server[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.server">$_SERVER</a> variables, %session[key] to add any of the special PHP <a href="http://www.php.net/reserved.variables#reserved.variables.session">$_SESSION</a> variables and %get[key] to create prefilled forms from the <a href="http://www.php.net/reserved.variables#reserved.variables.get">URL</a>. %cookie, %request and %post also work with their respective PHP variables. For example %server[HTTP_USER_AGENT], %session[id], or %get[q].') . '</p>';
!     if (module_exists('profile'))
!     {
!         $output .= '<p>' . t('If you are using the Profile module, you can also access all profile data using the syntax %profile[form_name]. If you for example have a profile value named profile_city, add the variable %profile[profile_city].') . '</p>';
!     }
! 
!     $fieldset = array(
!         '#title' => t('Token values'),
!         '#type' => 'fieldset',
!         '#collapsible' => TRUE,
!         '#collapsed' => TRUE,
!         '#children' => '<div>' . $output . '</div>',
!     );
!     return theme('fieldset', $fieldset);
! }
! 
! function _webform_safe_name($name)
! {
!     $new = trim($name);
! 
!     // If transliteration is available, use it to convert names to ASCII.
!     if (function_exists('transliteration_get'))
!     {
!         $new = transliteration_get($new, '');
!         $new = str_replace(array(' ', '-', '/'), array('_', '_', '_'), $new);
!     }
!     else
!     {
!         $new = str_replace(
!                         array(' ', '-', '/', '€', 'ƒ', 'Š', 'Ž', 'š', 'ž', 'Ÿ', '¢', '¥', 'µ', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'à', 'á', 'â', 'ã', 'ä', 'å', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Œ', 'œ', 'Æ', 'Ð', 'Þ', 'ß', 'æ', 'ð', 'þ'),
!                         array('_', '_', '_', 'E', 'f', 'S', 'Z', 's', 'z', 'Y', 'c', 'Y', 'u', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'),
!                         $new);
!     }
! 
!     $new = drupal_strtolower($new);
!     $new = preg_replace('/[^a-z0-9_]/', '', $new);
!     return $new;
  }
  
  /**
***************
*** 2819,3019 ****
   *   The e-mail format, defaults to the site-wide setting. May be either "short"
   *   or "long".
   */
! function webform_format_email_address($address, $name, $node = NULL, $submission = NULL, $encode = TRUE, $single = TRUE, $format = NULL) {
!   if (!isset($format)) {
!     $format = variable_get('webform_email_address_format', 'long');
!   }
! 
!   if ($name == 'default') {
!     $name = webform_variable_get('webform_default_from_name');
!   }
!   elseif (is_numeric($name) && isset($node->webform['components'][$name])) {
!     if (isset($submission->data[$name]['value'])) {
!       $name = $submission->data[$name]['value'];
!     }
!     else {
!       $name = t('Value of !component', array('!component' => $node->webform['components'][$name]['name']));
!     }
!   }
! 
!   if ($address == 'default') {
!     $address = webform_variable_get('webform_default_from_address');
!   }
!   elseif (is_numeric($address) && isset($node->webform['components'][$address])) {
!     if (isset($submission->data[$address]['value'])) {
!       $values = $submission->data[$address]['value'];;
!       $address = array();
!       foreach ($values as $value) {
!         $address = array_merge($address, explode(',', $value));
!       }
!     }
!     else {
!       $address = t('Value of "!component"', array('!component' => $node->webform['components'][$address]['name']));
!     }
!   }
! 
!   // Convert arrays into a single value for From values.
!   if ($single) {
!     $address = is_array($address) ? reset($address) : $address;
!     $name = is_array($name) ? reset($name) : $name;
!   }
! 
!   // Address may be an array if a component value was used on checkboxes.
!   if (is_array($address)) {
!     foreach ($address as $key => $individual_address) {
!       $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE, TRUE);
!     }
!   }
!   else {
!     $address = _webform_filter_values($address, $node, $submission, NULL, FALSE, TRUE);
!   }
! 
!   if ($format == 'long' && !empty($name)) {
!     $name = _webform_filter_values($name, $node, $submission, NULL, FALSE, TRUE);
!     if ($encode) {
!       $name = mime_header_encode($name);
!     }
!     return '"' . $name . '" <' . $address . '>';
!   }
!   else {
!     return $address;
!   }
  }
  
  /**
   * Given an email subject, format it with any needed replacements.
   */
! function webform_format_email_subject($subject, $node = NULL, $submission = NULL) {
!   if ($subject == 'default') {
!     $subject = webform_variable_get('webform_default_subject');
!   }
!   elseif (is_numeric($subject) && isset($node->webform['components'][$subject])) {
!     $component = $node->webform['components'][$subject];
!     if (isset($submission->data[$subject]['value'])) {
!       $display_function = '_webform_display_' . $component['type'];
!       $value = $submission->data[$subject]['value'];
! 
!       // Convert the value to a clean text representation if possible.
!       if (function_exists($display_function)) {
!         $display = $display_function($component, $value, 'text');
!         $display['#theme_wrappers'] = array();
!         $subject = str_replace("\n", ' ', drupal_render($display));
!       }
!       else {
!         $subject = $value;
!       }
!     }
!     else {
!       $subject = t('Value of "!component"', array('!component' => $component['name']));
!     }
!   }
! 
!   // Convert arrays to strings (may happen if checkboxes are used as the value).
!   if (is_array($subject)) {
!     $subject = reset($subject);
!   }
  
!   return _webform_filter_values($subject, $node, $submission, NULL, FALSE, TRUE);
  }
  
  /**
   * Convert an array of components into a tree
   */
! function _webform_components_tree_build($src, &$tree, $parent, &$page_count) {
!   foreach ($src as $cid => $component) {
!     if ($component['pid'] == $parent) {
!       _webform_components_tree_build($src, $component, $cid, $page_count);
!       if ($component['type'] == 'pagebreak') {
!         $page_count++;
!       }
!       $tree['children'][$cid] = $component;
!       $tree['children'][$cid]['page_num'] = $page_count;
      }
!   }
!   return $tree;
  }
  
  /**
   * Flatten a component tree into a flat list.
   */
! function _webform_components_tree_flatten($tree) {
!   $components = array();
!   foreach ($tree as $cid => $component) {
!     if (isset($component['children'])) {
!       unset($component['children']);
!       $components[$cid] = $component;
!       // array_merge() can't be used here because the keys are numeric.
!       $children = _webform_components_tree_flatten($tree[$cid]['children']);
!       foreach ($children as $ccid => $ccomponent) {
!         $components[$ccid] = $ccomponent;
!       }
!     }
!     else {
!       $components[$cid] = $component;
      }
!   }
!   return $components;
  }
  
  /**
   * Helper for the uasort in webform_tree_sort()
   */
! function _webform_components_sort($a, $b) {
!   if ($a['weight'] == $b['weight']) {
!     return strcasecmp($a['name'], $b['name']);
!   }
!   return ($a['weight'] < $b['weight']) ? -1 : 1;
  }
  
  /**
   * Sort each level of a component tree by weight and name
   */
! function _webform_components_tree_sort($tree) {
!   if (isset($tree['children']) && is_array($tree['children'])) {
!     $children = array();
!     uasort($tree['children'], '_webform_components_sort');
!     foreach ($tree['children'] as $cid => $component) {
!       $children[$cid] = _webform_components_tree_sort($component);
!     }
!     $tree['children'] = $children;
!   }
!   return $tree;
  }
  
  /**
   * Get a list of all available component definitions.
   */
! function webform_components($include_disabled = FALSE, $reset = FALSE) {
!   static $components, $disabled;
! 
!   if (!isset($components) || $reset) {
!     $components = array();
!     $disabled = array_flip(variable_get('webform_disabled_components', array()));
!     foreach (module_implements('webform_component_info') as $module) {
!       $module_components = module_invoke($module, 'webform_component_info');
!       foreach ($module_components as $type => $info) {
!         $module_components[$type]['module'] = $module;
!         $module_components[$type]['enabled'] = !array_key_exists($type, $disabled);
!       }
!       $components += $module_components;
!     }
!     drupal_alter('webform_component_info', $components);
!     ksort($components);
!   }
  
!   return $include_disabled ? $components : array_diff_key($components, $disabled);
  }
  
  /**
   * Build a list of components suitable for use as select list options.
   */
! function webform_component_options($include_disabled = FALSE) {
!   $component_info = webform_components($include_disabled);
!   $options = array();
!   foreach ($component_info as $type => $info) {
!     $options[$type] = $info['label'];
!   }
!   return $options;
  }
  
  /**
--- 3192,3439 ----
   *   The e-mail format, defaults to the site-wide setting. May be either "short"
   *   or "long".
   */
! function webform_format_email_address($address, $name, $node = NULL, $submission = NULL, $encode = TRUE, $single = TRUE, $format = NULL)
! {
!     if (!isset($format))
!     {
!         $format = variable_get('webform_email_address_format', 'long');
!     }
! 
!     if ($name == 'default')
!     {
!         $name = webform_variable_get('webform_default_from_name');
!     }
!     elseif (is_numeric($name) && isset($node->webform['components'][$name]))
!     {
!         if (isset($submission->data[$name]['value']))
!         {
!             $name = $submission->data[$name]['value'];
!         }
!         else
!         {
!             $name = t('Value of !component', array('!component' => $node->webform['components'][$name]['name']));
!         }
!     }
! 
!     if ($address == 'default')
!     {
!         $address = webform_variable_get('webform_default_from_address');
!     }
!     elseif (is_numeric($address) && isset($node->webform['components'][$address]))
!     {
!         if (isset($submission->data[$address]['value']))
!         {
!             $values = $submission->data[$address]['value'];
!             ;
!             $address = array();
!             foreach ($values as $value)
!             {
!                 $address = array_merge($address, explode(',', $value));
!             }
!         }
!         else
!         {
!             $address = t('Value of "!component"', array('!component' => $node->webform['components'][$address]['name']));
!         }
!     }
! 
!     // Convert arrays into a single value for From values.
!     if ($single)
!     {
!         $address = is_array($address) ? reset($address) : $address;
!         $name = is_array($name) ? reset($name) : $name;
!     }
! 
!     // Address may be an array if a component value was used on checkboxes.
!     if (is_array($address))
!     {
!         foreach ($address as $key => $individual_address)
!         {
!             $address[$key] = _webform_filter_values($individual_address, $node, $submission, NULL, FALSE, TRUE);
!         }
!     }
!     else
!     {
!         $address = _webform_filter_values($address, $node, $submission, NULL, FALSE, TRUE);
!     }
! 
!     if ($format == 'long' && !empty($name))
!     {
!         $name = _webform_filter_values($name, $node, $submission, NULL, FALSE, TRUE);
!         if ($encode)
!         {
!             $name = mime_header_encode($name);
!         }
!         return '"' . $name . '" <' . $address . '>';
!     }
!     else
!     {
!         return $address;
!     }
  }
  
  /**
   * Given an email subject, format it with any needed replacements.
   */
! function webform_format_email_subject($subject, $node = NULL, $submission = NULL)
! {
!     if ($subject == 'default')
!     {
!         $subject = webform_variable_get('webform_default_subject');
!     }
!     elseif (is_numeric($subject) && isset($node->webform['components'][$subject]))
!     {
!         $component = $node->webform['components'][$subject];
!         if (isset($submission->data[$subject]['value']))
!         {
!             $display_function = '_webform_display_' . $component['type'];
!             $value = $submission->data[$subject]['value'];
! 
!             // Convert the value to a clean text representation if possible.
!             if (function_exists($display_function))
!             {
!                 $display = $display_function($component, $value, 'text');
!                 $display['#theme_wrappers'] = array();
!                 $subject = str_replace("\n", ' ', drupal_render($display));
!             }
!             else
!             {
!                 $subject = $value;
!             }
!         }
!         else
!         {
!             $subject = t('Value of "!component"', array('!component' => $component['name']));
!         }
!     }
! 
!     // Convert arrays to strings (may happen if checkboxes are used as the value).
!     if (is_array($subject))
!     {
!         $subject = reset($subject);
!     }
  
!     return _webform_filter_values($subject, $node, $submission, NULL, FALSE, TRUE);
  }
  
  /**
   * Convert an array of components into a tree
   */
! function _webform_components_tree_build($src, &$tree, $parent, &$page_count)
! {
!     foreach ($src as $cid => $component)
!     {
!         if ($component['pid'] == $parent)
!         {
!             _webform_components_tree_build($src, $component, $cid, $page_count);
!             if ($component['type'] == 'pagebreak')
!             {
!                 $page_count++;
!             }
!             $tree['children'][$cid] = $component;
!             $tree['children'][$cid]['page_num'] = $page_count;
!         }
      }
!     return $tree;
  }
  
  /**
   * Flatten a component tree into a flat list.
   */
! function _webform_components_tree_flatten($tree)
! {
!     $components = array();
!     foreach ($tree as $cid => $component)
!     {
!         if (isset($component['children']))
!         {
!             unset($component['children']);
!             $components[$cid] = $component;
!             // array_merge() can't be used here because the keys are numeric.
!             $children = _webform_components_tree_flatten($tree[$cid]['children']);
!             foreach ($children as $ccid => $ccomponent)
!             {
!                 $components[$ccid] = $ccomponent;
!             }
!         }
!         else
!         {
!             $components[$cid] = $component;
!         }
      }
!     return $components;
  }
  
  /**
   * Helper for the uasort in webform_tree_sort()
   */
! function _webform_components_sort($a, $b)
! {
!     if ($a['weight'] == $b['weight'])
!     {
!         return strcasecmp($a['name'], $b['name']);
!     }
!     return ($a['weight'] < $b['weight']) ? -1 : 1;
  }
  
  /**
   * Sort each level of a component tree by weight and name
   */
! function _webform_components_tree_sort($tree)
! {
!     if (isset($tree['children']) && is_array($tree['children']))
!     {
!         $children = array();
!         uasort($tree['children'], '_webform_components_sort');
!         foreach ($tree['children'] as $cid => $component)
!         {
!             $children[$cid] = _webform_components_tree_sort($component);
!         }
!         $tree['children'] = $children;
!     }
!     return $tree;
  }
  
  /**
   * Get a list of all available component definitions.
   */
! function webform_components($include_disabled = FALSE, $reset = FALSE)
! {
!     static $components, $disabled;
! 
!     if (!isset($components) || $reset)
!     {
!         $components = array();
!         $disabled = array_flip(variable_get('webform_disabled_components', array()));
!         foreach (module_implements('webform_component_info') as $module)
!         {
!             $module_components = module_invoke($module, 'webform_component_info');
!             foreach ($module_components as $type => $info)
!             {
!                 $module_components[$type]['module'] = $module;
!                 $module_components[$type]['enabled'] = !array_key_exists($type, $disabled);
!             }
!             $components += $module_components;
!         }
!         drupal_alter('webform_component_info', $components);
!         ksort($components);
!     }
  
!     return $include_disabled ? $components : array_diff_key($components, $disabled);
  }
  
  /**
   * Build a list of components suitable for use as select list options.
   */
! function webform_component_options($include_disabled = FALSE)
! {
!     $component_info = webform_components($include_disabled);
!     $options = array();
!     foreach ($component_info as $type => $info)
!     {
!         $options[$type] = $info['label'];
!     }
!     return $options;
  }
  
  /**
***************
*** 3022,3042 ****
   * @param $component_type
   *   The string machine name of a component.
   */
! function webform_component_include($component_type) {
!   static $included = array();
! 
!   // No need to load components that have already been added once.
!   if (!isset($included[$component_type])) {
!     $components = webform_components(TRUE);
!     $included[$component_type] = TRUE;
! 
!     if (($info = $components[$component_type]) && isset($info['file'])) {
!       $pathinfo = pathinfo($info['file']);
!       $basename = basename($pathinfo['basename'], '.' . $pathinfo['extension']);
!       $path = (!empty($pathinfo['dirname']) ? $pathinfo['dirname'] . '/' : '') . $basename;
!       module_load_include($pathinfo['extension'], $info['module'], $path);
      }
-   }
  }
  
  /**
--- 3442,3465 ----
   * @param $component_type
   *   The string machine name of a component.
   */
! function webform_component_include($component_type)
! {
!     static $included = array();
! 
!     // No need to load components that have already been added once.
!     if (!isset($included[$component_type]))
!     {
!         $components = webform_components(TRUE);
!         $included[$component_type] = TRUE;
! 
!         if (($info = $components[$component_type]) && isset($info['file']))
!         {
!             $pathinfo = pathinfo($info['file']);
!             $basename = basename($pathinfo['basename'], '.' . $pathinfo['extension']);
!             $path = (!empty($pathinfo['dirname']) ? $pathinfo['dirname'] . '/' : '') . $basename;
!             module_load_include($pathinfo['extension'], $info['module'], $path);
!         }
      }
  }
  
  /**
***************
*** 3049,3222 ****
   * @param ...
   *   Any additional parameters required by the $callback.
   */
! function webform_component_invoke($type, $callback) {
!   $args = func_get_args();
!   $type = array_shift($args);
!   $callback = array_shift($args);
!   $function = '_webform_' . $callback . '_' . $type;
!   webform_component_include($type);
!   if (function_exists($function)) {
!     return call_user_func_array($function, $args);
!   }
  }
  
  /**
   * Disable the Drupal page cache.
   */
! function webform_disable_page_cache() {
!   // PressFlow and Drupal 7 method.
!   if (function_exists('drupal_page_is_cacheable')) {
!     drupal_page_is_cacheable(FALSE);
!   }
!   // Drupal 6 hack to disable page cache.
!   else {
!     $GLOBALS['conf']['cache'] = FALSE;
!   }
  }
  
  /**
   * Set the necessary breadcrumb for the page we are on.
   */
! function webform_set_breadcrumb($node, $submission = NULL) {
!   $breadcrumb = drupal_get_breadcrumb();
! 
!   if (isset($node)) {
!     $webform_breadcrumb = array();
!     $webform_breadcrumb[] = array_shift($breadcrumb);
!     $webform_breadcrumb[] = l($node->title, 'node/' . $node->nid);
!     if (isset($submission)) {
!       $last_link = array_shift($breadcrumb);
!       $webform_breadcrumb[] = l(t('Submissions'), 'node/' . $node->nid . '/submissions');
!       if (isset($last_link)) {
!         $webform_breadcrumb[] = $last_link;
!       }
      }
-     $breadcrumb = $webform_breadcrumb;
-   }
  
!   drupal_set_breadcrumb($breadcrumb);
  }
  
  /**
   * Wrapper function for tt() if i18nstrings enabled.
   */
! function webform_tt($name, $string, $langcode = NULL, $update = FALSE) {
!   if (function_exists('tt')) {
!     return tt($name, $string, $langcode, $update);
!   }
!   else {
!     return $string;
!   }
  }
  
  /**
   * Implementation of hook_views_api().
   */
! function webform_views_api() {
!   return array(
!     'api' => 2.0,
!     'path' => drupal_get_path('module', 'webform') .'/views',
!   );
  }
  
  /**
   * Implementation of hook_content_extra_fields().
   */
! function webform_content_extra_fields($type_name) {
!   $extra = array();
!   if (in_array($type_name, webform_variable_get('webform_node_types'))) {
!     $extra['webform'] = array(
!       'label' => t('Webform'),
!       'description' => t('Webform client form.'),
!       'weight' => 10,
!     );
!   }
!   return $extra;
  }
  
  /**
   * Implements hook_mollom_form_list().
   */
! function webform_mollom_form_list() {
!   $forms = array();
!   $webform_types = webform_variable_get('webform_node_types');
!   if (empty($webform_types)) {
      return $forms;
-   }
- 
-   $placeholders = db_placeholders($webform_types, 'varchar');
-   $result = db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n WHERE n.type IN ($placeholders)", 'n', 'nid', $webform_types), $webform_types);
- 
-   while ($node = db_fetch_object($result)) {
-     $form_id = 'webform_client_form_' . $node->nid;
-     $forms[$form_id] = array(
-       'title' => t('@name form', array('@name' => $node->title)),
-       'entity' => 'webform',
-       'delete form' => 'webform_submission_delete_form',
-     );
-   }
-   return $forms;
  }
  
  /**
   * Implements hook_mollom_form_info().
   */
! function webform_mollom_form_info($form_id) {
!   module_load_include('inc', 'webform', 'includes/webform.components');
  
!   $nid = drupal_substr($form_id, 20);
!   $node = node_load($nid);
!   $form_info = array(
!     'title' => t('@name form', array('@name' => $node->title)),
!     'mode' => MOLLOM_MODE_ANALYSIS,
!     'bypass access' => array('edit all webform submissions', 'edit any webform content'),
!     'entity' => 'webform',
!     'elements' => array(),
!     'mapping' => array(
!       'post_id' => 'details][sid',
!       'author_id' => 'details][uid',
!     ),
!   );
!   // Add components as elements.
!   // These components can be enabled for textual analysis (when not using a
!   // CAPTCHA-only protection) in Mollom's form configuration.
!   foreach ($node->webform['components'] as $cid => $component) {
!     if (webform_component_feature($component['type'], 'spam_analysis')) {
!       $parents = implode('][', webform_component_parent_keys($node, $component));
!       $form_info['elements']['submitted][' . $parents] = check_plain(t($component['name']));
!     }
!   }
!   // Assign field mappings based on webform configuration.
!   // Since multiple emails can be configured, we iterate over all and take
!   // over the assigned component for the field mapping in any email, unless
!   // we already assigned one. We are not interested in administratively
!   // configured static strings, only user-submitted values.
!   foreach ($node->webform['emails'] as $email) {
!     // Subject (post_title).
!     if (!isset($form_info['mapping']['post_title'])) {
!       $cid = $email['subject'];
!       if (is_numeric($cid)) {
!         $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
!         $form_info['mapping']['post_title'] = 'submitted][' . $parents;
!       }
!     }
!     // From name (author_name).
!     if (!isset($form_info['mapping']['author_name'])) {
!       $cid = $email['from_name'];
!       if (is_numeric($cid)) {
!         $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
!         $form_info['mapping']['author_name'] = 'submitted][' . $parents;
!       }
!     }
!     // From address (author_mail).
!     if (!isset($form_info['mapping']['author_mail'])) {
!       $cid = $email['from_address'];
!       if (is_numeric($cid)) {
!         $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
!         $form_info['mapping']['author_mail'] = 'submitted][' . $parents;
!       }
      }
-   }
  
!   return $form_info;
  }
--- 3472,3673 ----
   * @param ...
   *   Any additional parameters required by the $callback.
   */
! function webform_component_invoke($type, $callback)
! {
!     $args = func_get_args();
!     $type = array_shift($args);
!     $callback = array_shift($args);
!     $function = '_webform_' . $callback . '_' . $type;
!     webform_component_include($type);
!     if (function_exists($function))
!     {
!         return call_user_func_array($function, $args);
!     }
  }
  
  /**
   * Disable the Drupal page cache.
   */
! function webform_disable_page_cache()
! {
!     // PressFlow and Drupal 7 method.
!     if (function_exists('drupal_page_is_cacheable'))
!     {
!         drupal_page_is_cacheable(FALSE);
!     }
!     // Drupal 6 hack to disable page cache.
!     else
!     {
!         $GLOBALS['conf']['cache'] = FALSE;
!     }
  }
  
  /**
   * Set the necessary breadcrumb for the page we are on.
   */
! function webform_set_breadcrumb($node, $submission = NULL)
! {
!     $breadcrumb = drupal_get_breadcrumb();
! 
!     if (isset($node))
!     {
!         $webform_breadcrumb = array();
!         $webform_breadcrumb[] = array_shift($breadcrumb);
!         $webform_breadcrumb[] = l($node->title, 'node/' . $node->nid);
!         if (isset($submission))
!         {
!             $last_link = array_shift($breadcrumb);
!             $webform_breadcrumb[] = l(t('Submissions'), 'node/' . $node->nid . '/submissions');
!             if (isset($last_link))
!             {
!                 $webform_breadcrumb[] = $last_link;
!             }
!         }
!         $breadcrumb = $webform_breadcrumb;
      }
  
!     drupal_set_breadcrumb($breadcrumb);
  }
  
  /**
   * Wrapper function for tt() if i18nstrings enabled.
   */
! function webform_tt($name, $string, $langcode = NULL, $update = FALSE)
! {
!     if (function_exists('tt'))
!     {
!         return tt($name, $string, $langcode, $update);
!     }
!     else
!     {
!         return $string;
!     }
  }
  
  /**
   * Implementation of hook_views_api().
   */
! function webform_views_api()
! {
!     return array(
!         'api' => 2.0,
!         'path' => drupal_get_path('module', 'webform') . '/views',
!     );
  }
  
  /**
   * Implementation of hook_content_extra_fields().
   */
! function webform_content_extra_fields($type_name)
! {
!     $extra = array();
!     if (in_array($type_name, webform_variable_get('webform_node_types')))
!     {
!         $extra['webform'] = array(
!             'label' => t('Webform'),
!             'description' => t('Webform client form.'),
!             'weight' => 10,
!         );
!     }
!     return $extra;
  }
  
  /**
   * Implements hook_mollom_form_list().
   */
! function webform_mollom_form_list()
! {
!     $forms = array();
!     $webform_types = webform_variable_get('webform_node_types');
!     if (empty($webform_types))
!     {
!         return $forms;
!     }
! 
!     $placeholders = db_placeholders($webform_types, 'varchar');
!     $result = db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n WHERE n.type IN ($placeholders)", 'n', 'nid', $webform_types), $webform_types);
! 
!     while ($node = db_fetch_object($result))
!     {
!         $form_id = 'webform_client_form_' . $node->nid;
!         $forms[$form_id] = array(
!             'title' => t('@name form', array('@name' => $node->title)),
!             'entity' => 'webform',
!             'delete form' => 'webform_submission_delete_form',
!         );
!     }
      return $forms;
  }
  
  /**
   * Implements hook_mollom_form_info().
   */
! function webform_mollom_form_info($form_id)
! {
!     module_load_include('inc', 'webform', 'includes/webform.components');
  
!     $nid = drupal_substr($form_id, 20);
!     $node = node_load($nid);
!     $form_info = array(
!         'title' => t('@name form', array('@name' => $node->title)),
!         'mode' => MOLLOM_MODE_ANALYSIS,
!         'bypass access' => array('edit all webform submissions', 'edit any webform content'),
!         'entity' => 'webform',
!         'elements' => array(),
!         'mapping' => array(
!             'post_id' => 'details][sid',
!             'author_id' => 'details][uid',
!         ),
!     );
!     // Add components as elements.
!     // These components can be enabled for textual analysis (when not using a
!     // CAPTCHA-only protection) in Mollom's form configuration.
!     foreach ($node->webform['components'] as $cid => $component)
!     {
!         if (webform_component_feature($component['type'], 'spam_analysis'))
!         {
!             $parents = implode('][', webform_component_parent_keys($node, $component));
!             $form_info['elements']['submitted][' . $parents] = check_plain(t($component['name']));
!         }
!     }
!     // Assign field mappings based on webform configuration.
!     // Since multiple emails can be configured, we iterate over all and take
!     // over the assigned component for the field mapping in any email, unless
!     // we already assigned one. We are not interested in administratively
!     // configured static strings, only user-submitted values.
!     foreach ($node->webform['emails'] as $email)
!     {
!         // Subject (post_title).
!         if (!isset($form_info['mapping']['post_title']))
!         {
!             $cid = $email['subject'];
!             if (is_numeric($cid))
!             {
!                 $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
!                 $form_info['mapping']['post_title'] = 'submitted][' . $parents;
!             }
!         }
!         // From name (author_name).
!         if (!isset($form_info['mapping']['author_name']))
!         {
!             $cid = $email['from_name'];
!             if (is_numeric($cid))
!             {
!                 $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
!                 $form_info['mapping']['author_name'] = 'submitted][' . $parents;
!             }
!         }
!         // From address (author_mail).
!         if (!isset($form_info['mapping']['author_mail']))
!         {
!             $cid = $email['from_address'];
!             if (is_numeric($cid))
!             {
!                 $parents = implode('][', webform_component_parent_keys($node, $node->webform['components'][$cid]));
!                 $form_info['mapping']['author_mail'] = 'submitted][' . $parents;
!             }
!         }
      }
  
!     return $form_info;
  }
