/*
* Copyright � 2006, Ashley van Gerven - ashley._v_g_@gmail.com (NB: remove '_' chars)
* All rights reserved.
*
* Use of this script, with or without modification, is permitted
* provided that the above copyright notice and disclaimer below is not removed.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#define CLR_v1 // << N.B. Comment this line if compiling with .NET 2.0 or later
using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Text.RegularExpressions;
using System.Drawing;
#if !CLR_v1
[assembly: WebResource("Avg.Controls.SmartPager.js", "text/javascript")]
#endif
namespace Avg.Controls
{
/// <summary>
/// Flickr-style pager control with a go-to-page feature
/// </summary>
[ToolboxData("<{0}:SmartPager runat=\"server\" />")]
[ToolboxBitmap(typeof(SmartPager), "SmartPagerIcon.bmp")]
public class SmartPager : Control, ICloneable, IPostBackDataHandler
{
#region Public Properties
/// <summary>
/// Gets or sets the number of page numbers to display in the list
/// </summary>
[Category("Appearance"), DefaultValue(9), Description("Number of page numbers to display in the list")]
public int Display
{
get { return display; }
set { display = value; }
}
/// <summary>
/// Gets or sets the number of pages available in the data source
/// </summary>
[Category("Misc"), Description("Number of pages available in the data source")]
public int PageCount
{
get { return pageCount; }
set { pageCount = value; }
}
/// <summary>
/// Gets or sets the current page number
/// </summary>
[Browsable(false)]
public int CurrentPage
{
get { return currentPage; }
set
{
currentPage = value;
ViewState[ClientID + "_CurrentPage"] = currentPage;
}
}
/// <summary>
/// Gets or sets a value indicating whether to output the Next & Previous links
/// </summary>
[Category("Appearance"), Description("Number of pages available in the data source")]
public bool OutputNextPrevLinks
{
get { return outputNextPrevLinks; }
set { outputNextPrevLinks = value; }
}
/// <summary>
/// Gets or sets the style for non-clickable Next & Previous links
/// </summary>
[Category("Appearance"), Description("Number of pages available in the data source")]
public string DisabledNextPrevStyle
{
get { return disabledNextPrevStyle; }
set { disabledNextPrevStyle = value; }
}
/// <summary>
/// Gets or sets the text for the 'Previous' link
/// </summary>
[Category("Appearance"), Description("Number of pages available in the data source")]
public string NavigatePreviousText
{
get { return navigatePreviousText; }
set { navigatePreviousText = value; }
}
/// <summary>
/// Gets or sets the text for the 'Next' link
/// </summary>
[Category("Appearance"), Description("Number of pages available in the data source")]
public string NavigateNextText
{
get { return navigateNextText; }
set { navigateNextText = value; }
}
/// <summary>
/// Gets or sets the style attribute for the main table
/// </summary>
[Category("Appearance"), DefaultValue(""), Description("Number of pages available in the data source")]
public string MainTableStyle
{
get { return mainTableStyle; }
set { mainTableStyle = value; }
}
/// <summary>
/// Gets or sets the location of SmartPager.js
/// </summary>
[Category("Misc"), DefaultValue(null), Description("Location of SmartPager.js")]
public string ScriptPath
{
get { return scriptPath; }
set { scriptPath = value; }
}
/// <summary>
/// Gets or sets a value indicating whether to output the first and last page links
/// </summary>
[Category("Appearance"), Description("Determines whether to output the first and last page links")]
public bool OutputFirstAndLastLinks
{
get { return outputFirstAndLastLinks; }
set { outputFirstAndLastLinks = value; }
}
/// <summary>
/// Gets or sets a value indicating whether clicking the ellipses should display the Go-to-page layer
/// </summary>
[Category("Behavior"), Description("Determines whether clicking the ellipses should display the Go-to-page layer")]
public bool EnableGoToPage
{
get { return enableGoToPage; }
set { enableGoToPage = value; }
}
/// <summary>
/// Gets or sets the font size for page links
/// </summary>
[Category("Appearance")]
public string FontSize
{
get { return fontSize; }
set { fontSize = value; }
}
/// <summary>
/// Gets or sets the color for page links
/// </summary>
[Category("Appearance")]
public string PageLinkForeColor
{
get { return pageLinkForeColor; }
set { pageLinkForeColor = value; }
}
/// <summary>
/// Gets or sets the background color for page links
/// </summary>
[Category("Appearance")]
public string PageLinkBackColor
{
get { return pageLinkBackColor; }
set { pageLinkBackColor = value; }
}
/// <summary>
/// Gets or sets the hover color for page links
/// </summary>
[Category("Appearance")]
public string PageLinkHoverForeColor
{
get { return pageLinkHoverForeColor; }
set { pageLinkHoverForeColor = value; }
}
/// <summary>
/// Gets or sets the hover background color for page links
/// </summary>
[Category("Appearance")]
public string PageLinkHoverBackColor
{
get { return pageLinkHoverBackColor; }
set { pageLinkHoverBackColor = value; }
}
/// <summary>
/// Gets or sets the color for selected page links
/// </summary>
[Category("Appearance")]
public string PageLinkSelectedForeColor
{
get { return pageLinkSelectedForeColor; }
set { pageLinkSelectedForeColor = value; }
}
/// <summary>
/// Gets or sets the background color for selected page links
/// </summary>
[Category("Appearance")]
public string PageLinkSelectedBackColor
{
get { return pageLinkSelectedBackColor; }
set { pageLinkSelectedBackColor = value; }
}
/// <summary>
/// Gets or sets the amount of padding in pixels of page number boxes
/// </summary>
[Category("Appearance")]
public int PageNumberBoxPadding
{
get { return pageNumberBoxPadding; }
set { pageNumberBoxPadding = value; }
}
/// <summary>
/// Gets or sets the border width in pixels of page number boxes
/// </summary>
[Category("Appearance")]
public int PageNumberBoxBorderWidth
{
get { return pageNumberBoxBorderWidth; }
set { pageNumberBoxBorderWidth = value; }
}
/// <summary>
/// Gets or sets the border color of page number boxes
/// </summary>
[Category("Appearance")]
public string PageNumberBoxBorderColor
{
get { return pageNumberBoxBorderColor; }
set { pageNumberBoxBorderColor = value; }
}
/// <summary>
/// Gets or sets the text to indicate page numbers that are omitted
/// </summary>
[Category("Appearance")]
public string EllipsisText
{
get { return ellipsisText; }
set { ellipsisText = value; }
}
/// <summary>
/// Gets or sets the name of the JavaScript function to handle the page-change (overriding the default Postback behavior)
/// </summary>
[Category("Behavior"), Description("Specify a JS function to handle the page-change (overriding the default Postback behavior)")]
public string ClientPageChanged
{
get { return clientPageChanged; }
set { clientPageChanged = value; }
}
/// <summary>
/// Gets or sets the text for the textbox-label on the Go-to-page layer
/// </summary>
[Category("Appearance")]
public string PageLabelText
{
get { return pageLabelText; }
set { pageLabelText = value; }
}
/// <summary>
/// Gets or sets the text for the GO button on the Go-to-page layer
/// </summary>
[Category("Appearance")]
public string GoButtonText
{
get { return goButtonText; }
set { goButtonText = value; }
}
#endregion
#region Public Events
/// <summary>
/// Occurs when the user navigates to a page on this control
/// </summary>
public event EventHandler PageChanged;
#endregion
#region Private Members
private int display = 9;
private int pageCount = 20;
private int currentPage = 1;
private bool outputNextPrevLinks = true;
private string fontSize = null;
private bool enableGoToPage = true;
private bool outputFirstAndLastLinks = true;
private string scriptPath = "";
private string mainTableStyle = "";
private string navigateNextText = "Next »";
private string navigatePreviousText = "« Previous";
private string disabledNextPrevStyle = "display:none";
private string pageLinkForeColor = null;
private string pageLinkBackColor = null;
private string pageLinkHoverForeColor = null;
private string pageLinkHoverBackColor = null;
private string pageLinkSelectedForeColor = "red";
private string pageLinkSelectedBackColor = null;
private int pageNumberBoxPadding = 5;
private int pageNumberBoxBorderWidth = 1;
private string pageNumberBoxBorderColor = "#ccc";
private string ellipsisText = " … ";
private string clientPageChanged = null;
private string pageLabelText = "Page:";
private string goButtonText = "GO";
private string[] tooltips = null;
#endregion
protected override void OnInit(EventArgs e)
{
this.Load += new EventHandler(SmartPager_Load);
base.OnInit(e);
}
private void SmartPager_Load(object sender, EventArgs e)
{
#if CLR_v1
Page.RegisterClientScriptBlock("SmartPager_js", "<script language=JavaScript src=\"" + scriptPath + "SmartPager.js\"></script>");
Page.RegisterClientScriptBlock("SmartPager_js2", string.Format("<script language=JavaScript>document.getElementById('smartPagerPageLabel').innerHTML='{0}'; document.getElementById('smartPagerGoLabel').innerHTML='{1}'</script>", pageLabelText, goButtonText));
#else
bool foundRsrc = false;
foreach (string rsrcName in this.GetType().Assembly.GetManifestResourceNames())
{
if (rsrcName.EndsWith(".SmartPager.js"))
{
foundRsrc = true;
break;
}
}
string scriptUrl = (foundRsrc) ? Page.ClientScript.GetWebResourceUrl(this.GetType(), "Avg.Controls.SmartPager.js") : scriptPath + "SmartPager.js";
Page.ClientScript.RegisterClientScriptInclude("SmartPager_js", scriptUrl);
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "SmartPager_js2", string.Format("<script language=JavaScript>document.getElementById('smartPagerPageLabel').innerHTML='{0}'; document.getElementById('smartPagerGoLabel').innerHTML='{1}'</script>", pageLabelText, goButtonText));
#endif
}
protected override void Render(HtmlTextWriter writer)
{
if (PageCount <= 1)
Display = -1;
int[] links;
if (Display == -1)
{
links = new int[PageCount];
for (int i = 0; i < links.Length; i++)
links[i] = i + 1;
}
else
{
links = new int[Display];
// current page in the middle of our range
int middle = (int)Math.Ceiling(Display / 2.0) - 1;
links[middle] = currentPage;
// pages preceding current page
for (int i = middle - 1; i >= 0; i--)
links[i] = links[i + 1] - 1;
// pages following current page
for (int i = middle + 1; i < links.Length; i++)
links[i] = links[i - 1] + 1;
// Get rid of page numbers exceeding PageCount ("shift" page numbers to the right)
while (links[links.Length - 1] > PageCount)
{
for (int i = 0; i < links.Length; i++)
links[i]--;
}
// Get rid of 0 or negative pages ("shift" page numbers to the left)
while (links[0] <= 0)
{
for (int i = 0; i < links.Length; i++)
links[i]++;
}
// assign -1 to pages over PageCount
for (int i = links.Length - 1; i >= 0; i--)
{
if (links[i] > PageCount)
links[i] = -1;
else
break;
}
}
// javascript tooltips array
StringBuilder tooltipsJsArr = new StringBuilder();
if (tooltips != null)
{
foreach (string s in tooltips)
tooltipsJsArr.AppendFormat("\"{0}\",", Page.Server.HtmlEncode(s));
if (tooltipsJsArr.Length > 0)
tooltipsJsArr.Remove(tooltipsJsArr.Length - 1, 1);
}
// ** HTML output begins **
// JS variables for go-to-page popup
writer.WriteLine("<script>var {0}$pagerPageCount = {1}; var {0}$tooltipsArr = [{2}]; var {0}$callJS = '{3}'</script>", ClientID, PageCount, tooltipsJsArr, clientPageChanged);
// CSS
string userAgent = "MSIE";
try
{
userAgent = Page.Request.UserAgent; // VS designer doesn't have a Request
}
catch { }
string MsIEOnly = (userAgent != null && userAgent.IndexOf("MSIE") != -1) ? "background-color:{2};" : "";
writer.WriteLine("<style type=text/css>");
writer.WriteLine("a.pagerLink_{0} {{ color:{1}; text-decoration:none; font-size:{3}; }}", this.ClientID, pageLinkForeColor, pageLinkBackColor, fontSize); // background-color:{2};
writer.WriteLine("a.pagerLink_{0} span {{ background-color:{1}; }}", this.ClientID, pageLinkBackColor);
writer.WriteLine("a.pagerLink_{0}:hover {{ color:{1}; " + MsIEOnly + "}}", this.ClientID, pageLinkHoverForeColor, pageLinkHoverBackColor);
writer.WriteLine("a.pagerLink_{0}:hover span {{ background-color:{1}; }}", this.ClientID, pageLinkSelectedBackColor); // This has no effect in IE
writer.WriteLine("a.pagerLinkSel_{0} {{ color:{1}; text-decoration:none; font-weight:bold; font-size:{3}; }}", this.ClientID, pageLinkSelectedForeColor, pageLinkSelectedBackColor, fontSize); // background-color:{2};
writer.WriteLine("a.pagerLinkSel_{0} span {{ background-color:{1}; }}", this.ClientID, pageLinkSelectedBackColor);
writer.WriteLine("span.pagerPageNo_{0} {{ height:1; cursor:hand; padding:{1}px; margin-left:2px; margin-right:2px; border:{2}px solid {3}; }}", this.ClientID, pageNumberBoxPadding, pageNumberBoxBorderWidth, pageNumberBoxBorderColor);
writer.WriteLine("</style>");
// main table
writer.WriteLine("<table border=0 cellpadding=0 cellspacing=0 style=\"" + mainTableStyle + "\"><tr>");
if (outputNextPrevLinks)
{
// previous link
writer.Write("<td nowrap><nobr>");
if (currentPage > 1)
writer.Write("<a href=\"javascript:SmartPagerSelectPage('{0}', '{1}')\" class=pagerLink_{0} title=\"{2}\">« Previous</a> ", ClientID, currentPage - 1, getTooltipText(currentPage - 2));
else
writer.Write("<div style=\"{0}\">{1} </div>", disabledNextPrevStyle, navigatePreviousText);
writer.WriteLine("</nobr></td>");
}
// page numbers
writer.Write("<td align=center style='padding:3 0 3 0'>");
if (links[0] != 1)
{
if (outputFirstAndLastLinks)
// output link to first page number
writer.Write("<a href=\"javascript:SmartPagerSelectPage('{0}', '{1}')\" class={2}_{0} title=\"{3}\"><span class=pagerPageNo_{0}>{1}</span></a>", ClientID, 1, (1 == currentPage) ? "pagerLinkSel" : "pagerLink", getTooltipText(0));
if (links[0] != 2)
{
// output ellipsis
if (enableGoToPage)
writer.Write("<a href=# onclick=\"SmartPagerID='{0}';showPager(this);return false\" class=pagerLink_{0} style='position:relative;background-color:transparent;'>{1}</a>", ClientID, ellipsisText);
else
writer.Write(ellipsisText);
}
}
// output page number links
for (int i = 0; i < links.Length; i++)
{
if (links[i] == -1)
break;
writer.WriteLine("<a href=\"javascript:SmartPagerSelectPage('{0}', '{1}')\" class={2}_{0} title=\"{3}\"><span class=pagerPageNo_{0}>{1}</span></a", ClientID, links[i], (links[i] == currentPage) ? "pagerLinkSel" : "pagerLink", getTooltipText(links[i] - 1));
writer.Write(">"); // span the </a> tag accross two lines so no line break between tags in HTML
if (Display == -1)
writer.Write(" "); // write a space so page numbers can wrap if displaying ALL page links
}
if (links[links.Length - 1] != -1 && links[links.Length - 1] != PageCount)
{
if (links[links.Length - 1] != PageCount - 1)
{
// output ellipsis
if (enableGoToPage)
writer.Write("<a href=#o onclick=\"SmartPagerID='{0}';showPager(this);return false\" class=pagerLink_{0} style='position:relative;background-color:transparent;'>{1}</a>", ClientID, ellipsisText);
else
writer.Write(ellipsisText);
}
if (outputFirstAndLastLinks)
// output link to last page number
writer.Write("<a href=\"javascript:SmartPagerSelectPage('{0}', '{1}')\" class={2}_{0} title=\"{3}\"><span class=pagerPageNo_{0}>{1}</span></a>", ClientID, PageCount, (PageCount == currentPage) ? "pagerLinkSel" : "pagerLink", getTooltipText(PageCount - 1));
}
writer.WriteLine("</div></td>");
if (outputNextPrevLinks)
{
// next link
writer.Write("<td nowrap align=right><nobr>");
if (currentPage < PageCount)
writer.Write(" <a href=\"javascript:SmartPagerSelectPage('{0}', '{1}')\" class=pagerLink_{0} title=\"{2}\">Next »</a>", ClientID, currentPage + 1, getTooltipText(currentPage));
else
writer.Write("<div style=\"{0}\"> {1}</div>", disabledNextPrevStyle, navigateNextText);
writer.WriteLine("</nobr></td>");
}
writer.WriteLine("</tr></table>");
writer.WriteLine("<input type=hidden name='{0}' id='hdn{1}'>", UniqueID, ClientID);
}
// Check postback data (IPostBackDataHandler required method)
public virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
// get the previously selected page from the viewstate
if (ViewState[ClientID + "_CurrentPage"] != null)
currentPage = Convert.ToInt32(ViewState[ClientID + "_CurrentPage"]);
bool changed = (postCollection[postDataKey] != "");
if (changed)
CurrentPage = Convert.ToInt32(postCollection[postDataKey]);
return changed;
}
// Raise the PageChanged event (IPostBackDataHandler required method)
public virtual void RaisePostDataChangedEvent()
{
OnPageChanged(EventArgs.Empty);
}
protected virtual void OnPageChanged(EventArgs e)
{
if (PageChanged != null)
PageChanged(this, e);
}
/// <summary>
/// Specify the tooltips for the page numbers
/// </summary>
/// <param name="tooltips">String array of tooltip values</param>
public void SetTooltips(string[] tooltips)
{
this.tooltips = new string[PageCount];
Array.Copy(tooltips, 0, this.tooltips, 0, tooltips.Length);
}
private string getTooltipText(int index)
{
if (tooltips == null)
return "";
else
return Page.Server.HtmlEncode(tooltips[index]);
}
/// <summary>
/// Clones the urrent instance of the control
/// </summary>
/// <returns>Clone of this control instance</returns>
public object Clone()
{
SmartPager inst = new SmartPager();
inst.display = display;
inst.pageCount = pageCount;
inst.currentPage = currentPage;
inst.scriptPath = scriptPath;
inst.mainTableStyle = mainTableStyle;
inst.outputNextPrevLinks = outputNextPrevLinks;
inst.navigatePreviousText = navigatePreviousText;
inst.navigateNextText = navigateNextText;
inst.outputFirstAndLastLinks = outputFirstAndLastLinks;
inst.enableGoToPage = enableGoToPage;
inst.fontSize = fontSize;
inst.pageLinkForeColor = pageLinkForeColor;
inst.pageLinkBackColor = pageLinkBackColor;
inst.pageLinkHoverForeColor = pageLinkHoverForeColor;
inst.pageLinkHoverBackColor = pageLinkHoverBackColor;
inst.pageLinkSelectedForeColor = pageLinkSelectedForeColor;
inst.pageLinkSelectedBackColor = pageLinkSelectedBackColor;
inst.pageNumberBoxPadding = pageNumberBoxPadding;
inst.pageNumberBoxBorderWidth = pageNumberBoxBorderWidth;
inst.pageNumberBoxBorderColor = pageNumberBoxBorderColor;
inst.ellipsisText = ellipsisText;
inst.disabledNextPrevStyle = disabledNextPrevStyle;
inst.tooltips = tooltips;
return inst;
}
}
}