text:
package com.jcwhatever.nucleus.collections.players;

import com.jcwhatever.nucleus.collections.CircularQueue;
import com.jcwhatever.nucleus.collections.players.PlayerElement.PlayerElementMatcher;
import com.jcwhatever.nucleus.collections.wrap.ConversionIteratorWrapper;
import com.jcwhatever.nucleus.collections.wrap.SyncStrategy;
import com.jcwhatever.nucleus.utils.CollectionUtils;
import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.validate.IValidator;

import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * A {@link CircularQueue} for {@link Player} objects that automatically removes
 * players when they log out.
 *
 *
Thread safe.

 *
 *
The queues iterators must be used inside a synchronized block which locks the
 * queue instance. Otherwise, a {@link java.lang.IllegalStateException} is thrown.

 */
public class PlayerCircularQueue implements IPlayerCollection, Deque<Player> {

    private final Plugin _plugin;
    private final CircularQueue<PlayerElement> _queue = new CircularQueue<>();
    private final PlayerCollectionTracker _tracker;
    private final Object _sync;
    private final SyncStrategy _strategy;

    /**
     * Constructor.
     *
     * @param plugin  The owning plugin.
     */
    public PlayerCircularQueue(Plugin plugin) {
        PreCon.notNull(plugin);

        _plugin = plugin;
        _sync = this;
        _strategy = new SyncStrategy(this);
        _tracker = new PlayerCollectionTracker(this);
    }

    @Override
    public Plugin getPlugin() {
        return _plugin;
    }

    @Override
    public void addFirst(Player player) {
        PreCon.notNull(player);

        synchronized (_sync) {
            PlayerElement element = new PlayerElement(player);

            if (!_queue.contains(element)) {
                _tracker.notifyPlayerAdded(player.getUniqueId());
            }

            _queue.addFirst(element);
        }
    }

    @Override
    public void addLast(Player player) {
        PreCon.notNull(player);

        PlayerElement element = new PlayerElement(player);

        synchronized (_sync) {

            if (!_queue.contains(element)) {
                _tracker.notifyPlayerAdded(player.getUniqueId());
            }

            _queue.addLast(element);
        }
    }

    @Override
    public boolean offerFirst(Player player) {
        PreCon.notNull(player);

        PlayerElement element = new PlayerElement(player);

        synchronized (_sync) {

            boolean has = _queue.contains(element);

            if (_queue.offerFirst(element)) {

                if (!has)
                    _tracker.notifyPlayerAdded(player.getUniqueId());

                return true;
            }

            return false;
        }
    }

    @Override
    public boolean offerLast(Player player) {
        PreCon.notNull(player);

        PlayerElement element = new PlayerElement(player);

        synchronized (_sync) {

            boolean has = _queue.contains(element);

            if (_queue.offerLast(element)) {

                if (!has)
                    _tracker.notifyPlayerAdded(player.getUniqueId());

                return true;
            }

            return false;
        }
    }

    @Override
    public Player removeFirst() {
        synchronized (_sync) {
            PlayerElement element = _queue.removeFirst();
            Player result = element != null ? element.getPlayer() : null;

            if (result != null && !_queue.contains(element)) {
                _tracker.notifyPlayerRemoved(result.getUniqueId());
            }

            return result;
        }
    }

    @Override
    public Player removeLast() {
        synchronized (_sync) {
            PlayerElement element = _queue.removeLast();
            Player result = element != null ? element.getPlayer() : null;

            if (result != null && !_queue.contains(element)) {
                _tracker.notifyPlayerRemoved(result.getUniqueId());
            }

            return result;
        }
    }

    @Override
    public Player pollFirst() {
        synchronized (_sync) {
            PlayerElement element = _queue.pollFirst();
            Player result = element != null ? element.getPlayer() : null;

            if (result != null && !_queue.contains(element)) {
                _tracker.notifyPlayerRemoved(result.getUniqueId());
            }

            return result;
        }
    }

    @Override
    public Player pollLast() {
        synchronized (_sync) {
            PlayerElement element = _queue.pollLast();
            Player result = element != null ? element.getPlayer() : null;

            if (result != null && !_queue.contains(element)) {
                _tracker.notifyPlayerRemoved(result.getUniqueId());
            }

            return result;
        }
    }

    @Override
    public Player getFirst() {
        synchronized (_sync) {
            PlayerElement entry = _queue.getFirst();
            return entry != null ? entry.getPlayer() : null;
        }
    }

    @Override
    public Player getLast() {
        synchronized (_sync) {
            PlayerElement entry = _queue.getLast();
            return entry != null ? entry.getPlayer() : null;
        }
    }

    @Override
    public Player peekFirst() {
        synchronized (_sync) {
            PlayerElement entry = _queue.peekFirst();
            return entry != null ? entry.getPlayer() : null;
        }
    }

    @Override
    public Player peekLast() {
        synchronized (_sync) {
            PlayerElement entry = _queue.peekLast();
            return entry != null ? entry.getPlayer() : null;
        }
    }

    @Override
    public boolean removeFirstOccurrence(Object o) {
        synchronized (_sync) {
            PlayerElementMatcher matcher = new PlayerElementMatcher(o);
            if (_queue.removeFirstOccurrence(matcher)) {
                //noinspection SuspiciousMethodCalls
                if (matcher.getUniqueId() != null && !_queue.contains(matcher)) {
                    _tracker.notifyPlayerRemoved(matcher.getUniqueId());
                }
                return true;
            }
            return false;
        }
    }

    @Override
    public boolean removeLastOccurrence(Object o) {
        synchronized (_sync) {
            PlayerElementMatcher matcher = new PlayerElementMatcher(o);
            if (_queue.removeLastOccurrence(matcher)) {
                //noinspection SuspiciousMethodCalls
                if (matcher.getUniqueId() != null && !_queue.contains(matcher)) {
                    _tracker.notifyPlayerRemoved(matcher.getUniqueId());
                }
                return true;
            }
            return false;
        }
    }

    @Override
    public boolean add(Player player) {
        PreCon.notNull(player);

        synchronized (_sync) {

            PlayerElement element = new PlayerElement(player);

            boolean has = _queue.contains(element);

            if (_queue.add(element)) {

                if (!has)
                    _tracker.notifyPlayerAdded(player.getUniqueId());

                return true;
            }
            return false;
        }
    }

    @Override
    public boolean offer(Player player) {
        PreCon.notNull(player);

        synchronized (_sync) {

            PlayerElement element = new PlayerElement(player);

            boolean has = _queue.contains(element);

            if (_queue.offer(element)) {

                if (!has)
                    _tracker.notifyPlayerAdded(player.getUniqueId());

                return true;
            }
            return false;
        }
    }

    @Override
    public Player remove() {
        synchronized (_sync) {
            PlayerElement entry = _queue.remove();
            Player result = entry != null ? entry.getPlayer() : null;

            if (result != null && !_queue.contains(entry))
                _tracker.notifyPlayerRemoved(entry.getUniqueId());

            return result;
        }
    }

    @Override
    public Player poll() {
        synchronized (_sync) {
            PlayerElement entry = _queue.poll();
            Player result = entry != null ? entry.getPlayer() : null;

            if (result != null && !_queue.contains(entry))
                _tracker.notifyPlayerRemoved(entry.getUniqueId());

            return result;
        }
    }

    @Override
    public Player element() {
        synchronized (_sync) {
            PlayerElement entry = _queue.element();
            return entry != null ? entry.getPlayer() : null;
        }
    }

    @Override
    public Player peek() {
        synchronized (_sync) {
            PlayerElement entry = _queue.peek();
            return entry != null ? entry.getPlayer() : null;
        }
    }

    @Override
    public void push(Player player) {
        PreCon.notNull(player);

        synchronized (_sync) {

            PlayerElement element = new PlayerElement(player);
            boolean has = _queue.contains(element);

            _queue.push(element);

            if (!has)
                _tracker.notifyPlayerAdded(player.getUniqueId());
        }
    }

    @Override
    public Player pop() {
        synchronized (_sync) {
            PlayerElement entry = _queue.pop();
            Player result = entry != null ? entry.getPlayer() : null;

            if (result != null && !_queue.contains(entry))
                _tracker.notifyPlayerRemoved(entry.getUniqueId());

            return result;
        }
    }

    @Override
    public boolean remove(Object o) {
        synchronized (_sync) {
            PlayerElementMatcher matcher = new PlayerElementMatcher(o);
            if (_queue.remove(matcher)) {

                //noinspection SuspiciousMethodCalls
                if (matcher.getUniqueId() != null && !_queue.contains(matcher))
                    _tracker.notifyPlayerRemoved(matcher.getUniqueId());

                return true;
            }
            return false;
        }
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        PreCon.notNull(c);

        for (Object obj : c) {
            if (!contains(obj))
                return false;
        }
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends Player> c) {
        PreCon.notNull(c);

        boolean isChanged = false;
        synchronized (_sync) {
            for (Player player : c) {
                isChanged = add(player) || isChanged;
            }
        }
        return isChanged;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        PreCon.notNull(c);

        boolean isChanged = false;
        synchronized (_sync) {
            for (Object obj : c) {
                isChanged = remove(obj) || isChanged;
            }
        }
        return isChanged;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        PreCon.notNull(c);

        final List<PlayerElement> toRemove = new ArrayList<>(_queue);
        for (Object obj : c) {
            PlayerElementMatcher matcher = new PlayerElementMatcher(obj);

            //noinspection SuspiciousMethodCalls
            toRemove.remove(matcher);
        }

        synchronized (_sync) {

            List<PlayerElement> removed = CollectionUtils.retainAll(_queue, new IValidator<PlayerElement>() {
                @Override
                public boolean isValid(PlayerElement element) {
                    if (toRemove.contains(element)) {
                        _tracker.notifyPlayerRemoved(element.getUniqueId());
                        return false;
                    } else {
                        return true;
                    }
                }
            });

            return !removed.isEmpty();
        }
    }

    @Override
    public void removePlayer(UUID playerId) {
        PreCon.notNull(playerId);

        synchronized (_sync) {
            //noinspection StatementWithEmptyBody,SuspiciousMethodCalls
            while (_queue.remove(new PlayerElementMatcher(playerId)));
        }
    }

    @Override
    public void clear() {
        Set<PlayerElement> removed;
        synchronized (_sync) {
            removed = new HashSet<>(_queue);
            _queue.clear();
        }

        for (PlayerElement element : removed) {
            _tracker.notifyPlayerRemoved(element.getUniqueId());
        }
    }

    @Override
    public boolean contains(Object o) {
        synchronized (_sync) {
            return _queue.contains(new PlayerElementMatcher(o));
        }
    }

    @Override
    public int size() {
        return _queue.size();
    }

    @Override
    public boolean isEmpty() {
        synchronized (_sync) {
            return _queue.isEmpty();
        }
    }

    @Override
    public Iterator<Player> iterator() {
        return new ItrRight();
    }

    @Override
    public Object[] toArray() {
        Object[] elements;
        synchronized (_sync) {
            elements = _queue.toArray();
        }
        Object[] results = new Object[elements.length];
        for (int i=0; i < elements.length; i++) {
            results[i] = ((PlayerElement)elements[i]).getPlayer();
        }
        return results;
    }

    @Override
    public <T> T[] toArray(T[] a) {
        PreCon.notNull(a);

        Object[] elements;
        synchronized (_sync) {
            elements = _queue.toArray();
        }
        for (int i=0; i < elements.length; i++) {

            @SuppressWarnings("unchecked")
            T player = (T)((PlayerElement)elements[i]).getPlayer();

            a[i] = player;
        }
        return a;
    }

    @Override
    public Iterator<Player> descendingIterator() {
        return new ItrLeft();
    }

    private final class ItrRight extends ConversionIteratorWrapper<Player, PlayerElement> {

        ItrRight() {
            super(PlayerCircularQueue.this._strategy);
        }

        Iterator<PlayerElement> iterator = _queue.iterator();

        @Override
        protected Player convert(PlayerElement internal) {
            return internal.getPlayer();
        }

        @Override
        protected Iterator<PlayerElement> iterator() {
            return iterator;
        }

        @Override
        public void remove() {
            synchronized (_sync) {
                PlayerElement removed = getCurrent();
                iterator.remove();
                if (!contains(removed)) {
                    _tracker.notifyPlayerRemoved(removed.getUniqueId());
                }
            }
        }
    }

    private final class ItrLeft extends ConversionIteratorWrapper<Player, PlayerElement> {

        ItrLeft() {
            super(PlayerCircularQueue.this._strategy);
        }

        Iterator<PlayerElement> iterator = _queue.descendingIterator();

        @Override
        protected Player convert(PlayerElement internal) {
            return internal.getPlayer();
        }

        @Override
        protected Iterator<PlayerElement> iterator() {
            return iterator;
        }

        @Override
        public void remove() {
            synchronized (_sync) {
                PlayerElement removed = getCurrent();
                iterator.remove();
                if (!contains(removed)) {
                    _tracker.notifyPlayerRemoved(removed.getUniqueId());
                }
            }
        }
    }
}


text:
Many have heard of the common utility `wc`, but few know it's humble beginnings. We know that today `wc` counts the lines, bytes, or words contained in a file. I assume that when the authors of `wc` set out to write their program, perhaps it was because they had a particular file in mind which was in desperate need of counting, I'll also imagine this file was called `unknown.txt`.

So they set out to write a utility to count the lines, words, and bytes of `unknown.txt`. Several fortnights passed with missed weekends and birthdays, but it all was worth it in the end. `wc` was born. Sure enough, the program diligently spit out the number of lines, words, and bytes and all rejoiced.

I happen to have a historical recount of that initial run. It looked as follows:

```
$ wc
 6 13 104 unknown.txt
```

Some time later, there came another file, `evil.txt` which was in desperate need of counting. The lead developer said, "This is no issue, we'll change the source in a jiffy, re-compile, and that evil file who's count was unknown would be a mystery no more!" Sure enough, after a quick change of the code, the dutiful `wc` spit out the number of lines in `evil.txt`.

```
$ wc
 4 26 221 evil.txt
```

Again, there was peace in the land. Although, again this peace was short lived.

The lead developer grew tired of constantly changing the source code, and re-compiling each time she found a new file to count. "There has to be a better way." She exclaimed in agony.

After many sleepless afternoons pondering her woes she determined life would be grand again if only little `wc` could get this vital piece of information at runtime, for there was no way know such things before. The lead developer set forth to her team, the best and brightest asking for ways to obtain this information at runtime.

It was there, at this very team meeting that command line applications changed forever.

"Throughout all of our calculations, there appears to be only a handful of methods for getting information at runtime!" one developer proclaimed.

"From the environment!" Yelled one.

"Also from a file, or by asking!" Said another with far too much enthusiasm.

"Ney!" Said the lead developer, "Asking for the file, while seemingly simple, won't allow `wc` to be included in scripts! Setting an environmental variable for each invocation seems cumbersome. Especially with the lack of namespacing!"

"And adding information to a file, that must be created, read, and parsed is far too much leg work!" said the overly enthusiastic developer.

The lead developer was beginning to worry that perhaps there wasn't a solution, that perhaps `wc` was doomed to a life of re-complilation.

Then, a small voice in the back began to speak. At first, everyone thought it may have been a television left on in another room. But sure enough, the voice spoke louder, and a young developer stood up, "The answer is argument parsing."

"Yes, I've dreamed of such magic." Said the lead developer, "We will have users tell `wc` which file to parse while invoking `wc` itself!"

Developers large and small, young and old, gathered round to watch this small developer with a shy voice change the source ever so slightly to use this new sorcery. Once the compiler returned the successful binary, all gathered round to watch what had transpired.

```
$ wc test.txt
 50 78 549 test.txt
```

The heavens had heard no such joys before! The problem was solved, for now...

redpajama_set_name: RedPajamaGithub


text:
<?php
namespace Thongtinlienhe\Form;
use Zend\Form\Element;
use Zend\Form\Form;
class ThongtinlienheForm extends Form{

    public function __construct($name=null)
    {
        parent::__construct('thongtinlienhe');
        $this->setAttribute('method','post');

        $this->add(array(
            'name' =>'id',
            'attributes'=>array(
                'type' =>'hidden',
            ),
        ));

        $namecontact = new Element\Text('namecontact');
        $namecontact->setAttributes(array('class'=>'validate[required]'));
        $this->add($namecontact);

        $contactperson = new Element\Text('contactperson');
        $contactperson->setAttributes(array('class'=>'validate[required]'));
        $this->add($contactperson);

        $business = new Element\Text('business');
        $this->add($business);

        $address = new Element\Text('address');
        $this->add($address);

        $license = new Element\Text('license');
        $this->add($license);

        $taxcode = new Element\Text('taxcode');
        $this->add($taxcode);

        $phone = new Element\Text('phone');
        $this->add($phone);

        $email = new Element\Text('email');
        $this->add($email);

        $yahoo = new Element\Text('yahoo');
        $this->add($yahoo);

        $skype = new Element\Text('skype');
        $this->add($skype);

    }
}


text: export { default } from './LoginModal';


text:
title: Accessible Drag and Drop Using WAI-ARIA
authors:
- gez-lemon
intro: 'There is increasing interest in, and importance being laid upon, making modern dynamic web applications more accessible. New specifications such as WAI-ARIA and HTML5 provide the means by which to do this; in this article Gez Lemon presents a solution for implementing accessible drag and drop functionality using JavaScript and WAI-ARIA. Some discussion of HTML5 drag-and-drop features is also included for good measure.'
tags:
- accessibility
- drag-and-drop
- html5
- wai-aria
license: cc-by-nc-sa-2.5
---

## Introduction

This article is intended for people who create rich internet applications with drag and drop functionality and want to make them accessible. No prior knowledge of WAI-ARIA is assumed, although it is recommended you read my [introduction to WAI-ARIA][1] article before starting this article. A basic knowledge of scripting is assumed, and is necessary to understand exactly what is going on in the code example, but this knowledge is not necessary to understand the basic concepts discussed.

[1]: /articles/introduction-to-wai-aria/

After reading this article you will have an understanding of how to structure applications with drag and drop functionality so they are accessible.

The term drag and drop infers using a mouse to move an object from one location to another. Wikipedia has the following description for drag and drop:

> In computer graphical user interfaces, drag-and-drop or DnD is the action of (or support for the action of) clicking on a virtual object and dragging it to a different location or onto another virtual object. In general, it can be used to invoke many kinds of actions, or create various types of associations between two abstract objects.

## Accessibility challenges of drag and drop

Some people with poor [fine motor control][3] have difficulties making small movements and remaining steady. For this group of people, the dexterity requirements to accurately select and move an object using a pointing device can cause them problems, and they benefit from a keyboard equivalent being made available to allow them to use such functionality in a different way.

[3]: http://en.wikipedia.org/wiki/Fine_motor_skill

People with very poor eyesight are unable to perceive the start and end points of a drag and drop operation when it is conveyed in purely visual terms. For these two groups, drag and drop operations can be impossible to perform without accessibility considerations being designed into the process.

To be accessible, [all functionality should be operable through the keyboard alone][4]. Making drag and drop operations keyboard accessible is a relatively simple process, but there are a few extra challenges for people using assistive technologies, such as screen reader users and screen magnifier users. These challenges include:

[4]: http://www.w3.org/TR/WCAG20/#keyboard-operation

- Determining which objects are draggable
- Determining whether an object has been selected for dragging
- Determining the operation that will be performed when the object is dropped
- Identifying targets where the object may be dropped

Fortunately, WAI-ARIA has properties to overcome these problems.

## Drag and drop using WAI-ARIA

WAI-ARIA provides two properties to help make drag and drop accessible to users of assistive technologies:

- [`aria-grabbed`][5]
- [`aria-dropeffect`][6]

[5]: http://www.w3.org/WAI/PF/aria/#aria-grabbed
[6]: http://www.w3.org/WAI/PF/aria/#aria-dropeffect

### The `aria-grabbed` property

The `aria-grabbed` property is used to determine whether an element is in a grabbed state for a drag and drop operation.

`aria-grabbed` values

	Value
	Description

	`true`
	Indicates the element has been selected for dragging.

	`false`
	Indicates that the element is not currently selected for dragging, but can be made available for dragging by setting the property to `true`.

If an element does not have an `aria-grabbed` property at all, the element cannot be dragged.

The `aria-grabbed` attribute in the following example indicates that the list item has been selected to take part in a drag and drop operation.

### The `aria-dropeffect` property

The `aria-dropeffect` property is a state that indicates the type of operation that will occur when an object taking part in a drag and drop operation is released on the target. More than one value can be provided as a space-separated list of tokens. The following table lists the possible values for `aria-dropeffect`.

`aria-dropeffect` values

	Value
	Description

	`copy`
	The source is duplicated and dropped on the target.

	`move`
	The source is removed from its current location and dropped on the target.

	`reference`
	A reference or shortcut to the source object will be created in the target.

	`execute`
	A function supported by the target is executed using the drag source as the input.

	`popup`
	A popup menu or dialog is presented so the user can choose one of the supported operations (copy, move, reference and execute) or other drag functionality.

	`none`
	The target will not accept the source.

The `aria-dropeffect` attribute in the following example indicates that the effect of dropping an object on this unordered list would be to remove the object from its current location, and copy it into this list.

## The HTML 5 drag and drop event model

There is a well-defined [HTML 5 event model for drag and drop operations][7]. The model is too complex to cover in detail in this article, but I will cover the properties that could be useful for accessibility.

[7]: http://dev.w3.org/html5/spec/Overview.html#dnd

### The `draggable` Attribute

HTML 5 has a [`draggable` attribute][8] to indicate whether an object can take part in a drag and drop operation. The attribute has three possible values:

[8]: http://dev.w3.org/html5/spec/Overview.html#dom-draggable

`draggable` values

	Value
	Description

	`true`
	The element can be selected for dragging.

	`false`
	The element cannot be selected for dragging.

	`auto`
	The element uses the default behaviour of the user agent.

The HTML 5 drag and drop event model does not have a property that indicates whether an object has been selected to take part in a drag and drop operation, but it does have a [`dragstart` event][9] to initiate a drag and drop operation.

[9]: http://dev.w3.org/html5/spec/Overview.html#event-dragstart

The `draggable` attribute in the following example indicates that the list item can be selected to take part in a drag and drop operation.

This information could be augmented with WAI-ARIA's `aria-grabbed` property to indicate not only that it is draggable, but also whether or not it has been selected to take part in a drag and drop operation.

### The `dropEffect` Attribute

The [`DataTransfer`][10] object manages the attributes for the drag and drop operation and has a [`dropEffect` attribute][11] to determine what happens when the object is released. The attribute accepts four values.

[10]: http://dev.w3.org/html5/spec/Overview.html#datatransfer
[11]: http://dev.w3.org/html5/spec/Overview.html#dom-datatransfer-dropeffect

`dropEffect` values

	Value
	Description

	`none`
	No operation allowed.

	`copy`
	The source will be duplicated and dropped on the target.

	`link`
	The source will be linked.

	`move`
	The source will be removed from its current location and dropped on the target.

	objEvent.dataTransfer.dropEffect='move';

The value of the `dropEffect` must match one of those specified in the [`effectAllowed` attribute][12] of the `DataTransfer` object, which can be one of `none`, `copy`, `copyLink`, `copyMove`, `link`, `linkMove`, `move`, `all`, or `uninitialized`.

[12]: http://dev.w3.org/html5/spec/Overview.html#dom-datatransfer-effectallowed

## A workflow for drag and drop

I have created a simple application to demonstrate drag and drop with ARIA and outlined a workflow based on guidance from WAI-ARIA's [best practice for drag and drop operations][13]. The demonstration consists of a list of artists that can be dragged and dropped into lists under different headings, as seen in Figure 1.

[13]: http://www.w3.org/WAI/PF/aria-practices/#dragdrop

Figure 1: List of artists with three targets: favourites, tolerable, rejected

You can [check out the drag and drop example running live][15], or [download the source code][16] and play with it yourself.

[15]: {{ page.id }}/example.html
[16]: {{ page.id }}/code.zip

### Ensure objects can be reached using the keyboard

All objects that are draggable must be reachable using the keyboard alone. Some elements are already reachable using the keyboard alone, such as standard interface elements and anchors. For elements that do not usually receive focus, you can use the [`tabindex` attribute][17].

[17]: http://www.w3.org/TR/wai-aria/#tabindex

There are two useful values: a `tabindex` attribute value of `0` puts elements in keyboard tab order identical to the order they appear in the source; a negative value for the `tabindex` attribute does not put the element into the keyboard tab order, but allows it to receive programmatic focus. Using a negative `tabindex` attribute value is useful if you do not want to clutter the keyboard tab order and control navigation by defining custom keystrokes, for example using the Up and Down cursor keys to navigate through a menu.

The simple demonstration used with this article adds the list items for artists into the keyboard tab order by providing a `tabindex` attribute value of `0` so they can be navigated to using the Tab key.

	objItems[iCounter].tabIndex = 0;

### Identify draggable objects

Objects that can be dragged should have their `aria-grabbed` property set to `false` to indicate they are available for dragging, but are not currently selected for dragging.

	objItems[iCounter].setAttribute('aria-grabbed', 'false');

In the demonstration, the draggable items specify a CSS `cursor` property to provide a visual indication that items are draggable using a mouse.

	li.draggable {
		cursor:move;
		}

It is also important to ensure that interface elements have a [visible keyboard focus indicator][18]. The demonstration adds a _focus_ `class` when the source objects receive focus. The CSS for the _focus_ `class` changes the background colour to provide a visible focus indicator, in the same way items are highlighted when the mouse is hovered over draggable objects.

[18]: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#navigation-mechanisms-focus-visible

	ul li.hover,
	ul li.focused {
		background:#fc0;
		}

### Ensure the user can initiate the drag operation with the keyboard

Users must be able to select one or more elements to drag. The [DHTML Style Guide][19] suggests keystrokes to use for drag and drop. They recommended `Space` is used for single selection. `Shift Space` may be used to select contiguous objects, and `Control Space` may be used to add non-contiguous objects to the set. For each object that is selected, the `aria-grabbed` property must be set to `true`.

[19]: http://dev.aol.com/node/757/#draganddrop

In the demonstration, the `aria-grabbed` attribute is set in response to a `mousedown` or `keydown` event.

	drag.objCurrent.setAttribute('aria-grabbed', 'true');

### Identify the drop targets

After source objects have been grabbed, valid targets should be identified using the `aria-dropeffect` property with an appropriate value. In the demonstration, a function is called after an item has been selected to identify the targets with `aria-dropeffect`; this also uses a `class` to make the targets visually obvious.

	objList.className = 'highlight';
	objList.setAttribute('aria-dropeffect', 'move');

The CSS class simply sets the background colour on the list to make it visually evident it is a target (see Figure 2):

	ul.highlight {
		background:#c00;
		color:#000;
		}

Figure 2: List targets visually identified with a red background

### Ensure drop can be executed using the keyboard

The user should be able to quickly navigate to the target and perform the drop operation. The recommended keystroke combination for dropping an object onto a target is `Control M`. If the drop is likely to result in the user having to navigate through lots of interface elements in order to reach the target, it is recommended that a context menu be provided that can be activated with `Shift F10`, containing a list of possible target areas that the user can select using the Up and Down cursor keys.

In the demonstration, only one item can be dragged at a time. As pressing the `Spacebar` when the source has focus is used to indicate that the item is to take part in a drag and drop operation, it seemed unnecessary to introduce extra keystrokes to activate a context menu, so the example automatically provides the context menu when an artist is selected. The first stage is to indicate that each item has a popup menu.

	objItems[iCounter].setAttribute('aria-haspopup', 'true');

When an artist is selected with the keyboard for dragging, a dialog for the user to select a target is automatically presented, as shown in Figure 3.

Figure 3: Popup with two targets to choose from

If more than one operation is allowable, such as `copy` or `move`, an ARIA-enabled dialog should be provided whereby keyboard users can choose the operation they want to perform, and the targets should use an `aria-dropeffect` property of `popup`.

	objList.setAttribute('aria-dropeffect', 'move');

If a dialog is being used to select the target, the possible drop operations can be provided as a sub-menu for each target; otherwise, the target dialog should be displayed on the target.

### Cancelling the drag operation

The user should be able to cancel the drag operation. The recommended key to cancel the operation is the `Escape` key. After the operation has been cancelled, all draggable objects should have their `aria-grabbed` values set to `false`, and all targets should have their `aria-dropeffect` properties either set to `none` or removed. In the demonstration, the `aria-grabbed` properties are set to `false` and the `aria-dropeffect` properties are removed.

	drag.objCurrent.setAttribute('aria-grabbed', 'false');
	objList.removeAttribute('aria-dropeffect');

Keyboard focus should be set to the last grabbed source object when the drag is cancelled.

	drag.objCurrent.focus();

### Cleaning up after drag and drop operations

As with cancelling the drag operation, all targets with an `aria-dropeffect` property should either have their value set to `none`, or the property should be completely removed after the drag and drop operation. All objects with an `aria-grabbed` property should be set to `false`.

	drag.objCurrent.setAttribute('aria-grabbed', 'false');
	objList.removeAttribute('aria-dropeffect');

Objects that are no longer grabbable and that have an `aria-grabbed` property should either set the value to `undefined`, or remove the property from the DOM.

	drag.objCurrent.removeAttribute('aria-grabbed');

## Current Support

My colleague, [Steve Faulkner][22], tested the demonstration and provided feedback about how it works with current user agents. Both Internet Explorer and Firefox expose the aria-grabbed property. Unfortunately, at the time of writing this article, no screen readers support these ARIA properties, although [NVDA plans future support][23]. Even though support is not good at the moment, it is recommended to use WAI-ARIA's `aria-grabbed` and `aria-dropeffect` properties; it is simple to provide, does no harm, and implementing it now means it will be in place for when support is finally available.

[22]: http://www.paciellogroup.com/blog/
[23]: http://www.nvda-project.org/wiki/FutureGoals#ARIADragandDrop

### A quick note about CSS support

If you look at the CSS for the example, you'll notice that I have used classes for hover and focus:

	ul li.hover,
	ul li.focused {
		background:#fc0;
		}

This is because IE 6 and below don't support `:hover ` and `:focus ` pseudo-classes on non-link elements.

## Further Reading

- [Drag and drop from WAI-ARIA best practices][24].
- [HTML 5's drag and drop event model][25].
- [iCITA's Drag and Drop Examples][26].
- [Drag and drop script from Quirksmode][27].
- [Wikipedia's definition of drag and drop][28].
- [Remy Sharp's tutorial on native HTML 5 drag and drop][29]

[24]: http://www.w3.org/WAI/PF/aria-practices/#dragdrop
[25]: http://dev.w3.org/html5/spec/Overview.html#dnd
[26]: http://test.cita.uiuc.edu/aria/index.php
[27]: http://www.quirksmode.org/js/dragdrop.html
[28]: http://en.wikipedia.org/wiki/Drag-and-drop
[29]: http://html5doctor.com/native-drag-and-drop/


text:
package de.cosmocode.palava.ipc.legacy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.inject.Inject;

import de.cosmocode.palava.bridge.Content;
import de.cosmocode.palava.bridge.Server;
import de.cosmocode.palava.bridge.call.Call;
import de.cosmocode.palava.bridge.command.Job;
import de.cosmocode.palava.bridge.command.Response;
import de.cosmocode.palava.bridge.content.ErrorContent;
import de.cosmocode.palava.bridge.session.HttpSession;

/**
 * Legacy implementation of the {@link JobExecutor} interface.
 *
 * @since 1.0
 * @author Willi Schoenborn
 */
@SuppressWarnings("deprecation")
final class LegacyJobExecutor implements JobExecutor {

    private static final Logger LOG = LoggerFactory.getLogger(LegacyJobExecutor.class);

    private final Server server;

    @Inject
    public LegacyJobExecutor(Server server) {
        this.server = Preconditions.checkNotNull(server, "Server");
    }

    @Override
    public Content execute(Job job, Call call) {
        final Response response = new InternalResponse();
        final HttpSession session = call.getHttpRequest().getHttpSession();

        try {
            job.process(call, response, session, server, null);
            Preconditions.checkState(response.hasContent(), "No content set");
            return response.getContent();
        /* CHECKSTYLE:OFF */
        } catch (Exception e) {
        /* CHECKSTYLE:ON */
            LOG.error("An undeclared exception was thrown while executing " + job, e);
            return ErrorContent.create(e);
        }
    }

    /**
     * Internal implementation of the {@link Response} interface which allows simple
     * content retrieval.
     *
     * @author Willi Schoenborn
     */
    private static final class InternalResponse implements Response {

        private Content content;

        @Override
        public void setContent(Content content) {
            this.content = content;
        }

        @Override
        public Content getContent() {
            return content;
        }

        @Override
        public boolean hasContent() {
            return content != null;
        }

    }

}