baja/ord/ViewQuery.js

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

/**
 * Defines {@link baja.ViewQuery}.
 * @module baja/ord/ViewQuery
 */
define([ "bajaScript/sys",
        "bajaScript/baja/ord/OrdQuery" ], 
    function (baja, OrdQuery) {
  
  "use strict";
  
  var subclass = baja.subclass,
      callSuper = baja.callSuper,
      strictArg = baja.strictArg;
  
  function parseViewQuery(body) {
    // Parse the view query (viewId?params)
    var res,
        view,
        regex,
        params;
    
    if (body) {
      res = /^([^?]*)(?:[?](.*))?$/.exec(body);
    }
    
    if (!res || (res && res.length < 2)) {
      throw new Error("Invalid view query: " + body);
    }
    
    // Note down the view query information onto the ORD target so it can be accessed
    view = {
      id: res[1] || "",
      params: {}
    };
    
    // If there are some view parameters then parse them
    if (res[2]) {
      regex = /([^=]+)=([^;]*);?/g;
      params = regex.exec(res[2]);
          
      while (params) { 
        view.params[decodeURIComponent(params[1])] = decodeURIComponent(params[2]);
        params = regex.exec(res[2]);
      }
    }
    
    return view;
  }

  /**
   * `ViewQuery` defines user agent information.
   *
   * @class
   * @alias baja.ViewQuery
   * @extends OrdQuery
   *
   * @param {String|Object} body the view query body or an object literal for 
   *                             the view id and parameters.
   * @param {String} [body.id] view id.
   * @param {Object} [body.params] view parameters (key value pairs in an Object Literal).
   */
  var ViewQuery = function ViewQuery(body) {
    // If an Object is passed in then attempt to get id and params from object
    if (body && typeof body === "object") {
      var params = body.params || {},
          i;
    
      this.$view = {
        id: body.id || "",
        params: params
      };
    
      // Build up a new view query body String
      i = 0;
      body = this.$view.id;
          
      baja.iterate(params, function (prop, propName) {
        if (i === 0) {
          body += "?";
        } else if (i > 0) {
          body += ";";
        }
        body += propName + "=" + prop;
        ++i;
      });
    } else {
      this.$view = parseViewQuery(body);
    }
  
    callSuper(ViewQuery, this, [ {
      scheme: baja.ViewScheme.DEFAULT,
      schemeName: "view",
      body: strictArg(body, String)
    } ]);
  };
  
  subclass(ViewQuery, OrdQuery);
  
  /**
   * Normalize the query and return true if modified.
   *
   * @private
   *
   * @param {baja.OrdQueryList} list
   * @param {Number} index
   *
   * @returns {Boolean}
   */
  ViewQuery.prototype.normalize = function (list, index) {
    var modified = false;     
    if (list.get(index + 1) && 
        list.get(index + 1).getSchemeName().equals("view")) {

      //NCCB-20196: merge adjacent ViewQueries
      var first = list.get(index),
          firstParams = first.getParameters(),
          second = list.get(index + 1),
          secondParams = second.getParameters(),
          mergedViewId = second.getViewId() ? second.getViewId() : first.getViewId(), //use second if present
          mergeParams = {};

      Object.keys(firstParams).forEach(function (attr) {
        mergeParams[attr] = firstParams[attr];
      });

      Object.keys(secondParams).forEach(function (attr) {
        mergeParams[attr] = secondParams[attr];
      });

      list.set(index, new ViewQuery({
        id: mergedViewId,
        params: mergeParams
      }));

      // If the next scheme is the same as this one then
      list.remove(index + 1);
      modified = true;
    } else if (index < list.size() - 1) {
      // Ensure view query is always the last in the list
      list.remove(index);
      modified = true;
    }
    return modified;
  };
  
  /**
   * Return the view id (this could be view type spec or the name of a Px view).
   *
   * @returns {String}
   */
  ViewQuery.prototype.getViewId = function () {
    return this.$view.id;
  };
    
  /**
   * Return the view parameters as an object literal.
   * 
   * Please note, this returns a defensive copy of the parameters.
   *
   * @returns {Object} the parameters.
   */
  ViewQuery.prototype.getParameters = function () {
    var o = {};
    baja.iterate(this.$view.params, function (prop, propName) {
      o[propName] = prop;
    });
    return o;
  };
  
  return ViewQuery;
});