menu

SiteGenesis / Server-side JS / Source: app_storefront_controllers/cartridge/scripts/models/CompareModel.js

'use strict';
/**
 * Model for the custom logic of the product compare feature.
 * The implementation is bundled into a class which provides
 * method access the compare data. The compare data itself is stored as
 * a JSON string in the privacy attributes of the session.
 *
 * @module models/CompareModel
 */

/**
 * Constructs a new compare list.
 * @constructor
 * @constructs module:models/CompareModel~CompareList
 */
function CompareList() {
    /** Copy of reference to this, for use in scopes where this refers to another object. */
    var that = this;

    /** The ID of the category currently being compared. */
    var currentCategoryID = null;

    /** Hash of category IDs to arrays of product IDs. */
    var categoryProducts = {};

    /** Returns the current category of products to compare.
    * @alias module:models/CompareModel~CompareList/getCategory
    * @return {String} Current category ID.
    */
    this.getCategory = function () {
        return currentCategoryID;
    };

    /**
     * Returns a set of IDs of products being compared for the current category,
     * or the empty set if no category is selected.
     *
     * @alias module:models/CompareModel~CompareList/getProducts
     * @return {dw.util.LinkedHashSet} Product IDs of any products being compared for the current category.
     */
    this.getProducts = function () {
        var products = new dw.util.LinkedHashSet();

        if (currentCategoryID !== null) {
            var categoryProductArray = categoryProducts[currentCategoryID];
            if (categoryProductArray) {
                for (var i = 0; i < categoryProductArray.length; i++) {
                    products.add(categoryProductArray[i]);
                }
            }
        }

        return products;
    };

    /**
     * Returns a map of category IDs and names, for all categories
     * that have at least one product to compare.
     *
     * @alias module:models/CompareModel~CompareList/getCategories
     * @return {dw.util.LinkedHashMap} Map of category IDs and display names.
     */
    this.getCategories = function () {
        var categories = new dw.util.LinkedHashMap();

        for (var categoryID in categoryProducts) {
            var category = dw.catalog.CatalogMgr.getCategory(categoryID);
            if (category !== null) {
                categories.put(categoryID, category.getDisplayName());
            }
        }

        return categories;
    };

    /** Returns a set of maps, each map representing an attribute group.
    *
    * @alias module:models/CompareModel~CompareList/getAttributeGroups
    * @return {dw.util.LinkedHashSet} Attribute groups are returned as a set of maps, where each attribute group
    * is a separate map.
    * @see module:models/CompareModel~CompareList/findAttributeGroups for map structure information.
    */
    this.getAttributeGroups = function () {
        if (currentCategoryID === null) {
            return new dw.util.LinkedHashSet();
        }

        var categoryProductArray = categoryProducts[currentCategoryID];
        if (!categoryProductArray) {
            return new dw.util.LinkedHashSet();
        }

        // Creates a list of paths from root to classification category for products.
        var paths = new dw.util.ArrayList();
        for (var i = 0; i < categoryProductArray.length; i++) {
            // Gets the product with this ID.
            var p = dw.catalog.ProductMgr.getProduct(categoryProductArray[i]);
            if (p !== null) {
                var category = p.getClassificationCategory();

                var path = new dw.util.ArrayList();
                while (category !== null) {
                    path.addAt(0, category);
                    category = category.getParent();
                }

                paths.add(path);
            }
        }
        return findAttributeGroups(findDeepestCommonCategory(paths));
    };

    /**
     * Returns the deepest common category among the given list of paths starting at the root.
     *
     * @alias module:models/CompareModel~CompareList/findDeepestCommonCategory
     * @param {dw.util.ArrayList} paths - List of paths from root to category.
     */
    function findDeepestCommonCategory(paths) {
        // No common category if no paths.
        if ((paths === null) || paths.isEmpty()) {
            return null;
        }

        // Assume no common category.
        /** @type {dw.catalog.Category} */
        var deepestCommonCategory = null;

        // Compares the first path to the others.
        /** @type {dw.util.ArrayList} */
        var comparePath = paths.get(0);
        for (var i = 0; i < comparePath.size(); i++) {
            // Gets category at level i in the first path.
            var compareCategory = comparePath.get(i);
            if (compareCategory === null) {
                return deepestCommonCategory;
            }

            // Compares category to level i categories in other paths.
            for (var j = 1; j < paths.size(); j++) {
                var otherPath = paths.get(j);

                // Quit if other path is shorter.
                if (i >= otherPath.size()) {
                    return deepestCommonCategory;
                }

                // Quits if the other path has a different category at level i.
                var otherCategory = otherPath.get(i);
                if ((otherCategory === null) || (otherCategory.getID() !== compareCategory.getID())) {
                    return deepestCommonCategory;
                }
            }

            // Updates deepest common category among paths.
            deepestCommonCategory = compareCategory;
        }

        return deepestCommonCategory;
    }

    /**
     * Returns the set of attribute groups for the given classification category,
     * each one represented as a map.
     *
     * @alias module:models/CompareModel~CompareList/findAttributeGroups
     * @param {dw.catalog.Category} classificationCategory - The classification category.
     * @return {dw.util.LinkedHashSet} Set of maps containing attribute groups.
     * Each map contains:
     *<ul>
     *<li>descriptor - description of visible attributes in the group.</li>
     *<li>displayName - display name of visible attributes in the group.</li>
     *<li>attributes - an ArrayList of HashMaps. Each map contains the descriptor and display name for an attribute in the group. </li>
     *</ul>
     *
     */
    function findAttributeGroups(classificationCategory) {
        // Gets the product attribute model.
        var model;
        if (classificationCategory === null) {
            model = new dw.catalog.ProductAttributeModel();
        } else {
            model = classificationCategory.getProductAttributeModel();
        }

        // Gets the attribute groups for attribute model.
        var persistentAttributeGroups = model.getVisibleAttributeGroups().iterator();

        var attributeGroups = new dw.util.LinkedHashSet();
        while (persistentAttributeGroups.hasNext()) {
            var persistentAttributeGroup = persistentAttributeGroups.next();
            var persistentAttributeDescriptors = model.getVisibleAttributeDefinitions(persistentAttributeGroup).iterator();

            // Creates attributes.
            var groupAttributes = new dw.util.ArrayList();
            while (persistentAttributeDescriptors.hasNext()) {
                var persistentAttributeDescription = persistentAttributeDescriptors.next();
                var attributeDesc = new dw.util.HashMap();

                attributeDesc.put('descriptor', persistentAttributeDescription);
                attributeDesc.put('displayName', persistentAttributeDescription.getDisplayName());

                groupAttributes.add(attributeDesc);
            }

            // Creates attribute group.
            var attributeGroup = new dw.util.HashMap();
            attributeGroup.put('descriptor', persistentAttributeGroup);
            attributeGroup.put('displayName', persistentAttributeGroup.getDisplayName());
            attributeGroup.put('attributes', groupAttributes);

            attributeGroups.add(attributeGroup);
        }

        return attributeGroups;
    }

    /** Stores a representation of this product comparison in the session.
    *
    * @alias module:models/CompareModel~CompareList/store
    */
    function store() {
        session.privacy.productComparison = toJSON();
    }

    /** Returns a string representation of this compare list, used to store comparison information in the session.
    * @return {String} JSON object representing a product comparison.
    */
    function toJSON() {
        var o = {
            cid: (currentCategoryID ? currentCategoryID : null),
            prods: categoryProducts
        };
        return JSON.stringify(o);
    }

    /**
     * Sets the state of this compare list based on the given string, typically from the session.
     * Saves the product comparison to the session.
     * @alias module:models/CompareModel~CompareList/fromJSON
     * @param {String} json JSON data
     * @see module:models/CompareModel~CompareList/store
     */
    this.fromJSON = function (json) {
        if (!json) {
            return;
        }
        try {
            var data = JSON.parse(json);
            currentCategoryID = data.cid ? data.cid : null;
            categoryProducts = data.prods ? data.prods : {};
        } catch (e) {
            dw.system.Logger.error(e);
        }
    };

    /**
     * Sets the current category of products to compare to the one with the given ID.
     * Saves the current product comparison to the session.
     * @alias module:models/CompareModel~CompareList/setCategory
     * @param {String} categoryID - The category ID.
     * @see module:models/CompareModel~CompareList/store
     */
    this.setCategory = function (categoryID) {
        currentCategoryID = categoryID;
        copyParentCategory();

        store();
    };

    /**
     * Adds a product to the set of compared products for the given category.
     * Saves the current product comparison to the session.
     * @alias module:models/CompareModel~CompareList/add
     * @param {dw.catalog.Product} p the product to add
     * @param {dw.catalog.Category} c the category for which to add the object
     * @see module:models/CompareModel~CompareList/store
     */
    this.add = function (p, c) {
        var products = categoryProducts[c.getID()];

        // Creates set if necessary.
        if (!products) {
            products = [];
            categoryProducts[c.getID()] = products;
        }

        // Checks if the product is already in set.
        var found = false;
        for (var i = 0; i < products.length; i++) {
            found |= (products[i] === p.getID());
        }

        // Adds the product if it is not.
        if (!found) {
            products.push(p.getID());
        }

        store();
    };

    /**
     * Copies to the current category the applicable compare products of the parent category,
     * if there are no products to compare for the current category.
     *
     * @alias module:models/CompareModel~CompareList/copyParentCategory
     */
    function copyParentCategory() {
        // Quits if no category set is set.
        if (currentCategoryID === null) {
            return;
        }

        // Quits if the category already has products.
        if (categoryProducts[currentCategoryID]) {
            return;
        }

        // Gets the category.
        var category = dw.catalog.CatalogMgr.getCategory(currentCategoryID);
        if (category === null) {
            return;
        }

        // Gets the parent category.
        var parent = category.getParent();
        if (parent === null) {
            return;
        }

        // Gets product IDs for parent category.
        var products = categoryProducts[parent.getID()];
        if (!products) {
            return;
        }

        // Adds products from parent category also assigned to the category.
        for (var i = 0; i < products.length; i++) {
            var product = dw.catalog.ProductMgr.getProduct(products[i]);
            if ((product !== null) && productAssignedToCategory(product, category)) {
                that.add(product, category);
            }
        }
    }

    /**
     * Checks if a product is assigned to a category or any child category of the category.
     *
     * @alias module:models/CompareModel~CompareList/productAssignedToCategory
     * @param {dw.catalog.Product} p product
     * @param {dw.catalog.Category} c  category
     * @returns {Boolean} true if the product is assigned to the given category or one of its children,
     * or false if it is not.
     */
    function productAssignedToCategory(p, c) {
        var assignments = p.getCategoryAssignments();
        for (var it = assignments.iterator(); it.hasNext();) {
            var assignment = it.next();

            var assignedCategory = assignment.getCategory();
            while (assignedCategory !== null) {
                if (assignedCategory.getID() === c.getID()) {
                    return true;
                }

                assignedCategory = assignedCategory.getParent();
            }
        }

        return false;
    }

    /**
     * Removes the given product from the set of compared products for the given category.
     * Saves the current product comparison to the session.
     *
     * @alias module:models/CompareModel~CompareList/remove
     * @param {dw.catalog.Product} p the product to remove
     * @param {dw.catalog.Category} c the category for which to remove the object
     * @see module:models/CompareModel~CompareList/store
     */
    this.remove = function (p, c) {
        var products = categoryProducts[c.getID()];

        // Quits if there are no products for the category.
        if (!products) {
            return;
        }

        // Builds copy of products array without the product.
        var newProducts = [];
        for (var i = 0; i < products.length; i++) {
            if (products[i] !== p.getID()) {
                newProducts.push(products[i]);
            }
        }

        // Removes category if the last product was removed.
        if (newProducts.length === 0) {
            delete categoryProducts[c.getID()];
        // Otherwise, sets the updated products array for category.
        } else {
            categoryProducts[c.getID()] = newProducts;
        }

        // Removes the product from subcategories of the category
        for (var it = c.getSubCategories().iterator(); it.hasNext();) {
            this.remove(p, it.next());
        }

        store();
    };
}

/**
 * Returns the product compare list, possibly restored using JSON data from the session.
 *
 * @return {models/CompareModel~CompareList} The compare list
 */
exports.get = function () {
    // Creates the transient compare list.
    var compareList = new CompareList();

    // If there is compare data stored in the session
    // restores the compare list with this data.
    compareList.fromJSON(session.privacy.productComparison);

    return compareList;
};

X Privacy Update: We use cookies to make interactions with our websites and services easy and meaningful, to better understand how they are used. By continuing to use this site you are giving us your consent to do this. Privacy Policy.