Monday, November 12, 2007

CodeIgniter and jQuery

On the CodeIgniter forum there are quite a few jQuery questions so I'm bringing up some pointers.

jQuery file splitting


One big jquery file is great if you want to save http connections but it's not always the best approach. For instance if you have page specific code that doesn't need to be loaded until the visitor gets on that page.

Once you separated the page specific code into different files you can do two things :
- add the link to the javascript file to your page specific view
- have a variable in your view file and add the link to the javascript file when it's needed.
With the first method you can run into trouble if someone uploads his view without the javascript link. The second method can be a source of bugs if you have a templated site because the variable always needs a value if you add it to the basic lay-out.

Ajax


The relative url is most of the time (/index.php)/controller/method but if you refer to the same controller you can use ../method.

If you are using a get request don't add the parameters using jQuerys build in functionality but add them to the url (controller/method/key/value/key/value) and retrieve the key-value pairs using the CodeIgniter uri->uri_to_assoc method.

My latest jQuery snippets on the CodeIgniter forum


Content to new window
Highlight words using a sentence/keywords on the page
hack for inline links

Wednesday, October 31, 2007

jquery plugin : switchcss

Introduction



I was answering a question posed on the CodeIgniter forum about a stylesheet switcher and i came up with this code. It is rough but it worked.

Later that day i read in the mailinglist of jquery there was A Plugin Development Pattern tutorial

After reading this i wanted to make the rough code into a plugin and switchcss was born.

Features



Other stylesheet switchers i found where based on alternative stylesheets that had to be loaded and thus making the pages heavier to load. This was the concern of the poster on the CodeIgniter forum so i made this stylesheet switcher to work with one theme css that gets changed.

It remembers the chosen theme using a cookie.

Files



Dependencies:


switchcss plugin

Usage



Link to the files and at least one theme css file and add following code to your ready method

$(function(){
// previously chosen theme
$.cookieswitchcss();
// elements that hold the alternate themes
$('.switchcss').switchcss();
});

Now clicking on the elements changes the stylesheet and adds the theme to the cookie.

By default the css filename is prefixed with theme_ and the text of the element is extracted to add to the prefix of the name. And the link element should have the title themecss.

All the defaults :

cookiename

default : csstheme

linktitle

the value of the title attribute from the link element that holds the theme css file.

default : themecss

cssprefix

general prefix used by the theme css files

default : theme_

themename

Which value to use to complete the css file name.

text : content of the element

other : value of the attribute you define here

default : text



These options are used by the two functions so i made them public (thank you tutorial) so you only have to define them once.


$.fn.switchcss.defaults.linktitle = 'theme';
// gets the value of the title attribute of the anchors
$.fn.switchcss.defaults.themename = 'title';
$.cookieswitchcss();
$('a').switchcss();


I hope it comes in handy for someone.

Thursday, October 18, 2007

Instant form validation

When you think about javascript validation and when to trigger it the first thing you think about is the onblur event. The trouble with the onblur even is not everyone is tabbing through form so it's possible not all fields are validated. The same goes for a key event.

So most of the programmers use the click event to submit the form. The problem in asp.net is that if you use the asp:net button tag the javascript of the asp.net control is handled after your own javascript code so the form is processed even if you added return false for the event.

I don't know how to influence the control behavior yet so what i did is attach the validation to the button mouseover event. This way you see the errors before you even click the button.

I don't know why i haven't thought of that before because it's so easy and noticeable.

You can find a demo at mouseover button validation

Friday, August 31, 2007

date class update

I hadn't used the class before i posted it, stupid me, therefore i wasn't aware of the problem inserting the custom dateformat in a sql statement.

Mysql formats the dates as following yyyy-mm-dd hh:mm:ss so i had to convert the custom format into the database format. As you can see the added function dbformat is pretty simple leaving room for self formatted date strings.

In the interface functions i call the dbformat function and that gets the class up and running.




class Datemodel extends Model
{
function Datemodel()
{
parent::Model();
}

/**
*
* interface functions
*
*/

function get($format = 'YmdHis', $date = null)
{
$date = (!isset($date))?$this->timestamp('db'):$this->dbformat($date);
return $this->format($date,$this->preformat($format));
}

function getstr($str,$format = 'YmdHis', $date = null)
{
$date = (!isset($date))?$this->timestamp('db'):$this->dbformat($date);
$sign = substr($str,0,1);
$period = trim(substr($str,1));
return $this->get($format,$this->interval($date,$sign,$period));
}

/**
*
* helper functions
*
*/

function timestamp($type = '')
{
switch($type)
{
case 'db': $query = $this->db->query('select NOW() as ts'); break;
default: $query = $this->db->query('select NOW()+0 as ts'); break;
}
$row = $query->row();
return $row->ts;
}

function format($date,$format)
{
$query = $this->db->query('select date_format("'.$date.'","'.$format.'") as val');
$row = $query->row();
return $row->val;
}

function interval($date,$sign,$period)
{
switch($sign)
{
case '+': $query = $this->db->query('select date_add("'.$date.'", interval '.$period.') as val'); break;
case '-': $query = $this->db->query('select date_sub("'.$date.'", interval '.$period.') as val'); break;
}
$row = $query->row();
return $row->val;
}

function dbformat($date)
{
if(strlen($date) == 14)
{
$year = substr($date,0,4);
$month = substr($date,4,2);
$day = substr($date,6,2);
$hour = substr($date,8,2);
$min = substr($date,10,2);
$sec = substr($date,12,2);
$date = $year.'-'.$month.'-'.$day.' '.$hour.':'.$min.':'.$sec;
}
return $date;
}

function preformat($str)
{
$array = array('%','a','b','c','d','e','h','i','j','k','l','m','p','r','s','u','v','w','x','y','D','H','I','M','S','U','V','W','X','Y');
foreach($array as $char)
{
$str = str_replace($char,'%'.$char,$str);
}
return $str;
}
}
?>


Saturday, August 25, 2007

mysql date functions for own timestamp format

Introduction

I recently got familiar with the limitations of the unix_timestamp. I had to make it possible to generate dates from before 1970. This was a real pain because i had used the normal date functions of php.

So i had to come up with something quick and my first thought was using the date functions of mysql. Mysql doesn't have the 1970 limitation so i was free to use any date possible.

Because i don't wanted to change all the datatypes of the fields containing dates i had to find a numeric timestamp. This was not so hard, YYYYMMDDHHMMSS gives me a date range from 10000101010101 to 99991231235959.

The code
I got used to have two functions to generate timestamps, date and strtotime. Because i wanted my functions in a class i had to use short names because no self respecting programmer want to type a lot, but they do it any way.
get and getstr seemed descriptive enough because i named the class date.

I made the class as a model class for code igniter because that's the framework i use nowadays for almost all my projects. But changing it to native php code is not going be to hard.
i only show the basic, alpha class here because i first want to test the more complex functions.



class Datemodel extends Model
{
function Datemodel()
{
parent::Model();
}

/**
*
* interface functions
*
*/

function get($format = 'YmdHis', $date = null)
{
if(!isset($date)){ $date = $this->timestamp('db'); }
return $this->format($date,$this->preformat($format));
}

function getstr($str,$format = 'YmdHis', $date = null)
{
if(!isset($date)){ $date = $this->timestamp('db'); }
$sign = substr($str,0,1);
$period = trim(substr($str,1));
return $this->get($format,$this->interval($date,$sign,$period));
}

/**
*
* helper functions
*
*/

function timestamp($type = '')
{
switch($type)
{
case 'db': $query = $this->db->query('select NOW() as ts'); break;
default: $query = $this->db->query('select NOW()+0 as ts'); break;
}
$row = $query->row();
return $row->ts;
}

function format($date,$format)
{
$query = $this->db->query('select date_format("'.$date.'","'.$format.'") as val');
$row = $query->row();
return $row->val;
}

function interval($date,$sign,$period)
{
switch($sign)
{
case '+': $query = $this->db->query('select date_add("'.$date.'", interval '.$period.') as val'); break;
case '-': $query = $this->db->query('select date_sub("'.$date.'", interval '.$period.') as val'); break;
}
$row = $query->row();
return $row->val;
}

function preformat($str)
{
$array = array('%','a','b','c','d','e','h','i','j','k','l','m','p','r','s','u','v','w','x','y','D','H','I','M','S','U','V','W','X','Y');
foreach($array as $char)
{
$str = str_replace($char,'%'.$char,$str);
}
return $str;
}
}
?>



As you can see the sql statements are very simple and for the format i made it possible to use the letter only format instead of the mysql format.

Future
This is only a basic, alpha class which should not be used on production sites.

i'm planning to make the class cross-database so it can be used everywhere and for now i'm testing a more complex method, diff, which takes care of date calculations. And the possibility for getstr and diff to be aware of weekends and vacations.

Saturday, July 28, 2007

Programmers and black holes

it's been awhile since i posted because i'm busy working on some things. I have to many ideas and to little time to work things out.

The one thing that is visible is the simple view library for the code igniter framework. I have abandoned it for a while because i needed to train a puppy but i will pick it up soon.

Apart from that i had the idea of creating an integrator library to generate jQuery code using only php. It's an effort which is maybe to much to handle alone but i want to have at least a proof of concept code.

In the past month(s) i also realised there is a big gap between developers and users. Where developers speak about rendering engines and applications, users speak about websites. Users don't care about programming languages, progressive enchantment, ajax, ... . They care about does the site does what i want it to do and does it do it fast and does it look nice enough to work on. I never understood why people wanted to work with the black and white/green programs and still do. I don't care much about how a certain button looks like but it has to te be descriptive or at least consistent within the GUI.

i'm going to have myself a summer of code on my own so keep checking the blog.

Thursday, May 31, 2007

Php timestamp

I've been working with dates recently and today i became aware there are four methods to generate a timestamp.

echo time();
echo date('U');

These are the simplest functions. Then there is the mktime function which is the worst function of them all. The arguments are hour,minutes,seconds,month,day,year. I don't know which type of dateformat that should mirror but it's not a format i know of.

And then there is the most human accessible function strtotime. But there is one problem if you want the last day of the next month for example then you have to use strtotime two times.

My function combines mktime, time and strtotime functions into one and as an extra it's possible to have multiple strtotime strings in an array. Another extra is the possibility to change the timestamp output using the date function. The mktime function is accessed by a numeric value with the format yyyymmddhhmmss.

function tsgen($fromnow = '',$ts = '')
{
if($fromnow == '')
{
return ($ts == '')?time():date($ts);
}
else
{
if(is_array($fromnow))
{
$tempbegin = array_shift($fromnow);
$timestamp = strtotime($tempbegin);
if($timestamp == -1){ $timestamp = false; }
if (!$timestamp) {
return false;
}
else
{
$error = 0;
foreach($fromnow as $str)
{
$timestamp = strtotime($str,$timestamp);
if($timestamp == -1){ $timestamp = false; }
if (!$timestamp) { $error++; }
}
if($error > 0)
{
return false;
}
else
{
return ($ts == '')?$timestamp:date($ts,$timestamp);
}

}
}
else
{
if(is_numeric($fromnow) && strlen($fromnow) == 14)
{
return mktime(substr($fromnow,8,2), substr($fromnow,10,2), substr($fromnow,12,2), substr($fromnow,4,2), substr($fromnow,6,2), substr($fromnow,0,4));
}
else
{
$timestamp = strtotime($fromnow);
if($timestamp == -1){ $timestamp = false; }
if (!$timestamp) {
return false;
}
else
{
return ($ts == '')?$timestamp:date($ts,$timestamp);
}
}
}
}
}

Examples

echo tsgen();
echo tsgen('+1 day');
echo tsgen(20070101010101);
echo tsgen(array('+28 days','last friday'));
echo tsgen('next month','d-m-Y');

I hope people will enjoy using this unified timestamp/date function

Monday, April 09, 2007

GTD my way

Getting things done seems to be a new lifestyle but it's only new for the people that do only talking. Lifehacker is a blog devoted to make you do things with the least amount of time spend on additional tasks. Some ideas are good but i have a feeling other posts just want to sell you software.

If you already read my blog you know i already tried to improve working with windows but there are some changes which i want to share with you.

The software


There are only 3 applications that make me more productive and help me to give a better overview of my tasks.

Launchy : using the key combination alt+spacebar i never have to click on the startbutton to find an application or a folder.

TaskSwitchXP : alt+tab is already a powerful key combination of windons but this applications adds screenshots and it remembers the previous application for fast switching. It also lets you not only select through the key combination but holding the keys it lets you select with your mouse.

Yod'm 3d : the most recent addition. Virtual desktops are old news but the cube view made it more attractive and understandable. The key combination is alt+control+arrow key.

How i make it work for me


Hiding the taskbar is the first thing when i set up windows. It only is a distraction and with taskswitchxp you don't need to click on the applications to show them.

Setting up the system is easy. Because most of my time i use the browser it goes on the first virtual desktop together with my email application, this is my internet desktop. Using yod'm 3d keys(left arrow) i go to the second desktop to open explorer, this is my file desktop. using the right arrow yod keys twice i set up my development desktop. The ide application i need and other tools.

Now everything is set up i can go to work. I open each application full screen so other applications don't distract me. Because of this setup i only have a few applications to screen through to find the right one.

Improvements


I open my browser and my email client from startup. I could open explorer too but i don't know how to set it default to the desktop on the left.

Because yod'm 3d doesn't allow to scroll vertical through the desktops i'm limited to three quick accessible desktops instead of five.

Not every application is accessible through launchy but using bat shortcuts it's possible.

These key combinations can be changed and desktops can be adjusted to your own needs. The speed improvement lays in the quick access to short lists of applications.

Friday, April 06, 2007

The imagebutton

This week i needed a table with buttons to manipulate the individual rows. My first instinct was to use button tags so i could use their value instead of a hidden input but that was without thinking about internet explorer so i used the imagebutton.

The problem in internet explorer is that the value is always the text of the button. the image button is just an input tag with image as type. To make the button unique just add a number after the name.

for($i=0;$i<10;$i++){
?><p><input type="image" name="button<?php echo $i; ?>" src="image.jpg" value="button"></p><?php
}

Now there are ten unique buttons. The following step is to get the button from the post array. In the example we have a limited, static number of buttons but if it's a databasetable you need to connect the buttons with and unique field in the database so there can't occur any mistakes.
Because $_POST['button0'] will only return the button text in internet explorer so you need to use the mouse click coordinates of the button that get send. The coordinates are button0_x and button0_y.

for($i=0;$i<10;$i++){
if(strlen(array_search('button'.$i.'_x',array_keys($_POST))) > 0){
// do something
}
}

Now that you know click coordinates are send you can use them to make one button act as two.

$click = $_POST['test_x'];
if($click > 24){
$test = 'Delete button';
}else{
$test = 'Update button';
}

The previous code is the code i used on the example page.

conclusion


The image button can spare you an hidden input and act as two buttons.

Saturday, March 17, 2007

Asp.net : the datagrid

The datagrid controle is a powerful datacontrol but because of the amount of code to get it to work the way you want, it's a hard thing to crack for beginners. All code from this article is used in the example.

The html code



<asp:DataGrid ID="datagrid" runat="server" AutoGenerateColumns="false" ShowFooter="true"

The tag is named asp:DataGrid and like all controls it needs an id and a runat attribute.
The autogeneratecolumns attribute is needed when you want more control over the output of the data, this will be the case most of the time.
The showfooter attribute is selfexplaining and i added it because i want a row to insert new data.

OnEditCommand="datagridEdit" OnCancelCommand="datagridCancel" OnUpdateCommand="datagridUpdate" OnItemDataBound="datagridBound" OnItemCommand="datagridVarCommands" >

These are all hooks to functions for the different events.

Now we are going to style the datagrid. You can do it inside the datagrid tag but for better readability you have different style tags.

<HeaderStyle CssClass="th" />
<AlternatingItemStyle CssClass="even" />
<SelectedItemStyle CssClass="editrow" />

All style attributes can be use inside these tags but they don't have the same names as the standard html tags. There is also a FooterStyle tag.

The next thing to do is to set up the columns.

<Columns>
<asp:BoundColumn HeaderText="Id" DataField="id" Visible="false" ReadOnly="true"></asp:BoundColumn>

<asp:TemplateColumn HeaderText="Title">
<ItemTemplate><asp:Literal ID="title" runat="server"></asp:Literal></ItemTemplate>
<EditItemTemplate><asp:TextBox ID="editTitle" runat="server"></asp:TextBox></EditItemTemplate>
<FooterTemplate><asp:TextBox ID="addTitle" runat="server">></asp:TextBox></FooterTemplate>
</asp:TemplateColumn>

All columns must be inside the Columns tag.
The first column is of the type BoundColumn. This type has the least flexibility but is good for important processing data like an id or if the data can be displayed without manipulation. If you want to get the data from the column it's best to set the readonly attribute true so you can get it in every event as a string. If you set the visible attribute false the column will not be visible on the page but you can use the data that is stored in the column.

The second column is of the TemplateColumn type. Inside the template you have several tags to control the content and/or display of the column. I used ItemTemplate, that is manditory, EditItemTemplate and FooterTemplate. I repeated this for the other columns.
It is allowed to put input fields in every templatetag.

Now that we have the content of our data grid we need to add the buttons to manipulate it.

<asp:EditCommandColumn HeaderText="" EditText="Edit Info" UpdateText="Update" CancelText="Cancel"></asp:EditCommandColumn>

<asp:TemplateColumn HeaderText="">
<ItemTemplate><asp:LinkButton CommandName="Delete" Text="Delete" ID="btnDel" Runat="server" /></ItemTemplate>
<FooterTemplate><asp:LinkButton CommandName="Insert" Text="Toevoegen" ID="btnAdd" Runat="server" /></FooterTemplate>
</asp:TemplateColumn>

There are two special types of command columns. The EditCommandColumn generates three buttons for you:

- the edit button is used to change the row from regular view to edit view.
- the cancel button is used to change the row from edit view to regular view
- the update button is used to commit the changes in the edit view and return to the regular view if needed

These actions aren't programmed by default that is why you need to put the function hooks in the DataGrid tag.

The second command column is the ButtonColumn. It gives you a column with buttons who are bound to a function. It's less code but i need a column where i have delete buttons for the existing rows and an add button for inserting a row. That is why i added another TemplateColumn.

That was it for the html side of the control. If you have a long datagrid you can set the MaintainScrollPositionOnPostback true if you have the 2.0 framework or higher. If you use the 1.x framework you can use the 4GuysFromRolla solution, it worked for me.

C# code


The most important function in the code behind page is Page_Load. Here you call everything you need to display the page. Mine looks like this.

void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Data();
}
}

Data is the function that binds the database to the datagrid control. And because the data can be different after a button click it only binds the data when there is no button click. If you don't do this your data will never change because the function is called before all the others.

public void Data()
{
try
{
conn.Open();
string sql = "select id, title,description,online from Content order by title";
SqlCommand com = new SqlCommand(sql, conn);
SqlDataAdapter myDA = new SqlDataAdapter();
myDA.SelectCommand = com;

DataSet myDS = new DataSet();
myDA.Fill(myDS);

datagrid.DataSource = myDS;
datagrid.DataBind();

}
catch (SqlException ex)
{
message.Text = ex.Message;
}
catch (Exception ex)
{
message.Text = ex.Message;
}
finally
{
conn.Close();
}
}

With the datagrid.DataSource variable you transfer the database query to the datagrid and datagrid.DataBind() does exactly what it says.

Now is you data connected with the datagrid but if you would run the code now you would see that only the BoundColumn has values. The template columns need some more attention.

public void datagridBound(Object sender, DataGridItemEventArgs e)
{
// get bound data row
DataRowView row = (DataRowView)e.Item.DataItem;

switch(e.Item.ItemType)
{
// @@@@ EDIT TEMPLATE
case ListItemType.EditItem:
((TextBox)e.Item.FindControl("editTitle")).Text = row["title"].ToString();
((TextBox)e.Item.FindControl("editContent")).Text = row["description"].ToString();
if ("1" == row["online"].ToString()) { ((CheckBox)e.Item.FindControl("editOnline")).Checked = true; }
break;
// @@@@ REGULAR TEMPLATES
case ListItemType.AlternatingItem:
case ListItemType.Item:
((Literal)e.Item.FindControl("title")).Text = row["title"].ToString();
((Literal)e.Item.FindControl("content")).Text = row["description"].ToString();
string online = "No";
if("1" == row["online"].ToString()){ online = "Yes"; }
((Literal)e.Item.FindControl("online")).Text = online;
break;
}

}

The datagridbound function takes care of the data for the regular templates and for the edit template. This way you have all the bound data manipulations in one function.
There is another way to bind the data in the templatetags.

<ItemTemplate><%# DataBinder.Eval(Container, "DataItem.id")%></ItemTemplate>

To manipulate this data you need to create a function, if you need to do this for several columns it can add a lot of code.

Next we have the event functions

public void datagridEdit(Object sender, DataGridCommandEventArgs e)
{
datagrid.EditItemIndex = (int)e.Item.ItemIndex;
Data();
}

public void datagridCancel(Object sender, DataGridCommandEventArgs e)
{
datagrid.EditItemIndex = -1;
Data();
}

These functions are for the edit and cancel button from the EditCommanndColumn.

public void datagridUpdate(Object sender, DataGridCommandEventArgs e)
{
message.Text = "<p>Updated row:</p><p>Id : " + e.Item.Cells[0].Text + "</p>";
message.Text += "<p>Title : " + ((TextBox)e.Item.FindControl("editTitle")).Text+"</p>";
message.Text += "<p>Content : " + ((TextBox)e.Item.FindControl("editContent")).Text + "</p>";
string online = "No";
if (((CheckBox)e.Item.FindControl("editOnline")).Checked) { online = "Yes"; }
message.Text += "<p>Online : " + online + "</p>";
Data();
}

This function supports the update button. Normally you will put your database code to update the databasefields.

public void datagridVarCommands(Object sender, DataGridCommandEventArgs e)
{
switch(e.CommandName)
{
// !!!! DELETE
case "Delete":
message.Text = "<p>Delete row</p><p>Id : " + e.Item.Cells[0].Text + "</p>";
break;
// !!!! ADD
case "Insert":
message.Text = "<p>Insert row:</p>";
message.Text += "<p>Title : " + ((TextBox)e.Item.FindControl("addTitle")).Text + "</p>";
message.Text += "<p>Content : " + ((TextBox)e.Item.FindControl("addContent")).Text + "</p>";
string online = "No";
if (((CheckBox)e.Item.FindControl("addOnline")).Checked) { online = "Yes"; }
message.Text += "<p>Online : " + online + "</p>";
break;
}
}

This last function is used to handle the delete and add buttons.

Conclusion


There is a fair amount of code necessary to come to a working datagrid but there are also parts where the datagrid control does the legwork. You can do a lot more with the datagrid control but the code in this article will come back most of the times.

The full code can be dowloaded.

Friday, March 16, 2007

Menu with javascript elements

There are a lot of javascript menu enchanters but the one thing seems to be missing the ability to degrade the menu.

I made an example that generates a part of the menu in javascript and a part that is accessible all the time. I wanted to have a different design for the non javascript menu.

The code can be shorter but i wrote it out so it's easier to understand.

Saturday, March 03, 2007

Google services going down?

In one day i got accused by google search and blogger of being a robot. Now i know i like technology but there aren't any computerized parts in my body, yet.

At work i was looking for code snippets and suddenly i got a page telling me my computer was infected and i had to fill in the captcha and then i could continue.

When i came home i wanted to write a poem on my other blog but it was flagged as a spam blog. I had to verify again that i was a human but to make it harder the captcha kept disappearing in firefox. I had to do the verification with internet explorer.

I read in the feeds people were having trouble with gmail as well. My gmail is a secondary email so i don't have a problem if it breaks down but people who depend on gmail as their master mail center must have had a hard time.

I think it's a conspiracy to promote the image captcha. People are coming up with other ways to protect their sites against bots and google can't have that. Then they can't do anything with the ocr software they bought or developed, i'm not sure. Tricks to outsmart the king of bots is a big no-no.

Anyway i'm declared human again by google search and blogger, sci-fi has become reality, now i can implant many rfid tags and a bioport for games.

Saturday, February 24, 2007

Lay-out html

i had to do a cross browser design and i adapted some new techniques and learned some new things.

The html code


The 'new' thing on the block for css layout are the conditional statements. They are comments that acts like if statements. Browsers don't render css the same and when you use these statements you can keep your css files clean of hacks. Much of the rendering quirks have to do with positioning the elements so you can create a general css file for the cross-browser styles. Because some of the rendering is different in IE6 and IE7 you can create a general IE file. So lets look at the code for it now, the css examples will follow later.

<link rel="stylesheet" type="text/css" href="css/default.all.css">
<!--[if IE]> <link href="css/default.ie.css" rel="stylesheet" type="text/css"> <![endif]-->
<!--[if lte IE 7]> <link href="css/default.ie7.css" rel="stylesheet" type="text/css"> <![endif]-->
<!--[if lte IE 6]> <link href="css/default.ie6.css" rel="stylesheet" type="text/css"> <![endif]-->
<!--[if !IE]>--> <link href="css/default.non.ie.css" rel="stylesheet" type="text/css"> <!--<![endif]-->

The conditional comments can be hidden, like the IE stylesheets, or visible like the non iE stylesheets.

The css code


Another thing i discovered is the peek-a-boo bug when there are floating divs inside another div. If you want to display a border for the container div the border disappears and appears magically if you scroll the page. Of course this only happens with IE.

There are two solutions. One is to set the position to relative but i experienced that if there is a hover-to-showmenu that works with absolute positions the shown items appear to be under the relative positioned element.
Here is where solution 2 kicks in. With the IE only zoom style you can get rid of the peek-a-boo bug without breaking javacripted behaviour if you set it to 1.

It's a bit of common knowledge but maybe someone can benefit from this.

Saturday, January 27, 2007

From php to c#and asp.net

I learned asp.net for a few months three years ago and now i have to do a website for my work so i had to get acquainted again with the programming logic and c# syntax. I use c# because i can't say goodbye to the curly brackets blocks, Maybe python can make me abandon them but not VB.net.

The basics


The first thing i needed to know is how to include files because in php the master-content pages is set up by including files. Because the server only has the .net 1.1 framework i couldn't work with the master-content pages. This is where i first noticed a big difference between php and asp.net/c#. Where php is an embedded programming language, asp.net is the templating language for c#. You can go the php way and embed the c# code into your html code but if you use the asp.net controls and ids for other html elements you can keep your html clean enough so that a designer can understand what you are doing. All the html ids maybe a torn in the eye of css architects and javascript coders but it's workable.

But now back to the include files syntax. It's a weird html comment but it's easier than in php

<!--# file="c:\httpdocs\site\\test.aspx" -->

<!--# virtual="/site/testpage" -->

The first example is the counterpart of the include function from php even with the same double quote behaviour. The second example is nicer because it's relative to the root directory. In php i had to provide that myself by using a constant.

The next thing was how to get the php $_POST and $_GET globals. That was easy enough Request.Form["test"] gets you the posted form values, Request.QueryString["test"] gets you the url parameters. The Request object is like the $_SERVER global in php but the $_GET and $_POST are a part of it too which is cleaner than in php.

Now i got the basics i can make things work, i thought. c# is a strong typed language so you have to know which sort of content your variable will hold. You can guess Request.QueryString["test"] is a string but to compare it with a numeric variable you need to convert it.

int test = Convert.ToInt32(Request.QueryString["test"]);


The database


Now for the database connection. In php code there is morethan one way to do this but most of the database manipulation code now is object oriented and so is the c# code. The difference in c# is you have to use parametrized queries.

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.Odbc" %>
<script language="c#" runat="server">
// variables
int number = 1;
string query = "SELECT name FROM testtable WHERE id = ?";
// prepare connection
OdbcConnection conn = new OdbcConnection("Driver={Mysql Odbc 3.15 Driver}; Server=localhost; Database=test; User=user; Password=password; Option=3;");
// prepare database connection
OdbcCommand com = new OdbcCommand(query, conn);
// add value to query string
com.Parameters.Add("",OdbcType.BigInt, 20).Value = number;
// start error handling
try
{
// open connection
conn.Open();
// get database result
OdbcDataReader reader = com.ExecuteReader();
// process database result
while(reader.Read())
{
textboxName.Text = reader.GetString(0);
}
// because it's a single result it can be shorter
textboxName2.Text = com.ExecuteScalar();
reader.Close();
}
// database error
catch (OdbcException ex)
{
labelMessage.Text = ex.Message;
}
// general errors
catch (Exception ex)
{
labelMessage.Text = ex.Message;
}
finally
{
conn.Close();
}
</script>
<html>
<head>
<title>test</title>
</head>
<body>
<p><asp:Label id="labelMessage" runat="server" /></p>
<p><input type="text" id="textboxName" runat="server" /></p>
<p><input type="text" id="textboxName2" runat="server" /></p>
</body>
</html>

To summarize the previous code.

  • Define classes needed for database manipulation

  • C# code

    • Prepare database manipulation

    • Display database result



  • Html/asp.net code



I will give you some rest now and i hope i helped other php programmers to take their first steps in asp.net/c#.

Sunday, January 14, 2007

New installation, different taskbar

I bought a new laptop and as a windows power user i adjust many things to make my experience easier. I found a few new things, for me, this time.

I never cared too much about all the toolbars in the taskbar except for the quicklaunch one. As an experiment i added the address toolbar. I found out it was good for launching websites, even with firefox as default browser, and for opening directories/folders without the trail of double clicks to get to the folder. As a bonus the address field remembers previous inputs.

I also tried if cmd would run and it did so i searched for a way to start other programs from the address bar. The solution i come up with is simple and expandable.

I created a batch file, a text file with the extension .bat, with only one line.

START c:\start\%1

The start command lets you run the program. The folder is one i made to put all my program shortcuts in. %1 is the first parameter of the batch file. I saved the file as start.bat in the c:\windows\system32 folder.
In the start folder i made a shortcut to 7-zip with that name so i can type in the address field 'start 7-zip' and the program starts. The only thing you have to do is to add shortcuts with the name you prefer to start other programs.

I like visual aids to search the window i'm looking for. In firefox i use the ctrl tab preview add-on and for programs i use the Microsoft Alt-Tab Replacement powertoy. I had downloaded the Visual Task Tips program but i hadn't installed it before. This shows a thumbnail screenshot of the program if you hover over the program in the taskbar. Because i'm using the address toolbar i put the taskbar on top which made the visual task tips more visible too. Now together with the Taskbar Shuffle program the taskbar has become more than a collection of clickable things.

Update : i changed from alt-tab replacement powertoy to TaskSwitchXP because the screenshots are bigger and it selects windows that aren't in the taskbar too.

Monday, January 01, 2007

Beginning python : function

Functions are another basic part of any programming language, without it you have to rewrite/copy all your code. And knowing how bad programmers are at copying it's not a good idea. And again python beats php for this part. functions are called definitions in python.

Building functions



function name($arg1,$arg2='optional',$arg3='optional'){
echo $arg1.' '.$arg2.' '.$arg3;
}

A simple function with 2 optional arguments that shows the values of the arguments. Now the same function in python.

def name(arg1,arg2='optional',arg3='optional'):
    print arg1, arg2, arg3


The differences in syntax are:

  • def instead of function

  • open function with a : character

  • indentation of codeblocks is necessary

  • the , character instead of the . character

  • close function with a white line


You also notice that you don't have to use a string with a space to separate the values of the arguments because the print function processes the white space in the code.

Using functions



name(1);
name(1,2,3);
name(1,'optional',3);

Above you can see a few function calls for the php function. The last call shows a pain in php. You have to know the default value of an argument to access an argument later in the list. To break this behaviour you can put your optional arguments in an associative array and sort them out in the function.

function name($arg1,$array){
echo $arg1;
$arg2 = ' optional';
$arg3 = ' optional';
if(count($array) == 0){
echo $arg2.$arg3;
}else{
foreach($array as $key => $value){
if($value != ''){
switch($key){
case 'arg2': echo $arg2; break;
case 'arg3': echo $arg3; break;
}
}
}
}
}

As you can see this extends the function code quite a lot and in this example you can only use it if the optional arguments don't depend on each other. Now lets look at some python magic.

name(1)
name(1,2,3)
name(1,arg3=3)

It are the same function calls and as you can see the last one sets the argument using a keyword/value pair. You can call all arguments like that but once you start setting arguments by keyword/value pairs you can't use the value only setting any more.

name(1,arg2=2,3)

This example will cause an error. Wrong keywords also raise an error.

The setting function arguments by using key/value pairs is a real programming relief.

First of all you don't have the remember the default values of optional arguments when you want to set an argument later in the list, you don't even have to add it to your function call.

Second reason : you don't have to remember the place of arguments in the list. Remembering the argument names is easy if you use consistent argument names, remembering where you put the arguments in the list is harder if you made the function six months ago and haven't used it a lot.

Third reason : you can use an associate array (dictionary in python speech) or even an array (list in pythonese) to set the arguments.
A dictionary is build by a key/value pair(s) surrounded by curly brackets.

listtest = [1,2]
name(*listtest)
dicttest = {'arg1': 1, 'arg2': 2}
name(**dicttest)

You notice the use of the single and double asterisk to identify the datatype to unpack it in the function.
This is great because you don't have put different function calls in if statements if the arguments for the function call are different. You can build different argument dictionaries or lists and call the function in the end. This makes the code more readable.