development blog.

Copy Images from Clipboard in Javascript

Friday, January 20th, 2012

One of the pretty common in a Windows environment is copy/pasting image data across programs. In recent versions of chrome, this is now possible in the browser. Here is a quick demo of the javascript we’ll be starting from — you can copy image data from anywhere (Paint, Word, Screenshot, etc) and paste it into the div to have it appended.

http://jsfiddle.net/H9wgv/

This just appends an image that looks something like:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAAAqCAYAAACHr...C">

Which is pretty powerful in it’s own right, but it’s not terribly well supported across browsers – for Foliotek Presentation, ideally we would create a file they can manage just like any of their other files from the paste data, so, with a quick change to the reader.onload, we’ll upload the image to the server:

reader.onload = function(evt) {

var result = evt.target.result;
var arr = result.split(",");
var data = arr[1]; // raw base64
var contentType = arr[0].split(";")[0].split(":")[1]; // image/png, image/gif, etc

$.post("imageupload", {
    data: data,
    contenttype: contentType,
}, function (ev) {
    var img = $("<img style='display:none;' src='" + ev.URL + "' />");
    img[0].onload = function () {
        var width = img.width();
        var height = img.height();
        var src = "<img src='" + ev.URL + "' width='" + width + "' height='" + height + "' />";
        div.append($(src));
        img.remove();
    };

    $("body").append(img);
});

};

And the content of the “imageupload” server route is pretty straightforward, and not too different than what you’d have for uploading an image from Post data:

public JsonResult imageupload(string data, string contenttype)
{
    byte[] bytes = Convert.FromBase64String(data);
    var ms = new MemoryStream(bytes, 0, bytes.Length);
    UserFile file = SaveByteArrayAsUserFile(User, bytes, contentType); // saves the content as a file associated with that user
    return Json(new {
        file.Name,
        file.URL
    });
}
 

Pretty powerful, and definitely one further step in making web-apps feel like native OS apps.

Copy Images from Clipboard in Javascript

Friday, January 20th, 2012

One of the pretty common in a Windows environment is copy/pasting image data across programs. In recent versions of chrome, this is now possible in the browser. Here is a quick demo of the javascript we’ll be starting from — you can copy image data from anywhere (Paint, Word, Screenshot, etc) and paste it into the div to have it appended.

http://jsfiddle.net/H9wgv/

This just appends an image that looks something like:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAAAqCAYAAACHr...C">

Which is pretty powerful in it’s own right, but it’s not terribly well supported across browsers – for Foliotek Presentation, ideally we would create a file they can manage just like any of their other files from the paste data, so, with a quick change to the reader.onload, we’ll upload the image to the server:

reader.onload = function(evt) {

var result = evt.target.result;
var arr = result.split(",");
var data = arr[1]; // raw base64
var contentType = arr[0].split(";")[0].split(":")[1]; // image/png, image/gif, etc

$.post("imageupload", {
    data: data,
    contenttype: contentType,
}, function (ev) {
    var img = $("<img style='display:none;' src='" + ev.URL + "' />");
    img[0].onload = function () {
        var width = img.width();
        var height = img.height();
        var src = "<img src='" + ev.URL + "' width='" + width + "' height='" + height + "' />";
        div.append($(src));
        img.remove();
    };

    $("body").append(img);
});

};

And the content of the “imageupload” server route is pretty straightforward, and not too different than what you’d have for uploading an image from Post data:

public JsonResult imageupload(string data, string contenttype)
{
    byte[] bytes = Convert.FromBase64String(data);
    var ms = new MemoryStream(bytes, 0, bytes.Length);
    UserFile file = SaveByteArrayAsUserFile(User, bytes, contentType); // saves the content as a file associated with that user
    return Json(new {
        file.Name,
        file.URL
    });
}
 

Pretty powerful, and definitely one further step in making web-apps feel like native OS apps.

Datagrid Checkbox Column

Wednesday, October 26th, 2011

In Foliotek, there are a lot of instances where we have a table with a check box column that allows users to select rows and do some sort of action to them. This leads to a lot of redundancy in our markup and code. For example, here is what our datagrids looked like when we placed a checkbox in them (we’re using a custom server control for our datagrid):

Datagrid with a checkbox


<Components:ExtendedDataGrid runat="server" id="dgItems">
	<Columns>
		<asp:TemplateColumn>
			<HeaderTemplate>
				<input class="check" onclick="FLTK.checkbox.selectAll(this.checked, 'chkSelect');" type="checkbox" />
			</HeaderTemplate>
			<ItemTemplate>
				<input class="check" type="checkbox" id="chkSelect" runat="server" />
			</ItemTemplate>
		</asp:TemplateColumn>
		<asp:TemplateColumn>
			<ItemTemplate>
				<%# Eval("ItemName") %>
			</ItemTemplate>
		</asp:TemplateColumn>
	</Columns>
</Components:ExtendedDataGrid>

Check All JS

Here’s our javascript code that controls the select/deselect all functionality. See this blog post in reference to the :asp() selector, and this post for more information on the select all functionality.


FLTK.checkbox = {
    selectAll: function (checked, endingwith) {
        var $checkboxes = $(":asp(" + endingwith + ")");
		// we don't want to check a box that is hidden on the page
        if (checked) {
            $checkboxes = $checkboxes.filter(":visible").not(":disabled");
        }
        $checkboxes.attr("checked", checked);
    }
}

Get selected rows (C#)

Then, to get the selected items in the code behind and perform some action on them, we’d have to do the following:


foreach(DataGridItem item in dgItems.Items)
{
	if(((HtmlInputCheckBox)item.FindControl("chkSelect")).Checked)
	{
		// perform action
	}
}

// which can also be written as...

foreach(DataGridItem in dgItems.Items.Cast<DataGridItem>().Where(i => ((HtmlInputCheckBox)i.FindControl("chkSelect")).Checked))
{
	// perform action
}

Creating the Custom Datagrid Checkbox Column

Since the above code and markup is used so frequently in Foliotek, we wanted to abstract this logic into something that was easier to use. As I stated above, we use a custom server control that extends the datagrid control, but if you don’t have a custom server control it shouldn’t be hard to create one.

I would suggest reading up on custom server controls, if you’re interested in how the following code works.

Below is our server controls for the custom DataGrid, CheckBoxColumn, and CheckBoxTemplate. Keep in mind the Checked and VisibilityDataField on the CheckBoxColumn are optional.


namespace Components
{
	// here is our custom datagrid control
    public class ExtendedDataGrid : DataGrid
    {
		public IEnumerable<DataGridItem> GetSelectedItems()
        {
            if (!this.Columns.Cast<DataGridColumn>().Any(c => c is CheckBoxColumn))
            {
                throw new Exception("ExtendedDataGrid must have a 'CheckBoxColumn' in order to use GetSelectedItems");
            }

            return this.Items.Cast<DataGridItem>().Where(i => ((CheckBox)i.FindControl("cb_" + this.ID)).Checked);
        }
    }

	// Usage: <Components:CheckBoxColumn Checked="false" VisibilityDataField="Show"></Components:CheckBoxColumn>
	// Alternative Usage: VisiblityDataField="!Hide"
	public class CheckBoxColumn : TemplateColumn
    {
        public string VisibilityDataField { get; set; }
        public bool Checked { get; set; }

        public CheckBoxColumn() : base()
        {
        }

        public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)
        {
            if (this.Owner != null)
            {
                this.HeaderTemplate = new CheckBoxTemplate(this.Owner.ID, VisibilityDataField, true, Checked);
                this.ItemTemplate = new CheckBoxTemplate(this.Owner.ID, VisibilityDataField, false, Checked);
            }
            base.InitializeCell(cell, columnIndex, itemType);
        }
    }

    public class CheckBoxTemplate : ITemplate
    {
        private string _tableID;
        private bool _isHeader;
        private string _visibilityDataField;
        private bool _isChecked;

        public CheckBoxTemplate(string tableID, string visibilityDataField, bool isHeader, bool isChecked)
        {
            _tableID = tableID;
            _isHeader = isHeader;
            _visibilityDataField = visibilityDataField;
            _isChecked = isChecked;
        }

        public void InstantiateIn(Control c)
        {
            if (_isHeader)
            {
				// If the template container is the header, then we need to add the check all functionality to the checkbox
                HtmlInputCheckBox input = new HtmlInputCheckBox();
                input.Attributes["onclick"] = "FLTK.checkbox.selectAll(this.checked, 'cb_" + this._tableID + "');"; // This id is determined below.
                input.Checked = _isChecked; // Set the checked status by default
                c.Controls.Add(input);
            }
            else
            {
                CheckBox cb = new CheckBox();
                cb.ID = "cb_" + this._tableID;
                if (!String.IsNullOrEmpty(_visibilityDataField))
                {
                    cb.DataBinding += new EventHandler(cb_DataBinding); // doing this in the databind event allows us to access properties in the dataitem.
                }
                cb.Checked = _isChecked;
                c.Controls.Add(cb);
            }
        }

        void cb_DataBinding(object sender, EventArgs e)
        {
            var cb = (CheckBox)sender;
            var dataitem = ((DataGridItem)cb.NamingContainer).DataItem;

            bool show = true;
            if (!String.IsNullOrEmpty(this._visibilityDataField))
            {
                bool not = this._visibilityDataField.StartsWith("!");
                show = (bool)DataBinder.Eval(dataitem, (not ? this._visibilityDataField.Substring(1) : this._visibilityDataField));

                show = not ? !show : show;
            }

            cb.Visible = show;
        }
    }
}

New markup and code behind

Here’s our new markup. Notice how we only have one line now for the checkbox column, compared to 8 lines before.


<Components:ExtendedDataGrid runat="server" id="dgItems">
	<Columns>
		<Components:CheckBoxColumn></Components:CheckBoxColumn>
		<asp:TemplateColumn>
			<ItemTemplate>
				<%# Eval("ItemName") %>
			</ItemTemplate>
		</asp:TemplateColum>
	</Columns>
</Components:ExtendedDataGrid>

… and our code behind, which definitely simplifies the prior statement…


foreach(DataGridItem item in dgItems.GetSelectedItems())
{
	//perform action
}

Datagrid Checkbox Column

Wednesday, October 26th, 2011

In Foliotek, there are a lot of instances where we have a table with a check box column that allows users to select rows and do some sort of action to them. This leads to a lot of redundancy in our markup and code. For example, here is what our datagrids looked like when we placed a checkbox in them (we’re using a custom server control for our datagrid):

Datagrid with a checkbox


<Components:ExtendedDataGrid runat="server" id="dgItems">
	<Columns>
		<asp:TemplateColumn>
			<HeaderTemplate>
				<input class="check" onclick="FLTK.checkbox.selectAll(this.checked, 'chkSelect');" type="checkbox" />
			</HeaderTemplate>
			<ItemTemplate>
				<input class="check" type="checkbox" id="chkSelect" runat="server" />
			</ItemTemplate>
		</asp:TemplateColumn>
		<asp:TemplateColumn>
			<ItemTemplate>
				<%# Eval("ItemName") %>
			</ItemTemplate>
		</asp:TemplateColumn>
	</Columns>
</Components:ExtendedDataGrid>

Check All JS

Here’s our javascript code that controls the select/deselect all functionality. See this blog post in reference to the :asp() selector, and this post for more information on the select all functionality.


FLTK.checkbox = {
    selectAll: function (checked, endingwith) {
        var $checkboxes = $(":asp(" + endingwith + ")");
		// we don't want to check a box that is hidden on the page
        if (checked) {
            $checkboxes = $checkboxes.filter(":visible").not(":disabled");
        }
        $checkboxes.attr("checked", checked);
    }
}

Get selected rows (C#)

Then, to get the selected items in the code behind and perform some action on them, we’d have to do the following:


foreach(DataGridItem item in dgItems.Items)
{
	if(((HtmlInputCheckBox)item.FindControl("chkSelect")).Checked)
	{
		// perform action
	}
}

// which can also be written as...

foreach(DataGridItem in dgItems.Items.Cast<DataGridItem>().Where(i => ((HtmlInputCheckBox)i.FindControl("chkSelect")).Checked))
{
	// perform action
}

Creating the Custom Datagrid Checkbox Column

Since the above code and markup is used so frequently in Foliotek, we wanted to abstract this logic into something that was easier to use. As I stated above, we use a custom server control that extends the datagrid control, but if you don’t have a custom server control it shouldn’t be hard to create one.

I would suggest reading up on custom server controls, if you’re interested in how the following code works.

Below is our server controls for the custom DataGrid, CheckBoxColumn, and CheckBoxTemplate. Keep in mind the Checked and VisibilityDataField on the CheckBoxColumn are optional.


namespace Components
{
	// here is our custom datagrid control
    public class ExtendedDataGrid : DataGrid
    {
		public IEnumerable<DataGridItem> GetSelectedItems()
        {
            if (!this.Columns.Cast<DataGridColumn>().Any(c => c is CheckBoxColumn))
            {
                throw new Exception("ExtendedDataGrid must have a 'CheckBoxColumn' in order to use GetSelectedItems");
            }

            return this.Items.Cast<DataGridItem>().Where(i => ((CheckBox)i.FindControl("cb_" + this.ID)).Checked);
        }
    }

	// Usage: <Components:CheckBoxColumn Checked="false" VisibilityDataField="Show"></Components:CheckBoxColumn>
	// Alternative Usage: VisiblityDataField="!Hide"
	public class CheckBoxColumn : TemplateColumn
    {
        public string VisibilityDataField { get; set; }
        public bool Checked { get; set; }

        public CheckBoxColumn() : base()
        {
        }

        public override void InitializeCell(TableCell cell, int columnIndex, ListItemType itemType)
        {
            if (this.Owner != null)
            {
                this.HeaderTemplate = new CheckBoxTemplate(this.Owner.ID, VisibilityDataField, true, Checked);
                this.ItemTemplate = new CheckBoxTemplate(this.Owner.ID, VisibilityDataField, false, Checked);
            }
            base.InitializeCell(cell, columnIndex, itemType);
        }
    }

    public class CheckBoxTemplate : ITemplate
    {
        private string _tableID;
        private bool _isHeader;
        private string _visibilityDataField;
        private bool _isChecked;

        public CheckBoxTemplate(string tableID, string visibilityDataField, bool isHeader, bool isChecked)
        {
            _tableID = tableID;
            _isHeader = isHeader;
            _visibilityDataField = visibilityDataField;
            _isChecked = isChecked;
        }

        public void InstantiateIn(Control c)
        {
            if (_isHeader)
            {
				// If the template container is the header, then we need to add the check all functionality to the checkbox
                HtmlInputCheckBox input = new HtmlInputCheckBox();
                input.Attributes["onclick"] = "FLTK.checkbox.selectAll(this.checked, 'cb_" + this._tableID + "');"; // This id is determined below.
                input.Checked = _isChecked; // Set the checked status by default
                c.Controls.Add(input);
            }
            else
            {
                CheckBox cb = new CheckBox();
                cb.ID = "cb_" + this._tableID;
                if (!String.IsNullOrEmpty(_visibilityDataField))
                {
                    cb.DataBinding += new EventHandler(cb_DataBinding); // doing this in the databind event allows us to access properties in the dataitem.
                }
                cb.Checked = _isChecked;
                c.Controls.Add(cb);
            }
        }

        void cb_DataBinding(object sender, EventArgs e)
        {
            var cb = (CheckBox)sender;
            var dataitem = ((DataGridItem)cb.NamingContainer).DataItem;

            bool show = true;
            if (!String.IsNullOrEmpty(this._visibilityDataField))
            {
                bool not = this._visibilityDataField.StartsWith("!");
                show = (bool)DataBinder.Eval(dataitem, (not ? this._visibilityDataField.Substring(1) : this._visibilityDataField));

                show = not ? !show : show;
            }

            cb.Visible = show;
        }
    }
}

New markup and code behind

Here’s our new markup. Notice how we only have one line now for the checkbox column, compared to 8 lines before.


<Components:ExtendedDataGrid runat="server" id="dgItems">
	<Columns>
		<Components:CheckBoxColumn></Components:CheckBoxColumn>
		<asp:TemplateColumn>
			<ItemTemplate>
				<%# Eval("ItemName") %>
			</ItemTemplate>
		</asp:TemplateColum>
	</Columns>
</Components:ExtendedDataGrid>

… and our code behind, which definitely simplifies the prior statement…


foreach(DataGridItem item in dgItems.GetSelectedItems())
{
	//perform action
}

Color Scheme Generator in JavaScript

Friday, September 23rd, 2011

ColorStash is a tiny web app I built for the 10K Apart contest. The goal of the contest is to build an application in under 10 Kilobytes. This includes all HTML, JavaScript, CSS, and images. This is a tight limit, but luckily you can include jQuery and not have it count against your size quota.

The main goal is to provide an easy way for to choose a nice color or scheme, and output those colors in a variety of formats. It supports hex, css names, rgb, hsv, and hsl as input and output formats. You don’t really need to know anything about these formats to use the colorpicker or built in image eyedropper.

I started learning about color when working on the template editor and color schemes

You can read more http://www.briangrinstead.com/blog/colorstash, check out the 10K entry, or see the more permanent (and possibly bigger than 10K as I make updates) app here: http://briangrinstead.com/colorstash/.