Stop Nagging Your IE Users. They Get It Already

This week, that grand old bane of a Web developer’s existence—IE6—turned 12 years old. Like any tween, it has been subjected to bullying over the years. A year an a half ago, Microsoft finally took the step toward beating it to death by forcing updates to IE8. And they were widely successful. I go months both here on my little slice of the ‘net and on larger sites maintained by my employer without seeing IE6 pop up in the logs.

So, we’ve turned our attention to bullying its younger sibling, IE7, which turns 7 in October.  Again, those users should have been upgraded to IE9 as part of the Microsoft push, but a few computers still linger. We continue to bully. Some of us even go after four-year-old IE8 when it tantrums over our attempts to use CSS3 and HTML5 (or are we throwing those tantrums?).

Our favorite tactic seems to be telling users to update their browser with imposing messages (standard in templates like HTML5 Boilerplate directing them to browsehappy.com). Some people go farther to redirect to a special page entirely (*cough Jeff Starr*).

Why do we do this? Sure, it’s great to not have to do ugly things to our code to make those browsers display beautiful designs, particularly when our clients won’t listen to why websites don’t need to look the same in every browser.

It’s awesome to have fewer configurations to support for web applications.

We get to show off and go on a power trip, yelling “mwahaha, I refuse to bow to your ancient browser demands, I am all powerful, and I decide what you should use.”

But, what we don’t consider in these decisions is the user.

After more than half a decade, and after Microsoft has tried forcing upgrades, we have to ask why these users are still accessing our sites with browsers that should be a distant memory.

The reason is likely that they have to. For one reason or another (options too myriad to list here) they HAVE to use those browsers. It’s highly unlikely that a user on IE6 or IE7 doesn’t realize that they are using an outdated browser at this point in 2013.

So, why are we bullying our visitors? Why are we telling them that every time they visit our site that we think they don’t deserve to read our content because they have an old browser?

Other than attempting to alienate visitors, I can’t come up with an answer to that.

I’m not saying that we need to care if the design is broken (we don’t). I’m not even sure we need to care if interaction is half-broken. But we should care that our visitors have been reminded about using an old browser countless times yet they continue to do so, and that ultimately they just want to access our content.

So, why not let them access the information we’re providing without reminding them that they are second-class internet citizens? Really, what do you have to lose?

N.B. Yes, I know, pot, kettle, black. I have one of those notices on this very site. I’ve been running this design for over four years now, and a lot has changed in the landscape since then (I think those notices may have been valuable before Microsoft’s auto update move), but my lack of time to set up a redesign has not. Moving forward, though, I’ll be practicing what I post.

Google Mini KeyMatch and Related Query Imports

The Web is ever changing, and this article is relatively ancient having been published 4 years ago. It is likely out of date or even blatantly incorrect in relation to modern better practices, so proceed at your own risk.

Filed under “you mean the documentation doesn’t exist somewhere?”, was my issue trying to import KeyMatches and Related Queries to a Google Mini we have set up for a client. Google’s documentation on them is very straight forward: select the file, press import— but it lacked instructions about how to set up the import file. Since I took the time to sort it all out, here’s a quick rundown.

Import File Specifics

Your import file should be .csv format. With UTF-8 encoding. And, most importantly (especially for those of us using OSX), UNIX line endings.

Your favorite text editor may be able to change those settings if they are not the default when you save a file. I used Coda to do so.

Related Queries

Your related queries import should have two fields (don’t give your columns headings, though):
[search term],[suggested/related query]

Set up your file per the above import file specifics, and import.

KeyMatches

Your KeyMatch import will have four fields:
[search term],[KeywordMatch|PhraseMatch|ExactMatch],[URI],[Result Title]

Word of warning: there is a maximum of five keymatch results per URI, but default settings will only show three.

Simple, yet previously undocumented (as far as my Google-fu skills could determine). Hope it helps.

Creating Quarter-Circles in Adobe Illustrator

Here’s a quick way to draw a quarter-circle in Adobe Illustrator. It involves using just one tool: the arc tool.

These instructions will work in Adobe Illustrator from CS–CS5.

How to create a quarter-circle (or quarter-ellipse) in Illustrator

  1. Select the arc tool. (Click and hold the straight-line–segment tool to bring up the sub-menu of additional line-segment tools)
  2. Use the arc tool to make your quarter-circle:
    A. Freehand method
    Before drawing a freehand quarter-circle for the first time, you must use the exact-size method to enable closed-shape drawing. If you do not, you will only get the curved line, not the whole the shape.
    Quarter-circle: Hold down the shift key on your keyboard, click and drag to draw a quarter-circle of the desired size.
    Quarter-elipse: Click and drag to draw a quarter-elipse of the desired size.
    B. Exact-size method
    Click once anywhere on your artboard to open the arc tool options dialogue.

    Type in your desired dimensions for the radius of your quarter-circle, and select Closed from the Type drop-down. If your y-axis and x-values are equal, you will be drawing a quarter-circle, but if they differ, you will draw a quarter-ellipse.

    Click ok.

  3. Success. You now have a quarter circle. You can change the fill, size, stroke, etc as with any other shape.

How to Schedule Planned Maintenance Using RewriteEngine

… or how I set my sites up to go black tomorrow in protest of SOPA and PIPA. You might not see this prior to that blackout, but it has useful applications for other things—essentially any time you want to do a redirect that has a specific schedule.

If you are running your sites on an Apache server with the rewrite module, scheduling a temporary redirect to a maintenance page is pretty simple:

# MAINTENANCE REDIRECT 
RewriteEngine on
RewriteCond %{TIME} >20120118000001 [NC]
RewriteCond %{TIME} <20120118235959 [NC]
RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
RewriteRule ^(.*)$ /maintenance.html [R=307,L]

To explain the code:

Line 2: RewriteEngine on
Turns on the RewriteEngine, if it isn’t already.
Line 3: RewriteCond %{TIME} >20120118000001 [NC]
Checks to see if the date is greater than a specific time. In this case 2012-01-18 00:00:01.
Line 4: RewriteCond %{TIME} <20120118235959 [NC]
Checks to see if the date is less than a specific time. In this case 2012-01-18 23:59:59.
Line 5: RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
Checks to be sure that you’re not trying to access the maintenance page itself, otherwise we’d end up in a redirect loop.
Line 6: RewriteRule ^(.*)$ /maintenance.html [R=307,L]
If all of the above conditions are true, it does a temporary (307) redirect to your maintenance page.

Here’s the code again, with the variables you need to modify italicized.

# MAINTENANCE REDIRECT 
RewriteEngine on
RewriteCond %{TIME} >20120118000001 [NC]
RewriteCond %{TIME} <20120118235959 [NC]
RewriteCond %{REQUEST_URI} !/maintenance.html$ [NC]
RewriteRule ^(.*)$ /maintenance.html [R=307,L]

Drop it in your .htaccess file, and you should be good to go.

What time value should I use?

We’re talking server time, so your time will be based on what your server setting is. Mine is set to EST, although it gets funky about Daylight Savings Time sometimes, so it might be off by an hour. If you know your server is running 3 hours slower than your timezone, adjust your times by 3 hours.

Then, figure out your date and time in the format YYYY-MM-DD HH:MM:SS and remove anything that isn’t a number: YYYYMMDDHHMMSS.

Why are you doing a redirect instead of rewrite?

I like to make it very clear to search engines that we’re on a new page and it is only temporary, so no caching should happen. If a search engine hits my homepage and I’ve rewritten the URI, the search engine thinks that my maintenance content should be indexed, which I definitely do not want to happen. Instead, it knows that there was a redirect.

You want the redirect to be temporary (307) so that the search engines know to continue to request the original URI in the future, and not the maintenance page.

What is this SOPA and PIPA stuff?

The American legislative bodies are both currently considering bills that will allow easy censorship on the Web in the name of protecting copyright and intellectual property. A good analogy is that the bills are kind of like trying to kill a squirrel with an atomic bomb. As someone who is both a Web developer and creative professional, I strongly believe that this bill will hinder creative and economic progress as well as the dissemination of information.

If you’re reading this on Jan 17, or any day after Jan 18, you can learn more on Wikipedia, but it will be blacked out on Jan 18. Of course, your favorite search engine will surely return plenty of hits as well.

When to Use Events vs. Pageviews for Google Analytics

How to set up a schema for tracking certain things on a website is a polarizing topic. I’ve seen numerous schema that rely solely on pageviews to track everything—outbound link clicks, downloads, modal dialogs, etc—and the creator is dogmatic about the approach. But, semantically speaking, one of those three actions is a legitimate pageview; two aren’t. They’re events. I’m admittedly zealous when it comes to semantic use of Web technologies, but let’s face it: it makes for intelligent information architecture.

A good philosophy for tracking is: if new information is shown in the browser, at an address owned by you, it is a pageview. Everything else is an event.

Tracking link clicks

The short answer: tracking a link is tracking an event.

For instance, clicking on an outbound link does not result in a pageview for your website. Viewers are not looking at content on your site. Thus, an outbound link click is an event.

Clicking on a related link—such as to another similar blog post—is not a pageview, it is an event. The pageview itself will be tracked when the new page loads, but if you want to track the actual link click, you should register an event. Otherwise, you’ll be double tracking pageviews or tracking pageviews for a single page under multiple paths. Neither is ideal or desirable in an intelligent tracking schema.

Clicking a link that triggers a popup is covered in:

Tracking pop-up, modal, or ajax-inserted content

Are you tracking this based on link clicks? Why?

When tracking a popup, you should add tracking code to the popup file. The new window is a pageview. There’s no reason to track the link click as the pageview when you can add basic code to the popup and let Google handle it all.

For modal or ajax-inserted content, proper tracking means triggering a pageview when the content is shown. This is different from when the triggering link is clicked. Most plugins that allow modal dialogs or on-screen popups have an API that features some sort of onShow or afterRendering hook. This is the best place to put your pageview tracking. And yes, this is a pageview, because it is showing new content.

There are some cases where the link should be tracked, and those should be tracked as events. For instance, if you have a modal or popup contact form, you’ll want to track a pageview when the form is displayed, and an event to track which page the form is on or which link is used to display the form. Two different types of tracking that happen at two different (but very close together) points in the interaction.

The tricky case of PDF documents

PDF documents are one case where I entertain either option, but that is because there are two possible ways that PDFs extend the website: as downloadable content or as browser-viewable content. Older browsers and the default installation of Firefox treat PDFs as downloads. They are not displayed in-browser. Thus, under my definition of a pageview, clicking on a PDF link is an event. Think of it this way: we know that the user clicked the link, but we can’t be sure that the user actually downloaded or opened the document, so there’s no empirical evidence that there was a pageview.

However, modern browsers like Safari and Chrome, and certain mobile platforms will open PDF files seamlessly. In that case, for most intents and purposes, clicking a link results in a definite pageview.

Example cases:

Server forces PDF download, will not serve in browser
Event. There is no possibility the page is viewed in the browser. Any content is downloadable, not viewable.
PDF is provided because resources/desire/ability to convert to Web are not available, but is considered Web content
Pageview. There is a possibility that the page will be viewed in the browser, and that is the intended functionality anyway.
File is of a form or tutorial intended for printing
Event. The intent is not to view the content, but to provide a format for printing and interacting with outside of the Web. This is not pageview content.

In most situations where I’ve implemented tracking for PDFs, their use has fallen under the event philosophy, but I do see instances where a pageview does make sense.

How do you make decisions about pageviews or events? How do you use the resulting data?

This post follows , a post about debugging your events and pageview code. I hope to follow this with a tutorial about implementing JavaScript for this tracking.

How to Check If Google Analytics Click Tracking is Triggered

When I add new Event or PageView tracking code that is bound to click events, I want to be sure that the tracking code is fired without waiting for a site visitor to trigger it (or in some cases before the code is even on a live page). Because my own traffic is filtered out of all of my Google Analytics reports, I can’t rely on my clicks to show in my reports (not to mention the delay). But, even with the filter in place, the clicks are sent to Google, and I can check to be sure they’re sending the right information in Firefox using Firebug’s Net panel. Here’s how.

Enable the Net tab

If you don’t already have Firebug installed in Firefox, get it, because you need it for this.

Once it’s installed, enable the Net panel.

Load the page you want to test. Once you do, you’ll see a lot of stuff pop up in the Firebug Net panel. One of my posts looks something like:

Look for the PageView

Once you have enabled the Net panel, you can see if your Google code triggers the initial pageview. Look through the requests for one that starts with _utm.gif?. Expand it by clicking on the arrow to the left of it.

Looking at the params tab (it defaults to headers), you can see the information being passed to Google. The first line is your unique tracking ID. You’ll also see your page title, the domain, and most importantly, the URL being passed.

If you don’t find the _utm.gif? entry, something is wrong with your basic GA tracking code and no data is being sent to Google.

Trigger your event or new pageview and find the tracking info

After verifying that the GA code is tracking the pageview, you can see if your click event code is working. To do so, trigger an event that you’ve attached tracking code to. In my screenshot, I open an external link, which I’ve coded to trigger a GA event. It’s important that if you are triggering a link to a new page that you open it in a new tab or window so that your Firebug Net panel still tracks the original page.

A new _utm.gif? line should show up in the Net panel. Expand this new tab. If you triggered another pageview, the params will look similar to those mentioned earlier. Events look a bit different.

Click to view larger version

The important field here is utme, which shows the information you passed in the _trackEvent call. The arguments you passed to _trackEvent are separated by asterisks. Mine reads Outbound Link*us.php.net/strtotime*Why is date() returning 12/31/1969. This is because I track my outbound links under the category Outbound Link, with the external URL as the action and the h1 text on the page the call was triggered from as the label.

Yours will likely differ depending on your schema for event tracking.

Again, if you don’t find this new _utm.gif? call in the Net panel, your click event code isn’t running, or the GA _trackEvent/_trackPageview code is set up incorrectly. You’ll need to debug it, then check again.

Now you know how to check if your code is working. What do you use Google Event or Pageview tracking for?

New Project Launch

The Web is ever changing, and this article is relatively ancient having been published 6 years ago. It is likely out of date or even blatantly incorrect in relation to modern better practices, so proceed at your own risk.

I launched a new Web project a couple of months ago, but am just now getting a chance to post about it. The project is the Student Loan Facts Page, a site and blog about student loans. The importance of it to this Front-end dev blog, however, is that I want to let you know my thoughts on the HTML5 Boilerplate.

I’ll start with a caveat: this is a personal project, so I made a conscious decision to use elements in the design and coding that won’t work in any current release of IE. That’s not to say that IE users can’t see the site or anything, just that some stuff might be a little wonky. Like, the homepage makes use of the content CSS attribute to display numbers next to the six main links.

But my main goal with this site was to play around with WordPress a little more and start something with HTML 5. HTML5 Boilerplate seemed like a good place to start. I’m a fan of CSS resets, so that’s all there. I’m also intrigued by some of their approaches:

  • CDN hosted jQuery with local fallback failsafe.
  • JS located at the bottom of the page.
  • IE/JS-specific classes on the html tag (or body tag, depending on which version you’re using) to allow for progressive enhancement.
  • Asynchronous Google Analytics use.
  • Forcing a scrollbar in non-IE browsers to for design consistency.
  • Consideration for a:focus.
  • Text input/label alignment.
  • Progressive HTML5 form error classes using the :valid and :invalid selectors.

… and much more.

There’s also some stuff that I’m not as happy with, like non-semantic classes, but I can see why they included it.

As a whole, I think it’s a great start for developers who know what all of the code does. It is a very simple setup to modify as a solid basis for your own personal framework. It’s not really a straight out of the box solution, though.

It’s also not a great solution for a newbie to use. They’ll end up with bloated code and unnecessary stuff. Although, my current code on that site isn’t exactly pretty.

I’m impressed enough to use it as a jumping-off point for a redesign of this site coming later in the year, though. I hope you have a good experience with it as well.

IE Change Event Delay

The Web is ever changing, and this article is relatively ancient having been published 6 years ago. It is likely out of date or even blatantly incorrect in relation to modern better practices, so proceed at your own risk.

I recently developed a sign-up form for a client that includes on-page price total calculation using JavaScript (jQuery). The premise is simple: the user provides information and specify options, then clicks a radio button to choose a specific price plan. The initial total price calculation is triggered by the change() event for the radio button elements. But, the client was concerned (and with user testing, it turned out rightly so) because in IE, the price calculation didn’t happen until the user clicked somewhere else on the page. In cases where they first clicked one option, then a different one, the price would seemingly lag behind because of IE’s delayed change event firing. It was confusing to the user, but worse—confusing for me to “fix” IE’s implementation.

The awesome news is that this has been fixed in jQuery 1.4, but the concern is still valid for older versions (which my application was using) and straight-JavaScript implementations.

The problem

In Internet Explorer, the change event fires when focus leaves the form element (an event know as blur). That means that the event happens only once a user has clicked on—or used the keyboard to navigate to—another element on the page (form or other).  In cases like mine, where a user is expecting instant feedback to their click, this causes issues with user experience. Unfortunately, this isn’t exactly a “bug,” as it’s how IE handles this event in 6, 7 and 8.

In other browsers—Firefox, Webkit-based (Safari/Chrome) and Opera—the event fires off immediately, so in order to have consistent, intelligent operation, we have to hack IE’s basic behavior. The easiest solution is to bind your function to a different event, such as the click event, but that’s generally not the right solution. There is a better one.

Why using the click event is wrong

One word: accessibility. Users—whether they have a disability that restricts their use of the mouse or like to tab about the page with the keyboard for speed—don’t always use the mouse to move from form element to form element. So, if you bind your functionality to the click event, you may end up messing with a user’s workflow, which makes for unhappy visitors. In some cases, it may even make a user unable to use your application. So don’t use that as your solution.

The real solution

If IE needs a blur event to know that the change event should fire, give it one when the element is clicked. In jQuery, that looks something like:

$("#element").click(function(){
	// In IE, we need to do blur then focus to trigger a change event
	if ($.browser.msie) {
		this.blur();
		this.focus();
	}
}).change(function(){ actionIWantOnChange(); });

This code tricks IE into thinking that focus has been changed away from the element when it is clicked. But since the change event is also triggered, the actions attached to that event also happen. Keyboard navigability still works, because even though there is no click, the change event will fire when they use the keyboard to move to another field meaning the feedback is instantaneous.

Now, you can probably improve my above example by using better feature-sniffing to test for IE instead of the browser object in jQuery, but my time for creating a fix was limited—and this code gets the job done.

Elegant Code Patterns—Set and Return a Variable in One Line

I’ve been working on a Project that Never Ends™ for well… months now. Many months. The project has moved in to phase I’ve-lost-count, and the current milestone work is speed improvement. The project is a wonderful example of what goes wrong when you don’t have a detailed plan to start with, and has evolved into a terrible mess of convoluted code. The first step in any speed improvement is to reduce as much duplication as possible. One way of doing that is by caching calculations. And you can do that with a very small amount of code, using an elegant code pattern of setting and returning a variable in a single call.

The premise

When a complicated calculation is going to be used multiple times on a page, efficiency dictates saving the result (so that the calculation is only done once). So, instead of calculating the result every time you need it, why not cache the value? You can do this using a global variable and a single function. That way, any time you need the calculation, you can call the same function—including the first time—but only calculate the value once.

A case study

A function exists to get the total sales for a specific product during a given year. It does so by querying a database that has thousands of entries for each sales transaction. This time-expensive function is used multiple times on the page and is causing the page to take far too long to load.

It’s really a rather simple function:

function getSales($customer,$year) {
     $db_record = query_database("SELECT sum(sales) as total »
                FROM sales_records WHERE customer='$customer' »
                AND year='$year'");
     return $db_record->total;
}

Line breaks marked by »

While the above function glosses over the actual database calculation (that’s all pseudo code, the functions don’t really exist), you get the idea. It’s simple, but expensive when you’re querying many thousands of records each time.

By caching the result, we can reduce the page load time by quite a bit.

$CACHE = array();
function getSales($customer,$year) {
     global $CACHE;
     if (isset($CACHE[$customer][$year])) return $CACHE[$customer][$year];
     $db_record = query_database("SELECT sum(sales) as total »
                FROM sales_records WHERE customer='$customer' »
                AND year='$year'");
     return $CACHE[$customer][$year] = $db_record->total;
}

Explaining the new code, line by line

$CACHE = array();
This line of code simply creates a global variable (a variable outside the scope of our function) to use as a cache. In this case, we’re using an array.
global $CACHE;
Now that we’re in the function, we need to get access to our global variable. This line of code does just that. It declares that any time the variable $CACHE is used, it is referring to the global version.
if (isset($CACHE[$customer][$year])) return $CACHE[$customer][$year];
This is where the cache magic happens. If this is the second or later time the function has been called, the value has already been cached. We check to see if the correct cache value exists, and if it does, we return it. That exits the function immediately, meaning we return the correct value, but don’t do the expensive calculation.
$db_record = query_database…
This line is the calculation from the unoptimized function. If the result hasn’t been cached (that is, this is the first time the function has been called), we have to do the calculation. There’s no need to wrap this in an else clause, though, because when the previous if statement is true, this code is never reached.
return $CACHE[$customer][$year] = $db_record->total;

This is the magic from the title of the blog post. It sets and returns a value (the value to the right of the assignment operator [=]) at the same time. You’ve probably written something like this before:

$variable = $value;
return $variable

In PHP, however, you can do that same thing with a single line of code:

return $variable = $value

It’s elegant and simple. That’s what we’re doing in this line.

Caveat: this code pattern is wonderful for returning simple values. It doesn’t work for whole arrays. That is, you can return the value of one item in an array, as we do in the function, but you couldn’t return a whole array. That must be done with the two lines of code.

The result

I’m not going to put precise empirical evidence (execution times) on here, because your results will vary widely depending on your project and data. But, to give an idea, the project the case study is based on saw an average page load time reduction from 20s to 4s. Not a single other thing was done to the code beyond caching the values of the calculation. Your results may see less of a savings or more. In my case, we were dealing with a database containing more than 15,000 individual records that were being looked at each time the calculation query was made.

Depending on your exact project, there can be more improvements to this code. For instance, lookups in a multi-dimensional array are more expensive than ones in a one-dimensional array or an integer variable. But the concept transcends projects: cache and return the value at once, and you can save precious time by not recalculating each time the function is called.

Have you seen or used this code pattern before? Do you love it or hate it?

Elegant Code Patterns—Drop the Else When Code is Repeated

I’m working on launching a new project that has been eating up a lot of my time. Partially because any new project has a habit of eating up a lot of time, but also because I’m really focusing on using this project as a solid base for a few others, meaning I want my code to be elegant, my ideas well-executed, and my implementation in a way that means I won’t want to scrap it and start over in the future as I do far too often. Part of that employing elegant code patterns. One of my most often used patterns involves dropping the else statement when I have code repeated in different logic clauses.

The premise

One of the countless ways of using the if…else construct is to do one thing if a certain variable is one value, or do something else if it is not. A lot of times this results in duplicating code. Duplicate code is inelegant, and introduces a greater opportunity for error when, for instance, one line is changed, but its clone is not.

The solution: structure your code to reduce duplicate code.

A case study

A project is using a CMS that provides a tag to retrieve the path to a featured image for a page. If no such image exists, it returns the path for an empty filler image. There’s no way within the CMS to change what that filler image is, but this project calls for using something else as a filler.

The goal: display the proper featured image or correct placeholder using an img tag.

The process: get the image path, and check if it is the default filler image. If it is, change the path to the new filler. Display the correct image.

The inherited code looked something like this (PHP):

$image_path = get_image_path($page);
if ($image_path == "/path/to/default_filler.png") {
   echo "<img src="/path/to/desired/filler.png" alt=" " />";
} else {
   echo "<img src="$image_path" alt=" " />";
}

In this case, the logical clauses are doing the exact same thing: printing out an image tag. The only difference is the path being printed. What if you later decide to add in an alt value, or reuse this code on an HTML page instead of XHTML? You might forget to change both lines. There’s a more elegant way.

The refined code

Refining the code, we can completely drop the else statement. Instead of printing the tag in each clause, we simply change the value of $image_path to be equal to the new filler image path if it is set to the wrong one. Then, we print the image tag using the value of $image_path.

$image_path = get_image_path($page);
if ($image_path == "/path/to/default_filler.png") {
    $image_path = "/path/to/desired/filler.png";
}
echo "<img src="$image_path" alt=" " />";

Depending on your school of thought on using braces with logical constructs, you can even reduce this code to three lines. But, regardless of brace use, the refined code is a lot more elegant and maintainable than the old code.

Going further, in this case our code pattern was only being used once, but if you were also repeating this pattern, you would turn it into a function to be called each time rather than copying the code.

Any time you find yourself repeating code, especially inside of logical constructs, see if you can simplify and refine your code. It makes it easier to read, easier to update, and is often much more efficient.