public PropertyValueApplier Apply(OrderedDictionary propertyValues)
 {
     return Apply(
         propertyValues.Cast<DictionaryEntry>()
             .ToDictionary(
                 x => (string)x.Key,
                 x => x.Value
             )
     );
 }
        private static string htmlToPlainText( string html )
        {
            const string emptyString = "";
            const string singleSpace = " ";

            // Maintains insert-order. Sadly, is not generic.
            var regexToReplacements = new OrderedDictionary
                {
                    { "(\r\n|\r|\n)+", singleSpace },
                    { "\t", emptyString },
                    { @"/\*.*\*/", emptyString },
                    { @"<!--.*-->", emptyString },
                    { @"\s+", singleSpace },
                    { @"<\s*head([^>])*>.*(<\s*(/)\s*head\s*>)", emptyString },
                    { @"<\s*script([^>])*>.*(<\s*(/)\s*script\s*>)", emptyString },
                    { @"<\s*style([^>])*>.*(<\s*(/)\s*style\s*>)", emptyString },
                    { @"<\s*td([^>])*>", "\t" },
                    { @"<\s*br\s*/?>", Environment.NewLine },
                    { @"<\s*li\s*>", Environment.NewLine },
                    { @"<\s*div([^>])*>", Environment.NewLine + Environment.NewLine },
                    { @"<\s*tr([^>])*>", Environment.NewLine + Environment.NewLine },
                    { @"<\s*p([^>])*>", Environment.NewLine + Environment.NewLine },
                    { RegularExpressions.HtmlTag, emptyString },
                    { @"<![^>]*>", emptyString },
                    { @"&bull;", " * " },
                    { @"&lsaquo;", "<" },
                    { @"&rsaquo;", ">" },
                    { @"&trade;", "(tm)" },
                    { @"&frasl;", "/" },
                    { @"&lt;", "<" },
                    { @"&gt;", ">" },
                    { @"&copy;", "(c)" },
                    { @"&reg;", "(r)" },
                    { @"&(.{2,6});", emptyString },
                    { Environment.NewLine + @"\s+" + Environment.NewLine, Environment.NewLine + Environment.NewLine },
                    { @"\t\s+\t", "\t\t" },
                    { @"\t\s+" + Environment.NewLine, "\t" + Environment.NewLine },
                    { Environment.NewLine + @"\s+\t", Environment.NewLine + "\t" },
                    { Environment.NewLine + @"\t+" + Environment.NewLine, Environment.NewLine + Environment.NewLine },
                    { Environment.NewLine + @"\t+", Environment.NewLine + "\t" }
                };

            return
                regexToReplacements.Cast<DictionaryEntry>()
                    .Aggregate(
                        html,
                        ( current, regexToReplacement ) => Regex.Replace( current, (string)regexToReplacement.Key, (string)regexToReplacement.Value, RegexOptions.IgnoreCase ) )
                    .Trim();
        }
        private OrderedDictionary CreatePayeTable(string YearSelected, out decimal personalAllowance, out string taxCodeLetter)
        {
            OrderedDictionary payetable = new OrderedDictionary();
            personalAllowance = 0m;
            taxCodeLetter = "";
            decimal numericAmount = 0m;

            List<string> TaxCodes = ReadXML("//YearEnd[@Year='"+YearSelected+"']/PAYE/BandCodes//@letter");
            List<string> TaxCodesEndLetters = ReadXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/BandEndLetters/Letter");
            List<string> TaxCodesStartLetters = ReadXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/BandStartLetters/Letter");

            //If no Tax code was enter, use default and call method again.
            if (TaxCode.Text == "") {
                TaxCode.Text = ReadXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/Default").First();
                payetable = CreatePayeTable(YearSelected, out personalAllowance, out taxCodeLetter);
            }
            else if (TaxCodes.Contains(TaxCode.Text)) //Checks for single rate NT, BR, D0 etc.
            {
                payetable = ReadRatesXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/BandCodes/code[@letter='" + TaxCode.Text + "']");
            }
            //The case for the L (and related) codes is complex. We need to take into consideration that the personal Allowance
            //reduces by 2 after the salary goes past £100,000 (the adjusted level).
            //To do this, the rate will be increased by 1.5 post £100,000 until the personal allowance reaches 0.
            //This threshold will be 100,000 + 2 * personal allowance.
            else if (TaxCodesEndLetters.Contains(TaxCode.Text.Substring(TaxCode.Text.Length - 1, 1)))
            {
                if (decimal.TryParse(TaxCode.Text.Substring(0, TaxCode.Text.Length - 1), out numericAmount))
                {
                    personalAllowance = numericAmount * 10 + 9;
                    OrderedDictionary payetableRate = new OrderedDictionary();
                    payetableRate = ReadRatesXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/Bands", personalAllowance);
                    decimal Adjusted = 0;
                    if (ReadXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/Adjusted").Count == 1)
                    {
                        Adjusted = Convert.ToDecimal(ReadXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/Adjusted").First());
                    }

                    if (Adjusted != 0)
                    {
                        decimal AdjustedUpper = Adjusted + 2 * (personalAllowance-9);
                        int i = 0;
                        decimal Bound = 0;
                        decimal Rate = 0;
                        decimal RateAdjusted = 0;
                        int NumberOfEntries = payetableRate.Count;
                        payetable.Add(Bound, Rate);

                        //3 stages here
                        //i. Add thresholds below the Adjusted level
                        //ii. Add thresholds between Adjusted level and Adjusted level + 2 * personal allowance
                        //iii. Add thresholds post Adjusted level + 2 * personal allowance

                        //Stage i
                        while (true) //infinite loop but breaking when either of two conditions are met
                        {
                            Bound = Convert.ToDecimal(payetableRate.Cast<DictionaryEntry>().ElementAt(i).Key);
                            Rate = Convert.ToDecimal(payetableRate[i]);
                            if (Bound < Adjusted)
                            { payetable.Add(Bound, Rate); }
                            else //break when Adjusted level is reached
                            {
                                RateAdjusted = Convert.ToDecimal(payetableRate[i - 1]) * 1.5m;
                                payetable.Add(Adjusted, RateAdjusted);
                                break;
                            }
                            if (i < NumberOfEntries - 1)
                            { i++; }
                            else  //break also when end of table is reached
                            {
                                RateAdjusted = Convert.ToDecimal(payetableRate[i]) * 1.5m;
                                payetable.Add(Adjusted, RateAdjusted);
                                break;
                            }
                        }

                        //Stage ii
                        decimal BoundAdjusted = 0;
                        while (AdjustedUpper > Bound - personalAllowance)
                        {
                            Bound = Convert.ToDecimal(payetableRate.Cast<DictionaryEntry>().ElementAt(i).Key);
                            if (Bound < Adjusted)
                            {
                                Rate = Convert.ToDecimal(payetableRate[i]);
                                payetable.Add(AdjustedUpper, Rate);
                                break;
                            }
                            if (Bound - personalAllowance < AdjustedUpper)
                            {
                                BoundAdjusted = AdjustedUpper / 3 + 2 * (Bound - personalAllowance) / 3;
                                RateAdjusted = Convert.ToDecimal(payetableRate[i]) * 1.5m;
                                payetable.Add(BoundAdjusted, RateAdjusted);
                            }
                            else
                            {
                                Rate = Convert.ToDecimal(payetableRate[i - 1]);
                                payetable.Add(AdjustedUpper, Rate);
                                break;
                            }
                            if (i < NumberOfEntries - 1)
                            { i++; }
                            else
                            {
                                Rate = Convert.ToDecimal(payetableRate[i]);
                                payetable.Add(AdjustedUpper, Rate);
                                break;
                            }
                        }

                        //Stage iii
                        while (true)//infinite loop but breaking when either of two conditions are met
                        {
                            Bound = Convert.ToDecimal(payetableRate.Cast<DictionaryEntry>().ElementAt(i).Key);
                            Rate = Convert.ToDecimal(payetableRate[i]);
                            if (Bound - personalAllowance > AdjustedUpper)
                            {
                                payetable.Add(Bound - personalAllowance, Rate);
                            }
                            else { break; } //breaks if this threshold is last in table
                            if (i < NumberOfEntries - 1)
                            { i++; } //breaks when end of table is reached
                            else { break; }
                        }
                    }
                    else //Case when there is no adjusted level
                    {
                        int i = 0;
                        decimal Bound = 0;
                        decimal Rate = 0;
                        int NumberOfEntries = payetableRate.Count;
                        while (i < NumberOfEntries)
                        {
                            payetable.Add(Bound, Rate);
                            Bound = Convert.ToDecimal(payetableRate.Cast<DictionaryEntry>().ElementAt(i).Key);
                            Rate = Convert.ToDecimal(payetableRate[i]);
                            i++;
                        }
                    }
                    taxCodeLetter = TaxCode.Text.Substring(TaxCode.Text.Length - 1, 1);
                }
            }
            else if (TaxCodesStartLetters.Contains(TaxCode.Text.Substring(0, 1))) //Case for K codes
            {
                if (decimal.TryParse(TaxCode.Text.Substring(1, TaxCode.Text.Length - 1), out numericAmount)){
                    personalAllowance = -numericAmount * 10;
                    payetable = ReadRatesXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/Bands", personalAllowance);

                    taxCodeLetter = TaxCode.Text.Substring(0, 1);
                }
            }
            else  //Case for invalid tax code enter. Then use default and call method again
            {
                TaxCode.Text = ReadXML("//YearEnd[@Year='" + YearSelected + "']/PAYE/Default").First();
                payetable = CreatePayeTable(YearSelected, out personalAllowance, out taxCodeLetter);
            }

            return payetable;
        }
		/// <summary>
		/// Generates HTML for the publishing mode
		/// </summary>
		/// <param name="content">Smart content object</param>
		/// <param name="publishingContext">Publishing context</param>
		/// <returns>Html</returns>
		public override string GeneratePublishHtml(ISmartContent content, IPublishingContext publishingContext)
		{
			// Get non-default options
			EffectiveOption[] options = DC.Crayon.Wlw.Framework.
				Options.GetEffectiveOptions(content.Properties, Options)
					.Where(p => p.Location != OptionLocation.Default)
					.ToArray();

			// Html Element
			HtmlElement preElement = new HtmlElement() { Tag = "pre" };
			OrderedDictionary classAttrParts = new OrderedDictionary(StringComparer.Ordinal);
			OrderedDictionary styleAttr = new OrderedDictionary(StringComparer.Ordinal);

			// Decode
			classAttrParts["decode"] = "true";

			// Setting Groups
			ApplyContentSettings(false, preElement, classAttrParts, styleAttr, options);
			ApplyMetricsSettings(false, preElement, classAttrParts, styleAttr, options);
			ApplyToolbarSettings(true, preElement, classAttrParts, styleAttr, options);
			ApplyLinesSettings(true, preElement, classAttrParts, styleAttr, options);
			ApplyCodeSettings(true, preElement, classAttrParts, styleAttr, options);

			// Set Class
			if (classAttrParts.Count > 0)
			{
				preElement.Attributes["class"] = string.Join(" ",
					classAttrParts.Cast<DictionaryEntry>()
					.Select(kv => string.Format(CultureInfo.InvariantCulture, "{0}:{1}", kv.Key, HtmlElement.EscapeAttributeValue(kv.Value.ToString())))
					.ToArray());
			}

			// Return
			return preElement.ToString();
		}