'use strict';
// Controls Interface class is used to provide something to the UI that it can bind to.
// It helps the UI keep in line with the layer state.
// Due to bindings, we cannot destroy & recreate an interface when a legend item
// goes from 'Unknown Placeholder' to 'Specific Layer Type'. This means we cannot
// do object heirarchies, as to go from PlaceholderInterface to FeatureLayerInterface
// would require a new object. Instead, we have a class that exposes all possible
// methods and properties as error throwing stubs. Then we replace those functions
// with real ones once we know the flavour of interface we want.
class LayerInterface {
/**
* @param {Object} source object that provides info to the interface. usually a LayerRecord or FeatureClass
*/
constructor (source) {
this._source = source;
// TODO revisit isPlaceholder after grand refactor
this._isPlaceholder = true;
}
// shortcut function for throwing errors on unimplemented functions.
_iAmError () {
throw new Error('Call not supported.');
}
get isPlaceholder () { return this._isPlaceholder; } // returns Boolean
// these expose ui controls available on the interface and indicate which ones are disabled
get symbology () { this._iAmError(); } // returns Array
// can be group or node name
get name () { this._iAmError(); } // returns String
// these are needed for the type flag
get layerType () { this._iAmError(); } // returns String
get geometryType () { this._iAmError(); } // returns String
get featureCount () { this._iAmError(); } // returns Integer
get supportsOpacity () { this._iAmError(); } // returns Boolean
// layer states
get state () { this._iAmError(); } // returns String
// these return the current values of the corresponding controls
get visibility () { this._iAmError(); } // returns Boolean
get opacity () { this._iAmError(); } // returns Decimal
get boundingBox () { this._iAmError(); } // returns Boolean
get query () { this._iAmError(); } // returns Boolean
get snapshot () { this._iAmError(); } // returns Boolean
// fetches attributes for use in the datatable
get formattedAttributes () { this._iAmError(); } // returns Promise of Object
// these set values to the corresponding controls
setVisibility () { this._iAmError(); }
setOpacity () { this._iAmError(); }
setBoundingBox () { this._iAmError(); }
setQuery () { this._iAmError(); }
setSnapshot () { this._iAmError(); }
// updates what this interface is pointing to, in terms of layer data source.
// often, the interface starts with a placeholder to avoid errors and return
// defaults. This update happens after a layer has loaded, and new now want
// the interface reading off the real FC.
// TODO docs
updateSource (newSource) {
this._source = newSource;
}
convertToSingleLayer (layerRecord) {
this._source = layerRecord;
this._isPlaceholder = false;
newProp(this, 'symbology', standardGetSymbology);
newProp(this, 'state', standardGetState);
newProp(this, 'visibility', standardGetVisibility);
newProp(this, 'opacity', standardGetOpacity);
newProp(this, 'boundingBox', standardGetBoundingBox);
newProp(this, 'query', standardGetQuery);
newProp(this, 'name', standardGetName);
newProp(this, 'geometryType', standardGetGeometryType);
newProp(this, 'layerType', standardGetLayerType);
newProp(this, 'featureCount', standardGetFeatureCount);
newProp(this, 'supportsOpacity', standardGetSupportsOpacity);
this.setVisibility = standardSetVisibility;
this.setOpacity = standardSetOpacity;
this.setBoundingBox = standardSetBoundingBox;
this.setQuery = standardSetQuery;
}
convertToFeatureLayer (layerRecord) {
this.convertToSingleLayer(layerRecord);
newProp(this, 'snapshot', featureGetSnapshot);
newProp(this, 'formattedAttributes', standardGetFormattedAttributes);
newProp(this, 'geometryType', featureGetGeometryType);
newProp(this, 'featureCount', featureGetFeatureCount);
this.setSnapshot = featureSetSnapshot;
}
convertToDynamicLeaf (dynamicFC) {
this._source = dynamicFC;
this._isPlaceholder = false;
newProp(this, 'symbology', dynamicLeafGetSymbology);
newProp(this, 'state', dynamicLeafGetState);
newProp(this, 'name', dynamicLeafGetName);
newProp(this, 'visibility', dynamicLeafGetVisibility);
newProp(this, 'opacity', dynamicLeafGetOpacity);
newProp(this, 'query', dynamicLeafGetQuery);
newProp(this, 'formattedAttributes', dynamicLeafGetFormattedAttributes);
newProp(this, 'geometryType', dynamicLeafGetGeometryType);
newProp(this, 'layerType', dynamicLeafGetLayerType);
newProp(this, 'featureCount', dynamicLeafGetFeatureCount);
newProp(this, 'supportsOpacity', dynamicLeafGetSupportsOpacity);
this.setVisibility = dynamicLeafSetVisibility;
this.setOpacity = dynamicLeafSetOpacity;
this.setQuery = dynamicLeafSetQuery;
}
convertToPlaceholder (placeholderFC) {
this._source = placeholderFC;
this._isPlaceholder = true;
newProp(this, 'symbology', standardGetSymbology);
newProp(this, 'name', standardGetName);
newProp(this, 'state', standardGetState);
newProp(this, 'layerType', standardGetLayerType);
newProp(this, 'supportsOpacity', placeholderGetSupportsOpacity);
}
}
/**
* Worker function to add or override a get property on an object
*
* @function newProp
* @private
* @param {Object} target the object that will receive the new property
* @param {String} propName name of the get property
* @param {Function} getter the function defining the guts of the get property.
*/
function newProp(target, propName, getter) {
Object.defineProperty(target, propName, {
get: getter,
enumerable: true,
configurable: true
});
}
// these functions are upgrades to the duds above.
// we don't use arrow notation, as we want the `this` to point at the object
// that these functions get smashed into.
function standardGetState() {
/* jshint validthis: true */
return this._source.state;
}
function dynamicLeafGetState() {
/* jshint validthis: true */
return this._source.state;
}
function standardGetVisibility() {
/* jshint validthis: true */
return this._source.visibility;
}
function dynamicLeafGetVisibility() {
/* jshint validthis: true */
return this._source.getVisibility();
}
function standardGetName() {
/* jshint validthis: true */
return this._source.name;
}
function dynamicLeafGetName() {
/* jshint validthis: true */
return this._source.name;
}
function standardGetOpacity() {
/* jshint validthis: true */
return this._source.opacity;
}
function dynamicLeafGetOpacity() {
/* jshint validthis: true */
return this._source.opacity;
}
function standardGetLayerType() {
/* jshint validthis: true */
return this._source.layerType;
}
function dynamicLeafGetLayerType() {
/* jshint validthis: true */
return this._source.layerType;
}
function standardGetBoundingBox() {
/* jshint validthis: true */
// dont be fooled by function/prop name, we are returning bbox visibility,
// not the box itself
return this._source.isBBoxVisible();
}
function standardGetQuery() {
/* jshint validthis: true */
return this._source.isQueryable();
}
function dynamicLeafGetQuery() {
/* jshint validthis: true */
return this._source.queryable;
}
function standardGetFormattedAttributes() {
/* jshint validthis: true */
return this._source.getFormattedAttributes();
}
function dynamicLeafGetFormattedAttributes() {
/* jshint validthis: true */
// TODO code-wise this looks identical to standardGetFormattedAttributes.
// however in this case, ._source is a DynamicFC, not a LayerRecord.
// This is safer. Deleting this would avoid the duplication. Decide.
return this._source.getFormattedAttributes();
}
function standardGetSymbology() {
/* jshint validthis: true */
return this._source.symbology;
}
function dynamicLeafGetSymbology() {
/* jshint validthis: true */
// TODO code-wise this looks identical to standardGetSymbology.
// however in this case, ._source is a DynamicFC, not a LayerRecord.
// This is safer. Deleting this would avoid the duplication. Decide.
return this._source.symbology;
}
function standardGetGeometryType() {
/* jshint validthis: true */
return undefined;
}
function featureGetGeometryType() {
/* jshint validthis: true */
return this._source.getGeomType();
}
function dynamicLeafGetGeometryType() {
/* jshint validthis: true */
// TEST STATUS none
return this._source.geomType;
}
function standardGetFeatureCount() {
/* jshint validthis: true */
return undefined;
}
function featureGetFeatureCount() {
/* jshint validthis: true */
return this._source.featureCount;
}
function dynamicLeafGetFeatureCount() {
/* jshint validthis: true */
return this._source.featureCount;
}
function standardGetSupportsOpacity() {
/* jshint validthis: true */
// at top level, layers support opacity.
return true;
}
function dynamicLeafGetSupportsOpacity() {
/* jshint validthis: true */
// leaves are the weird case. it depends on the service they live in.
return this._source.supportsOpacity;
}
function placeholderGetSupportsOpacity() {
/* jshint validthis: true */
// TODO UI might need something more useful?
return false;
}
function standardSetVisibility(value) {
/* jshint validthis: true */
this._source.visibility = value;
}
function dynamicLeafSetVisibility(value) {
/* jshint validthis: true */
this._source.setVisibility(value);
// TODO see if we need to trigger any refresh of parents.
// it may be that the bindings automatically work.
}
function standardSetOpacity(value) {
/* jshint validthis: true */
this._source.opacity = value;
}
function dynamicLeafSetOpacity(value) {
/* jshint validthis: true */
this._source.opacity = value;
}
function standardSetBoundingBox(value) {
/* jshint validthis: true */
if (this._source.bbox) {
this._source.bbox.setVisibility(value);
}
}
function standardSetQuery(value) {
/* jshint validthis: true */
this._source.setQueryable(value);
}
function dynamicLeafSetQuery(value) {
/* jshint validthis: true */
this._source.queryable = value;
}
function featureGetSnapshot() {
/* jshint validthis: true */
return this._source.isSnapshot;
}
function featureSetSnapshot() {
// TEST STATUS none
// TODO trigger the snapshot process. need the big picture on how this orchestrates.
// it involves a layer reload so possible this function is irrelevant, as the record
// will likely get nuked
console.log('MOCKING THE SNAPSHOT PROCESS');
}
// TODO implement infoType / infoContent for static entry.
// who supplies this? how does it get passed in.
module.exports = () => ({
LayerInterface
});