[Edit] Implement EditToolbar

Implement EditToolbar (sufficient to satisfy spec) which will
construct an mct-toolbar-friendly form structure based on both
the current selection and the view's defined toolbar structure.
WTD-878.
This commit is contained in:
Victor Woeltjen 2015-02-17 13:47:14 -08:00
parent 8f14f4e5eb
commit 5dc72b15c2
2 changed files with 151 additions and 6 deletions

View File

@ -4,17 +4,162 @@ define(
function () {
"use strict";
// Utility functions for reducing truth arrays
function and(a, b) { return a && b; }
function or(a, b) { return a || b; }
/**
* Provides initial structure and state (as suitable for provision
* to the `mct-toolbar` directive) for a view's tool bar, based on
* that view's declaration of what belongs in its tool bar and on
* the current selection.
*
* @param structure toolbar structure, as provided by view definition
* @param {Array} selection the current selection state
* @constructor
*/
function EditToolbar(structure, selection) {
var toolbarStructure = Object.create(structure || {}),
toolbarState,
properties = [];
// Generate a new key for an item's property
function addKey(property) {
properties.push(property);
return properties.length - 1; // Return index of property
}
// Update value for this property in all elements of the
// selection which have this property.
function updateProperties(property, value) {
// Update property in a selected element
function updateProperty(selected) {
// Ignore selected elements which don't have this property
if (selected[property] !== undefined) {
// Check if this is a setter, or just assignable
if (typeof selected[property] === 'function') {
selected[property](value);
} else {
selected[property] = value;
}
}
}
// Update property in all selected elements
selection.forEach(updateProperty);
}
// Look up the current value associated with a property
// in selection i
function lookupState(property, selected) {
var value = selected[property];
return (typeof value === 'function') ? value() : value;
}
// Get initial value for a given property
function initializeState(property) {
var result;
// Look through all selections for this property;
// values should all match by the time we perform
// this lookup anyway.
selection.forEach(function (selected) {
result = (selected[property] !== undefined) ?
lookupState(property, selected) :
result;
});
return result;
}
// Check if all elements of the selection which have this
// property have the same value for this property.
function isConsistent(property) {
var consistent = true,
observed = false,
state;
// Check if a given element of the selection is consistent
// with previously-observed elements for this property.
function checkConsistency(selected) {
var next;
// Ignore selections which don't have this property
if (selected[property] !== undefined) {
// Look up state of this element in the selection
next = lookupState(property, selected);
// Detect inconsistency
if (observed) {
consistent = consistent && (next === state);
}
// Track state for next iteration
state = next;
observed = true;
}
}
// Iterate through selections
selection.forEach(checkConsistency);
return consistent;
}
// Used to filter out items which are applicable (or not)
// to the current selection.
function isApplicable(item) {
var property = (item || {}).property,
exclusive = !(item || {}).inclusive;
// Check if a selected item defines this property
function hasProperty(selected) {
return selected[property] !== undefined;
}
return property && selection.map(hasProperty).reduce(
exclusive ? and : or,
exclusive
) && isConsistent(property);
}
// Prepare a toolbar item based on current selection
function convertItem(item) {
var converted = Object.create(item || {});
converted.key = addKey(item.property);
return converted;
}
// Used to filter out sections that have become empty
function nonEmpty(section) {
return section && section.items && section.items.length > 0;
}
// Prepare a toolbar section based on current selection
function convertSection(section) {
var converted = Object.create(section || {});
converted.items =
((section || {}).items || [])
.map(convertItem)
.filter(isApplicable);
return converted;
}
toolbarStructure.sections =
((structure || {}).sections || [])
.map(convertSection)
.filter(nonEmpty);
toolbarState = properties.map(initializeState);
return {
/**
*
*/
getStructure: function () {
return toolbarStructure;
},
getState: function () {
return toolbarState;
},
updateState: function (key) {
updateState: function (key, value) {
updateProperties(properties[key], value);
}
};
}

View File

@ -35,8 +35,8 @@ define(
testAB = { a: 0, b: 1 };
testABC = { a: 0, b: 1, c: 2 };
testABC2 = { a: 4, b: 1, c: 2 }; // For inconsistent-state checking
testABCXYZ = { a: 'A!', b: 'B!', c: 'C!', x: 'X!', y: 'Y!', z: 'Z!' };
testABCYZ = { a: 'A!', b: 'B!', c: 'C!', y: 'Y!', z: 'Z!' };
testABCXYZ = { a: 0, b: 1, c: 2, x: 'X!', y: 'Y!', z: 'Z!' };
testABCYZ = { a: 0, b: 1, c: 2, y: 'Y!', z: 'Z!' };
});
it("provides properties from the original structure", function () {