DomTaskPlayer

This is a continuation of the article Project - AutoPlayDom. The project was renamed to DomTaskPlayer, because the new name is semantically more correct, as the object plays through a series of Dom related tasks. The full source code is available at http://www.mattsnider.com/assets/js/widget/DomTaskPlayer.js. The source code has drastically changed from last week, as supporting backwards navigation through the already played DomTasks, required a massive redesign of the code.

For starters the "DomTaskPlayer.add" method has changed, to accept a collection of step objects, which will contain task objects, which will contain action objects:

Example 1: A Step Object

var step = {
	_isValid: false, // used internally, updated after first next call
	name: , // what this step is
	tasks: [ // an array of tasks
		{ // task
			action: [
				{fx: function() {}, method: , params: [], reverseMethod: , reverseFx: function() {}}, // action
				…
			],
			search: [
				{fx: function() {}, method: , params: []}, // action
				…
			],
			reverse: [] // internally managed
		},
		…
	]
};

A step object is a collection of task objects and a name to label the step. The task object is a collection of search action objects (to find the node(s) to apply actions to), and a collection of action action objects (actually describing how to change the nodes found when searching). There is no limit to the number of tasks per step and actions per task. When the "DomTaskPlayer.next" method is called an internal collection of action objects are attached to the reverse list, explaining how to reverse the current step.

The next and previous methods have been simplified, delegating the work to smaller more targeted internal assertion and action functions.

Example 2: DomTaskPlayer.next

next: function() {
    // go to the next position
    if (_that.hasNext()) {
        _index += 1;

        var step = _steps[_index];

        // already validated and found nodes, no need to assert
        if (step._isValid) {
            _array_walk(step.tasks, function(task) { // iterate on the tasks
                _array_walk(task.action, _execute_action); // iterate on the actions
            });
        }
        else {
            _array_walk(step.tasks, function(task) { // iterate on the tasks
                _assert_valid_task(task);

                var tobj = {};

                _array_walk(task.search, _execute_search, tobj);

                _array_walk(task.action, function(action) { // iterate on the actions
                    _assert_valid_action(action); // validates and/or sets action.fx; assures action.params is an array
                    _assert_user_reversible(action); // user fx is reversible
                    action.nodes = tobj.nodes;
                    _execute_action(action);
                });
            });

            step._isValid = true;
        }

        _that.onStepChange.fire([step, next]);
    }
},

This method first asserts that there is a next step available, then grabs the step from the internal step array. If the step has the internal variable _isValid set to true, then the method knows that it has previously already validated this step and can skip the search actions, executing the action tasks on cached DOM nodes. Otherwise, it iterates through each task and asserts that the tasks are valid. Afterwards, it searches the DOM using the search action objects, producing an object with key nodes. Then it iterates on the action action objects and asserts they are valid and reversible. If no errors have been thrown, terminating the flow, then the method actually executes the action objects. As each action object is executed, the internal methods create the reverse actions, which will be used with the previous method.

In addition, to supporting previous and the massive revisions to next, there is now a build-in, public method initKeyListeners that hijacks the arrow keys (and a - left arrow, s - down arrow, d - right arrow, w - up arrow) to call one of the public action methods of DomTaskPlayer (previous, stop, next, play). The developer can require that any combination of Ctrl, Alt, or Shift be pressed as well, so not to be obtrusive.

Example 3: DomTaskPlayer.initKeyListeners

initKeyListeners: function(forceCtrl, forceShift, forceAlt) {
    _YE.on(document, keydown, function(e) {
        if (forceCtrl && ! e.ctrlKey) {return;} // may trigger browser events
        if (forceShift && ! e.shiftKey) {return;}
        if (forceAlt && ! e.altKey) {return;}

        _YE.preventDefault(e); // stop browser events

        switch (_YE.getCharCode(e)) {
            case _YK.DOWN:
            case 83: // s
                _that.stop();
            break;

            case _YK.LEFT:
            case 65: // a
                _that.previous();
            break;

            case _YK.RIGHT:
            case 68: // d
                _that.next();
            break;

            case _YK.UP:
            case 87: // w
                _that.play();
            break;

            default: // do nothing
        }
    });
    _that.initKeyListeners = function() {};
},

At the end of initKeyListeners the method is set to empty function, thereby preventing the developer from accidentally attaching these key listeners more than once. This method can be called anytime after the DomTaskPlayer object has been initialized and it is recommended that at least two special keys be set to true, such as Shift and Ctrl:

Example 4: Calling initKeyListeners

yourDomTaskPlayerObject.initKeyListeners(true, true);

In Example 4, the user will need to hold Shift + Ctrl + ArrowKey to trigger the desired method.

Most importantly, this tool is easy to use; see Dom Task Player Test Page. Although easy to use, this tool is very complicated, please let me know if something does not work for you or are having trouble getting it working.

Lastly, please keep in mind that order of operations are very important with tasks, so if you first apply a class that changes the background, then change the background with a style directly in the same step object, then the reverse operation will be confused and apply the wrong background color.