baja/ord/OrdTarget.js

/**
 * @copyright 2015 Tridium, Inc. All Rights Reserved.
 * @author Gareth Johnson
 */

/**
 * Defines `OrdTarget` (not exposed on `baja` namespace).
 * @module baja/ord/OrdTarget
 */
define([ "bajaScript/sys" ], function (baja) {
  
  "use strict";
  
  var subclass = baja.subclass,
      BaseBajaObj = baja.BaseBajaObj;
  
  /**
   * ORD Target.
   * 
   * This constructor shouldn't be invoked directly.
   *  
   * @class
   * @alias module:baja/ord/OrdTarget
   * @extends baja.BaseBajaObj
   * @inner
   * @public
   *
   * @param {module:baja/ord/OrdTarget} [base]  the base ORD Target
   */
  var OrdTarget = function OrdTarget(base) {    
    this.base = base || null;  
    this.slot = base && base.slot;
    this.propertyPath = base && base.propertyPath;
    this.slotPath = base && base.slotPath;
    this.container = base && base.container;
    this.object = null;
    this.ord = (base && base.ord) || null;
    this.$serverPermissionsStr = base && base.$serverPermissionsStr;
    this.$canRead = base && base.$canRead;
    this.$canWrite = base && base.$canWrite;
    this.$canInvoke = base && base.$canInvoke;
  };
  
  subclass(OrdTarget, BaseBajaObj);

  /**
   * Return a clone of this OrdTarget object.
   *
   * @returns {module:baja/ord/OrdTarget}
   * @since Niagara 4.14
   */
  OrdTarget.prototype.clone = function () {
    const target =  new OrdTarget();
    target.base = this.base;
    target.slot = this.slot;
    target.propertyPath = this.propertyPath;
    target.slotPath = this.slotPath;
    target.container = this.container;
    target.object = this.object;
    target.ord = this.ord;
    target.$facets = this.getFacets();
    target.$canRead = this.canRead();
    target.$canWrite = this.canWrite();
    target.$canInvoke = this.canInvoke();
    target.$serverPermissionsStr = this.$serverPermissionsStr;
    return target;
  };

  /**
   * Return the ord used to resolve the target.
   *
   * @returns {baja.Ord}
   * @since Niagara 4.10
   */
  OrdTarget.prototype.getOrd = function () {
    return this.ord;
  };
  
  /**
   * Return the Component for the ORD Target.
   *
   * @returns {baja.Component}
   */
  OrdTarget.prototype.getComponent = function () {
    var that = this,
        container = that.container,
        object = that.object,
        base = that.base,
        o;
    
    if (baja.hasType(container, 'baja:Component')) {
      return container;
    }
    
    if (baja.hasType(object)) {
      if (object.getType().isComponent()) {
        return object;
      }
      
      if (object.getType().isComplex()) {
        o = object.getParent();
        while (o !== null) {
          if (o.getType().isComponent()) {
            break;
          }
          o = o.getParent();
        }
        if (o !== null) {
          return o;
        }
      }
    }
    
    if (base && typeof base === "object") {
      return base.getComponent();
    }
    
    return null;
  };  
  
  /**
   * Return the object associated with this `OrdTarget`.
   * 
   * This method will attempt to access any resolved value from its
   * Property. If the resolved value wasn't resolved from a Property then 
   * the original resolved object is returned.
   *
   * @returns {baja.Object}
   */
  OrdTarget.prototype.getObject = function () {
    var that = this,
        parent = that.container,
        slot = that.slot,
        propertyPath = that.propertyPath,
        i,
        val;
      
    if (parent) {
      // Walk up the Property Path
      if (propertyPath) {
        if (propertyPath.length > 0) {
          val = parent;
          for (i = 0; i < propertyPath.length; ++i) {
            val = val.get(propertyPath[i]);
          } 
          return val;          
        }
      // If no PropertyPath then access from the Slot
      } else if (slot && slot.isProperty()) {
        return parent.get(slot);
      }
    }
    
    // By default, just return the object originally resolved
    return that.object;
  };

  /**
   * If the ord resolves to a slot within a component, then return the slot
   * of `getComponent()`. If not applicable, then return null.
   * @since Niagara 4.10
   * @returns {baja.Slot | null}
   */
  OrdTarget.prototype.getSlotInComponent = function () {

    var that = this,
      base = that.base,
      container = that.container,
      object = that.object,
      slot = that.slot;


    if (slot) {
      return slot;
    }

    if (!object || !base || container) {
      return null;
    }

    if (!object.getType().isComplex() || !object.getParentComponent()) {
      return base.getSlotInComponent();
    }

    return null;
  };

  /**
   * Convenience to check if the underlying target is readable.
   *
   * @since Niagara 4.12
   * @returns {boolean}
   */
  OrdTarget.prototype.canRead = function () {
    const comp = this.getComponent();
    if (comp) {
      return comp.canRead(this);
    }
    return !!this.$canRead;   
  };
  
  /**
   * Convenience to check if the underlying target is writable.
   * 
   * @since Niagara 4.10
   * @returns {boolean}
   */
  OrdTarget.prototype.canWrite = function () {
    const comp = this.getComponent();
    if (comp) {
      return comp.canWrite(this);
    }
    return !!this.$canWrite;
  };

  /**
   * Convenience to check if the underlying target is invokable.
   *
   * @since Niagara 4.12
   * @returns {boolean}
   */
  OrdTarget.prototype.canInvoke = function () {
    const comp = this.getComponent();
    if (comp) {
      return comp.canInvoke(this);
    }
    return !!this.$canInvoke;
  };

  /**
   * @since Niagara 4.14
   * @returns {baja.Permissions}
   */
  OrdTarget.prototype.getPermissionsOnTarget = function () {
    return this.$serverPermissionsStr ?
      baja.Permissions.make(this.$serverPermissionsStr) : baja.Permissions.all;
  };

  /**
   * @returns {baja.Facets} the correct slot facets, merged from the parent
   * component through to the target object. If no parent component or no
   * property path, returns `baja.Facets.DEFAULT`.
   * @since Niagara 4.10
   */
  OrdTarget.prototype.getFacets = function () {
    var facets = this.$facets;

    if (facets) { return facets; }

    var serverFacets = this.$serverFacets;
    var comp = this.getComponent();
    if (comp) {
      facets = baja.Facets.mergeSlotFacets(comp, this.propertyPath || this.slot);
    }

    if (serverFacets) {
      facets = baja.Facets.make(serverFacets, facets);
      delete this.$serverFacets;
    }

    return (this.$facets = facets || baja.Facets.DEFAULT);
  };
  
  return OrdTarget;
});