<commit_before>/*
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-ul
 *
 * iotagent-ul is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-ul 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-ul.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[iot_support@tid.es]
 *
 * Modified by: Fernando Méndez, Daniel Calvo - ATOS Research & Innovation
 */

'use strict';

var iotAgentLib = require('iotagent-node-lib'),
    regenerateTransid = iotAgentLib.regenerateTransid,
    config = require('./configService'),
    transportSelector = require('./transportSelector'),
    intoTrans = iotAgentLib.intoTrans,
    _ = require('underscore'),
    utils = require('./iotaUtils'),
    async = require('async'),
    ulParser = require('./ulParser'),
    constants = require('./constants'),
    context = {
        op: 'IOTAUL.Common.Binding'
    };

/**
 * Find the attribute given by its name between all the active attributes of the given device, returning its type, or
 * null otherwise.
 *
 * @param {String} attribute        Name of the attribute to find.
 * @param {Object} device           Device object containing all the information about a device.
 * @return {String}                 String identifier of the attribute type.
 */
function guessType(attribute, device) {
    for (var i = 0; i < device.active.length; i++) {
        if (device.active[i].name === attribute) {
            return device.active[i].type;
        }
    }

    if (attribute === constants.TIMESTAMP_ATTRIBUTE) {
        if (iotAgentLib.configModule.checkNgsi2()) {
            return constants.TIMESTAMP_TYPE_NGSI2;
        } else {
            return constants.TIMESTAMP_TYPE;
        }
    } else {
        return constants.DEFAULT_ATTRIBUTE_TYPE;
    }
}

function sendConfigurationToDevice(device, apiKey, deviceId, results, callback) {
    transportSelector.applyFunctionFromBinding(
        [apiKey, deviceId, results],
        'sendConfigurationToDevice',
        device.transport,
        callback
    );
}

/**
 * Deals with configuration requests coming from the device. Whenever a new configuration requests arrives with a list
 * of attributes to retrieve, this handler asks the Context Broker for the values of those attributes, and publish a
 * new message in the "/1234/MQTT_2/configuration/values" topic
 *
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
 * @param {String} deviceId         Id of the device to be updated.
 * @param {Object} device           Device object containing all the information about a device.
 * @param {Object} objMessage          JSON object received.
 */
function manageConfigurationRequest(apiKey, deviceId, device, objMessage) {
    utils.manageConfiguration(
        apiKey,
        deviceId,
        device,
        objMessage,
        async.apply(sendConfigurationToDevice, device),
        function(error) {
            if (error) {
                iotAgentLib.alarms.raise(constants.MQTTB_ALARM, error);
            } else {
                iotAgentLib.alarms.release(constants.MQTTB_ALARM);
                config
                    .getLogger()
                    .debug(context, 'Configuration request finished for APIKey [%s] and Device [%s]', apiKey, deviceId);
            }
        }
    );
}

/**
 * Utility function used to reduce UL Payload group arrays and process them to update their value in the Context Broker.
 * This function process a new element of the payload group, generating a function ready to be called with a callback
 * in a async.series() call.
 *
 * @param {Object} device       Object containing all the information about the device sending the measures.
 * @param {String} apikey       APIKey of the service the device belongs to.
 * @param {Array} previous      Array of prepared functions that send information to the Context Broker.
 * @param {Object} current      Information sent by the device.
 * @param {Number} index        Index of the group in the array.
 * @return {Array}             Updated array of functions.
 */
function processMeasureGroup(device, apikey, previous, current, index) {
    var values = [];

    if (current.command) {
        previous.push(
            iotAgentLib.setCommandResult.bind(
                null,
                device.name,
                config.getConfig().iota.defaultResource,
                apikey,
                current.command,
                current.value,
                constants.COMMAND_STATUS_COMPLETED,
                device
            )
        );
    } else {
        for (var i in current) {
            if (current.hasOwnProperty(i)) {
                values.push({
                    name: i,
                    type: guessType(i, device),
                    value: current[i]
                });
            }
        }

        previous.push(iotAgentLib.update.bind(null, device.name, device.type, '', values, device));
    }

    return previous;
}

/**
 * Adds multiple measures to the Context Broker. Multiple measures come in the form of single-level JSON objects,
 * whose keys are the attribute names and whose values are the attribute values.
 *
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
 * @param {Object} device           Device object containing all the information about a device.
 * @param {String} message          UL payload.
 */
function multipleMeasures(apiKey, device, message) {
    var updates = [],
        messageStr = message.toString(),
        parsedMessage;

    config.getLogger().debug('Processing multiple measures for device [%s] with apiKey [%s]', device.id, apiKey);

    try {
        parsedMessage = ulParser.parse(messageStr);
    } catch (e) {
        config.getLogger().error(context, 'MEASURES-003: Parse error parsing incoming message [%]', messageStr);
        return;
    }

    updates = parsedMessage.reduce(processMeasureGroup.bind(null, device, apiKey), []);

    async.series(updates, function(error) {
        if (error) {
            config.getLogger().error(
                context,
                /*jshint quotmark: double */
                " MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %s",
                /*jshint quotmark: single */
                error
            );
        } else {
            // prettier-ignore
            config.getLogger().debug(
                context,
                'Multiple measures for device [%s] with apiKey [%s] successfully updated',
                device.id,
                apiKey
            );
        }
    });
}

/**
 * Adds a single measure to the context broker. The message for single measures contains the direct value to
 * be inserted in the attribute, given by its name.
 *
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
 * @param {String} attribute        Name of the attribute to update.
 * @param {Object} device           Device object containing all the information about a device.
 * @param {Buffer} message          Raw message coming from the client.
 */
function singleMeasure(apiKey, attribute, device, message) {
    var values;

    config.getLogger().debug('Processing single measure for device [%s] with apiKey [%s]', device.id, apiKey);

    values = [
        {
            name: attribute,
            type: guessType(attribute, device),
            value: message.toString()
        }
    ];

    iotAgentLib.update(device.name, device.type, '', values, device, function(error) {
        if (error) {
            config.getLogger().error(
                context,
                /*jshint quotmark: double */
                " MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %s",
                /*jshint quotmark: single */
                error
            );
        } else {
            // prettier-ignore
            config.getLogger().debug(
                context,
                'Single measure for device [%s] with apiKey [%s] successfully updated',
                device.id,
                apiKey
            );
        }
    });
}

/**
 * Handles an incoming message, extracting the API Key, device Id and attribute to update (in the case of single
 * measures) from the topic.
 *
 * @param {String} topic        Topic of the form: '/<APIKey>/deviceId/attributes[/<attributeName>]'.
 * @param {Object} message      message body (Object or Buffer, depending on the value).
 */
function messageHandler(topic, message, protocol) {
    var topicInformation = topic.split('/'),
        apiKey,
        deviceId,
        messageStr,
        parsedMessage;

    if ('ul' === topicInformation[1].toLowerCase()) {
        topicInformation.splice(1, 1);
    }
    apiKey = topicInformation[1];
    deviceId = topicInformation[2];
    messageStr = message.toString();

    function processMessageForDevice(device, apiKey, topicInformation) {
        iotAgentLib.alarms.release(constants.MQTTB_ALARM);

        if (
            topicInformation[3] === constants.CONFIGURATION_SUFIX &&
            topicInformation[4] === constants.CONFIGURATION_COMMAND_SUFIX &&
            message
        ) {
            parsedMessage = ulParser.parseConfigurationRequest(messageStr);
            manageConfigurationRequest(apiKey, deviceId, device, parsedMessage);
        } else if (topicInformation[3] === constants.CONFIGURATION_COMMAND_UPDATE) {
            var commandObj = ulParser.result(message.toString());
            utils.updateCommand(
                apiKey,
                device,
                commandObj.result,
                commandObj.command,
                constants.COMMAND_STATUS_COMPLETED,
                function(error) {
                    config.getLogger().debug('Command updated with result: %s', error);
                }
            );
        } else if (topicInformation[4]) {
            singleMeasure(apiKey, topicInformation[4], device, message);
        } else if (topicInformation[3] === constants.MEASURES_SUFIX) {
            multipleMeasures(apiKey, device, message.toString());
        } else {
            config.getLogger().error(
                context,
                /*jshint quotmark: double */
                "MEASURES-004: Couldn't process message [%s] due to format issues.",
                /*jshint quotmark: single */
                message
            );
        }
    }

    function processDeviceMeasure(error, device) {
        if (error) {
            config.getLogger().warn(context, 'MEASURES-004: Device not found for topic [%s]', topic);
        } else {
            var localContext = _.clone(context);

            localContext.service = device.service;
            localContext.subservice = device.subservice;

            intoTrans(localContext, processMessageForDevice)(device, apiKey, topicInformation);
        }
    }

    utils.retrieveDevice(deviceId, apiKey, protocol, processDeviceMeasure);
}

/**
 * Handles an incoming AMQP message, extracting the API Key, device Id and attribute to update (in the case of single
 * measures) from the AMQP topic.
 *
 * @param {String} topic        Topic of the form: '/<APIKey>/deviceId/attributes[/<attributeName>]'.
 * @param {Object} message      AMQP message body (Object or Buffer, depending on the value).
 */
function amqpMessageHandler(topic, message) {
    regenerateTransid(topic);
    messageHandler(topic, message, 'AMQP');
}

/**
 * Handles an incoming MQTT message, extracting the API Key, device Id and attribute to update (in the case of single
 * measures) from the MQTT topic.
 *
 * @param {String} topic        Topic of the form: '/<APIKey>/deviceId/attributes[/<attributeName>]'.
 * @param {Object} message      MQTT message body (Object or Buffer, depending on the value).
 */
function mqttMessageHandler(topic, message) {
    regenerateTransid(topic);
    config.getLogger().debug(context, 'message topic: %s', topic);
    messageHandler(topic, message, 'MQTT');
}

exports.amqpMessageHandler = amqpMessageHandler;
exports.mqttMessageHandler = mqttMessageHandler;
exports.messageHandler = messageHandler;
exports.processMeasureGroup = processMeasureGroup;
exports.guessType = guessType;
<commit_msg>handle missing slashes<commit_after>/*
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-ul
 *
 * iotagent-ul is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-ul 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-ul.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[iot_support@tid.es]
 *
 * Modified by: Fernando Méndez, Daniel Calvo - ATOS Research & Innovation
 */

'use strict';

var iotAgentLib = require('iotagent-node-lib'),
    regenerateTransid = iotAgentLib.regenerateTransid,
    config = require('./configService'),
    transportSelector = require('./transportSelector'),
    intoTrans = iotAgentLib.intoTrans,
    _ = require('underscore'),
    utils = require('./iotaUtils'),
    async = require('async'),
    ulParser = require('./ulParser'),
    constants = require('./constants'),
    context = {
        op: 'IOTAUL.Common.Binding'
    };

/**
 * Find the attribute given by its name between all the active attributes of the given device, returning its type, or
 * null otherwise.
 *
 * @param {String} attribute        Name of the attribute to find.
 * @param {Object} device           Device object containing all the information about a device.
 * @return {String}                 String identifier of the attribute type.
 */
function guessType(attribute, device) {
    for (var i = 0; i < device.active.length; i++) {
        if (device.active[i].name === attribute) {
            return device.active[i].type;
        }
    }

    if (attribute === constants.TIMESTAMP_ATTRIBUTE) {
        if (iotAgentLib.configModule.checkNgsi2()) {
            return constants.TIMESTAMP_TYPE_NGSI2;
        } else {
            return constants.TIMESTAMP_TYPE;
        }
    } else {
        return constants.DEFAULT_ATTRIBUTE_TYPE;
    }
}

function sendConfigurationToDevice(device, apiKey, deviceId, results, callback) {
    transportSelector.applyFunctionFromBinding(
        [apiKey, deviceId, results],
        'sendConfigurationToDevice',
        device.transport,
        callback
    );
}

/**
 * Deals with configuration requests coming from the device. Whenever a new configuration requests arrives with a list
 * of attributes to retrieve, this handler asks the Context Broker for the values of those attributes, and publish a
 * new message in the "/1234/MQTT_2/configuration/values" topic
 *
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
 * @param {String} deviceId         Id of the device to be updated.
 * @param {Object} device           Device object containing all the information about a device.
 * @param {Object} objMessage          JSON object received.
 */
function manageConfigurationRequest(apiKey, deviceId, device, objMessage) {
    utils.manageConfiguration(
        apiKey,
        deviceId,
        device,
        objMessage,
        async.apply(sendConfigurationToDevice, device),
        function(error) {
            if (error) {
                iotAgentLib.alarms.raise(constants.MQTTB_ALARM, error);
            } else {
                iotAgentLib.alarms.release(constants.MQTTB_ALARM);
                config
                    .getLogger()
                    .debug(context, 'Configuration request finished for APIKey [%s] and Device [%s]', apiKey, deviceId);
            }
        }
    );
}

/**
 * Utility function used to reduce UL Payload group arrays and process them to update their value in the Context Broker.
 * This function process a new element of the payload group, generating a function ready to be called with a callback
 * in a async.series() call.
 *
 * @param {Object} device       Object containing all the information about the device sending the measures.
 * @param {String} apikey       APIKey of the service the device belongs to.
 * @param {Array} previous      Array of prepared functions that send information to the Context Broker.
 * @param {Object} current      Information sent by the device.
 * @param {Number} index        Index of the group in the array.
 * @return {Array}             Updated array of functions.
 */
function processMeasureGroup(device, apikey, previous, current, index) {
    var values = [];

    if (current.command) {
        previous.push(
            iotAgentLib.setCommandResult.bind(
                null,
                device.name,
                config.getConfig().iota.defaultResource,
                apikey,
                current.command,
                current.value,
                constants.COMMAND_STATUS_COMPLETED,
                device
            )
        );
    } else {
        for (var i in current) {
            if (current.hasOwnProperty(i)) {
                values.push({
                    name: i,
                    type: guessType(i, device),
                    value: current[i]
                });
            }
        }

        previous.push(iotAgentLib.update.bind(null, device.name, device.type, '', values, device));
    }

    return previous;
}

/**
 * Adds multiple measures to the Context Broker. Multiple measures come in the form of single-level JSON objects,
 * whose keys are the attribute names and whose values are the attribute values.
 *
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
 * @param {Object} device           Device object containing all the information about a device.
 * @param {String} message          UL payload.
 */
function multipleMeasures(apiKey, device, message) {
    var updates = [],
        messageStr = message.toString(),
        parsedMessage;

    config.getLogger().debug('Processing multiple measures for device [%s] with apiKey [%s]', device.id, apiKey);

    try {
        parsedMessage = ulParser.parse(messageStr);
    } catch (e) {
        config.getLogger().error(context, 'MEASURES-003: Parse error parsing incoming message [%]', messageStr);
        return;
    }

    updates = parsedMessage.reduce(processMeasureGroup.bind(null, device, apiKey), []);

    async.series(updates, function(error) {
        if (error) {
            config.getLogger().error(
                context,
                /*jshint quotmark: double */
                " MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %s",
                /*jshint quotmark: single */
                error
            );
        } else {
            // prettier-ignore
            config.getLogger().debug(
                context,
                'Multiple measures for device [%s] with apiKey [%s] successfully updated',
                device.id,
                apiKey
            );
        }
    });
}

/**
 * Adds a single measure to the context broker. The message for single measures contains the direct value to
 * be inserted in the attribute, given by its name.
 *
 * @param {String} apiKey           API Key corresponding to the Devices configuration.
 * @param {String} attribute        Name of the attribute to update.
 * @param {Object} device           Device object containing all the information about a device.
 * @param {Buffer} message          Raw message coming from the client.
 */
function singleMeasure(apiKey, attribute, device, message) {
    var values;

    config.getLogger().debug('Processing single measure for device [%s] with apiKey [%s]', device.id, apiKey);

    values = [
        {
            name: attribute,
            type: guessType(attribute, device),
            value: message.toString()
        }
    ];

    iotAgentLib.update(device.name, device.type, '', values, device, function(error) {
        if (error) {
            config.getLogger().error(
                context,
                /*jshint quotmark: double */
                " MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %s",
                /*jshint quotmark: single */
                error
            );
        } else {
            // prettier-ignore
            config.getLogger().debug(
                context,
                'Single measure for device [%s] with apiKey [%s] successfully updated',
                device.id,
                apiKey
            );
        }
    });
}

/**
 * Handles an incoming message, extracting the API Key, device Id and attribute to update (in the case of single
 * measures) from the topic.
 *
 * @param {String} topic        Topic of the form: '/<APIKey>/deviceId/attributes[/<attributeName>]'.
 * @param {Object} message      message body (Object or Buffer, depending on the value).
 */
function messageHandler(topic, message, protocol) {
    if (topic[0] !== '/') topic = '/' + topic;
    var topicInformation = topic.split('/'),
        apiKey,
        deviceId,
        messageStr,
        parsedMessage;

    if ('ul' === topicInformation[1].toLowerCase()) {
        topicInformation.splice(1, 1);
    }
    apiKey = topicInformation[1];
    deviceId = topicInformation[2];
    messageStr = message.toString();

    function processMessageForDevice(device, apiKey, topicInformation) {
        iotAgentLib.alarms.release(constants.MQTTB_ALARM);

        if (
            topicInformation[3] === constants.CONFIGURATION_SUFIX &&
            topicInformation[4] === constants.CONFIGURATION_COMMAND_SUFIX &&
            message
        ) {
            parsedMessage = ulParser.parseConfigurationRequest(messageStr);
            manageConfigurationRequest(apiKey, deviceId, device, parsedMessage);
        } else if (topicInformation[3] === constants.CONFIGURATION_COMMAND_UPDATE) {
            var commandObj = ulParser.result(message.toString());
            utils.updateCommand(
                apiKey,
                device,
                commandObj.result,
                commandObj.command,
                constants.COMMAND_STATUS_COMPLETED,
                function(error) {
                    config.getLogger().debug('Command updated with result: %s', error);
                }
            );
        } else if (topicInformation[4]) {
            singleMeasure(apiKey, topicInformation[4], device, message);
        } else if (topicInformation[3] === constants.MEASURES_SUFIX) {
            multipleMeasures(apiKey, device, message.toString());
        } else {
            config.getLogger().error(
                context,
                /*jshint quotmark: double */
                "MEASURES-004: Couldn't process message [%s] due to format issues.",
                /*jshint quotmark: single */
                message
            );
        }
    }

    function processDeviceMeasure(error, device) {
        if (error) {
            config.getLogger().warn(context, 'MEASURES-004: Device not found for topic [%s]', topic);
        } else {
            var localContext = _.clone(context);

            localContext.service = device.service;
            localContext.subservice = device.subservice;

            intoTrans(localContext, processMessageForDevice)(device, apiKey, topicInformation);
        }
    }

    utils.retrieveDevice(deviceId, apiKey, protocol, processDeviceMeasure);
}

/**
 * Handles an incoming AMQP message, extracting the API Key, device Id and attribute to update (in the case of single
 * measures) from the AMQP topic.
 *
 * @param {String} topic        Topic of the form: '/<APIKey>/deviceId/attributes[/<attributeName>]'.
 * @param {Object} message      AMQP message body (Object or Buffer, depending on the value).
 */
function amqpMessageHandler(topic, message) {
    regenerateTransid(topic);
    messageHandler(topic, message, 'AMQP');
}

/**
 * Handles an incoming MQTT message, extracting the API Key, device Id and attribute to update (in the case of single
 * measures) from the MQTT topic.
 *
 * @param {String} topic        Topic of the form: '/<APIKey>/deviceId/attributes[/<attributeName>]'.
 * @param {Object} message      MQTT message body (Object or Buffer, depending on the value).
 */
function mqttMessageHandler(topic, message) {
    regenerateTransid(topic);
    config.getLogger().debug(context, 'message topic: %s', topic);
    messageHandler(topic, message, 'MQTT');
}

exports.amqpMessageHandler = amqpMessageHandler;
exports.mqttMessageHandler = mqttMessageHandler;
exports.messageHandler = messageHandler;
exports.processMeasureGroup = processMeasureGroup;
exports.guessType = guessType;
<|endoftext|>


<commit_before>//
//  File: skin.js
//  Project: R7.Epsilon
//
//  Author: Roman M. Yagodin <roman.yagodin@gmail.com>
//
//  Copyright (c) 2015-2019 Roman M. Yagodin, R7.Labs
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

import A11y from "./a11y";
import Cookies from "js-cookie";

const supportedBrowsers = require ("./supportedBrowsers");

// TODO: Move global functions to eplilon object?

window.skinGoogleTranslatePage = function (fromLang) {
    window.open ("http://translate.google.com/translate?hl=en&sl=" + fromLang + "&u=" + encodeURI (document.location));
};

window.skinGetBaseFeedbackUrl = function () {
    const feedbackParams = "returntabid=" + epsilon.queryParams ["TabId"] + "&feedbackmid=" + epsilon.feedbackModuleId;
    return epsilon.feedbackUrl + "?" + feedbackParams;
}

window.skinOpenFeedback = function () {
    const selection = encodeURIComponent (rangy.getSelection ().toString ().replace (/(\n|\r)/gm," ").replace (/\s+/g, " ").replace (/\"/g, "").trim ().substring (0,100));
    const feedbackSelection = ((!!selection)? "&feedbackselection=" + selection : "");
    const feedbackUrl = skinGetBaseFeedbackUrl () + feedbackSelection;
    window.open (feedbackUrl, "_blank");
};

window.skinSearchExternalClick = function (e, link) {
    const searchText = $("input[id$='_dnnSearch_txtSearch']").val ();
    const urlFormat = $(link).data ("url-format");
    $(link).attr ("href", urlFormat.replace ("{website}", encodeURIComponent (epsilon.portalAlias)).replace ("{searchText}", encodeURIComponent (searchText)));
};

window.skinCookiesAlertButtonClick = function (e) {
    Cookies.set (epsilon.cookiePrefix + "CookiesAlert", {expires: 14});
    $(e.target).closest (".toast").toast ("hide");
};

window.skinCookiesDisabledAlertButtonClick = function (e) {
    $(e.target).closest (".toast").toast ("hide");
};

window.skinBrowserAlertButtonClick = function (e) {
    Cookies.set (epsilon.cookiePrefix + "BrowserAlert", {expires: 1});
    $(e.target).closest (".toast").toast ("hide");
};

(function ($, window, document) {

    function getLocationOrigin (location) {
        return (!!location.origin)
            ? location.origin
            : location.protocol + "//" + location.hostname + (location.port ? ":" + location.port: "");
    }

    function initFeedbackButton () {
        $("#btnSkinFeedback").on ("inserted.bs.popover", function () {
            $("#btnSkinOpenFeedback")
                .attr ("href", skinGetBaseFeedbackUrl ())
                .attr ("target", "_blank")
                .click (function (e) {
                    e.preventDefault ();
                    skinOpenFeedback ();
                });
        });
    }

    function setupFeedbackModule () {
        if (!!epsilon.queryParams ["feedbackmid"]) {

            var feedbackMessage = epsilon.localization ["feedbackMessageTemplate"];

            if (!!epsilon.queryParams ["returntabid"]) {
                feedbackMessage = feedbackMessage.replace (/\{page\}/, getLocationOrigin (window.location) + "/tabid/" + epsilon.queryParams ["returntabid"]);
            }
            else {
                feedbackMessage = feedbackMessage.replace (/\{page\}/, epsilon.localization ["notSpecified"]);
            }

            if (!!epsilon.queryParams ["feedbackselection"]) {
                feedbackMessage = feedbackMessage.replace (/\{selection\}/, epsilon.queryParams ["feedbackselection"]);
            }
            else {
                feedbackMessage = feedbackMessage.replace (/\{selection\}/, epsilon.localization ["notSpecified"]);
            }

            var moduleSelector = "#dnn_ctr" + epsilon.queryParams ["feedbackmid"] + "_ContentPane";
            $(moduleSelector + " textarea").first ().val (feedbackMessage).trigger ("change").trigger ("keyup");
        }
    }

    function initUpButton (offset, duration) {
        $(window).scroll(function() {
            if ($(this).scrollTop() > offset) {
                $('.skin-float-btn-up').fadeIn(duration);
            } else {
                $('.skin-float-btn-up').fadeOut(duration);
            }
        });

        $('.skin-float-btn-up').on ("click.epsilon", function (e) {
            e.preventDefault();
            $(this).tooltip ('hide');
            $('html, body').animate({scrollTop: 0}, duration);
            return false;
        });
    }

    function emptyLayoutRows () {
        $('.row').each (function () {
            if ($(this).children ().length ===
                $(this).children ('.DNNEmptyPane').not ('.dnnSortable').length) {
                $(this).addClass ('d-none');
            }
        });
    }

    function emptySpecificTags () {
        $(".language-object").each (function () {
            if ($(this).children ().length === 0) {
                $(this).addClass ("d-none");
            }
        });
    }

    function alterLanguage () {
        $(".skin-languages .language-object a").each (function () {
            const lang = $(this).parent (".Language").attr ("title");
            const langCode = $(this).closest (".language-object").find ("option").filter (function () { return $(this).text() == lang; }).val ();
            $(this).addClass ("dropdown-item")
                .html ("" + langCode + " " + lang)
                .attr ("hreflang", langCode);
        });
    }

    function initBootstrapTooltips () {
        if (typeof ($.fn.tooltip) !== "undefined") {
            $("[data-toggle='tooltip']").tooltip ();
        }
    }

    function initBootstrapPopovers () {
        if (typeof ($.fn.popover) !== "undefined") {
            $("[data-toggle='popover']").popover ();
        }
    }

    function initBootstrapToasts () {
        if (typeof ($.fn.toast) !== "undefined") {
            $(".toast").toast ();
        }
    }

    function alterLogin () {
        $(".skin-login li.userDisplayName > a").addClass ("dropdown-item");
        $(".skin-login div.loginGroup > a").addClass ("dropdown-item");
        $(".skin-login li.userMessages > a").addClass ("dropdown-item");
        $(".skin-login li.userNotifications > a").addClass ("dropdown-item");
        $(".skin-login li.userProfileImg > a").addClass ("dropdown-item");

        const loginGroup = $(".skin-login .loginGroup").first ().detach ();
        const divider = "

";

        const profileImg = $(".skin-login li.userProfileImg > a > img");
        if (profileImg.length > 0) {
            profileImg.attr ("src", profileImg.attr ("src").replace ("h=32&w=32", "h=64&w=64"));

            const profileLinkBlock = $(".skin-login li.userDisplayName").first ().detach ();
            const profileImgBlock = $(".skin-login li.userProfileImg").first ().detach ();

            $(".skin-login ul.buttonGroup")
                .prepend (divider)
                .prepend (profileLinkBlock)
                .prepend (profileImgBlock)
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));

                $(".skin-login li.userMessages > a > span").addClass ("badge badge-primary");
                $(".skin-login li.userNotifications > a > span").addClass ("badge badge-secondary");
        }
        else {
            $(".skin-login ul.buttonGroup")
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));
        }
    }

    function initCustomContent () {
        // TODO: Also check for superusers and admins
        if (epsilon.isEditMode) {
            $(".skin-custom-content").each (function () {
                $(this).prepend ("
"
                                + "
"
                                // TODO: Localize label
                                // TODO: Generate actual edit URL
                                + "

"
                                + "
"
                                + "
");
            });
        }
    }

    function initSearch () {
        $("#searchModal").on ("shown.bs.modal", function (e) {
            $("#searchModal input[id$='_dnnSearch_txtSearch']").focus ();
        });
    }

    function initClipboard () {
        epsilon.clipboard = new ClipboardJS('.btn.btn-clipboard');
    }

    function initTags () {
        $(".skin-tags ul.categories > li > a").addClass ("badge badge-secondary");
    }

    function initMainMenu () {
        $(".skin-main-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skin-main-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });

        $(".skin-main-menu .skin-submenu.collapse")
            .on ("shown.bs.collapse", function (e) {
                const parentMenuItem = $(".skin-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab or Down
                    if ((e.keyCode === 9 && !e.shiftKey) || e.keyCode === 40) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        firstMenuItem.focus ();
                    }
                });
                firstMenuItem.on ("keydown.epsilon", function (e) {
                    // Shift-Tab or Up
                    if ((e.keyCode === 9 && e.shiftKey) || e.keyCode === 38) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        parentMenuItem.focus ();
                    }
                });
                lastMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab
                    if (e.keyCode === 9 && !e.shiftKey) {
                        const nextParentMenuItem = parentMenuItem.parent ().next ().children (".nav-link");
                        if (nextParentMenuItem.length > 0) {
                            e.preventDefault ();
                            e.stopPropagation ();
                            nextParentMenuItem.focus ();
                        }
                    }
                });
            })
            .on ("hidden.bs.collapse", function () {
                const parentMenuItem = $(".skin-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.off ("keydown.epsilon");
                firstMenuItem.off ("keydown.epsilon");
                lastMenuItem.off ("keydown.epsilon");
            });
    }

    function initBreadcrumb () {
        $(".skin-breadcrumb-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skin-breadcrumb-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });
    }

    function isCookiesEnabled () {
        // borrowed from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js
        try {
            document.cookie = epsilon.cookiePrefix + "CookieTest=1;"
            var isCookiesEnabled = document.cookie.indexOf (epsilon.cookiePrefix + "CookieTest=") !== -1;
            document.cookie = epsilon.cookiePrefix + "CookieTest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
            return isCookiesEnabled;
          }
          catch (e) {
            return false;
          }
    }

    function showToasts () {
        if (typeof Cookies.get (epsilon.cookiePrefix + "BrowserAlert") === "undefined") {
            if (! supportedBrowsers.test (navigator.userAgent)) {
                $("#skin_toastBrowserAlert").toast ("show");
            }
        }

        if (!isCookiesEnabled ()) {
            $("#skin_toastCookiesDisabledAlert").toast ("show");
        }
        else if (typeof Cookies.get (epsilon.cookiePrefix + "CookiesAlert") === "undefined") {
            $("#skin_toastCookiesAlert").toast ("show");
        }
    }

    $(function () {
        initBootstrapTooltips ();
        initBootstrapPopovers ();

        setupFeedbackModule ();

        if (! epsilon.inPopup) {
            initBootstrapToasts ();
            emptyLayoutRows ();
            emptySpecificTags ();
            initUpButton (320, 500);
            initCustomContent ();
            initSearch ();
            initClipboard ();
            initTags ();
            initMainMenu ();
            initBreadcrumb ();
            initFeedbackButton ();
            alterLanguage ();
            alterLogin ();
            showToasts ();
            window.skinA11y = new A11y ().init ();
        }
    });

}) (jQuery, window, document);
<commit_msg>Hide empty secondary menu<commit_after>//
//  File: skin.js
//  Project: R7.Epsilon
//
//  Author: Roman M. Yagodin <roman.yagodin@gmail.com>
//
//  Copyright (c) 2015-2019 Roman M. Yagodin, R7.Labs
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

import A11y from "./a11y";
import Cookies from "js-cookie";

const supportedBrowsers = require ("./supportedBrowsers");

// TODO: Move global functions to eplilon object?

window.skinGoogleTranslatePage = function (fromLang) {
    window.open ("http://translate.google.com/translate?hl=en&sl=" + fromLang + "&u=" + encodeURI (document.location));
};

window.skinGetBaseFeedbackUrl = function () {
    const feedbackParams = "returntabid=" + epsilon.queryParams ["TabId"] + "&feedbackmid=" + epsilon.feedbackModuleId;
    return epsilon.feedbackUrl + "?" + feedbackParams;
}

window.skinOpenFeedback = function () {
    const selection = encodeURIComponent (rangy.getSelection ().toString ().replace (/(\n|\r)/gm," ").replace (/\s+/g, " ").replace (/\"/g, "").trim ().substring (0,100));
    const feedbackSelection = ((!!selection)? "&feedbackselection=" + selection : "");
    const feedbackUrl = skinGetBaseFeedbackUrl () + feedbackSelection;
    window.open (feedbackUrl, "_blank");
};

window.skinSearchExternalClick = function (e, link) {
    const searchText = $("input[id$='_dnnSearch_txtSearch']").val ();
    const urlFormat = $(link).data ("url-format");
    $(link).attr ("href", urlFormat.replace ("{website}", encodeURIComponent (epsilon.portalAlias)).replace ("{searchText}", encodeURIComponent (searchText)));
};

window.skinCookiesAlertButtonClick = function (e) {
    Cookies.set (epsilon.cookiePrefix + "CookiesAlert", {expires: 14});
    $(e.target).closest (".toast").toast ("hide");
};

window.skinCookiesDisabledAlertButtonClick = function (e) {
    $(e.target).closest (".toast").toast ("hide");
};

window.skinBrowserAlertButtonClick = function (e) {
    Cookies.set (epsilon.cookiePrefix + "BrowserAlert", {expires: 1});
    $(e.target).closest (".toast").toast ("hide");
};

(function ($, window, document) {

    function getLocationOrigin (location) {
        return (!!location.origin)
            ? location.origin
            : location.protocol + "//" + location.hostname + (location.port ? ":" + location.port: "");
    }

    function initFeedbackButton () {
        $("#btnSkinFeedback").on ("inserted.bs.popover", function () {
            $("#btnSkinOpenFeedback")
                .attr ("href", skinGetBaseFeedbackUrl ())
                .attr ("target", "_blank")
                .click (function (e) {
                    e.preventDefault ();
                    skinOpenFeedback ();
                });
        });
    }

    function setupFeedbackModule () {
        if (!!epsilon.queryParams ["feedbackmid"]) {

            var feedbackMessage = epsilon.localization ["feedbackMessageTemplate"];

            if (!!epsilon.queryParams ["returntabid"]) {
                feedbackMessage = feedbackMessage.replace (/\{page\}/, getLocationOrigin (window.location) + "/tabid/" + epsilon.queryParams ["returntabid"]);
            }
            else {
                feedbackMessage = feedbackMessage.replace (/\{page\}/, epsilon.localization ["notSpecified"]);
            }

            if (!!epsilon.queryParams ["feedbackselection"]) {
                feedbackMessage = feedbackMessage.replace (/\{selection\}/, epsilon.queryParams ["feedbackselection"]);
            }
            else {
                feedbackMessage = feedbackMessage.replace (/\{selection\}/, epsilon.localization ["notSpecified"]);
            }

            var moduleSelector = "#dnn_ctr" + epsilon.queryParams ["feedbackmid"] + "_ContentPane";
            $(moduleSelector + " textarea").first ().val (feedbackMessage).trigger ("change").trigger ("keyup");
        }
    }

    function initUpButton (offset, duration) {
        $(window).scroll(function() {
            if ($(this).scrollTop() > offset) {
                $('.skin-float-btn-up').fadeIn(duration);
            } else {
                $('.skin-float-btn-up').fadeOut(duration);
            }
        });

        $('.skin-float-btn-up').on ("click.epsilon", function (e) {
            e.preventDefault();
            $(this).tooltip ('hide');
            $('html, body').animate({scrollTop: 0}, duration);
            return false;
        });
    }

    function emptyLayoutRows () {
        $('.row').each (function () {
            if ($(this).children ().length ===
                $(this).children ('.DNNEmptyPane').not ('.dnnSortable').length) {
                $(this).addClass ('d-none');
            }
        });
    }

    function emptySpecificTags () {
        $(".language-object").each (function () {
            if ($(this).children ().length === 0) {
                $(this).addClass ("d-none");
            }
        });
        $(".navbar.skin-secondary-menu .navbar-collapse").each (function () {
            if ($(this).children ().length === 0) {
                $(this).parent ().parent ().addClass ("d-none");
            }
        });
    }

    function alterLanguage () {
        $(".skin-languages .language-object a").each (function () {
            const lang = $(this).parent (".Language").attr ("title");
            const langCode = $(this).closest (".language-object").find ("option").filter (function () { return $(this).text() == lang; }).val ();
            $(this).addClass ("dropdown-item")
                .html ("" + langCode + " " + lang)
                .attr ("hreflang", langCode);
        });
    }

    function initBootstrapTooltips () {
        if (typeof ($.fn.tooltip) !== "undefined") {
            $("[data-toggle='tooltip']").tooltip ();
        }
    }

    function initBootstrapPopovers () {
        if (typeof ($.fn.popover) !== "undefined") {
            $("[data-toggle='popover']").popover ();
        }
    }

    function initBootstrapToasts () {
        if (typeof ($.fn.toast) !== "undefined") {
            $(".toast").toast ();
        }
    }

    function alterLogin () {
        $(".skin-login li.userDisplayName > a").addClass ("dropdown-item");
        $(".skin-login div.loginGroup > a").addClass ("dropdown-item");
        $(".skin-login li.userMessages > a").addClass ("dropdown-item");
        $(".skin-login li.userNotifications > a").addClass ("dropdown-item");
        $(".skin-login li.userProfileImg > a").addClass ("dropdown-item");

        const loginGroup = $(".skin-login .loginGroup").first ().detach ();
        const divider = "

";

        const profileImg = $(".skin-login li.userProfileImg > a > img");
        if (profileImg.length > 0) {
            profileImg.attr ("src", profileImg.attr ("src").replace ("h=32&w=32", "h=64&w=64"));

            const profileLinkBlock = $(".skin-login li.userDisplayName").first ().detach ();
            const profileImgBlock = $(".skin-login li.userProfileImg").first ().detach ();

            $(".skin-login ul.buttonGroup")
                .prepend (divider)
                .prepend (profileLinkBlock)
                .prepend (profileImgBlock)
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));

                $(".skin-login li.userMessages > a > span").addClass ("badge badge-primary");
                $(".skin-login li.userNotifications > a > span").addClass ("badge badge-secondary");
        }
        else {
            $(".skin-login ul.buttonGroup")
                .append (divider)
                .append (loginGroup.children ("a").prepend (" "));
        }
    }

    function initCustomContent () {
        // TODO: Also check for superusers and admins
        if (epsilon.isEditMode) {
            $(".skin-custom-content").each (function () {
                $(this).prepend ("
"
                                + "
"
                                // TODO: Localize label
                                // TODO: Generate actual edit URL
                                + "

"
                                + "
"
                                + "
");
            });
        }
    }

    function initSearch () {
        $("#searchModal").on ("shown.bs.modal", function (e) {
            $("#searchModal input[id$='_dnnSearch_txtSearch']").focus ();
        });
    }

    function initClipboard () {
        epsilon.clipboard = new ClipboardJS('.btn.btn-clipboard');
    }

    function initTags () {
        $(".skin-tags ul.categories > li > a").addClass ("badge badge-secondary");
    }

    function initMainMenu () {
        $(".skin-main-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skin-main-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });

        $(".skin-main-menu .skin-submenu.collapse")
            .on ("shown.bs.collapse", function (e) {
                const parentMenuItem = $(".skin-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab or Down
                    if ((e.keyCode === 9 && !e.shiftKey) || e.keyCode === 40) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        firstMenuItem.focus ();
                    }
                });
                firstMenuItem.on ("keydown.epsilon", function (e) {
                    // Shift-Tab or Up
                    if ((e.keyCode === 9 && e.shiftKey) || e.keyCode === 38) {
                        e.preventDefault ();
                        e.stopPropagation ();
                        parentMenuItem.focus ();
                    }
                });
                lastMenuItem.on ("keydown.epsilon", function (e) {
                    // Tab
                    if (e.keyCode === 9 && !e.shiftKey) {
                        const nextParentMenuItem = parentMenuItem.parent ().next ().children (".nav-link");
                        if (nextParentMenuItem.length > 0) {
                            e.preventDefault ();
                            e.stopPropagation ();
                            nextParentMenuItem.focus ();
                        }
                    }
                });
            })
            .on ("hidden.bs.collapse", function () {
                const parentMenuItem = $(".skin-main-menu .dropdown-toggle[href='#" + $(this).attr ("id") + "']");
                const firstMenuItem = $(this).find (".nav-link").first ();
                const lastMenuItem = $(this).find (".nav-link").last ();
                parentMenuItem.off ("keydown.epsilon");
                firstMenuItem.off ("keydown.epsilon");
                lastMenuItem.off ("keydown.epsilon");
            });
    }

    function initBreadcrumb () {
        $(".skin-breadcrumb-menu .collapse-toggle").on ("click.epsilon", function (e) {
            $(this).toggleClass ("show").next (".collapse").collapse ("toggle");
            e.stopPropagation ();
            e.preventDefault ();
        });
        // hide collapses when parent dropdown hides
        $(".skin-breadcrumb-menu .dropdown").on("hidden.bs.dropdown", function (e) {
            $(this).find (".collapse-toggle").removeClass ("show").next (".collapse").collapse ("hide");
        });
    }

    function isCookiesEnabled () {
        // borrowed from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cookies.js
        try {
            document.cookie = epsilon.cookiePrefix + "CookieTest=1;"
            var isCookiesEnabled = document.cookie.indexOf (epsilon.cookiePrefix + "CookieTest=") !== -1;
            document.cookie = epsilon.cookiePrefix + "CookieTest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
            return isCookiesEnabled;
          }
          catch (e) {
            return false;
          }
    }

    function showToasts () {
        if (typeof Cookies.get (epsilon.cookiePrefix + "BrowserAlert") === "undefined") {
            if (! supportedBrowsers.test (navigator.userAgent)) {
                $("#skin_toastBrowserAlert").toast ("show");
            }
        }

        if (!isCookiesEnabled ()) {
            $("#skin_toastCookiesDisabledAlert").toast ("show");
        }
        else if (typeof Cookies.get (epsilon.cookiePrefix + "CookiesAlert") === "undefined") {
            $("#skin_toastCookiesAlert").toast ("show");
        }
    }

    $(function () {
        initBootstrapTooltips ();
        initBootstrapPopovers ();

        setupFeedbackModule ();

        if (! epsilon.inPopup) {
            initBootstrapToasts ();
            emptyLayoutRows ();
            emptySpecificTags ();
            initUpButton (320, 500);
            initCustomContent ();
            initSearch ();
            initClipboard ();
            initTags ();
            initMainMenu ();
            initBreadcrumb ();
            initFeedbackButton ();
            alterLanguage ();
            alterLogin ();
            showToasts ();
            window.skinA11y = new A11y ().init ();
        }
    });

}) (jQuery, window, document);
<|endoftext|>


<commit_before>var assert = require("assert");

var Overlay = require("../src/Overlay/");
var LoadingBar = require("../src/Overlay/LoadingBar.js");
var Percentage = require("../src/Overlay/Percentage.js");
var Image = require("../src/ImagePreloader/Image.js");
var ImagePreloader = require("../src/ImagePreloader/");
var QueryLoader = require("../src/QueryLoader.js");

describe('LoadingBar', function() {
    describe('#create()', function () {
        var lb = new LoadingBar();
        lb.create();

        it('should create an element for itself', function () {
            assert.notEqual(null, lb.element);
            assert.notEqual("undefined", typeof lb.element);
            assert.notEqual("undefined", typeof lb.element.tagName);
            assert.equal("div", lb.element.tagName.toLowerCase());
            assert.equal(lb.className, lb.element.getAttribute("class"));
        });

        it('should apply default styling', function () {
            assert.equal("absolute", lb.element.style.position);
            assert.equal(lb.barHeight + "px", lb.element.style.height);
        });
    });

    describe('#updateProgress()', function () {
        it('should update the progress and adjust the loading bar', function () {
            var lb = new LoadingBar();
            lb.create();

            assert.equal("0%", lb.element.style.width);

            lb.updateProgress(10, 0);

            assert.equal("10%", lb.element.style.width);

            lb.updateProgress(50, 0);

            assert.equal("50%", lb.element.style.width);

            lb.updateProgress(100, 0);

            assert.equal("100%", lb.element.style.width);

            lb.updateProgress(-20, 0);

            assert.equal("0%", lb.element.style.width);

            lb.updateProgress(420, 0);

            assert.equal("100%", lb.element.style.width);
        });

        it('should not break on floats', function () {
            var lb = new LoadingBar();
            lb.create();

            lb.updateProgress(10.6, 0);

            assert.equal("10%", lb.element.style.width);

            lb.updateProgress(50.456, 0);

            assert.equal("50%", lb.element.style.width);
        });
    });
});

describe('Percentage', function() {
    describe('#create()', function () {
        var p = new Percentage();
        p.create();

        it('should create an element for itself', function () {
            assert.notEqual(null, p.element);
            assert.notEqual("undefined", typeof p.element);
            assert.notEqual("undefined", typeof p.element.tagName);
            assert.equal("div", p.element.tagName.toLowerCase());
            assert.equal(p.className, p.element.getAttribute("class"));
        });

        it('should apply default styling', function () {
            assert.equal("absolute", p.element.style.position);
        });
    });

    describe('#updateProgress()', function () {
        var p = new Percentage();
        p.create();

        it('should update the progress and adjust the loading bar', function () {
            assert.equal("0%", p.element.innerHTML);

            p.updateProgress(10, 0);

            assert.equal("10%", p.element.innerHTML);

            p.updateProgress(50, 0);

            assert.equal("50%", p.element.innerHTML);

            p.updateProgress(100, 0);

            assert.equal("100%", p.element.innerHTML);

            p.updateProgress(-20, 0);

            assert.equal("0%", p.element.innerHTML);

            p.updateProgress(420, 0);

            assert.equal("100%", p.element.innerHTML);
        });

        it('should not break on floats', function () {
            p.updateProgress(10.6, 0);

            assert.equal("10%", p.element.innerHTML);

            p.updateProgress(50.456, 0);

            assert.equal("50%", p.element.innerHTML);
        });
    });
});

describe('Overlay', function() {
    var fakeBody = document.createElement("body");

    describe('#create()', function () {
        var o = new Overlay(fakeBody);
        o.create();

        it('should create an element for itself', function () {
            assert.notEqual(null, o.element);
            assert.notEqual("undefined", typeof o.element);
            assert.notEqual("undefined", typeof o.element.tagName);
            assert.equal("div", o.element.tagName.toLowerCase());
            assert.equal(o.className, o.element.getAttribute("class"));
        });
    });

    describe('#calculatePosition()', function () {
        var o = new Overlay();

        it('should give the correct needed position of the overlay', function () {
            o.parentElement = fakeBody;

            assert.equal("fixed", o.calculatePosition());

            var fakeContainer = document.createElement("div");
            fakeContainer.style.position = "static";

            o.parentElement = fakeContainer;

            assert.equal("absolute", o.calculatePosition());
            assert.equal("relative", o.parentElement.style.position);

            o.parentElement.style.position = "absolute";

            assert.equal("absolute", o.calculatePosition());
        });
    });

    describe('#updateProgess()', function () {
        var o = new Overlay();
        o.parentElement = fakeBody;
        o.create();

        o.percentage = new Percentage();
        o.percentage.create();

        o.loadingBar = new LoadingBar();
        o.loadingBar.create();

        it('should update the loading progress of both percentage and loadingbar', function () {
            assert.equal("0%", o.percentage.element.innerHTML);
            assert.equal("0%", o.loadingBar.element.style.width);

            o.updateProgress(10, 0);

            assert.equal("10%", o.percentage.element.innerHTML);
            assert.equal("10%", o.loadingBar.element.style.width);
        });
    });
});

describe('Image', function() {
    describe('#constructor()', function () {
        it('should create an image object with given src', function () {
            var exampleImage = new Image("some/src");

            assert.equal("some/src", exampleImage.src);
        });

        it('should create a dom object with given src', function () {
            var exampleImage = new Image("some/src");

            assert.notEqual(-1, exampleImage.element.src.indexOf("some/src"));
        });
    });

    describe('#preload()', function () {
        it('should callback when an image is loaded', function (done) {
            var exampleImage = new Image("images/1.jpg");

            exampleImage.preload(done);
        });
    });
});

describe('QueryLoader', function() {
    describe('#createOverlay()', function () {
        var ql = new QueryLoader();

        it('should create an overlay when called', function () {
            ql.createOverlay();

            assert.equal(ql.overlay instanceof Overlay, true);
        });
    });

    describe('#createPreloader()', function () {
        var ql = new QueryLoader();

        it('should create the preloader', function () {
            ql.createPreloader();

            assert.equal(ql.preloader instanceof ImagePreloader, true);
        });
    });

    describe('#extend()', function () {
        it('should merge two objects', function () {
            var ql = new QueryLoader();

            var destination = {
                some: "thing",
                is: "not",
                right: "man"
            };

            var source = {
                some: "one",
                right: "dude"
            };

            assert.deepEqual({
                "some": "one",
                "is": "not",
                "right": "dude"
            }, ql.extend(destination, source));
        });
    });

    describe('#updateProgress()', function () {
        var called = false;
        var ql = new QueryLoader(document.createElement("div"), {
            onProgress: function (amount) {
                called = true;
            }
        });

        it('should call the onProgress event', function () {
            ql.updateProgress();

            assert.equal(called, true);
        });
    });
});

describe('ImagePreloader', function() {
    "use strict";
    describe('#getImageSrcs()', function () {
        var ip = new ImagePreloader();

        var fakeImagesContainer = document.createElement("div");

        var img1 = document.createElement("img");
        img1.setAttribute("src", "fakeimg1.png");
        fakeImagesContainer.appendChild(img1);

        var img2 = document.createElement("img");
        img2.setAttribute("src", "fakeimg2.png");
        fakeImagesContainer.appendChild(img2);

        var img3 = document.createElement("section");
        img3.style.backgroundImage = "url(fakeimg3.png)";
        fakeImagesContainer.appendChild(img3);

        var img4 = document.createElement("section");
        img4.style.backgroundImage = "linear-gradient(left, #fff, #eee)";
        fakeImagesContainer.appendChild(img4);

        var img5 = document.createElement("section");
        img5.style.background = "url(fakeimg5.png)";
        fakeImagesContainer.appendChild(img5);

        it('should get all images within the given element', function () {
            var images = ip.getImageSrcs(fakeImagesContainer);

            assert.equal(4, images.length);
            assert.notEqual(-1, images[0].indexOf("fakeimg1.png"));
            assert.notEqual(-1, images[1].indexOf("fakeimg2.png"));
            assert.notEqual(-1, images[2].indexOf("fakeimg3.png"));
            assert.notEqual(-1, images[3].indexOf("fakeimg5.png"));
        });
    });

    describe('#hasGradient()', function () {
        var ip = new ImagePreloader();

        it('should check if given url has a gradient', function () {
            assert.equal(false, ip.hasGradient("hasnogradienthere.png"));
            assert.equal(false, ip.hasGradient("grasdfsadg"));
            assert.equal(true, ip.hasGradient("linear-gradient(left, #fff, #fff)"));
        });
    });

    describe('#stripUrl()', function () {
        var ip = new ImagePreloader();

        it('should strip the url() part from given src', function () {
            assert.equal(-1, ip.stripUrl("url(this/path/file.png)").indexOf("url"));
            assert.equal(-1, ip.stripUrl("file.png").indexOf("url"));
        });
    });

    describe('#validUrl()', function () {
        var ip = new ImagePreloader();

        it('should check if given url is valid', function () {
            assert.equal(false, ip.validUrl(""));
            assert.equal(false, ip.validUrl("data:blablabla"));
            assert.equal(true, ip.validUrl("/this/is/valid.png"));
        });
    });

    describe('#urlIsNew()', function () {
        var ip = new ImagePreloader();
        ip.sources = ["test.png", "something.png", "image.jpg"];

        it('should check if given url is new in this.images', function () {
            assert.equal(false, ip.urlIsNew("image.jpg"));
            assert.equal(true, ip.urlIsNew("new.png"));
        });
    });
});<commit_msg>add instantiate checking<commit_after>var assert = require('assert');

var Overlay = require('../src/Overlay/');
var LoadingBar = require('../src/Overlay/LoadingBar.js');
var Percentage = require('../src/Overlay/Percentage.js');
var Image = require('../src/ImagePreloader/Image.js');
var ImagePreloader = require('../src/ImagePreloader/');
var QueryLoader = require('../src/QueryLoader.js');

describe('LoadingBar', function() {
    describe('#create()', function () {
        var lb = new LoadingBar();
        lb.create();

        it('should create an element for itself', function () {
            assert.notEqual(null, lb.element);
            assert.notEqual('undefined', typeof lb.element);
            assert.notEqual('undefined', typeof lb.element.tagName);
            assert.equal('div', lb.element.tagName.toLowerCase());
            assert.equal(lb.className, lb.element.getAttribute('class'));
        });

        it('should apply default styling', function () {
            assert.equal('absolute', lb.element.style.position);
            assert.equal(lb.barHeight + 'px', lb.element.style.height);
        });
    });

    describe('#updateProgress()', function () {
        it('should update the progress and adjust the loading bar', function () {
            var lb = new LoadingBar();
            lb.create();

            assert.equal('0%', lb.element.style.width);

            lb.updateProgress(10, 0);

            assert.equal('10%', lb.element.style.width);

            lb.updateProgress(50, 0);

            assert.equal('50%', lb.element.style.width);

            lb.updateProgress(100, 0);

            assert.equal('100%', lb.element.style.width);

            lb.updateProgress(-20, 0);

            assert.equal('0%', lb.element.style.width);

            lb.updateProgress(420, 0);

            assert.equal('100%', lb.element.style.width);
        });

        it('should not break on floats', function () {
            var lb = new LoadingBar();
            lb.create();

            lb.updateProgress(10.6, 0);

            assert.equal('10%', lb.element.style.width);

            lb.updateProgress(50.456, 0);

            assert.equal('50%', lb.element.style.width);
        });
    });
});

describe('Percentage', function() {
    describe('#create()', function () {
        var p = new Percentage();
        p.create();

        it('should create an element for itself', function () {
            assert.notEqual(null, p.element);
            assert.notEqual('undefined', typeof p.element);
            assert.notEqual('undefined', typeof p.element.tagName);
            assert.equal('div', p.element.tagName.toLowerCase());
            assert.equal(p.className, p.element.getAttribute('class'));
        });

        it('should apply default styling', function () {
            assert.equal('absolute', p.element.style.position);
        });
    });

    describe('#updateProgress()', function () {
        var p = new Percentage();
        p.create();

        it('should update the progress and adjust the loading bar', function () {
            assert.equal('0%', p.element.innerHTML);

            p.updateProgress(10, 0);

            assert.equal('10%', p.element.innerHTML);

            p.updateProgress(50, 0);

            assert.equal('50%', p.element.innerHTML);

            p.updateProgress(100, 0);

            assert.equal('100%', p.element.innerHTML);

            p.updateProgress(-20, 0);

            assert.equal('0%', p.element.innerHTML);

            p.updateProgress(420, 0);

            assert.equal('100%', p.element.innerHTML);
        });

        it('should not break on floats', function () {
            p.updateProgress(10.6, 0);

            assert.equal('10%', p.element.innerHTML);

            p.updateProgress(50.456, 0);

            assert.equal('50%', p.element.innerHTML);
        });
    });
});

describe('Overlay', function() {
    var fakeBody = document.createElement('body');

    describe('#create()', function () {
        var o = new Overlay(fakeBody);
        o.create();

        it('should create an element for itself', function () {
            assert.notEqual(null, o.element);
            assert.notEqual('undefined', typeof o.element);
            assert.notEqual('undefined', typeof o.element.tagName);
            assert.equal('div', o.element.tagName.toLowerCase());
            assert.equal(o.className, o.element.getAttribute('class'));
        });
    });

    describe('#calculatePosition()', function () {
        var o = new Overlay();

        it('should give the correct needed position of the overlay', function () {
            o.parentElement = fakeBody;

            assert.equal('fixed', o.calculatePosition());

            var fakeContainer = document.createElement('div');
            fakeContainer.style.position = 'static';

            o.parentElement = fakeContainer;

            assert.equal('absolute', o.calculatePosition());
            assert.equal('relative', o.parentElement.style.position);

            o.parentElement.style.position = 'absolute';

            assert.equal('absolute', o.calculatePosition());
        });
    });

    describe('#updateProgess()', function () {
        var o = new Overlay();
        o.parentElement = fakeBody;
        o.create();

        o.percentage = new Percentage();
        o.percentage.create();

        o.loadingBar = new LoadingBar();
        o.loadingBar.create();

        it('should update the loading progress of both percentage and loadingbar', function () {
            assert.equal('0%', o.percentage.element.innerHTML);
            assert.equal('0%', o.loadingBar.element.style.width);

            o.updateProgress(10, 0);

            assert.equal('10%', o.percentage.element.innerHTML);
            assert.equal('10%', o.loadingBar.element.style.width);
        });
    });
});

describe('Image', function() {
    describe('#constructor()', function () {
        it('should create an image object with given src', function () {
            var exampleImage = new Image('some/src');

            assert.equal('some/src', exampleImage.src);
        });

        it('should create a dom object with given src', function () {
            var exampleImage = new Image('some/src');

            assert.notEqual(-1, exampleImage.element.src.indexOf('some/src'));
        });
    });

    describe('#preload()', function () {
        it('should callback when an image is loaded', function (done) {
            var exampleImage = new Image('images/1.jpg');

            exampleImage.preload(done);
        });
    });
});

describe('QueryLoader', function() {
    describe('#constructor', function () {
      it('should be the same not matter how you instantiate', function () {
        var qlNew = new QueryLoader();
        var qlFactory = QueryLoader();

        assert.equal(qlNew, qlFactory);
      });
    });

    describe('#createOverlay()', function () {
        var ql = new QueryLoader();

        it('should create an overlay when called', function () {
            ql.createOverlay();

            assert.equal(ql.overlay instanceof Overlay, true);
        });
    });

    describe('#createPreloader()', function () {
        var ql = new QueryLoader();

        it('should create the preloader', function () {
            ql.createPreloader();

            assert.equal(ql.preloader instanceof ImagePreloader, true);
        });
    });

    describe('#extend()', function () {
        it('should merge two objects', function () {
            var ql = new QueryLoader();

            var destination = {
                some: 'thing',
                is: 'not',
                right: 'man'
            };

            var source = {
                some: 'one',
                right: 'dude'
            };

            assert.deepEqual({
                'some': 'one',
                'is': 'not',
                'right': 'dude'
            }, ql.extend(destination, source));
        });
    });

    describe('#updateProgress()', function () {
        var called = false;
        var ql = new QueryLoader(document.createElement('div'), {
            onProgress: function (amount) {
                called = true;
            }
        });

        it('should call the onProgress event', function () {
            ql.updateProgress();

            assert.equal(called, true);
        });
    });
});

describe('ImagePreloader', function() {
    'use strict';
    describe('#getImageSrcs()', function () {
        var ip = new ImagePreloader();

        var fakeImagesContainer = document.createElement('div');

        var img1 = document.createElement('img');
        img1.setAttribute('src', 'fakeimg1.png');
        fakeImagesContainer.appendChild(img1);

        var img2 = document.createElement('img');
        img2.setAttribute('src', 'fakeimg2.png');
        fakeImagesContainer.appendChild(img2);

        var img3 = document.createElement('section');
        img3.style.backgroundImage = 'url(fakeimg3.png)';
        fakeImagesContainer.appendChild(img3);

        var img4 = document.createElement('section');
        img4.style.backgroundImage = 'linear-gradient(left, #fff, #eee)';
        fakeImagesContainer.appendChild(img4);

        var img5 = document.createElement('section');
        img5.style.background = 'url(fakeimg5.png)';
        fakeImagesContainer.appendChild(img5);

        it('should get all images within the given element', function () {
            var images = ip.getImageSrcs(fakeImagesContainer);

            assert.equal(4, images.length);
            assert.notEqual(-1, images[0].indexOf('fakeimg1.png'));
            assert.notEqual(-1, images[1].indexOf('fakeimg2.png'));
            assert.notEqual(-1, images[2].indexOf('fakeimg3.png'));
            assert.notEqual(-1, images[3].indexOf('fakeimg5.png'));
        });
    });

    describe('#hasGradient()', function () {
        var ip = new ImagePreloader();

        it('should check if given url has a gradient', function () {
            assert.equal(false, ip.hasGradient('hasnogradienthere.png'));
            assert.equal(false, ip.hasGradient('grasdfsadg'));
            assert.equal(true, ip.hasGradient('linear-gradient(left, #fff, #fff)'));
        });
    });

    describe('#stripUrl()', function () {
        var ip = new ImagePreloader();

        it('should strip the url() part from given src', function () {
            assert.equal(-1, ip.stripUrl('url(this/path/file.png)').indexOf('url'));
            assert.equal(-1, ip.stripUrl('file.png').indexOf('url'));
        });
    });

    describe('#validUrl()', function () {
        var ip = new ImagePreloader();

        it('should check if given url is valid', function () {
            assert.equal(false, ip.validUrl(''));
            assert.equal(false, ip.validUrl('data:blablabla'));
            assert.equal(true, ip.validUrl('/this/is/valid.png'));
        });
    });

    describe('#urlIsNew()', function () {
        var ip = new ImagePreloader();
        ip.sources = ['test.png', 'something.png', 'image.jpg'];

        it('should check if given url is new in this.images', function () {
            assert.equal(false, ip.urlIsNew('image.jpg'));
            assert.equal(true, ip.urlIsNew('new.png'));
        });
    });
});<|endoftext|>


<commit_before>/*
 * Copyright © 2016 Cask Data, Inc.
 *
 * 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.
 */

class TrackerResultsController {
  constructor($state, myTrackerApi, $scope, $q) {
    this.$state = $state;
    this.$scope = $scope;
    this.myTrackerApi = myTrackerApi;
    this.$q = $q;

    this.loading = false;
    this.entitiesShowAllButton = false;
    this.metadataShowAllButton = false;
    this.currentPage = 1;
    this.fullResults = [];
    this.searchResults = [];
    this.sortByOptions = [
      {
        name: 'Create Date',
        sort: 'createDate'
      },
      {
        name: 'A-Z',
        sort: 'name'
      },
      {
        name: 'Z-A',
        sort: '-name'
      }
    ];

    this.sortBy = this.sortByOptions[0];

    this.entityFiltersList = [
      {
        name: 'Datasets',
        isActive: true,
        isHover: false,
        filter: 'Dataset',
        count: 0
      },
      {
        name: 'Streams',
        isActive: true,
        isHover: false,
        filter: 'Stream',
        count: 0
      },
      {
        name: 'Stream Views',
        isActive: true,
        isHover: false,
        filter: 'Stream View',
        count: 0
      }
    ];

    this.metadataFiltersList = [
      {
        name: 'Name',
        isActive: false,
        isHover: false
      },
      {
        name: 'Description',
        isActive: false,
        isHover: false
      },
      {
        name: 'User Tags',
        isActive: false,
        isHover: false
      },
      {
        name: 'System Tags',
        isActive: false,
        isHover: false
      },
      {
        name: 'User Properties',
        isActive: false,
        isHover: false
      },
      {
        name: 'System Properties',
        isActive: false,
        isHover: false
      },
      {
        name: 'Schema',
        isActive: false,
        isHover: false
      }
    ];

    this.fetchResults();
  }

  /* Can be updated to use single query once backend supports it */
  fetchResults () {
    this.loading = true;

    let paramsBase = {
      namespace: this.$state.params.namespace,
      query: this.$state.params.searchQuery,
      scope: this.$scope
    };
    let datasetParams = angular.extend({target: 'dataset'}, paramsBase);
    let streamParams = angular.extend({target: 'stream'}, paramsBase);
    let viewParams = angular.extend({target: 'view'}, paramsBase);

    this.$q.all([
      this.myTrackerApi.search(datasetParams).$promise,
      this.myTrackerApi.search(streamParams).$promise,
      this.myTrackerApi.search(viewParams).$promise
    ]).then( (res) => {
      this.fullResults = this.fullResults.concat(res[0], res[1], res[2]);
      this.fullResults = this.fullResults.map(this.parseResult.bind(this));
      this.searchResults = angular.copy(this.fullResults);
      this.loading = false;
    }, (err) => {
      console.log('error', err);
      this.loading = false;
    });

  }

  parseResult (entity) {
    let obj = {};
    if (entity.entityId.type === 'datasetinstance') {
      angular.extend(obj, {
        name: entity.entityId.id.instanceId,
        type: 'Dataset',
        icon: 'icon-datasets',
        description: 'This is some description while waiting for backend to add description to these entities. Meanwhile, you can read this nonsense.',
        createDate: 1456299781,
        queryFound: ['User Tags', 'Schema', 'System Tags']
      });
      this.entityFiltersList[0].count++;
    } else if (entity.entityId.type === 'stream') {
      angular.extend(obj, {
        name: entity.entityId.id.streamName,
        type: 'Stream',
        icon: 'icon-streams',
        description: 'This is some description while waiting for backend to add description to these entities. Meanwhile, you can read this nonsense.',
        createDate: 1456299781,
        queryFound: ['User Tags', 'Schema', 'System Tags']
      });
      this.entityFiltersList[1].count++;
    } else if (entity.entityId.type === 'view') {
      // THIS SECTION NEEDS TO BE UPDATED
      angular.extend(obj, {
        name: entity.entityId.id.streamName,
        type: 'Stream View',
        icon: 'icon-streams',
        description: 'This is some description while waiting for backend to add description to these entities. Meanwhile, you can read this nonsense.',
        createDate: 1456299781,
        queryFound: ['User Tags', 'Schema', 'System Tags']
      });
      this.entityFiltersList[2].count++;
    }

    return obj;
  }

  onlyFilter(event, filter, filterType) {
    event.preventDefault();

    let filterObj = [];
    if (filterType === 'ENTITIES') {
      filterObj = this.entityFiltersList;
    } else if (filterType === 'METADATA') {
      filterObj = this.metadataFiltersList;
    }

    angular.forEach(filterObj, (entity) => {
      entity.isActive = entity.name === filter.name ? true : false;
    });

    this.filterResults();
  }

  filterResults() {
    let filter = [];

    angular.forEach(this.entityFiltersList, (entity) => {
      if (entity.isActive) { filter.push(entity.filter); }
    });

    this.searchResults = this.fullResults.filter( (result) => { return filter.indexOf(result.type) > -1 ? true : false; });
  }

  showAll (filterType) {
    let filterArr = [];
    if (filterType === 'ENTITIES') {
      filterArr = this.entityFiltersList;
    } else if (filterType === 'METADATA') {
      filterArr = this.metadataFiltersList;
    }

    angular.forEach(filterArr, (filter) => {
      filter.isActive = true;
    });

    this.filterResults();
  }

  evaluateShowResultCount() {
    let lowerLimit = (this.currentPage - 1) * 10 + 1;
    let upperLimit = (this.currentPage - 1) * 10 + 10;

    upperLimit = upperLimit > this.searchResults.length ? this.searchResults.length : upperLimit;

    return this.searchResults.length === 0 ? '0' : lowerLimit + '-' + upperLimit;
  }
}

TrackerResultsController.$inject = ['$state', 'myTrackerApi', '$scope', '$q'];

angular.module(PKG.name + '.feature.tracker')
 .controller('TrackerResultsController', TrackerResultsController);
<commit_msg>update parser for stream views<commit_after>/*
 * Copyright © 2016 Cask Data, Inc.
 *
 * 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.
 */

class TrackerResultsController {
  constructor($state, myTrackerApi, $scope, $q) {
    this.$state = $state;
    this.$scope = $scope;
    this.myTrackerApi = myTrackerApi;
    this.$q = $q;

    this.loading = false;
    this.entitiesShowAllButton = false;
    this.metadataShowAllButton = false;
    this.currentPage = 1;
    this.fullResults = [];
    this.searchResults = [];
    this.sortByOptions = [
      {
        name: 'Create Date',
        sort: 'createDate'
      },
      {
        name: 'A-Z',
        sort: 'name'
      },
      {
        name: 'Z-A',
        sort: '-name'
      }
    ];

    this.sortBy = this.sortByOptions[0];

    this.entityFiltersList = [
      {
        name: 'Datasets',
        isActive: true,
        isHover: false,
        filter: 'Dataset',
        count: 0
      },
      {
        name: 'Streams',
        isActive: true,
        isHover: false,
        filter: 'Stream',
        count: 0
      },
      {
        name: 'Stream Views',
        isActive: true,
        isHover: false,
        filter: 'Stream View',
        count: 0
      }
    ];

    this.metadataFiltersList = [
      {
        name: 'Name',
        isActive: false,
        isHover: false
      },
      {
        name: 'Description',
        isActive: false,
        isHover: false
      },
      {
        name: 'User Tags',
        isActive: false,
        isHover: false
      },
      {
        name: 'System Tags',
        isActive: false,
        isHover: false
      },
      {
        name: 'User Properties',
        isActive: false,
        isHover: false
      },
      {
        name: 'System Properties',
        isActive: false,
        isHover: false
      },
      {
        name: 'Schema',
        isActive: false,
        isHover: false
      }
    ];

    this.fetchResults();
  }

  /* Can be updated to use single query once backend supports it */
  fetchResults () {
    this.loading = true;

    let paramsBase = {
      namespace: this.$state.params.namespace,
      query: this.$state.params.searchQuery,
      scope: this.$scope
    };
    let datasetParams = angular.extend({target: 'dataset'}, paramsBase);
    let streamParams = angular.extend({target: 'stream'}, paramsBase);
    let viewParams = angular.extend({target: 'view'}, paramsBase);

    this.$q.all([
      this.myTrackerApi.search(datasetParams).$promise,
      this.myTrackerApi.search(streamParams).$promise,
      this.myTrackerApi.search(viewParams).$promise
    ]).then( (res) => {
      this.fullResults = this.fullResults.concat(res[0], res[1], res[2]);
      this.fullResults = this.fullResults.map(this.parseResult.bind(this));
      this.searchResults = angular.copy(this.fullResults);
      this.loading = false;
    }, (err) => {
      console.log('error', err);
      this.loading = false;
    });

  }

  parseResult (entity) {
    let obj = {};
    if (entity.entityId.type === 'datasetinstance') {
      angular.extend(obj, {
        name: entity.entityId.id.instanceId,
        type: 'Dataset',
        icon: 'icon-datasets',
        description: 'This is some description while waiting for backend to add description to these entities. Meanwhile, you can read this nonsense.',
        createDate: 1456299781,
        queryFound: ['User Tags', 'Schema', 'System Tags']
      });
      this.entityFiltersList[0].count++;
    } else if (entity.entityId.type === 'stream') {
      angular.extend(obj, {
        name: entity.entityId.id.streamName,
        type: 'Stream',
        icon: 'icon-streams',
        description: 'This is some description while waiting for backend to add description to these entities. Meanwhile, you can read this nonsense.',
        createDate: 1456299781,
        queryFound: ['User Tags', 'Schema', 'System Tags']
      });
      this.entityFiltersList[1].count++;
    } else if (entity.entityId.type === 'view') {
      // THIS SECTION NEEDS TO BE UPDATED
      angular.extend(obj, {
        name: entity.entityId.id.id,
        type: 'Stream View',
        icon: 'icon-streams',
        description: 'This is some description while waiting for backend to add description to these entities. Meanwhile, you can read this nonsense.',
        createDate: 1456299781,
        queryFound: ['User Tags', 'Schema', 'System Tags']
      });
      this.entityFiltersList[2].count++;
    }

    return obj;
  }

  onlyFilter(event, filter, filterType) {
    event.preventDefault();

    let filterObj = [];
    if (filterType === 'ENTITIES') {
      filterObj = this.entityFiltersList;
    } else if (filterType === 'METADATA') {
      filterObj = this.metadataFiltersList;
    }

    angular.forEach(filterObj, (entity) => {
      entity.isActive = entity.name === filter.name ? true : false;
    });

    this.filterResults();
  }

  filterResults() {
    let filter = [];

    angular.forEach(this.entityFiltersList, (entity) => {
      if (entity.isActive) { filter.push(entity.filter); }
    });

    this.searchResults = this.fullResults.filter( (result) => { return filter.indexOf(result.type) > -1 ? true : false; });
  }

  showAll (filterType) {
    let filterArr = [];
    if (filterType === 'ENTITIES') {
      filterArr = this.entityFiltersList;
    } else if (filterType === 'METADATA') {
      filterArr = this.metadataFiltersList;
    }

    angular.forEach(filterArr, (filter) => {
      filter.isActive = true;
    });

    this.filterResults();
  }

  evaluateShowResultCount() {
    let lowerLimit = (this.currentPage - 1) * 10 + 1;
    let upperLimit = (this.currentPage - 1) * 10 + 10;

    upperLimit = upperLimit > this.searchResults.length ? this.searchResults.length : upperLimit;

    return this.searchResults.length === 0 ? '0' : lowerLimit + '-' + upperLimit;
  }
}

TrackerResultsController.$inject = ['$state', 'myTrackerApi', '$scope', '$q'];

angular.module(PKG.name + '.feature.tracker')
 .controller('TrackerResultsController', TrackerResultsController);
<|endoftext|>


<commit_before>// Copyright 2012 Google Inc.
//
// 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.

AuditRules.addRule({
    name: 'controlsWithoutLabel',
    severity: Severity.Severe,
    relevantNodesSelector: function() {
        var controlsSelector = ['input:not([type="hidden"]):not([disabled])',
                                'select:not([disabled])',
                                'textarea:not([disabled])',
                                'button:not([disabled])',
                                'video:not([disabled])'].join(', ');
        return this.auditscope_.querySelectorAll(controlsSelector);
    },
    test: function(control) {
        if (AccessibilityUtils.isElementOrAncestorHidden(control))
            return false;

        if (control.tagName.toLowerCase() == 'button') {
            var innerText = control.innerText.replace(/^\s+|\s+$/g, '');
            if (innerText.length)
                return false;
        }

        if (!AccessibilityUtils.hasLabel(control))
            return true;

        return false;
    },
    code: 'AX_TEXT_01',
    ruleName: 'Controls and media elements should have labels',
    url: 'https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_TEXT_01:_Controls_and_media_elements_should_have_labels
});
<commit_msg>Fix un-closed string on ControlsWithoutLabels<commit_after>// Copyright 2012 Google Inc.
//
// 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.

AuditRules.addRule({
    name: 'controlsWithoutLabel',
    severity: Severity.Severe,
    relevantNodesSelector: function() {
        var controlsSelector = ['input:not([type="hidden"]):not([disabled])',
                                'select:not([disabled])',
                                'textarea:not([disabled])',
                                'button:not([disabled])',
                                'video:not([disabled])'].join(', ');
        return this.auditscope_.querySelectorAll(controlsSelector);
    },
    test: function(control) {
        if (AccessibilityUtils.isElementOrAncestorHidden(control))
            return false;

        if (control.tagName.toLowerCase() == 'button') {
            var innerText = control.innerText.replace(/^\s+|\s+$/g, '');
            if (innerText.length)
                return false;
        }

        if (!AccessibilityUtils.hasLabel(control))
            return true;

        return false;
    },
    code: 'AX_TEXT_01',
    ruleName: 'Controls and media elements should have labels',
    url: 'https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_TEXT_01:_Controls_and_media_elements_should_have_labels'
});
<|endoftext|>


<commit_before>/* global module:false */
module.exports = function(grunt) {
	var port = grunt.option('port') || 8000;
	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		meta: {
			banner:
				'/*!\n' +
				' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
				' * http://lab.hakim.se/reveal-js\n' +
				' * MIT licensed\n' +
				' *\n' +
				' * Copyright (C) 2013 Hakim El Hattab, http://hakim.se\n' +
				' */'
		},

		qunit: {
			files: [ 'test/*.html' ]
		},

		uglify: {
			options: {
				banner: '<%= meta.banner %>\n'
			},
			build: {
				src: 'js/reveal.js',
				dest: 'js/reveal.min.js'
			}
		},

		cssmin: {
			compress: {
				files: {
					'css/reveal.min.css': [ 'css/reveal.css' ]
				}
			}
		},

		sass: {
			main: {
				files: {
					'css/theme/default.css': 'css/theme/source/default.scss',
					'css/theme/beige.css': 'css/theme/source/beige.scss',
					'css/theme/night.css': 'css/theme/source/night.scss',
					'css/theme/serif.css': 'css/theme/source/serif.scss',
					'css/theme/simple.css': 'css/theme/source/simple.scss',
					'css/theme/sky.css': 'css/theme/source/sky.scss',
					'css/theme/moon.css': 'css/theme/source/moon.scss',
					'css/theme/solarized.css': 'css/theme/source/solarized.scss',
					'css/theme/blood.css': 'css/theme/source/blood.scss'
					'css/theme/beigesmall.css': 'css/theme/source/beigesmall.scss'
					'css/theme/darkgray.css': 'css/theme/source/darkgray.scss'
				}
			}
		},

		jshint: {
			options: {
				curly: false,
				eqeqeq: true,
				immed: true,
				latedef: true,
				newcap: true,
				noarg: true,
				sub: true,
				undef: true,
				eqnull: true,
				browser: true,
				expr: true,
				globals: {
					head: false,
					module: false,
					console: false,
					unescape: false
				}
			},
			files: [ 'Gruntfile.js', 'js/reveal.js' ]
		},

		connect: {
			server: {
				options: {
					port: port,
					base: '.'
				}
			}
		},

		zip: {
			'reveal-js-presentation.zip': [
				'index.html',
				'css/**',
				'js/**',
				'lib/**',
				'images/**',
				'plugin/**'
			]
		},

		watch: {
			main: {
				files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ],
				tasks: 'default'
			},
			theme: {
				files: [ 'css/theme/source/*.scss', 'css/theme/template/*.scss' ],
				tasks: 'themes'
			}
		}

	});

	// Dependencies
	grunt.loadNpmTasks( 'grunt-contrib-qunit' );
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
	grunt.loadNpmTasks( 'grunt-contrib-uglify' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-contrib-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-connect' );
	grunt.loadNpmTasks( 'grunt-zip' );

	// Default task
	grunt.registerTask( 'default', [ 'jshint', 'cssmin', 'uglify', 'qunit' ] );

	// Theme task
	grunt.registerTask( 'themes', [ 'sass' ] );

	// Package presentation to archive
	grunt.registerTask( 'package', [ 'default', 'zip' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );

	// Run tests
	grunt.registerTask( 'test', [ 'jshint', 'qunit' ] );

};
<commit_msg>updates<commit_after>/* global module:false */
module.exports = function(grunt) {
	var port = grunt.option('port') || 8000;
	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		meta: {
			banner:
				'/*!\n' +
				' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
				' * http://lab.hakim.se/reveal-js\n' +
				' * MIT licensed\n' +
				' *\n' +
				' * Copyright (C) 2013 Hakim El Hattab, http://hakim.se\n' +
				' */'
		},

		qunit: {
			files: [ 'test/*.html' ]
		},

		uglify: {
			options: {
				banner: '<%= meta.banner %>\n'
			},
			build: {
				src: 'js/reveal.js',
				dest: 'js/reveal.min.js'
			}
		},

		cssmin: {
			compress: {
				files: {
					'css/reveal.min.css': [ 'css/reveal.css' ]
				}
			}
		},

		sass: {
			main: {
				files: {
					'css/theme/default.css': 'css/theme/source/default.scss',
					'css/theme/beige.css': 'css/theme/source/beige.scss',
					'css/theme/night.css': 'css/theme/source/night.scss',
					'css/theme/serif.css': 'css/theme/source/serif.scss',
					'css/theme/simple.css': 'css/theme/source/simple.scss',
					'css/theme/sky.css': 'css/theme/source/sky.scss',
					'css/theme/moon.css': 'css/theme/source/moon.scss',
					'css/theme/solarized.css': 'css/theme/source/solarized.scss',
				        'css/theme/blood.css': 'css/theme/source/blood.scss',
		   		        'css/theme/beigesmall.css': 'css/theme/source/beigesmall.scss',
	   			        'css/theme/darkgray.css': 'css/theme/source/darkgray.scss'
				}
			}
		},

		jshint: {
			options: {
				curly: false,
				eqeqeq: true,
				immed: true,
				latedef: true,
				newcap: true,
				noarg: true,
				sub: true,
				undef: true,
				eqnull: true,
				browser: true,
				expr: true,
				globals: {
					head: false,
					module: false,
					console: false,
					unescape: false
				}
			},
			files: [ 'Gruntfile.js', 'js/reveal.js' ]
		},

		connect: {
			server: {
				options: {
					port: port,
					base: '.'
				}
			}
		},

		zip: {
			'reveal-js-presentation.zip': [
				'index.html',
				'css/**',
				'js/**',
				'lib/**',
				'images/**',
				'plugin/**'
			]
		},

		watch: {
			main: {
				files: [ 'Gruntfile.js', 'js/reveal.js', 'css/reveal.css' ],
				tasks: 'default'
			},
			theme: {
				files: [ 'css/theme/source/*.scss', 'css/theme/template/*.scss' ],
				tasks: 'themes'
			}
		}

	});

	// Dependencies
	grunt.loadNpmTasks( 'grunt-contrib-qunit' );
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
	grunt.loadNpmTasks( 'grunt-contrib-uglify' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-contrib-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-connect' );
	grunt.loadNpmTasks( 'grunt-zip' );

	// Default task
	grunt.registerTask( 'default', [ 'jshint', 'cssmin', 'uglify', 'qunit' ] );

	// Theme task
	grunt.registerTask( 'themes', [ 'sass' ] );

	// Package presentation to archive
	grunt.registerTask( 'package', [ 'default', 'zip' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );

	// Run tests
	grunt.registerTask( 'test', [ 'jshint', 'qunit' ] );

};
<|endoftext|>