boogdesign posts

Longer posts on standards based web design, portable web development and Linux, intermingled with some stuff on my other nerd interests.

Rob Crowther, London, UK based Blogger, Web Developer, Web Designer and System Administrator - read my Curriculum Vitae

Buy my book!

Book CoverHello! HTML5 and CSS3 available now

Buy my other book!

Book CoverEarly access to HTML5 in Action available now

Category: Blogging and Internet Culture

13/09/13

02:30:00 am Permalink Tech for Good Meetup - September 2013

Categories: Blogging and Internet Culture, Society and Politics

On Monday I attended the first Tech for Good Meetup, a showcase for startups aiming to solve social and environmental problems. The format was a series of lightening talks, an introduction from the sponsors followed by short presentations from five startups. This post comtains my notes and links.

A picture from the back of the room at Google Campus London where the Tech for Good presentations were about to take place

Bethnal Green Ventures - Lily

The organisor/sponsor, Bethnal Green Ventures is a tech accelerator (like Y Combinator) for ?social good' startups. The idea is they give seed capital to a number of startups, along with some office space and a whole load of support and mentoring. Follow them at
@bg_ventures.

Echo - Matt

Echo stands for "EConomy of HOurs", the site is a broker for trading an hour of your work (an 'echo') for an hour of work from other people. They hope to create "a nationwide currency of echos". All hours are created equal, which I'm sure simplifies things but doesn't seem to me to offer much incentive to offer hours of work which require a lot of training (eg. legal or medical advice). Follow them at @economyofhours.

Winnow Solutions - Marc

1/3 of food is wasted globally per annum, this is equivalent to 300 billion dollars. Kitchens are very inefficient and waste 20% of food used. Winnow have developed a food waste 'smart meter' which allows easy logging and classification of food waste, leading to metrics and better awareness. The device saved 12k pa at its first trial site.
Follow them at @WinnowSolutions.

OpenSensors.io - Yodit

An aggregator for the internet of things, OpenSensors allows you to collect data from any device and easily perform data mashups combining both open and private data.
Follow them at @OpenSensorsIO.

SpeakSet - Ewan

The problem SpeakSet has set out to solve is that 1 million older people haven't spoken to friends or family in a month. This is not just a problem of lonliness, it is also a problem for healthcare delivery where there has been no innovation since Florence Nightingale. Average life expectancy goes up by 5 hours every day so this problem is only going to get worse. SpeakSet packages up a Skype style video calling service into a remote controlled TV interface. The grandchildren can use their existing smartphones to receive calls. Call your Grandparents tonight!
Follow them at @speaksetUK.

Flip Yourself - Bruno

A visual LinkedIn for kids. Young people don't know how to write CVs or evidence their achievements, they're not good at selling themselves. Flip Yourself allows them to collect evidence easily, using tools familiar to them from social media.
Follow them at @flipurself.


Tweet this!
Send feedback »PermalinkPermalink

23/05/13

02:09:00 am Permalink OpenTech 2013

Categories: Web Design, Web Develop, Blogging and Internet Culture, Society and Politics

It's been a while since I've written a proper blog post but now that my 'professional' writing obligations are mostly out of the way I've been meaning to return to my amateur ramblings. As I attended OpenTech 2013 at the weekend I thought this was a great opportunity to get back into the swing of things. I've been to a few of these events now and they're always well organised and thought provoking, this year was no exception. Each of the six timetable slots through the day had three rooms with sessions and each of those sessions had 2 or (usually) 3 talks. So obviously I was only able to be in one of those rooms at once and see only about one third of the speakers, but I've assembled all those speakers into my OpenTech 2013 twitter list and in this post I'll give my potted impressions of each talk.

Session 1 (Stream C)

  • Farmification - the joystick factory - Lisa Ma - When everyone is using touchscreen devices, what happens to all the people working in the mouse factories? Lisa has been creating programmes to encourage workers to do part time farming work so that they have something to fall back on when the hard times come. An interesting alternative to 20% time.
  • House on Github - Francis Irving - Francis is recording issues with his house on GitHub, he thinks that in the same way that he put his CV online in 1996 and now everyone's doing it, by 2023 everyone will be doing this too. The serious point of the talk was that to effect change in the behaviour of the general population, to cross the chasm, requires geeks to hack the market, not just the tech.
  • The Constitutional Excerpts Project - James Melton - a project to create semantically indexed and fully searchable database of the world's constitutions, because generally people that are writing constitutions are doing it for the first and only time in their lives and could do with some help.

Session 2 (Stream B)

  • The Children's Republic of Shoreditch - Lucy Macnab - a fascinating project run from the back of a fascinating shop, I urge you to check out the video.
  • Writers Centre Norwich - Chris Gribble - Chris wants 'creative people' and 'technical people' to get together and create fictional works featuring and about technology while still being 'literature'. However it was clear he'd never read any SciFi, which I think set him apart from most of his audience, and when directly asked why he didn't think 'technical people' couldn't also be 'creative people' his answer, even though he denied he thought that, described them as two separate communities who needed to be somehow united.
  • School of Data - Tony Hirst - a project to educate civil society organizations, journalists and citizens in the skills needed to analyse publically available data and 'find the story' through a combination of outreach, training and crowd sourcing.

Session 3 (Stream B)

Graeme Burnett talking about FPGA

  • Unix FPGA - Beyond just Finance - Graeme Burnett - using Field-programmable gate arrays in high speed trading platforms where 80GbE throughput will soon be a common requirement.
  • Raspberry Pi - Rob Bishop - An interesting talk on problems and possibilities from one of the Pi developers. Although four fifths of the audience owned a Pi only one fifth had actually built something with one.

Session 4 (Stream A)

KickBackStarter ushers in the politics of the future

  • Tiny Data - Richard Pope - A number of practical projects for easy data visualization, including the bicycle barometer.
  • Bribing MPs with Crowdsourcing *Satire* (probably) - Terence Eden - definitely the most amusing talk of the day, but inspired some discussion on the serious issue of how the general public can overcome the concentrated buying power of rich people with special interests.
  • The Domain Logic of Direct Action - Stephen Reid - Beautiful Trouble is a book and web toolbox that puts the accumulated wisdom of decades of creative protest into the hands of the next generation of change-makers.

Session 5 (Stream B)

  • Big Data for Real People - Chris Osborne - The 4 rules of data visualization are that data should be:
    • Personalized - relevant to the person viewing the data
    • Accessible - don't set a science exam because, unlike you, most people dislike science (and graphs)
    • Actionable - data is no use if you can't do anything as the result of seeing it
    • Instinctive - recognizes human behaviour and the environment the decision is being made in
    The full version of this talk was recorded for Big Data Week if you're interested.
  • Doing Good With (open) Data - Duncan Ross - As we enter the age of big data what will the legal and moral framework for using that data look like? The major pieces of legislation which govern big data are old (1995 for the EU Data Protection Directive, 1791 for the US Bill of Rights) and cannot hope to keep pace with the speed of change of the Internet. How will society ensure big business uses big data in a moral way when we're not event sure what that morality will be? DataKind UK brings together leading data scientists with high impact social organizations through a comprehensive, collaborative approach that leads to shared insights, greater understanding, and positive action through data in the service of humanity.
  • What do Open Sensor Networks mean for citizen science? - Dan McQuillan - There is so much data available nowadays that statistical methods are often used in place of traditional knowledge gathering, for instance drone strike targets are governed by a data mining algorithm rather than on the ground intelligence. But this huge pool of data can be used for good if we can stimulate people with technical and data mining skills to consider social issues.

Session 6 (Stream A)

  • The STEMettes - Stemettes - Providing events, support and strong role models for women in Science, Technology, Engineering and Maths. Role models are important: hack days delivered by women for women and girls produce better engagement than those delivered by men, similarly recruitment events delivered by a team including female members produce a far higher proportion of female applicants. Also women tend to be motivated by the outcomes rather than the opportunity to simply experiment.
  • FOSSbox - Paula Graham - it is understood that diverse teams produce better solutions, but only 3% of FLOSS contributions are from women. Fossbox actively support women contributing to open source projects.
  • Practical Diversity - Meri Williams - an excellent presentation with practical advice for fostering a more diverse work environment.

Tweet this!
Send feedback »PermalinkPermalink

03/06/11

02:51:00 pm Permalink Drag and Drop and Delegate

Categories: Front End Web Development, Blogging and Internet Culture

HTML5 Drag and Drop has been popular topic recently on web tech blogs, a good example is Alexis Goldstein's post on Sitepoint which I converted to work in IE. Fortunately for me this all coincided with spending a lot of time working on Drag and Drop for chapter 6 of my book and a project coming up at work where it was a natural fit. So I've been doing a lot of dragging and dropping in recent weeks, to the point where I started to feel like I knew as much about the practical implementation of the Drag and Drop API as any person living.

Of course, whenever I start getting a bit full of myself something soon happens to make me feel like an idiot again. In this case it's my continuing blind spot when it comes to DOM Event Delegation. If you look again at Alexis' example you'll note that all the event handlers are bound directly to the elements where the events are expected to happen. This is a common pattern in example code - it's a straightforward relationship for beginners to understand - but it's not really the best way to do it, and this post is going to explain why. Let's review the code for the drop event:

$('#todo, #inprogress, #done').bind('drop', function(event) {
    var notecard = event.originalEvent.dataTransfer.getData("text/plain");
    event.target.appendChild(document.getElementById(notecard));
    event.preventDefault();
});

What this code does is bind a drop handler to three separate elements specified by the IDs in the selector - #todo, #inprogress, #done. Now have a look at this screenshot of the original planning board in action:

A task embedded within another task

The task 'Learn HTML5' as been dropped directly inside the task 'Learn CSS3' and the 'Learn CSS3' task accepted the drop and ran the drop handler code despite not being mentioned above - why? The answer is because of event delegation. While in this particular case it may look like a bug, this is actually a very powerful and useful process. Here's what's happening when the 'Learn HTML5' task is dropped:

  1. The drop event is fired on the 'Learn CSS3' element
  2. Because there is no drop handler, the event is passed up the document tree< to the parent element - in this case #inprogress
  3. There is a drop handler on the #inprogress, so that handler is run

This process is called event bubbling - the events rise up through the DOM tree like bubbles through water, and the consequence of the bubbling here is that the event is fired on the 'Learn CSS3' element, which remains the target of the event, but it's processed by the event handler on the #inprogress element. Because the handler just says 'append the dragged element as a child of the event target' the 'Learn HTML5' element is added as the last child of 'Learn CSS3'.

Initially you might see this as a bug, but really it's an opportunity. As long as we deal with this unwanted side effect we're freed from having to attach event handlers to every single element. If this doesn't seem like a huge gain, consider these two scenarios:

  1. You want to scale up to handle have a large number of draggable items and drop targets, into the thousands
  2. You want to add and remove draggable items and drop targets dynamically

The first scenario was the one I found myself in with my work project - the drop targets were cells in a table, with over fifty columns and possibly hundreds of rows, and 30-50% of those cells had draggable items in. When I started with the naive approach of binding a handler to each item it took a second or two to run the initialisation code in the good browsers (in IE8 it took nearly 30 seconds...). Event delegation helps in this scenario because the number of events you have to bind is equal to the number of events you want to capture, not the number of elements you want to capture them on.

It's unlikely an agile planning board is going to end up with that many elements though - if it does I suspect there's a good chance you're not really being agile, but the second scenario is more likely to come up. The tasks and the available resources will likely change from sprint to sprint, you may also want to add in some extra statuses. With the current code, every time you want to add an element you will have to attach all the relevant event handlers to it (even if jQuery makes that easy for you, that could still be a lot of overhead). If you forget to do that in a particular code path then strange bugs will likely ensue, which is a shame when all you really want is for the new elements to execute the exact same handlers as all the existing ones. Let's look at how easy this all becomes if you don't try to bind event handlers to every single element.

First, some adjustments to the markup. Because there'll now (potentially) be multiple rows on the board we can't use id attributes to distinguish them so they'll have to be a class. We'll also add a 'row title' for the developer name, and

<div id="board">
    <div id="rob">
        <h1 class="title">Rob</h1>
        <div class="todo droptarget">
            <h2 class="title">To Do</h1>
            <a id="item1" draggable="true" href="#">
                <div class="cardTitle">
                    Learn HTML5
                </div>
            </a>
            <a id="item2" draggable="true" href="#">
                <div class="cardTitle">
                    Learn CSS3
                </div>
            </a>
        </div>
        <div class="inprogress droptarget">
            <h2 class="title">In Progress</h1>
        </div>
        <div class="done droptarget">
            <h2 class="title">Done</h1>
        </div>
    </div>
</div>

I've also added a class droptarget to make life easier in the event handlers. Those event handlers will, of course, have to change. Here's a before and after of the dragstart handler:

Before

$('#item1, #item2').bind('dragstart',
  function(event) {
    event.originalEvent.dataTransfer.setData(
        "Text", 
        event.target.getAttribute('id')
    );
});

After

$('#board').bind('dragstart',
  function(event) {
    event.originalEvent.dataTransfer.setData(
        "Text", 
        $(event.target).closest('a[id]').attr('id')
    );
});

The first, and most important, change is that the event is now attached to the #board element instead of to each individual draggable item. The second change is instead of assuming the event target is the planning item the code now looks for the closest parent element which matches what the draggable element is expected to look like. The next event to consider is dragover, here things are slightly more complicated:

Before

$('#todo, #inprogress, #done').bind('dragover', 
    function(event) {
        event.preventDefault();
});

After

$('#board').bind('dragover', function(event) {
    if ($(event.target)
          .closest('.droptarget')
          .hasClass('droptarget')) {
	      event.preventDefault();
    }
});

Now that the handler is being attached to #board every element will trigger the dragoverdroptarget class added earlier. Finally let's consider the drop event:

Before

$('#todo, #inprogress, #done')
  .bind('drop', 
    function(event) {
        var notecard = event
                .originalEvent
                .dataTransfer.getData("Text");
        event.target.appendChild(
            document.getElementById(notecard)
        );
        event.preventDefault();
  });

After

$('#board').bind('drop', function(event) {
    var notecard = event
            .originalEvent
            .dataTransfer.getData("Text");
    var drop = $(event.target).closest('.droptarget');
    drop.append($('#' + notecard));
    event.preventDefault();
});

Although every element which is a descendant of #board could potentially trigger this event, in practice it's only ever going to be the ones that triggered the event.preventDefault() on the dragover event - anything with class .droptarget or it's descendants. Instead of just assuming the target is where the dropped element has to be attached the closest ancestor with the right class is used, so we never end up with dropped tasks getting appended inside other tasks.

I think you'll agree it isn't really much more difficult to write the event handlers in such a way that they take advantage of event bubbling and so only need to be attached to a single element. Now that these changes have been made it's possible to add further developers, statuses or tasks just by generating and appending the appropriate HTML - no need to loop through the new elements and attach all the correct event handlers.

This example page adds several buttons for dynamically updating the planning board, just to show how easy it is. The code is also available on github.


Tweet this!
Send feedback »PermalinkPermalink

20/04/11

01:16:00 am Permalink IE10 and the Future of CSS Layout

Categories: Front End Web Development, Standards, HTML and CSS, Blogging and Internet Culture

Last week the first developer preview of IE10 was released. Among several experimental features included were the first Microsoft implementations of CSS3 Flexible Box Layout Module and CSS3 Grid Alignment. These are possibly the most exciting things to be added to CSS since drop shadows...

First up the flexible box layout module, or flexboxes. This has already been implemented in Firefox and WebKit, but that version of flexboxes isn't very intuitive, and the draft has since seen a lot of updates. Interestingly, IE10PR1 implements the same version of the spec as Firefox and WebKit but with one important addition: multi-line flexboxes. For me the multi-line properties are what make flexboxes worthwhile for layout, otherwise nearly everything you might currently want to achieve can be done just as easily (and with better backwards compatibility) with display: table-cell.

This is the sort of thing that multi-line flexboxes are useful for, it's a layout typical of shopping and photo gallery sites:

A multiline flexbox layout at 800 pixels width

Here's what my markup looks like, nothing more complex than a list with sixty items in it:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    ...
    <li>59</li>
    <li>60</li>
</ul>

And here's the CSS, the key things to look out for are the display: -ms-box and the -ms-box-lines:

body {
    width: 90%;
    margin: 0 5%;
}
ul {
    display: -ms-box;
    -ms-box-lines: multiple;
    list-style: none;
    width: auto;
    padding: 10px;
    border: 4px dashed #000;
}
li {
    display: block;
    -ms-box-flex: 1;
    padding: 1em;
    margin: 0.5em;
    min-width: 3em;
    border: 4px dashed #000;
}

You may be thinking that's not a hard thing to pull off, so let me show you the same page, in an 800 pixel wide browser above, at 640 and 480 pixels:

A multiline flexbox layout at 640 pixels width

A multiline flexbox layout at 480 pixels width

The number of cells across adjusts to match the width available, but, because these are flexboxes, the width of the elements themselves also adjust so that they always exactly fit the available width. This is unique among our current alternatives:

  • If you were using floats or inline blocks then you'd have to set each element to a fixed width, meaning the container would have to be a fixed width so that the elements could fill it exactly. You'd have to use media queries to assign different fixed widths to the container to change the items per row according to screen resolution.
  • If you were using a layout table or display: table-cell then, although the elements would expand to fit exactly, the number of items per row would be fixed by the markup.

However, nothing is perfect. My multi-line flexbox example has a total of 60 elements, and I picked this number because it is an exact multiple of 2, 3, 4, 5 and 6 (it's the expansion of them if I've got my maths terms right&amp;#58;&amp;#63;&amp;#58;) - meaning that the number of elements will fill the grid whether there's 2, 3, 4, 5 or 6 elements per row. If there's a less perfect number, say 24, then the grid will look a little strange on the last row:

A multiline flexbox layout at 640 pixels width with only 24 elements

I think the box-align property should help with this, but I couldn't make it work in IE10PR1.

So, multi-line flexboxes could be a useful addition to the CSS toolbox when everyone gets round to upgrading to IE10, but, cool as they are, that's not the coolest experimental CSS layout implementation in IE10PR1. There have been a few grid and template based CSS proposals over the years but, apart from an incomplete JavaScript library for CSS Template Layout, none of them have ever been implemented in a major desktop browser. That is until last week, when IE10PR1 implemented Microsoft's own CSS Grid Align proposal from October 2010.

I want you to reflect, now, on all the fun you've had over the last ten years constructing CSS layouts of three equal height columns, with a header and footer, and making them work in IE6. Layouts something like this:

A three column layout with header and footer

Are you remembering all the fun you had? Good, now look at this markup and contemplate how you'd turn it into a three column layout:

<header>Header</header>
<aside class="b">Side bar</aside>
<article>I never am really satisfied...</article>
<aside class="d">Side bar</aside>
<footer>Footer</footer>

Now look at this CSS and start wishing the future would arrive soon:

body {
    width: 90%;
    margin: 0 5%;
    display: -ms-grid;
    -ms-grid-columns: auto minmax(min-content, 1fr) auto;
    -ms-grid-rows: auto minmax(min-content, 1fr) auto;
}
article, aside, header, footer {
    margin: 1em;
    padding: 1em;
    outline: 4px dashed black;
}
header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }
aside.b { -ms-grid-column: 1; -ms-grid-row: 2; }
article { -ms-grid-column: 2; -ms-grid-row: 2; }
aside.d { -ms-grid-column: 3; -ms-grid-row: 2; }
footer { -ms-grid-column: 1; -ms-grid-row: 3; -ms-grid-column-span: 3; }

That's it, seven declarations is all you need for that three column layout with CSS Grid Align. For this first example I'm going to step through the key points line by line:

display: -ms-grid;

This is the bit which declares we will use the grid layout manager on the children of this element, in the same way that the flexbox layout manager was declared above with display: -ms-box.

-ms-grid-columns: auto minmax(min-content, 1fr) auto;

This line defines three columns, the first and last will shrink to fit the content - that's the default behaviour, similar to a table. The middle column will be a minimum of min-content, which is basically the same as auto, and a maximum of 1fr which is a 'fraction of available space' - quite similar to a flex unit and, since there's only one fractional column, basically equivalent to all the available space.

-ms-grid-rows: auto minmax(min-content, 1fr) auto;

We'll have three rows to go along with our three columns, I won't go over the individual values again. Now the fun stuff:

header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }

Put the header in the first column of the first row, and make it span three columns. That's it. Really, it's that simple. Let's just do one more:

article { -ms-grid-column: 2; -ms-grid-row: 2; }

Put the article in the second column and the second row. And make me some coffee, white, no sugar. Actually there isn't a CSS property for that yet, maybe CSS4...

But it gets more cool than that. In the example above the order of the elements in the markup matched up with the order they were placed into the grid, but it doesn't have to be that way:

A three column layout with header and footer, with elements placed in a different order

This amazing transformation was achieved without any changes in markup, just messing around with CSS:

header { -ms-grid-column: 1; -ms-grid-row: 2; }
aside.b { -ms-grid-column: 2; -ms-grid-row: 2; }
article { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }
aside.d { -ms-grid-column: 1; -ms-grid-row: 3; -ms-grid-column-span: 3; }
footer { -ms-grid-column: 3; -ms-grid-row: 2; }

In the current draft there's a grid-cell-stacking property which will let you flow multiple elements into a single cell, but in the IE10PR1 implementation things just stack on top of each other:

Multiple elements in cells not working

This is a shame, because the markup is quite straightforward, I won't post all of it because this is already getting quite long (have a look), but here's the nice bit:

article:nth-child(2n+1) { -ms-grid-column: 1; }
article:nth-child(2n) { -ms-grid-column: 2; }

The article elements are assigned to grid cells alternately, unfortunately it doesn't work yet. However you can nest elements set to display: -ms-grid inside each other:

A three column layout with header and footer

Have a look at the source code for that one, it gets pretty hairy, so rather than try and do that sort of thing it's probably best to use some wrapper elements like this:

Grid Align with wrapper elements for cells

Again, view the source code yourself to see how it's put together, but I think these things will be worth re-visiting in a later preview release.

I'm going to finish off with some adaptive layout. Grid Align is a great fit for this because of the complete independence of layout from source order. Here's what I put together based off the previous example at 800 pixel width:

An adaptive Grid Align at 800px width

Now here's the same page at 640 and 480 pixel widths:

An adaptive Grid Align at 640px width

An adaptive Grid Align at 480px width

The markup is, of course, the same in each case:

<header>Header</header>
<div id="sidebar">
    <aside>Side bar 1</aside>
    <aside>Side bar 2</aside>
</div>
<div id="content1">
    <article>Content 1</article>
    <article>Content 3</article>
    <article>Content 5</article>
</div>
<div id="content2">
    <article>Content 2</article>
    <article>Content 4</article>
    <article>Content 6</article>
</div>
<footer>Footer</footer>

By default I've assumed a single column layout:

body {
    width: 90%;
    height: 90%;
    margin: 0 5%;
    display: -ms-grid;
    -ms-grid-rows: auto;
    -ms-grid-columns: 1fr;
}
article, aside, header, footer {
     margin: 1em;
     padding: 1em;
     outline: 4px dashed black;
}
header { -ms-grid-row: 1; }
#sidebar { -ms-grid-row: 3; }
#content1 { -ms-grid-row: 2; }
#content2 { -ms-grid-row: 4; }
footer {  -ms-grid-row: 5; }

Moving on to windows of a minimum width of 600 pixels, move up to two columns:

@media screen and (min-width: 600px) {
    body {
        -ms-grid-columns: auto 1fr;
        -ms-grid-rows: auto 1fr 1fr auto;
    }
    header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 2; }
    #sidebar { -ms-grid-column: 1; -ms-grid-row: 2; -ms-grid-rowspan: 2; }
    #content1 { -ms-grid-column: 2; -ms-grid-row: 2; }
    #content2 { -ms-grid-column: 2; -ms-grid-row: 3; }
    footer { -ms-grid-column: 1; -ms-grid-row: 4; -ms-grid-column-span: 2; }
}

This is nothing you haven't seen already, but I'll just point out again how cool it is that you can place the elements wherever you want them. Finally, for windows of greater than 760 pixels width, switch back to the three column layout:

@media screen and (min-width: 760px) {
    body {
        -ms-grid-columns: auto 1fr 1fr;
        -ms-grid-rows: auto 1fr auto;
    }
    header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }
    #sidebar { -ms-grid-column: 1; -ms-grid-row: 2; }
    #content1 { -ms-grid-column: 2; -ms-grid-row: 2; }
    #content2 { -ms-grid-column: 3; -ms-grid-row: 2; }
    footer { -ms-grid-column: 1; -ms-grid-row: 3; -ms-grid-column-span: 3; }
}

If you've downloaded the IE10 developer preview have a play round with it yourself, and try not to think about how long you'll have to wait until all this stuff is available in production browsers &amp;#58;&amp;#112;


Tweet this!
Send feedback »PermalinkPermalink

31/01/11

01:32:00 am Permalink Client Side Server Side Includes

Categories: Front End Web Development, Blogging and Internet Culture

I spent some time over Xmas reflecting on my past web adventures. My first ever website, a guide to local drinking establishments, was hosted on a cast-off server at Edinburgh University in late 1993, and the particular server and site are now long gone (although the server has been replaced). Similarly, my original mid-nineties home page, replete with animated GIFs and ripped off Homer Simpson images has also be consigned to the recycle bin of web history (thankfully). However, the first website I worked on 'professionally' is still online: iwant2bhealthy.com is a thousand page static HTML monolith which we maintained with Dreamweaver 3.

The site did take advantage of some server processing, it used Server Side Includes (SSI) to embed particular common items such as the main navigation and footer. SSI isn't so common these days when nearly every cheap host offers some sort of server side scripting language, so often it isn't turned on my default. The result is that the iwant2bhealthy.com website is missing its main navigation and footer on most pages, all that's left is some markup like this:

<!--#include virtual="/Library/mainmenu.shtml" -->

Or this:

<!--#include virtual="/Library/footer.shtml" -->

I had a hankering to experience the website in all its turn of the century glory and it seemed to me that I ought to be able write a bookmarklet to grab jQuery, grab the comments, fetch the includes with AJAX and insert them in place of the comments.

It turns out that the first tricky thing is grabbing the comment elements. The jQuery selection engine purposely ignores comments so none of those handy little methods are much use. There's not much option but to loop through the document and select based on nodeType (8 for a comment element), fortunately someone on the web had already done most of the hard work so I was able to adapt his code:

parseSSI : function(el) {
    var nodes = el.childNodes;
    var l = nodes.length;
    while (l--) {
        current = nodes[l];
        if (current.nodeType == 8) {
            //do stuff here
        } else if (current.nodeType != 3 && current.childElementCount > 0) {
            this.parseSSI(current);
        }
    }
}

The function loops through all the child nodes of a supplied element and, if they're a comment, does some processing. If the node isn't a comment we check that it's also not a text node and then call the function recursively if there are any child nodes.

Of course it's entirely possible that there are comments in the web page that have nothing to do with server side includes, so some sort of check is probably in order. Following James' example I used a regular expression:

re : /#include virtual=\"(.*)\"/i

My next step was to convert those comment elements into something that could be more easily manipulated by jQuery, so I decided to convert them to links:

var match = this.re.exec(current.data);
if (match != null) {
    var a = document.createElement('a');
    a.href = match[1];
    current.parentNode.replaceChild(a, current);

Now we're back in the nice, succinct land of jQuery - fetch the URL with Ajax and replace the relevant link element in the callback:

$.ajax({ url: match[1],
         success: function(data, textStatus, XMLHttpRequest){
            $('[href=' + this.url + ']').replaceWith(data);
        }});

Try out the final code here.


Tweet this!
Send feedback »PermalinkPermalink

24/12/10

11:22:20 pm Permalink HTML5 Comment Forms in b2evolution

Categories: Web Develop, Blogging and Internet Culture

Following my post last week on HTML5 forms it occurred to me that I should be a little embarrassed devoting all this time to writing about HTML5 when my blog makes use of no HTML5 features. A quick hack around with the template corrected the DOCTYPE and I was looking for some other quick changes I could make without getting into to a full redesign (currently in process for about 5 months...). The comments form seemed an obvious target and turns out to be remarkably easy to do in b2evo.

The file you need to edit is _item_comment_form.inc.php, which will live inside your skins directory. If you have a custom skin set up you may also find inside the directory for that skin, these instructions assume you'll be editing the standard _item_comment_form.inc.php, whether in the skins directory or in a folder in that directory.

The parts we need to edit are on lines 142 and 143, the fields for email and website address:

$Form->text( 'i', $comment_author_email, 40, T_('Email'), '<br />'.T_('Your email address will <strong>not</strong> be revealed on this site.'), 100, 'bComment' );
$Form->text( 'o', $comment_author_url, 40, T_('Website'), '<br />'.T_('Your URL will be displayed.'), 100, 'bComment' );

The text method on the $Form object can take an optional sixth parameter which determines the input type. So to create HTML5 email and url inputs, simply add that sixth parameter:

$Form->text( 'i', $comment_author_email, 40, T_('Email'), '<br />'.T_('Your email address will <strong>not</strong> be revealed on this site.'), 100, 'bComment', 'email' );
$Form->text( 'o', $comment_author_url, 40, T_('Website'), '<br />'.T_('Your URL will be displayed.'), 100, 'bComment', 'url' );

Tweet this!
Send feedback »PermalinkPermalink

24/06/09

11:18:32 pm Permalink Fun with b2evolution plugins

Categories: Web Develop, Blogging and Internet Culture

As I've mentioned in my recent posts, this blog hasn't received a lot of love in the last two years or so. I'm slowly getting back on track but, following my upgrade to b2evo 2.x in February, I've been missing some of the features I used to have.

About two and half years ago I implemented social network sharing buttons on my skin. I didn't have the time to figure out how to make them work in my 2.x skin, but today I discovered BAE Social Bookmarks. The developer's website is sadly defunct but the plugin has been made available by someone who'd downloaded it before the site disappeared. I had a bit of trouble figuring out how it worked but I eventually worked it out from the code. Basically you insert a template for the link and insert $permanent_url$ and $title$ in an appropriate place:

<a href="http://del.icio.us/post" onclick="window.open('http://del.icio.us/post?v=4&noui&jump=close&url='+encodeURIComponent('$permanent_url$')+'&title='+encodeURIComponent('$title$'),'delicious', 'toolbar=no,width=700,height=400'); return false;" title="del.icio.us"><img src="/images/delicious.gif" border="0" height="16" width="16"></a>

<script type="text/javascript">document.write('  <a href="http://reddit.com/submit?url='+encodeURIComponent('$permanent_url$')+'&title='+encodeURIComponent('$title$')+'" target="blank" title="Reddit"><img src="/images/reddit.png" width="16" height="16" border="0" /></a>');</script>

After this success I turned my attention to my technorati tags. There are a couple of different versions of this, Technorati Plugin and Rich Tags Plugin, though I couldn't find a download for the former and the latter seemed to be for 1.9 and earlier versions. Also the 0.3 version Rich Tags plugin was missing the 'Technorati' button on the edit post screen which the 0.2 version had. Looking at the code, the AdminDisplayEditorButton function was still defined in /inc/plugins/_plugin.class.php so it seemed like all I had to do was simply copy that bit across:

function AdminDisplayEditorButton( & $params )
	{
		?><script language="JavaScript" type="text/javascript">
function WriteTag(){
		document.getElementById('itemform_post_content').value += "<!--tags MyTag My+Tag -->";
}
</script><input type="button" id="b2eRchTgs_button" value="Rich Tags" onclick="WriteTag()" /><?php
		return true;
	}

Tried it, and it worked &amp;#58;&amp;#41;

Intoxicated by this early success I decided to see what else I could do, so I read the documentation and also had another look at the BAE Social Bookmarks plugin. I thought it would be good to have options other than technorati for tagging - I use delicious a lot and there are some other good candidates. First thing I wanted was some configuration options, looking at the BAE code this didn't seem too difficult, but I didn't need a textarea like it uses and the one thing that did seem hard to find was documentation. The source code again comes to the rescue, there is some quite extensive documentation starting from line 361 in /inc/plugins/_plugin.class.php, so I figured out I wanted a checkbox which takes a value of 0 or 1:

   function GetDefaultSettings( & $params )
    {
      $r = array(
         'technorati_tags' => array(
               'label' => 'Enable Technorati Tags',
               'type' => 'checkbox',
               'defaultvalue' => 1,
                'note' => 'Inserts links to technorati tag pages, you should also add a CSS rule for either .tags (generic) or technorati_tags (specific)',
                ),
          'delicious_tags' => array(
               'label' => 'Enable Delicious Tags',
               'type' => 'checkbox',
               'defaultvalue' => 0,
                'note' => 'Inserts links to delicious tag pages, you should also add a CSS rule for either .tags (generic) or delicious_tags (specific)',
                ),
        );
      return $r;
    }

Easy enough, I also copied this function from the BAE plugin, because I assume it's just a bit of boilerplate:

    function PluginSettingsUpdateAction()
    {
      global $DB;
      $DB->query('DELETE FROM T_items__prerendering WHERE 1');
      $this->msg( sprintf( T_('Removed %d cached entries.'), $DB->rows_affected ), 'success' );
    }

Finally I modified the RenderItemAsHtml method to detect the settings and render the appropriate tags (nothing fancy here):

    if ($this->Settings->get( 'technorati_tags' ) == 1) {
            $AddedAtLeastOneTag |= $this->addTags( $content, $Matches[ 1 ], '<div class="tags technorati_tags"><strong>Technorati tags for this post :</strong> ', '</div>', 'http://technorati.com/tag/', ' rel="tag"' );
    }

    if ($this->Settings->get( 'delicious_tags' ) == 1) {
            $AddedAtLeastOneTag |= $this->addTags( $content, $Matches[ 1 ], '<div class="tags delicious_tags"><strong>Delicious tags for this post :</strong> ', '</div>', 'http://delicious.com/tag/', ' rel="tag"' );
    }

It all seems to work, so I added flickr and twitter just for the heck of it and then some extra CSS rules for my template to make them look distinctive. Download Rich Tags Plugin v0.4.


Tweet this!
4 feedbacks »PermalinkPermalink