<commit_before>/*
 * Copyright 2017 Crown Copyright
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

angular.module('app').controller('CustomFilterDialogController', ['$scope', '$mdDialog', 'schema', 'functions', function($scope, $mdDialog, schema, functions) {

    $scope.filter = { preAggregation: false }
    $scope.availablePredicates;
    $scope.availableProperties = [];
    $scope.predicateText;
    $scope.editMode = false;

    $scope.elementType = this.elementType;
    $scope.group = this.group;
    $scope.filterForEdit = this.filterForEdit;
    $scope.onSubmit = this.onSubmit;

    var createFilterFor = function(text) {
        var lowerCaseText = angular.lowercase(text);
        return function filterFn(predicate) {
            return angular.lowercase(predicate).indexOf(lowerCaseText) >= 0;
        }
    }

    $scope.search = function(text) {
        var results = text ? $scope.availablePredicates.filter( createFilterFor(text) ) : $scope.availablePredicates;
        return results;
    }

    $scope.createFriendlyName = function(javaClass) {
        var classParts = javaClass.split('.');
        return classParts[classParts.length - 1];
    }

    $scope.getProperties = function() {
        if ($scope.elementType === 'entity') {
            return schema.getEntityProperties($scope.group);
        } else if ($scope.elementType === 'edge') {
            return schema.getEdgeProperties($scope.group);
        } else throw 'Element type can be "edge" or "entity" but not ' + JSON.stringify($scope.elementType)
    }

    $scope.resetForm = function() {
        $scope.filter = { preAggregation: false };
        $scope.filterForm.$setUntouched(true);
    }

    $scope.cancel = function() {
        $mdDialog.cancel();
    }

    $scope.submit = function() {
        $scope.onSubmit($scope.filter, $scope.group, $scope.elementType);
        $mdDialog.hide()
    }

    $scope.addAnother = function() {
        $scope.onSubmit($scope.filter, $scope.group, $scope.elementType)
        $scope.resetForm();
    }

    $scope.getFlexValue = function() {
        var value = 33;
        if ($scope.filter.availableFunctionParameters.length === 2) {
            value = 50;
        } else if ($scope.filter.availableFunctionParameters.length === 1) {
            value = 100;
        }

        return value;
    }

    $scope.onSelectedPropertyChange = function(editModeInit) {
        functions.getFunctions($scope.group, $scope.filter.property, function(data) {
            $scope.availablePredicates = data;
        });
        if (!editModeInit) {
            $scope.filter.predicate = '';
        }
    }

    $scope.onSelectedPredicateChange = function() {
        if ($scope.filter.predicate === undefined || $scope.filter.predicate === '' || $scope.filter.predicate === null) {
            return;
        }
        functions.getFunctionParameters($scope.filter.predicate, function(data) {
            $scope.filter.availableFunctionParameters = data;
        });

        schema.get().then(function(gafferSchema) {
            var elementDef;
            if (gafferSchema.entities) {
                elementDef = gafferSchema.entities[$scope.group];
            }
            if(!elementDef && gafferSchema.edges) {
                 elementDef = gafferSchema.edges[$scope.group];
            }
            if (gafferSchema.types) {
                var propertyClass = gafferSchema.types[elementDef.properties[$scope.filter.property]].class;
                if("java.lang.String" !== propertyClass
                    && "java.lang.Boolean" !== propertyClass
                    && "java.lang.Integer" !== propertyClass) {
                    $scope.filter.propertyClass = propertyClass;
                }
            }
        });

        $scope.filter.parameters = {};
    }

    if ($scope.filterForEdit) {
        $scope.filter.preAggregation = $scope.filterForEdit.preAggregation;
        $scope.filter.property = $scope.filterForEdit.property;
        $scope.onSelectedPropertyChange(true);
        $scope.filter.predicate = $scope.filterForEdit.predicate;
        $scope.onSelectedPredicateChange();
        for(var name in $scope.filterForEdit.parameters) {
            var param = $scope.filterForEdit.parameters[name];
            if(typeof param === 'string' || param instanceof String) {
                $scope.filter.parameters[name] = param;
            } else {
                $scope.filter.parameters[name] = JSON.stringify(param)
            }
        }
        $scope.editMode = true;
    }

}]);
<commit_msg>gh-341 removed unused schema call<commit_after>/*
 * Copyright 2017 Crown Copyright
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

angular.module('app').controller('CustomFilterDialogController', ['$scope', '$mdDialog', 'schema', 'functions', function($scope, $mdDialog, schema, functions) {

    $scope.filter = { preAggregation: false }
    $scope.availablePredicates;
    $scope.availableProperties = [];
    $scope.predicateText;
    $scope.editMode = false;

    $scope.elementType = this.elementType;
    $scope.group = this.group;
    $scope.filterForEdit = this.filterForEdit;
    $scope.onSubmit = this.onSubmit;

    var createFilterFor = function(text) {
        var lowerCaseText = angular.lowercase(text);
        return function filterFn(predicate) {
            return angular.lowercase(predicate).indexOf(lowerCaseText) >= 0;
        }
    }

    $scope.search = function(text) {
        var results = text ? $scope.availablePredicates.filter( createFilterFor(text) ) : $scope.availablePredicates;
        return results;
    }

    $scope.createFriendlyName = function(javaClass) {
        var classParts = javaClass.split('.');
        return classParts[classParts.length - 1];
    }

    $scope.getProperties = function() {
        if ($scope.elementType === 'entity') {
            return schema.getEntityProperties($scope.group);
        } else if ($scope.elementType === 'edge') {
            return schema.getEdgeProperties($scope.group);
        } else throw 'Element type can be "edge" or "entity" but not ' + JSON.stringify($scope.elementType)
    }

    $scope.resetForm = function() {
        $scope.filter = { preAggregation: false };
        $scope.filterForm.$setUntouched(true);
    }

    $scope.cancel = function() {
        $mdDialog.cancel();
    }

    $scope.submit = function() {
        $scope.onSubmit($scope.filter, $scope.group, $scope.elementType);
        $mdDialog.hide()
    }

    $scope.addAnother = function() {
        $scope.onSubmit($scope.filter, $scope.group, $scope.elementType)
        $scope.resetForm();
    }

    $scope.getFlexValue = function() {
        var value = 33;
        if ($scope.filter.availableFunctionParameters.length === 2) {
            value = 50;
        } else if ($scope.filter.availableFunctionParameters.length === 1) {
            value = 100;
        }

        return value;
    }

    $scope.onSelectedPropertyChange = function(editModeInit) {
        functions.getFunctions($scope.group, $scope.filter.property, function(data) {
            $scope.availablePredicates = data;
        });
        if (!editModeInit) {
            $scope.filter.predicate = '';
        }
    }

    $scope.onSelectedPredicateChange = function() {
        if ($scope.filter.predicate === undefined || $scope.filter.predicate === '' || $scope.filter.predicate === null) {
            return;
        }
        functions.getFunctionParameters($scope.filter.predicate, function(data) {
            $scope.filter.availableFunctionParameters = data;
        });

        $scope.filter.parameters = {};
    }

    if ($scope.filterForEdit) {
        $scope.filter.preAggregation = $scope.filterForEdit.preAggregation;
        $scope.filter.property = $scope.filterForEdit.property;
        $scope.onSelectedPropertyChange(true);
        $scope.filter.predicate = $scope.filterForEdit.predicate;
        $scope.onSelectedPredicateChange();
        for(var name in $scope.filterForEdit.parameters) {
            var param = $scope.filterForEdit.parameters[name];
            if(typeof param === 'string' || param instanceof String) {
                $scope.filter.parameters[name] = param;
            } else {
                $scope.filter.parameters[name] = JSON.stringify(param)
            }
        }
        $scope.editMode = true;
    }

}]);
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var UUFClient = {};

(function (UUFClient) {

    var UUF_ZONE_COMMENT_PREFIX = "[UUF-ZONE]";
    var HTML_HEAD_ELEMENT = $('head');
    var HTML_BODY_ELEMENT = $('body');

    var CALLBACK_ON_SUCCESS = "onSuccess";
    var CALLBACK_ON_FAILURE = "onFailure";

    var MODE_APPEND = "APPEND";
    var MODE_PREPEND = "PREPEND";
    var MODE_OVERWRITE = "OVERWRITE";

    /**
     * Zones in the current DOM.
     * @type {{string: {start: Object, end: Object}}}
     */
    var zonesMap;

    /**
     * Array of rendered fragment names.
     * @type {string[]} Array of fragments fully qualified names
     */
    var renderedFragments = [];

    // Check whether the JQuery is available.
    if (!window.jQuery) {
        throw new UUFClientException("JQuery is required for UUFClient.");
    }
    // Check whether the Handlebars is available.
    if (!window.Handlebars) {
        throw new UUFClientException("Handlebars JS is required for UUFClient.");
    }

    /**
     * Indicates an error in UUFClient.
     *
     * @param {string} message message of this error
     * @param {Object} [cause] cause of this error
     * @param {number} [statusCode] status code of this error
     * @constructor
     */
    function UUFClientException(message, cause, statusCode) {
        this.message = message;
        this.cause = cause;
        this.statusCode = statusCode;
    }

    /**
     * Populates a zones map by scanning HTML comments in the DOM.
     */
    function populateZonesMap() {
        zonesMap = {};
        $("*").contents().filter(function () {
            // For HTML comments, node type will be 8.
            return this.nodeType === 8;
        }).each(function (i, node) {
            var commentText = node.nodeValue;
            if (!commentText.startsWith(UUF_ZONE_COMMENT_PREFIX)) {
                return;
            }

            var zone;
            try {
                zone = JSON.parse(commentText.substring(UUF_ZONE_COMMENT_PREFIX.length));
            } catch (e) {
                throw new UUFClientException("Cannot parse UUFClient zone comment '" + commentText
                                             + "' as a JSON.");
            }
            if (!zone.name) {
                throw new UUFClientException("Cannot find field 'name' in the UUFClient zone comment '"
                                             + commentText + "'.");
            }
            if (!zone.position) {
                throw new UUFClientException("Cannot find field 'position' in the UUFClient zone comment '"
                                             + commentText + "'.");
            }

            if (!zonesMap[zone.name]) {
                zonesMap[zone.name] = {};
            }
            zonesMap[zone.name][zone.position] = node;
        });
    }

    /**
     * Update the HTML page content with JS and CSS returned by the fragment.
     *
     * @param {Object} data JSON object with HTML,CSS and JS
     * @param {string} fragmentFullyQualifiedName fully qualified name of the fragment
     */
    function updateResources(data, fragmentFullyQualifiedName) {
        if (renderedFragments.indexOf(fragmentFullyQualifiedName) == -1) {
            renderedFragments.push(fragmentFullyQualifiedName);
            if (data.css) {
                HTML_HEAD_ELEMENT.append(data.css);
            }
            if (data.headJs) {
                $.each($(data.headJs), function (i, scriptEle) {
                    $.getScript(scriptEle.getAttribute('src'));
                });
            }
            if (data.js) {
                HTML_BODY_ELEMENT.append($(data.js));
                $.each($(data.js), function (i, scriptEle) {
                    $.getScript(scriptEle.getAttribute('src'));
                });
            }
        }
    }

    /**
     * Pushes the content to the specified zone according to the given mode.
     *
     * @param {string} content content to push
     * @param {string} zone name of the zone to push
     * @param {string} mode mode to use. Mode can be either PREPEND, APPEND or OVERWRITE. PREPEND will
     *     add the fragment to the beginning of the existing content of that zone. Mode can be "PREPEND" (put the
     *     pushing content before the existing content), "APPEND" (put the pushing content after the existing content)
     *     or "OVERWRITE" (replace the existing content with the pushing content)
     */
    function pushContent(content, zone, mode) {
        if (!content) {
            console.warn("Content is empty.");
            return;
        }

        switch (mode) {
            case MODE_OVERWRITE:
                var innerContents = $(zone.start).parent().contents();
                innerContents.slice(innerContents.index($(zone.start)) + 1, innerContents.index($(zone.end))).remove();
                $(zone.start).after(content);
                break;
            case MODE_APPEND:
                $(zone.end).before(content);
                break;
            case MODE_PREPEND:
                $(zone.start).after(content);
                break;
        }
    }

    /**
     * Check the zoneName variable is null or empty.
     *
     * @param {?string} zoneName
     */
    function validateZoneName(zoneName) {
        if (!zoneName) {
            throw new UUFClientException("Zone name cannot be null or empty.");
        }
    }

    /**
     * Check the mode variable is null or empty.
     *
     * @param {?string} mode
     */
    function validateMode(mode) {
        if (!mode) {
            throw new UUFClientException("Mode cannot be null or empty.");
        }
        if (mode != MODE_APPEND && mode != MODE_PREPEND && mode != MODE_OVERWRITE) {
            throw new UUFClientException("Mode should be one of '" + MODE_APPEND + "," + MODE_PREPEND + ","
                                         + MODE_OVERWRITE + "'. Instead found '" + mode + "'.");
        }
    }

    /**
     * Check the callback functions.
     *
     * @param {?Object} callbacks
     */
    function validateCallback(callbacks) {
        if (!callbacks[CALLBACK_ON_SUCCESS]) {
            throw new UUFClientException("Function '" + CALLBACK_ON_SUCCESS + "' not found in callbacks.");
        }
        if (!callbacks[CALLBACK_ON_FAILURE]) {
            throw new UUFClientException("Function '" + CALLBACK_ON_FAILURE + "' not found in callbacks.");
        }
    }

    /**
     * Get the fragment the content from Ajax call and pushes to a given zone or callback.
     *
     * @param {string} fragmentFullyQualifiedName fully qualified name of the fragment
     * @param {?Object} templateFillingObject data for the template
     * @param {string} zone name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     *
     */
    function renderFragmentImpl(fragmentFullyQualifiedName, templateFillingObject, zone, mode, callbacks) {
        var url = contextPath + "/fragments/" + fragmentFullyQualifiedName;
        $.ajax({
                   url: url,
                   type: "POST",
                   contentType: 'application/json',
                   data: JSON.stringify(templateFillingObject),
                   success: function (data, textStatus, jqXHR) {
                       try {
                           if (zone && mode) {
                               updateResources(data, fragmentFullyQualifiedName);
                               pushContent(data, zone, mode);
                           }
                           callbacks[CALLBACK_ON_SUCCESS](data);
                       } catch (e) {
                           callbacks[CALLBACK_ON_FAILURE]("Error occurred while pushing the content.", e);
                       }
                   },
                   error: function (jqXHR, textStatus, errorThrown) {
                       var msg = "Error occurred while retrieving fragment '" + fragmentFullyQualifiedName
                                 + "' from '" + url + "'.";
                       callbacks[CALLBACK_ON_FAILURE](msg, errorThrown);
                   }
               });
    }

    /**
     * Renders the specified fragment and pushes to the given zone according to the given mode or provide rendered
     * HTML content to the success function of the given callback.
     *
     * @param {string} fragmentFullyQualifiedName fully qualified name of the fragment
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push or callback function
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderFragment =
        function (fragmentFullyQualifiedName, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
            if (!fragmentFullyQualifiedName) {
                throw new UUFClientException("Fragment name cannot be null or empty.");
            }

            if (!mode && !callbacks) {
                callbacks = zoneNameOrCallbacks;
                validateCallback(callbacks);

                renderFragmentImpl(fragmentFullyQualifiedName, templateFillingObject, null, null, callbacks);
            } else {
                var zoneName = zoneNameOrCallbacks;
                validateZoneName(zoneName);
                validateMode(mode);
                validateCallback(callbacks);

                // This check is not done on renderTemplate* functions because this check will be done in pushContent
                // function.
                if (!zonesMap) {
                    populateZonesMap();
                }
                var zone = zonesMap[zoneName];
                if (!zone) {
                    throw new UUFClientException("Zone '" + zoneName + "' does not exists in current DOM.");
                }
                renderFragmentImpl(fragmentFullyQualifiedName, templateFillingObject, zone, mode, callbacks);
            }
        };

    /**
     * Renders the specified embedded Handlebars template and pushes to the given zone according to the given mode or
     * provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateId id of the handlebars template
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderTemplate = function (templateId, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
        if (!templateId) {
            throw new UUFClientException("Template name cannot be null or empty.");
        }

        if (!mode && !callbacks) {
            callbacks = zoneNameOrCallbacks;
            validateCallback(callbacks);
            var source = $("#" + templateId).html();
            if (source) {
                UUFClient.renderTemplateString(source, templateFillingObject, callbacks, null, null);
            } else {
                throw new UUFClientException("Template '" + templateId + "' not found");
            }

        } else {
            var zoneName = zoneNameOrCallbacks;

            validateZoneName(zoneName);
            validateMode(mode);
            validateCallback(callbacks);
            var source = $("#" + templateId).html();
            if (source) {
                UUFClient.renderTemplateString(source, templateFillingObject, zoneName, mode, callbacks);
            } else {
                throw new UUFClientException("Template '" + templateId + "' not found");
            }
        }
    };

    /**
     * Retrieves the Handlebars template from the specified URL and renders and pushes it to the given zone according
     * to the given mode or provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateUrl url of the template
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderTemplateUrl = function (templateUrl, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
        if (!templateUrl) {
            throw new UUFClientException("Template url cannot be null or empty.");
        }

        if (!mode && !callbacks) {
            callbacks = zoneNameOrCallbacks;
            validateCallback(callbacks);
            renderTemplateUrlImpl(templateUrl, templateFillingObject, null, null, callbacks);
        } else {
            var zoneName = zoneNameOrCallbacks;
            validateZoneName(zoneName);
            validateMode(mode);
            validateCallback(callbacks);

            renderTemplateUrlImpl(templateUrl, templateFillingObject, zoneName, mode, callbacks);
        }
    };

    /**
     * Renders the Handlebars template from the specified URL and pushes to the given zone according to the given mode
     * or provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateUrl url of the template
     * @param {?Object} templateFillingObject data for the template
     * @param {string} zoneName name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    function renderTemplateUrlImpl(templateUrl, templateFillingObject, zoneName, mode, callbacks) {
        $.ajax({
                   url: templateUrl,
                   type: "GET",
                   success: function (data, textStatus, jqXHR) {
                       var contentType = jqXHR.getResponseHeader("content-type") || "";
                       if (contentType.indexOf('text/x-handlebars-template') <= -1) {
                           throw new UUFClientException(
                               "Response content type is incorrect. Found '" + jqXHR.getResponseHeader("content-type")
                               + "' instead of 'text/x-handlebars-template'.");
                       }
                       if (!zoneName && !mode) {
                           UUFClient.renderTemplateString(data, templateFillingObject, zoneName, mode);
                       }
                       callbacks[CALLBACK_ON_SUCCESS](data);
                   },
                   error: function (jqXHR, textStatus, errorThrown) {
                       callbacks[CALLBACK_ON_FAILURE](
                           "Error occurred while retrieving template from " + templateUrl + ".",
                           errorThrown);
                   }
               });
    }

    /**
     * Renders the Handlebars template and pushes to the given zone according to the given mode
     * or provide rendered Handlebars template content to a given call back.
     *
     * @param {string} templateString Handlebars template
     * @param {?Object} templateFillingObject data for the template
     * @param {string} zone name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    function renderTemplateStringImpl(templateString, templateFillingObject, zone, mode, callbacks) {
        try {
            var template = Handlebars.compile(templateString);
            var html = template(templateFillingObject);
            if (zone && mode) {
                pushContent(html, zone, mode);
            }
            callbacks[CALLBACK_ON_SUCCESS](html);
        } catch (e) {
            callbacks[CALLBACK_ON_FAILURE]("Error occurred while compiling the handlebar template.", e);
        }
    }

    /**
     * Renders the Handlebars template and pushes to the given zone according to the given mode or
     * provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateString Handlebars template
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderTemplateString =
        function (templateString, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
            if (!templateString) {
                throw new UUFClientException("Template string cannot be null or empty.");
            }

            if (!mode && !callbacks) {
                callbacks = zoneNameOrCallbacks;
                validateCallback(callbacks);

                renderTemplateStringImpl(templateString, templateFillingObject, null, null, callbacks);
            } else {
                var zoneName = zoneNameOrCallbacks;
                validateZoneName(zoneName);
                validateMode(mode);
                validateCallback(callbacks);

                if (!zonesMap) {
                    populateZonesMap();
                }

                var zone = zonesMap[zoneName];
                if (!zone) {
                    throw new UUFClientException("Zone '" + zoneName + "' does not exists in current DOM.");
                }
                renderTemplateStringImpl(templateString, templateFillingObject, zone, mode, callbacks);
            }
        };

})(UUFClient);
<commit_msg>Revert "Fixing fragment rendering problem"<commit_after>/**
 * @license
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var UUFClient = {};

(function (UUFClient) {

    var UUF_ZONE_COMMENT_PREFIX = "[UUF-ZONE]";
    var HTML_HEAD_ELEMENT = $('head');
    var HTML_BODY_ELEMENT = $('body');

    var CALLBACK_ON_SUCCESS = "onSuccess";
    var CALLBACK_ON_FAILURE = "onFailure";

    var MODE_APPEND = "APPEND";
    var MODE_PREPEND = "PREPEND";
    var MODE_OVERWRITE = "OVERWRITE";

    /**
     * Zones in the current DOM.
     * @type {{string: {start: Object, end: Object}}}
     */
    var zonesMap;

    /**
     * Array of rendered fragment names.
     * @type {string[]} Array of fragments fully qualified names
     */
    var renderedFragments = [];

    // Check whether the JQuery is available.
    if (!window.jQuery) {
        throw new UUFClientException("JQuery is required for UUFClient.");
    }
    // Check whether the Handlebars is available.
    if (!window.Handlebars) {
        throw new UUFClientException("Handlebars JS is required for UUFClient.");
    }

    /**
     * Indicates an error in UUFClient.
     *
     * @param {string} message message of this error
     * @param {Object} [cause] cause of this error
     * @param {number} [statusCode] status code of this error
     * @constructor
     */
    function UUFClientException(message, cause, statusCode) {
        this.message = message;
        this.cause = cause;
        this.statusCode = statusCode;
    }

    /**
     * Populates a zones map by scanning HTML comments in the DOM.
     */
    function populateZonesMap() {
        zonesMap = {};
        $("*").contents().filter(function () {
            // For HTML comments, node type will be 8.
            return this.nodeType === 8;
        }).each(function (i, node) {
            var commentText = node.nodeValue;
            if (!commentText.startsWith(UUF_ZONE_COMMENT_PREFIX)) {
                return;
            }

            var zone;
            try {
                zone = JSON.parse(commentText.substring(UUF_ZONE_COMMENT_PREFIX.length));
            } catch (e) {
                throw new UUFClientException("Cannot parse UUFClient zone comment '" + commentText
                                             + "' as a JSON.");
            }
            if (!zone.name) {
                throw new UUFClientException("Cannot find field 'name' in the UUFClient zone comment '"
                                             + commentText + "'.");
            }
            if (!zone.position) {
                throw new UUFClientException("Cannot find field 'position' in the UUFClient zone comment '"
                                             + commentText + "'.");
            }

            if (!zonesMap[zone.name]) {
                zonesMap[zone.name] = {};
            }
            zonesMap[zone.name][zone.position] = node;
        });
    }

    /**
     * Update the HTML page content with JS and CSS returned by the fragment.
     *
     * @param {Object} data JSON object with HTML,CSS and JS
     * @param {string} fragmentFullyQualifiedName fully qualified name of the fragment
     */
    function updateResources(data, fragmentFullyQualifiedName) {
        if (renderedFragments.indexOf(fragmentFullyQualifiedName) == -1) {
            renderedFragments.push(fragmentFullyQualifiedName);
            if (data.css) {
                HTML_HEAD_ELEMENT.append(data.css);
            }
            if (data.headJs) {
                $.each($(data.headJs), function (i, scriptEle) {
                    $.getScript(scriptEle.getAttribute('src'));
                });
            }
            if (data.js) {
                HTML_BODY_ELEMENT.append($(data.js));
                $.each($(data.js), function (i, scriptEle) {
                    $.getScript(scriptEle.getAttribute('src'));
                });
            }
        }
    }

    /**
     * Pushes the content to the specified zone according to the given mode.
     *
     * @param {string} content content to push
     * @param {string} zone name of the zone to push
     * @param {string} mode mode to use. Mode can be either PREPEND, APPEND or OVERWRITE. PREPEND will
     *     add the fragment to the beginning of the existing content of that zone. Mode can be "PREPEND" (put the
     *     pushing content before the existing content), "APPEND" (put the pushing content after the existing content)
     *     or "OVERWRITE" (replace the existing content with the pushing content)
     */
    function pushContent(content, zone, mode) {
        if (!content) {
            console.warn("Content is empty.");
            return;
        }

        switch (mode) {
            case MODE_OVERWRITE:
                var innerContents = $(zone.start).parent().contents();
                innerContents.slice(innerContents.index($(zone.start)) + 1, innerContents.index($(zone.end))).remove();
                $(zone.start).after(content);
                break;
            case MODE_APPEND:
                $(zone.end).before(content);
                break;
            case MODE_PREPEND:
                $(zone.start).after(content);
                break;
        }
    }

    /**
     * Check the zoneName variable is null or empty.
     *
     * @param {?string} zoneName
     */
    function validateZoneName(zoneName) {
        if (!zoneName) {
            throw new UUFClientException("Zone name cannot be null or empty.");
        }
    }

    /**
     * Check the mode variable is null or empty.
     *
     * @param {?string} mode
     */
    function validateMode(mode) {
        if (!mode) {
            throw new UUFClientException("Mode cannot be null or empty.");
        }
        if (mode != MODE_APPEND && mode != MODE_PREPEND && mode != MODE_OVERWRITE) {
            throw new UUFClientException("Mode should be one of '" + MODE_APPEND + "," + MODE_PREPEND + ","
                                         + MODE_OVERWRITE + "'. Instead found '" + mode + "'.");
        }
    }

    /**
     * Check the callback functions.
     *
     * @param {?Object} callbacks
     */
    function validateCallback(callbacks) {
        if (!callbacks[CALLBACK_ON_SUCCESS]) {
            throw new UUFClientException("Function '" + CALLBACK_ON_SUCCESS + "' not found in callbacks.");
        }
        if (!callbacks[CALLBACK_ON_FAILURE]) {
            throw new UUFClientException("Function '" + CALLBACK_ON_FAILURE + "' not found in callbacks.");
        }
    }

    /**
     * Get the fragment the content from Ajax call and pushes to a given zone or callback.
     *
     * @param {string} fragmentFullyQualifiedName fully qualified name of the fragment
     * @param {?Object} templateFillingObject data for the template
     * @param {string} zone name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     *
     */
    function renderFragmentImpl(fragmentFullyQualifiedName, templateFillingObject, zone, mode, callbacks) {
        var url = contextPath + "/fragments/" + fragmentFullyQualifiedName;
        $.ajax({
                   url: url,
                   type: "POST",
                   contentType: 'application/json',
                   data: JSON.stringify(templateFillingObject),
                   success: function (data, textStatus, jqXHR) {
                       try {
                           if (zone && mode) {
                               updateResources(data, fragmentFullyQualifiedName);
                               pushContent(data.html, zone, mode);
                           }
                           callbacks[CALLBACK_ON_SUCCESS](data.html);
                       } catch (e) {
                           callbacks[CALLBACK_ON_FAILURE]("Error occurred while pushing the content.", e);
                       }
                   },
                   error: function (jqXHR, textStatus, errorThrown) {
                       var msg = "Error occurred while retrieving fragment '" + fragmentFullyQualifiedName
                                 + "' from '" + url + "'.";
                       callbacks[CALLBACK_ON_FAILURE](msg, errorThrown);
                   }
               });
    }

    /**
     * Renders the specified fragment and pushes to the given zone according to the given mode or provide rendered
     * HTML content to the success function of the given callback.
     *
     * @param {string} fragmentFullyQualifiedName fully qualified name of the fragment
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push or callback function
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderFragment =
        function (fragmentFullyQualifiedName, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
            if (!fragmentFullyQualifiedName) {
                throw new UUFClientException("Fragment name cannot be null or empty.");
            }

            if (!mode && !callbacks) {
                callbacks = zoneNameOrCallbacks;
                validateCallback(callbacks);

                renderFragmentImpl(fragmentFullyQualifiedName, templateFillingObject, null, null, callbacks);
            } else {
                var zoneName = zoneNameOrCallbacks;
                validateZoneName(zoneName);
                validateMode(mode);
                validateCallback(callbacks);

                // This check is not done on renderTemplate* functions because this check will be done in pushContent
                // function.
                if (!zonesMap) {
                    populateZonesMap();
                }
                var zone = zonesMap[zoneName];
                if (!zone) {
                    throw new UUFClientException("Zone '" + zoneName + "' does not exists in current DOM.");
                }
                renderFragmentImpl(fragmentFullyQualifiedName, templateFillingObject, zone, mode, callbacks);
            }
        };

    /**
     * Renders the specified embedded Handlebars template and pushes to the given zone according to the given mode or
     * provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateId id of the handlebars template
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderTemplate = function (templateId, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
        if (!templateId) {
            throw new UUFClientException("Template name cannot be null or empty.");
        }

        if (!mode && !callbacks) {
            callbacks = zoneNameOrCallbacks;
            validateCallback(callbacks);
            var source = $("#" + templateId).html();
            if (source) {
                UUFClient.renderTemplateString(source, templateFillingObject, callbacks, null, null);
            } else {
                throw new UUFClientException("Template '" + templateId + "' not found");
            }

        } else {
            var zoneName = zoneNameOrCallbacks;

            validateZoneName(zoneName);
            validateMode(mode);
            validateCallback(callbacks);
            var source = $("#" + templateId).html();
            if (source) {
                UUFClient.renderTemplateString(source, templateFillingObject, zoneName, mode, callbacks);
            } else {
                throw new UUFClientException("Template '" + templateId + "' not found");
            }
        }
    };

    /**
     * Retrieves the Handlebars template from the specified URL and renders and pushes it to the given zone according
     * to the given mode or provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateUrl url of the template
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderTemplateUrl = function (templateUrl, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
        if (!templateUrl) {
            throw new UUFClientException("Template url cannot be null or empty.");
        }

        if (!mode && !callbacks) {
            callbacks = zoneNameOrCallbacks;
            validateCallback(callbacks);
            renderTemplateUrlImpl(templateUrl, templateFillingObject, null, null, callbacks);
        } else {
            var zoneName = zoneNameOrCallbacks;
            validateZoneName(zoneName);
            validateMode(mode);
            validateCallback(callbacks);

            renderTemplateUrlImpl(templateUrl, templateFillingObject, zoneName, mode, callbacks);
        }
    };

    /**
     * Renders the Handlebars template from the specified URL and pushes to the given zone according to the given mode
     * or provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateUrl url of the template
     * @param {?Object} templateFillingObject data for the template
     * @param {string} zoneName name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    function renderTemplateUrlImpl(templateUrl, templateFillingObject, zoneName, mode, callbacks) {
        $.ajax({
                   url: templateUrl,
                   type: "GET",
                   success: function (data, textStatus, jqXHR) {
                       var contentType = jqXHR.getResponseHeader("content-type") || "";
                       if (contentType.indexOf('text/x-handlebars-template') <= -1) {
                           throw new UUFClientException(
                               "Response content type is incorrect. Found '" + jqXHR.getResponseHeader("content-type")
                               + "' instead of 'text/x-handlebars-template'.");
                       }
                       if (!zoneName && !mode) {
                           UUFClient.renderTemplateString(data, templateFillingObject, zoneName, mode);
                       }
                       callbacks[CALLBACK_ON_SUCCESS](data);
                   },
                   error: function (jqXHR, textStatus, errorThrown) {
                       callbacks[CALLBACK_ON_FAILURE](
                           "Error occurred while retrieving template from " + templateUrl + ".",
                           errorThrown);
                   }
               });
    }

    /**
     * Renders the Handlebars template and pushes to the given zone according to the given mode
     * or provide rendered Handlebars template content to a given call back.
     *
     * @param {string} templateString Handlebars template
     * @param {?Object} templateFillingObject data for the template
     * @param {string} zone name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    function renderTemplateStringImpl(templateString, templateFillingObject, zone, mode, callbacks) {
        try {
            var template = Handlebars.compile(templateString);
            var html = template(templateFillingObject);
            if (zone && mode) {
                pushContent(html, zone, mode);
            }
            callbacks[CALLBACK_ON_SUCCESS](html);
        } catch (e) {
            callbacks[CALLBACK_ON_FAILURE]("Error occurred while compiling the handlebar template.", e);
        }
    }

    /**
     * Renders the Handlebars template and pushes to the given zone according to the given mode or
     * provide rendered HTML content to the success function of the given callback.
     *
     * @param {string} templateString Handlebars template
     * @param {?Object} templateFillingObject data for the template
     * @param {string | Function} zoneNameOrCallbacks name of the zone to push
     * @param {string} mode mode to use
     * @param {Function} callbacks callback function
     */
    UUFClient.renderTemplateString =
        function (templateString, templateFillingObject, zoneNameOrCallbacks, mode, callbacks) {
            if (!templateString) {
                throw new UUFClientException("Template string cannot be null or empty.");
            }

            if (!mode && !callbacks) {
                callbacks = zoneNameOrCallbacks;
                validateCallback(callbacks);

                renderTemplateStringImpl(templateString, templateFillingObject, null, null, callbacks);
            } else {
                var zoneName = zoneNameOrCallbacks;
                validateZoneName(zoneName);
                validateMode(mode);
                validateCallback(callbacks);

                if (!zonesMap) {
                    populateZonesMap();
                }

                var zone = zonesMap[zoneName];
                if (!zone) {
                    throw new UUFClientException("Zone '" + zoneName + "' does not exists in current DOM.");
                }
                renderTemplateStringImpl(templateString, templateFillingObject, zone, mode, callbacks);
            }
        };

})(UUFClient);
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2010-present Abixen Systems. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

(function () {

    'use strict';

    angular
        .module('platformLayoutModule')
        .config(platformLayoutModuleTranslateConfig);

    platformLayoutModuleTranslateConfig.$inject = ['$translateProvider'];
    function platformLayoutModuleTranslateConfig($translateProvider) {

        $translateProvider.useSanitizeValueStrategy('escape');

        $translateProvider.translations('pl', {

        });

        $translateProvider.translations('en', {
            'module.layout.title.label': 'Title',
            'module.layout.title.placeholder': '',
            'module.layout.content.label': 'Xml',
            'module.layout.content.placeholder': ''
        });
    }
})();<commit_msg>Added Polish translations for the layout module.<commit_after>/**
 * Copyright (c) 2010-present Abixen Systems. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

(function () {

    'use strict';

    angular
        .module('platformLayoutModule')
        .config(platformLayoutModuleTranslateConfig);

    platformLayoutModuleTranslateConfig.$inject = ['$translateProvider'];
    function platformLayoutModuleTranslateConfig($translateProvider) {

        $translateProvider.useSanitizeValueStrategy('escape');

        $translateProvider.translations('pl', {
            'module.layout.title.label': 'Tytuł',
            'module.layout.title.placeholder': 'Np. 2 (50/50)',
            'module.layout.content.label': 'Xml',
            'module.layout.content.placeholder': 'Definicja układu strony'
        });

        $translateProvider.translations('en', {
            'module.layout.title.label': 'Title',
            'module.layout.title.placeholder': 'E.g. 2 (50/50)',
            'module.layout.content.label': 'Xml',
            'module.layout.content.placeholder': 'Layout\'s definition'
        });
    }
})();<|endoftext|>


<commit_before>({
    /**
     * Verify the smartstore adapter is selected for actions when auraStorage:init is used without
     * any specification.
     */
    testAdapterSelection : {
        test : function(cmp) {
                        var storage = $A.storageService.getStorage("actions");
                        $A.test.assertTruthy(storage, "Failed to fetch named storage.");
                        $A.test.assertEquals("smartstore", storage.getName());
                }
        },

    /**
     * Verify Action.setStorable() and auto refresh
     * setStorage() accepts configuration. These configuration are helpful for follow up actions
     * but not the first action to be stored.
     *
     * {ignoreExisting: "Ignore existing stored response, but cache my response after the action is complete"
     *  "refresh": "Time in seconds to override action's current storage expiration"}
     */
    testSetStorableAPI:{
        attributes:{
            defaultExpiration : "60",
            defaultAutoRefreshInterval : "60"
        },
        test:[function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage1.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage2.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage3.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage4.call(this,cmp);}
        ]
    },
    /**
     * Verify cache sweeping(expiration check).
     * defaultExpiration settings trumps defaultAutoRefreshInterval setting
     */
    /**
     * TODO:Fix cache expiration for smartStoreMock with asynch story
     * testCacheExpiration:{
     *    attributes:{
     *        defaultExpiration : 5, //I am king
     *        defaultAutoRefreshInterval : 60 //Very high but doesn't matter
     *    },
     *    test:[function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage1.call(this,cmp);},
     *          function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage2.call(this,cmp);},
     *          function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage3.call(this,cmp);},
     *          function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage4.call(this,cmp);}]
     *},
     */

    /**
     * Verify stored items are overwritten with identical action keys
     */
    testActionKeyOverloading:{
        test:[function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage1.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage2.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage3.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage4.call(this,cmp);}
        ]
    },

    /**
     * Grouping multiple actions and setting them to be storable.
     */
    testActionGrouping:{
        attributes:{
            defaultExpiration : 60,
            defaultAutoRefreshInterval : 60
        },
        test:[function(cmp) { cmp.getDef().getHelper().testActionGroupingStage1.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionGroupingStage2.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionGroupingStage3.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionGroupingStage4.call(this,cmp);}
        ]
    },

    resetCounter:function(cmp, testName){
        cmp.getDef().getHelper().resetCounters(cmp, testName);
    }
})<commit_msg>Set the test browsers to webkit only since that's all mobile SDK supports.<commit_after>({
    // We have a white-list of browsers('GOOGLECHROME', 'SAFARI') in this test because we only support webkit
    // browsers for the smart store adapter since the smart store is only accessible via our mobile SDK.

    browsers: ['GOOGLECHROME', 'SAFARI'],

    /**
     * Verify the smartstore adapter is selected for actions when auraStorage:init is used without
     * any specification.
     */
    testAdapterSelection : {
        test : function(cmp) {
                        var storage = $A.storageService.getStorage("actions");
                        $A.test.assertTruthy(storage, "Failed to fetch named storage.");
                        $A.test.assertEquals("smartstore", storage.getName());
                }
        },

    /**
     * Verify Action.setStorable() and auto refresh
     * setStorage() accepts configuration. These configuration are helpful for follow up actions
     * but not the first action to be stored.
     *
     * {ignoreExisting: "Ignore existing stored response, but cache my response after the action is complete"
     *  "refresh": "Time in seconds to override action's current storage expiration"}
     */
    testSetStorableAPI:{
        attributes:{
            defaultExpiration : "60",
            defaultAutoRefreshInterval : "60"
        },
        test:[function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage1.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage2.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage3.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testSetStorableAPIStage4.call(this,cmp);}
        ]
    },
    /**
     * Verify cache sweeping(expiration check).
     * defaultExpiration settings trumps defaultAutoRefreshInterval setting
     */
    /**
     * TODO:Fix cache expiration for smartStoreMock with asynch story
     * testCacheExpiration:{
     *    attributes:{
     *        defaultExpiration : 5, //I am king
     *        defaultAutoRefreshInterval : 60 //Very high but doesn't matter
     *    },
     *    test:[function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage1.call(this,cmp);},
     *          function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage2.call(this,cmp);},
     *          function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage3.call(this,cmp);},
     *          function(cmp) { cmp.getDef().getHelper().testCacheExpirationStage4.call(this,cmp);}]
     *},
     */

    /**
     * Verify stored items are overwritten with identical action keys
     */
    testActionKeyOverloading:{
        test:[function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage1.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage2.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage3.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionKeyOverloadingStage4.call(this,cmp);}
        ]
    },

    /**
     * Grouping multiple actions and setting them to be storable.
     */
    testActionGrouping:{
        attributes:{
            defaultExpiration : 60,
            defaultAutoRefreshInterval : 60
        },
        test:[function(cmp) { cmp.getDef().getHelper().testActionGroupingStage1.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionGroupingStage2.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionGroupingStage3.call(this,cmp);},
              function(cmp) { cmp.getDef().getHelper().testActionGroupingStage4.call(this,cmp);}
        ]
    },

    resetCounter:function(cmp, testName){
        cmp.getDef().getHelper().resetCounters(cmp, testName);
    }
})
<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var _ = require('underscore');
var StyleFormComponents = require('../style-form-components-dictionary');

module.exports = cdb.core.Model.extend({

  initialize: function (attrs, opts) {
    if (!opts.styleModel) throw new Error('Style model is required');
    this._styleModel = opts.styleModel;
    this._querySchemaModel = opts.querySchemaModel;
    this._setProperties();
    this.schema = this._generateSchema();
    this._initBinds();
  },

  _initBinds: function () {
    this.bind('change', this._onChange, this);
  },

  _setProperties: function () {
    var geom = this._querySchemaModel.getGeometry();
    var fill = this._styleModel.attributes.fill;

    if (geom && geom.getSimpleType() === 'polygon') {
      delete fill.size;
    }

    this.set({
      fill: fill,
      stroke: this._styleModel.get('stroke'),
      blending: this._styleModel.get('blending')
    });
  },

  _onChange: function () {
    this._styleModel.set(this.changed);
  },

  _generateSchema: function () {
    var self = this;
    return _.reduce(this.attributes, function (memo, value, key) {
      var d = StyleFormComponents[key];
      if (d) {
        memo[key] = d(self._querySchemaModel);
      }
      return memo;
    }, {});
  }

});
<commit_msg>Don't render stroke component within simple style when geometry is line<commit_after>var cdb = require('cartodb.js');
var _ = require('underscore');
var StyleFormComponents = require('../style-form-components-dictionary');

module.exports = cdb.core.Model.extend({

  initialize: function (attrs, opts) {
    if (!opts.styleModel) throw new Error('Style model is required');
    this._styleModel = opts.styleModel;
    this._querySchemaModel = opts.querySchemaModel;
    this._setProperties();
    this.schema = this._generateSchema();
    this._initBinds();
  },

  _initBinds: function () {
    this.bind('change', this._onChange, this);
  },

  _setProperties: function () {
    var geom = this._querySchemaModel.getGeometry();
    var fill = this._styleModel.get('fill');

    if (geom && geom.getSimpleType() === 'polygon') {
      delete fill.size;
    }

    var attrs = {
      fill: fill,
      stroke: this._styleModel.get('stroke'),
      blending: this._styleModel.get('blending')
    };

    if (geom && geom.getSimpleType() === 'line') {
      delete attrs.stroke;
    }

    this.set(attrs);
  },

  _onChange: function () {
    this._styleModel.set(this.changed);
  },

  _generateSchema: function () {
    var self = this;
    return _.reduce(this.attributes, function (memo, value, key) {
      var d = StyleFormComponents[key];
      if (d) {
        memo[key] = d(self._querySchemaModel);
      }
      return memo;
    }, {});
  }

});
<|endoftext|>


<commit_before>import React, { useEffect, useState, Fragment } from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import d3 from 'd3';
import { Tooltip } from 'vizzuality-components';

// Components
import Spinner from 'components/ui/Spinner';
import Icon from 'components/ui/icon';
import { WORLD_COUNTRY, US_COUNTRY_VALUES } from 'layout/app/dashboard-detail/energy-country-explorer/constants';
import InfoTooltip from './info-tooltip';

// Constants

// styles
import './styles.scss';
import RankingBar from './ranking-bar/component';

function IndicatorCard(props) {
  const { indicator, country } = props;
  const [queryResult, setQueryResult] = useState({
    value: null,
    ranking: null,
    count: null,
  });
  const [loading, setLoading] = useState(true);
  const [countryIsWorld, setCountryIsWorld] = useState(false);

  useEffect(() => {
    if (indicator) {
      let countryValue = indicator.param === 'ISO' ? country.value : country.label;
      const _countryIsWorld = country.value === WORLD_COUNTRY.value;
      // --- This patch is necessary since the country name varies in some cases -----
      if (countryValue === US_COUNTRY_VALUES.nameFoundInSource) {
        countryValue = US_COUNTRY_VALUES.newNameForQueries;
      }
      // -----------------------------------------------------------------------------

      setCountryIsWorld(_countryIsWorld);
      const query = _countryIsWorld ? indicator.worldQuery
        : indicator.query.replace(new RegExp(`{${indicator.param}}`, 'g'), `'${countryValue}'`);

      axios.get(query)
        .then((result) => {
          const { rows } = result.data;

          if (rows && rows.length > 0) {
            const resObj = rows[0];
            setQueryResult({
              value: d3.format(indicator.format)(resObj.x).replace('G', 'B'),
              ranking: resObj.ranking,
              count: resObj.count,
              year: resObj.year,
            });
          }
          setLoading(false);
        })
        .catch((err) => {
          console.error(`Error loading indicator ${indicator.name}`, err);
          setLoading(false);
        });
    }
  }, [country.label, country.value, indicator]);

  return (

      <Spinner isLoading={loading} className="-light -relative" />
      {!loading
        && (
        <>

            {indicator.name}

          <RankingBar
            ranking={queryResult && queryResult.ranking}
            count={queryResult && queryResult.count}
            showPoint={!countryIsWorld}
          />

            {(queryResult && queryResult.value) || '-'}

          <Tooltip
            overlay={(
              <InfoTooltip
                datasetID={indicator.datasetID}
                dataYear={queryResult && queryResult.year}
              />
            )}
            overlayClassName="c-rc-tooltip -default -no-max-width"
            placement="top"
            trigger={['click']}
            mouseLeaveDelay={0}
            destroyTooltipOnHide
          >

              <Icon name="icon-info" />

          </Tooltip>

        </>
        )}

  );
}

IndicatorCard.propTypes = {
  indicator: PropTypes.object.isRequired,
  country: PropTypes.object.isRequired,
};

export default IndicatorCard;
<commit_msg>fixes d3 regression<commit_after>import React, { useEffect, useState, Fragment } from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import { format } from 'd3-format';
import { Tooltip } from 'vizzuality-components';

// Components
import Spinner from 'components/ui/Spinner';
import Icon from 'components/ui/icon';
import { WORLD_COUNTRY, US_COUNTRY_VALUES } from 'layout/app/dashboard-detail/energy-country-explorer/constants';
import InfoTooltip from './info-tooltip';

// Constants

// styles
import './styles.scss';
import RankingBar from './ranking-bar/component';

function IndicatorCard(props) {
  const { indicator, country } = props;
  const [queryResult, setQueryResult] = useState({
    value: null,
    ranking: null,
    count: null,
  });
  const [loading, setLoading] = useState(true);
  const [countryIsWorld, setCountryIsWorld] = useState(false);

  useEffect(() => {
    if (indicator) {
      let countryValue = indicator.param === 'ISO' ? country.value : country.label;
      const _countryIsWorld = country.value === WORLD_COUNTRY.value;
      // --- This patch is necessary since the country name varies in some cases -----
      if (countryValue === US_COUNTRY_VALUES.nameFoundInSource) {
        countryValue = US_COUNTRY_VALUES.newNameForQueries;
      }
      // -----------------------------------------------------------------------------

      setCountryIsWorld(_countryIsWorld);
      const query = _countryIsWorld ? indicator.worldQuery
        : indicator.query.replace(new RegExp(`{${indicator.param}}`, 'g'), `'${countryValue}'`);

      axios.get(query)
        .then((result) => {
          const { rows } = result.data;

          if (rows && rows.length > 0) {
            const resObj = rows[0];
            setQueryResult({
              value: format(indicator.format)(resObj.x).replace('G', 'B'),
              ranking: resObj.ranking,
              count: resObj.count,
              year: resObj.year,
            });
          }
          setLoading(false);
        })
        .catch((err) => {
          console.error(`Error loading indicator ${indicator.name}`, err);
          setLoading(false);
        });
    }
  }, [country.label, country.value, indicator]);

  return (

      <Spinner isLoading={loading} className="-light -relative" />
      {!loading
        && (
        <>

            {indicator.name}

          <RankingBar
            ranking={queryResult && queryResult.ranking}
            count={queryResult && queryResult.count}
            showPoint={!countryIsWorld}
          />

            {(queryResult && queryResult.value) || '-'}

          <Tooltip
            overlay={(
              <InfoTooltip
                datasetID={indicator.datasetID}
                dataYear={queryResult && queryResult.year}
              />
            )}
            overlayClassName="c-rc-tooltip -default -no-max-width"
            placement="top"
            trigger={['click']}
            mouseLeaveDelay={0}
            destroyTooltipOnHide
          >

              <Icon name="icon-info" />

          </Tooltip>

        </>
        )}

  );
}

IndicatorCard.propTypes = {
  indicator: PropTypes.object.isRequired,
  country: PropTypes.object.isRequired,
};

export default IndicatorCard;
<|endoftext|>


<commit_before>Ext.define('CF.view.dataviews.Conf', {
  extend: 'Ext.grid.Panel',
  alias: 'widget.conf',

  autoScroll: true,
  disabled: true,
  requires: [
    'Ext.grid.plugin.CellEditing',
    'Ext.form.field.Number',
    'CF.view.Component'
  ],
  border: false,
  columns: [{
    header: 'Name',
    dataIndex: 'name'
  }, {
    header: 'Value',
    dataIndex: 'value',
    xtype: 'componentcolumn',
    renderer: function(value, meta, record) {
      var change = function(component, newValue, oldValue, options) {
        record.set('value', newValue, {
          // prevent breaking focus on the field
          silent: true
        });
      };

      if (record.get('type') === 'bool') {
        if (value === true || value === 'true' || value === 1 || value === '1' || value === 'on') {
          value = true;
        } else {
          value = false;
        }
        return {
          checked: value,
          xtype: 'checkbox',
          listeners: {
            change: change,
          },
          disabled: !record.get('editable')
        }
      } else if (record.get('type') === 'int') {
        return {
          value: value,
          xtype: 'numberfield',
          allowDecimals: false,
          step: record.get('step'),
          listeners: {
            change: change,
          },
          minValue: record.get('minValue'),
          maxValue: record.get('maxValue'),
          disabled: !record.get('editable')
        }
      } else if (record.get('type') === 'float') {
        return {
          value: Number(value),
          xtype: 'numberfield',
          allowDecimals: true,
          allowExponential: false,
          decimalPrecision: 10,
          step: record.get('step'),
          listeners: {
            change: change,
          },
          minValue: record.get('minValue'),
          maxValue: record.get('maxValue'),
          disabled: !record.get('editable')
        }
      } else if (record.get('type') === 'option') {
        var options = record.get('options');
        options.forEach(function(option) {
          if (option[0] === value) {
            value = option;
            return;
          }
        });
        return {
          value: value,
          store: options,
          queryMode: 'local',
          xtype: 'combobox',
          listeners: {
            change: change,
          },
          disabled: !record.get('editable')
        }
      } else {
        return {
          value: value,
          xtype: 'textfield',
          listeners: {
            change: change,
          },
          disabled: !record.get('editable')
        }
      }
    }
  }, {
    flex: 1,
    header: 'Description',
    dataIndex: 'desc'
  }],
  flex: 1,
  selType: 'cellmodel',
  features: [],
  initComponent: function() {
    this.callParent(arguments);
  }
});<commit_msg>To display description of the solver input parameters using an info icon<commit_after> Ext.define('CF.view.dataviews.Conf', {
  extend: 'Ext.grid.Panel',
  alias: 'widget.conf',

  autoScroll: true,
  disabled: true,
  requires: [
    'Ext.grid.plugin.CellEditing',
    'Ext.form.field.Number',
    'CF.view.Component'
  ],
  border: false,
  columns: [{
    header: 'Name',
    dataIndex: 'name',
    flex: 40 / 100,
  }, {
    header: 'Value',
    dataIndex: 'value',
    flex: 30 / 100,
    xtype: 'componentcolumn',
    renderer: function(value, meta, record) {
      var change = function(component, newValue, oldValue, options) {
        record.set('value', newValue, {
          // prevent breaking focus on the field
          silent: true
        });
      };

      if (record.get('type') === 'bool') {
        if (value === true || value === 'true' || value === 1 || value === '1' || value === 'on') {
          value = true;
        } else {
          value = false;
        }
        return {
          checked: value,
          xtype: 'checkbox',
          listeners: {
            change: change,
          },
          disabled: !record.get('editable')
        }
      } else if (record.get('type') === 'int') {
        return {
          value: value,
          xtype: 'numberfield',
          allowDecimals: false,
          step: record.get('step'),
          listeners: {
            change: change,
          },
          minValue: record.get('minValue'),
          maxValue: record.get('maxValue'),
          disabled: !record.get('editable')
        }
      } else if (record.get('type') === 'float') {
        return {
          value: Number(value),
          xtype: 'numberfield',
          allowDecimals: true,
          allowExponential: false,
          decimalPrecision: 10,
          step: record.get('step'),
          listeners: {
            change: change,
          },
          minValue: record.get('minValue'),
          maxValue: record.get('maxValue'),
          disabled: !record.get('editable')
        }
      } else if (record.get('type') === 'option') {
        var options = record.get('options');
        options.forEach(function(option) {
          if (option[0] === value) {
            value = option;
            return;
          }
        });
        return {
          value: value,
          store: options,
          queryMode: 'local',
          xtype: 'combobox',
          listeners: {
            change: change,
          },
          disabled: !record.get('editable')
        }
      } else {
        return {
          value: value,
          xtype: 'textfield',
          listeners: {
            change: change,
          },
          disabled: !record.get('editable')
        }
      }
    }
  }, {
    flex: 1,
    header: 'Description',
    dataIndex: 'desc',
    renderer: function(value, metaData, record, rowIdx, colIdx, store) {
        metaData.tdAttr = 'data-qtip="' + value + '"';
        return  '<img src="/../../forward-modelling-portlet/img/help.png" style="margin-left: 20px; width: 16px; height: 16px;" />';
    }
  }],
  flex: 1,
  selType: 'cellmodel',
  features: [],
  initComponent: function() {
    this.callParent(arguments);
  }
});<|endoftext|>


<commit_before>Ext.define('devilry_subjectadmin.view.passedpreviousperiod.GridBase', {
    extend: 'Ext.grid.Panel',
    requires: [
        'Ext.XTemplate'
    ],

    store: 'PassedPreviousPeriodItems',
    border: 1,
    frame: false,
    hideHeaders: true,

    col1Tpl: [
        '
',
            '
',
                '{displaynames}',
            '
',
            '<tpl if="name">',
                '
{name}
',
            '</tpl>',
            '
{usernames}
',
        '
'
    ],

    col2Tpl: [
        '
',
            '<tpl if="oldgroup">',
                '',
                    gettext('Passed {oldperiodname}.'),
                '',
            '<tpl else>',
                '',
                    '<tpl switch="whyignored">',
                        '<tpl case="has_alias_feedback">',
                            gettext('Is already marked as previously passed.'),
                        '<tpl case="only_failing_grade_in_previous">',
                            gettext('The student has delivered this assignment previously, but never achieved a passing grade.'),
                        '<tpl case="has_feedback">',
                            gettext('Group has feedback for this assignment.'),
                    '</tpl>',
                '',
            '</tpl>',
        '
'
    ],

    initComponent: function() {
        this.col1TplCompiled = Ext.create('Ext.XTemplate', this.col1Tpl);
        this.col2TplCompiled = Ext.create('Ext.XTemplate', this.col2Tpl);
        Ext.apply(this, {
            columns: [{
                dataIndex: 'id',
                flex: 7,
                menuDisabled: true,
                renderer: this.renderCol1,
                sortable: false
            }, {
                dataIndex: 'id',
                flex: 3,
                menuDisabled: true,
                renderer: this.renderCol2,
                sortable: false
            }]
        });
        this.callParent(arguments);
    },

    renderCol1: function(unused, unused2, record) {
        var group = record.get('group');
        var displaynames = [];
        var usernames = [];
        var no_fullnames = true;
        for(var index=0; index<group.candidates.length; index++)  {
            var candidate = group.candidates[index];
            if(!Ext.isEmpty(candidate.full_name)) {
                no_fullnames = false;
            }
            displaynames.push(candidate.user.displayname);
            usernames.push(candidate.user.username);
        }
        return this.col1TplCompiled.apply({
            id: group.id,
            name: group.name,
            displaynames: displaynames.join(', '),
            usernames: usernames.join(', '),
            no_fullnames: no_fullnames
        });
    },

    renderCol2: function(unused, unused2, record) {
        var oldgroup = record.get('oldgroup');
        var whyignored = record.get('whyignored');
        return this.col2TplCompiled.apply({
            id: record.get('group').id,
            oldgroup: oldgroup,
            oldperiodname: oldgroup? oldgroup.period.long_name: null,
            whyignored: whyignored
        });
    }
});
<commit_msg>subjectadmin...passedpreviousperiod.GridBase: Fix classnames.<commit_after>Ext.define('devilry_subjectadmin.view.passedpreviousperiod.GridBase', {
    extend: 'Ext.grid.Panel',
    requires: [
        'Ext.XTemplate'
    ],

    store: 'PassedPreviousPeriodItems',
    border: 1,
    frame: false,
    hideHeaders: true,

    col1Tpl: [
        '
',
            '
',
                '{displaynames}',
            '
',
            '<tpl if="name">',
                '
{name}
',
            '</tpl>',
            '
{usernames}
',
        '
'
    ],

    col2Tpl: [
        '
',
            '<tpl if="oldgroup">',
                '',
                    gettext('Passed {oldperiodname}.'),
                '',
            '<tpl else>',
                '',
                    '<tpl switch="whyignored">',
                        '<tpl case="has_alias_feedback">',
                            gettext('Is already marked as previously passed.'),
                        '<tpl case="only_failing_grade_in_previous">',
                            gettext('The student has delivered this assignment previously, but never achieved a passing grade.'),
                        '<tpl case="has_feedback">',
                            gettext('Group has feedback for this assignment.'),
                    '</tpl>',
                '',
            '</tpl>',
        '
'
    ],

    initComponent: function() {
        this.col1TplCompiled = Ext.create('Ext.XTemplate', this.col1Tpl);
        this.col2TplCompiled = Ext.create('Ext.XTemplate', this.col2Tpl);
        Ext.apply(this, {
            columns: [{
                dataIndex: 'id',
                flex: 7,
                menuDisabled: true,
                renderer: this.renderCol1,
                sortable: false
            }, {
                dataIndex: 'id',
                flex: 3,
                menuDisabled: true,
                renderer: this.renderCol2,
                sortable: false
            }]
        });
        this.callParent(arguments);
    },

    renderCol1: function(unused, unused2, record) {
        var group = record.get('group');
        var displaynames = [];
        var usernames = [];
        var no_fullnames = true;
        for(var index=0; index<group.candidates.length; index++)  {
            var candidate = group.candidates[index];
            if(!Ext.isEmpty(candidate.full_name)) {
                no_fullnames = false;
            }
            displaynames.push(candidate.user.displayname);
            usernames.push(candidate.user.username);
        }
        return this.col1TplCompiled.apply({
            id: group.id,
            name: group.name,
            displaynames: displaynames.join(', '),
            usernames: usernames.join(', '),
            no_fullnames: no_fullnames
        });
    },

    renderCol2: function(unused, unused2, record) {
        var oldgroup = record.get('oldgroup');
        var whyignored = record.get('whyignored');
        return this.col2TplCompiled.apply({
            id: record.get('group').id,
            oldgroup: oldgroup,
            oldperiodname: oldgroup? oldgroup.period.long_name: null,
            whyignored: whyignored
        });
    }
});
<|endoftext|>


<commit_before>/*
 * Translated default messages for the jQuery validation plugin.
 * Locale: ZH (Chinese, 中文 (Zhōngwén), 汉语, 漢語)
 */
$.extend( $.validator.messages, {
	required: "这是必填字段",
	remote: "请修正此字段",
	email: "请输入有效的电子邮件地址",
	url: "请输入有效的网址",
	date: "请输入有效的日期",
	dateISO: "请输入有效的日期 (YYYY-MM-DD)",
	number: "请输入有效的数字",
	digits: "只能输入数字",
	creditcard: "请输入有效的信用卡号码",
	equalTo: "你的输入不相同",
	extension: "请输入有效的后缀",
	maxlength: $.validator.format( "最多可以输入 {0} 个字符" ),
	minlength: $.validator.format( "最少要输入 {0} 个字符" ),
	rangelength: $.validator.format( "请输入长度在 {0} 到 {1} 之间的字符串" ),
	range: $.validator.format( "请输入范围在 {0} 到 {1} 之间的数值" ),
	max: $.validator.format( "请输入不大于 {0} 的数值" ),
	min: $.validator.format( "请输入不小于 {0} 的数值" )
} );
<commit_msg>Update messages_zh.js<commit_after>/*
 * Translated default messages for the jQuery validation plugin.
 * Locale: ZH (Chinese, 中文 (Zhōngwén), 汉语, 漢語)
 */
$.extend( $.validator.messages, {
	required: "这是必填字段",
	remote: "请修正此字段",
	email: "请输入有效的电子邮件地址",
	url: "请输入有效的网址",
	date: "请输入有效的日期",
	dateISO: "请输入有效的日期 (YYYY-MM-DD)",
	number: "请输入有效的数字",
	digits: "只能输入数字",
	creditcard: "请输入有效的信用卡号码",
	equalTo: "你的输入不相同",
	extension: "请输入有效的后缀",
	maxlength: $.validator.format( "最多可以输入 {0} 个字符" ),
	minlength: $.validator.format( "最少要输入 {0} 个字符" ),
	rangelength: $.validator.format( "请输入长度在 {0} 到 {1} 之间的字符串" ),
	range: $.validator.format( "请输入范围在 {0} 到 {1} 之间的数值" ),
	step: $.validator.format( "请输入 {0} 的整数倍值" ),
	max: $.validator.format( "请输入不大于 {0} 的数值" ),
	min: $.validator.format( "请输入不小于 {0} 的数值" )
} );
<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty';
import sumBy from 'lodash/sumBy';
import { sortByKey } from 'utils/data';
import { format } from 'd3-format';
import endsWith from 'lodash/endsWith';

// get list data
const getData = state => state.data;
const getSettings = state => state.settings;
const getLocationNames = state => state.locationNames;
const getIndicatorWhitelist = state => state.whitelist;
const getColors = state => state.colors;

// get lists selected
export const getTreeCoverPlantationsData = createSelector(
  [getData, getSettings, getIndicatorWhitelist, getColors],
  (data, settings, whitelist, colors) => {
    if (isEmpty(data) || isEmpty(whitelist)) return null;
    const { plantations } = data;
    const totalPlantations = sumBy(plantations, 'plantation_extent');

    return sortByKey(
      plantations.filter(d => d.plantation_extent).map(d => ({
        label: d.label,
        value: d.plantation_extent,
        color: colors[d.label],
        percentage: d.plantation_extent / totalPlantations * 100
      })),
      'value',
      true
    );
  }
);

export const getSentence = createSelector(
  [getTreeCoverPlantationsData, getSettings, getLocationNames],
  (data, settings, locationNames) => {
    if (isEmpty(data)) return null;
    const locationLabel = locationNames.current && locationNames.current.label;
    let topTypesSentence = '';
    let top = null;
    let remainSentence = '';
    if (settings.type === 'bound2') {
      top = data.slice(0, 2);
      if (top.length === 1) {
        topTypesSentence = `${
          top[0].label
        } represent the largest plantation by species`;
      } else {
        topTypesSentence = `${top[0].label} and ${
          top[1].label
        } represent the largest plantations by species`;
      }

      remainSentence = `The remaining ${format('.2s')(
        sumBy(data.slice(2), 'value')
      )}ha of tree cover is distributed between ${data.length -
        top.length} other plantation species.`;
    } else {
      top = data.slice(0, 1);
      topTypesSentence = `the largest plantation type by area are ${
        top[0].label
      }${endsWith(top[0].label, 's') ? '' : 's'}`;
    }

    return `In ${locationLabel}, ${topTypesSentence}, spanning ${format(
      '.2s'
    )(sumBy(top, 'value'))}ha. ${remainSentence}`;
  }
);
<commit_msg>only show remaining plantations sentence when greater than 0<commit_after>import { createSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty';
import sumBy from 'lodash/sumBy';
import { sortByKey } from 'utils/data';
import { format } from 'd3-format';
import endsWith from 'lodash/endsWith';

// get list data
const getData = state => state.data;
const getSettings = state => state.settings;
const getLocationNames = state => state.locationNames;
const getIndicatorWhitelist = state => state.whitelist;
const getColors = state => state.colors;

// get lists selected
export const getTreeCoverPlantationsData = createSelector(
  [getData, getSettings, getIndicatorWhitelist, getColors],
  (data, settings, whitelist, colors) => {
    if (isEmpty(data) || isEmpty(whitelist)) return null;
    const { plantations } = data;
    const totalPlantations = sumBy(plantations, 'plantation_extent');

    return sortByKey(
      plantations.filter(d => d.plantation_extent).map(d => ({
        label: d.label,
        value: d.plantation_extent,
        color: colors[d.label],
        percentage: d.plantation_extent / totalPlantations * 100
      })),
      'value',
      true
    );
  }
);

export const getSentence = createSelector(
  [getTreeCoverPlantationsData, getSettings, getLocationNames],
  (data, settings, locationNames) => {
    if (isEmpty(data)) return null;
    const locationLabel = locationNames.current && locationNames.current.label;
    let topTypesSentence = '';
    let top = null;
    let remainSentence = '';
    if (settings.type === 'bound2') {
      top = data.slice(0, 2);
      if (top.length === 1) {
        topTypesSentence = `${
          top[0].label
        } represent the largest plantation by species`;
      } else {
        topTypesSentence = `${top[0].label} and ${
          top[1].label
        } represent the largest plantations by species`;
      }

      remainSentence =
        data.length > top.length
          ? `The remaining ${format('.2s')(
            sumBy(data.slice(2), 'value')
          )}ha of tree cover is distributed between ${data.length -
              top.length} other plantation species.`
          : '';
    } else {
      top = data.slice(0, 1);
      topTypesSentence = `the largest plantation type by area are ${
        top[0].label
      }${endsWith(top[0].label, 's') ? '' : 's'}`;
    }

    return `In ${locationLabel}, ${topTypesSentence}, spanning ${format(
      '.2s'
    )(sumBy(top, 'value'))}ha. ${remainSentence}`;
  }
);
<|endoftext|>