<commit_before>/**
 * Created by mirel on 18.08.2015.
 */

var MPFForm_AutoComplete = {
    lastAjaxSearchedText: '',
    lastTextOnTheInput: '',
    selectionWasMade : false,
    /**
     * Call this method for each autocomplete element
     * @param element
     */
    init: function (element) {
        var enterPressed = false;
        var _self = this;
        $(element).focus(function () {
            if ($('.form-autocomplete-list', this.parentNode).find('li').length) { // only if there are any options available
                _self.displayList(this, 'focus');
            }
        }).blur(function () {
            var __this = this;
            setTimeout(function(){
                $('.form-autocomplete-list', __this.parentNode).fadeOut();
            }, 100);
        }).keyup(function () {
            if (_self.selectionWasMade || _self.lastAjaxSearchedText == this.value){
                return true;
            }
            if ($(this).attr('autc_ajax') == '1') {
                if ($(this).val().length >= $(this).attr('autc_minletters')) {
                    _self.displayList(this, 'ajax search');
                    _self.ajaxSearch($(this).val(), $(this).attr('autc_url'), JSON.parse($(this).attr('autc_extraparams') + ''), $('.form-autocomplete-list', this.parentNode));
                } else if ($('.form-autocomplete-list', this.parentNode).is(':visible')) {
                    $('.form-autocomplete-list', this.parentNode).fadeOut();
                }
            } else {
                _self.displayList(this, 'non-ajax search');
                _self.filterList(this.value, $('.form-autocomplete-list', this.parentNode));
            }
        }).keydown(function (e) {
            if (e.which == 40) { //down
                if (enterPressed && $('.form-autocomplete-list', this.parentNode).find('li').length) {
                    _self.displayList(this, 'down after enter');
                    enterPressed = false;
                }
                _self.moveDown($('.form-autocomplete-list', this.parentNode));
            } else if (e.which == 38) { //up
                if (enterPressed && $('.form-autocomplete-list', this.parentNode).find('li').length) {
                    _self.displayList(this, 'up after enter');
                    enterPressed = false;
                }
                _self.moveUp($('.form-autocomplete-list', this.parentNode));
            } else if (e.which == 13) {
                $(this).val($('.form-autocomplete-list', this.parentNode).find('li.selected').text());
                console.log("ENTER on: " + $('.form-autocomplete-list', this.parentNode).find('li.selected').text());
                _self.selectionWasMade = true;
                $('#' + $(this).attr('autc_for')).val($('.form-autocomplete-list', this.parentNode).find('li.selected').text());
                $('.form-autocomplete-list', this.parentNode).fadeOut();
                setTimeout(function(){
                    _self.selectionWasMade = false;
                }, 200);
                enterPressed = true;
                return false;
            } else if (e.which == 27) {
                enterPressed = false;
                $('.form-autocomplete-list', this.parentNode).fadeOut();
            }
        });
        $('.form-autocomplete-list li', element.parentNode).hover(function () {
            $('li', this.parentNode).removeClass('selected');
            $(this).addClass('selected');
        }).click(function () {
            $(element).val($(this).text());
            console.log('Click on: ' + $(this).text());
            _self.selectionWasMade = true;
            $('#' + $(element).attr('autc_for')).val($(this).text());
            $('.form-autocomplete-list', element.parentNode).fadeOut();
            setTimeout(function(){
                _self.selectionWasMade = false;
            }, 200);
            return false;
        })
    },
    checkScroll: function (list) {
        if ($('li.selected', list).position().top + $('list.selected', list).height() > $(list).height()) {
            $(list).scroll($('li.selected', list).position().top + $('list.selected', list).height());
        }
    },
    displayList: function (element, log) {
        if (!$('.form-autocomplete-list', element.parentNode).is(':visible')) {
            $('.form-autocomplete-list', element.parentNode).html('').fadeIn();
        }
        $('.form-autocomplete-list', element.parentNode)
            .css('top', $(element).position().top + $(element).height() + 5 + 'px')
            .css('left', $(element).position().left + 'px')
            .css('width', $(element).width() + 'px')
            .fadeIn();
        if (log){
            console.log('displayList: ' + log);
        }
    },
    moveUp: function (list) {
        var all = $('li', list);
        if (all.length == 0) {
            return;//no action if there are no items
        }
        if (!$('li.selected', list).length) {
            $(all[all.length - 1]).addClass('selected');
            this.checkScroll(list);
            return;//if none are selected then select the last one
        }

        for (var i = 0; i < all.length; i++) {
            if ($(all[i]).hasClass('selected')) {
                if (i != 0) {
                    $(all[i - 1]).addClass('selected');
                    $(all[i]).removeClass('selected');
                } else {
                    $(all[i]).removeClass('selected');
                    $(all[all.length - 1]).addClass('selected');
                }
                break;
            }
        }
        this.checkScroll(list);
    },
    moveDown: function (list) {
        var all = $('li:not(.hidden)', list);
        if (all.length == 0) {
            return;// no action if there are no items;
        }
        if (!$('li.selected', list).length) {
            $(all[0]).addClass('selected');
            this.checkScroll(list);
            return;//if none are selected then select the first one
        }
        for (var i = 0; i < all.length; i++) {
            if ($(all[i]).hasClass('selected')) {
                if (i != all.length - 1) {
                    $(all[i + 1]).addClass('selected');
                    $(all[i]).removeClass('selected');
                } else {
                    $(all[i]).removeClass('selected');
                    $(all[0]).addClass('selected');
                }
                break;
            }
        }
        this.checkScroll(list);
    },
    filterList: function (text, list) {
        if (text.length == 0) {
            $('li', list).removeClass('hidden');
            return;
        }
        $('li', list).each(function () {
            if ($(this).text().toLowerCase().indexOf(text.toLowerCase()) == -1) {
                $(this).addClass('hidden');
            } else {
                $(this).removeClass('hidden');
            }
        })
    },
    ajaxSearch: function (text, url, extraOptions, htmlListContainer) {
        var _self = this;
        text = text.trim();
        _self.lastTextOnTheInput = text;
        setTimeout(function () {
            if (text != _self.lastTextOnTheInput || text == _self.lastAjaxSearchedText) { // check for eventual changes
                return 0;
            }
            extraOptions['text'] = text;
            _self.lastAjaxSearchedText = text;
            $.post(url, extraOptions,
                function (result) {
                    htmlListContainer.html('');
                    $.each(result, function (index, value) {
                        htmlListContainer.append("
" + value + "
");
                    });
                }, 'json');
        }, 200);

    }
};

$.fn.autocomplete = function () {
    this.each(function () {
        MPFForm_AutoComplete.init(this);
    });
};<commit_msg>Fix selection by click and autocomplete on right click paste<commit_after>/**
 * Created by mirel on 18.08.2015.
 */

var MPFForm_AutoComplete = {
    lastAjaxSearchedText: '',
    lastTextOnTheInput: '',
    selectionWasMade : false,
    /**
     * Call this method for each autocomplete element
     * @param element
     */
    init: function (element) {
        var enterPressed = false;
        var _self = this;
        $(element).focus(function () {
            if ($('.form-autocomplete-list', this.parentNode).find('li').length) { // only if there are any options available
                _self.displayList(this, 'focus');
            }
        }).blur(function () {
            var __this = this;
            setTimeout(function(){
                $('.form-autocomplete-list', __this.parentNode).fadeOut();
            }, 100);
        }).bind('input propertychange', function(){
            $('#' + $(this).attr('autc_for')).val($(this).val());
            $(this).keyup();
        }).keyup(function () {
            if (_self.selectionWasMade || _self.lastAjaxSearchedText == this.value){
                return true;
            }
            if ($(this).attr('autc_ajax') == '1') {
                if ($(this).val().length >= $(this).attr('autc_minletters')) {
                    _self.displayList(this, 'ajax search');
                    _self.ajaxSearch($(this).val(), $(this).attr('autc_url'), JSON.parse($(this).attr('autc_extraparams') + ''), $('.form-autocomplete-list', this.parentNode), element);
                } else if ($('.form-autocomplete-list', this.parentNode).is(':visible')) {
                    $('.form-autocomplete-list', this.parentNode).fadeOut();
                }
            } else {
                _self.displayList(this, 'non-ajax search');
                _self.filterList(this.value, $('.form-autocomplete-list', this.parentNode));
            }
        }).keydown(function (e) {
            if (e.which == 40) { //down
                if (enterPressed && $('.form-autocomplete-list', this.parentNode).find('li').length) {
                    _self.displayList(this, 'down after enter');
                    enterPressed = false;
                }
                _self.moveDown($('.form-autocomplete-list', this.parentNode));
            } else if (e.which == 38) { //up
                if (enterPressed && $('.form-autocomplete-list', this.parentNode).find('li').length) {
                    _self.displayList(this, 'up after enter');
                    enterPressed = false;
                }
                _self.moveUp($('.form-autocomplete-list', this.parentNode));
            } else if (e.which == 13) {
                $(this).val($('.form-autocomplete-list', this.parentNode).find('li.selected').text());
                console.log("ENTER on: " + $('.form-autocomplete-list', this.parentNode).find('li.selected').text());
                _self.selectionWasMade = true;
                $('#' + $(this).attr('autc_for')).val($('.form-autocomplete-list', this.parentNode).find('li.selected').text());
                $('.form-autocomplete-list', this.parentNode).fadeOut();
                setTimeout(function(){
                    _self.selectionWasMade = false;
                }, 200);
                enterPressed = true;
                return false;
            } else if (e.which == 27) {
                enterPressed = false;
                $('.form-autocomplete-list', this.parentNode).fadeOut();
            }
        });
        $('.form-autocomplete-list li', element.parentNode).hover(function () {
            $('li', this.parentNode).removeClass('selected');
            $(this).addClass('selected');
        }).click(function () {
            $(element).val($(this).text());
            console.log('Click on: ' + $(this).text());
            _self.selectionWasMade = true;
            $('#' + $(element).attr('autc_for')).val($(this).text());
            $('.form-autocomplete-list', element.parentNode).fadeOut();
            setTimeout(function(){
                _self.selectionWasMade = false;
            }, 200);
            return false;
        });
    },
    checkScroll: function (list) {
        if ($('li.selected', list).position().top + $('list.selected', list).height() > $(list).height()) {
            $(list).scroll($('li.selected', list).position().top + $('list.selected', list).height());
        }
    },
    displayList: function (element, log) {
        if (!$('.form-autocomplete-list', element.parentNode).is(':visible')) {
            $('.form-autocomplete-list', element.parentNode).html('').fadeIn();
        }
        $('.form-autocomplete-list', element.parentNode)
            .css('top', $(element).position().top + $(element).height() + 5 + 'px')
            .css('left', $(element).position().left + 'px')
            .css('width', $(element).width() + 'px')
            .fadeIn();
        if (log){
            console.log('displayList: ' + log);
        }
    },
    moveUp: function (list) {
        var all = $('li', list);
        if (all.length == 0) {
            return;//no action if there are no items
        }
        if (!$('li.selected', list).length) {
            $(all[all.length - 1]).addClass('selected');
            this.checkScroll(list);
            return;//if none are selected then select the last one
        }

        for (var i = 0; i < all.length; i++) {
            if ($(all[i]).hasClass('selected')) {
                if (i != 0) {
                    $(all[i - 1]).addClass('selected');
                    $(all[i]).removeClass('selected');
                } else {
                    $(all[i]).removeClass('selected');
                    $(all[all.length - 1]).addClass('selected');
                }
                break;
            }
        }
        this.checkScroll(list);
    },
    moveDown: function (list) {
        var all = $('li:not(.hidden)', list);
        if (all.length == 0) {
            return;// no action if there are no items;
        }
        if (!$('li.selected', list).length) {
            $(all[0]).addClass('selected');
            this.checkScroll(list);
            return;//if none are selected then select the first one
        }
        for (var i = 0; i < all.length; i++) {
            if ($(all[i]).hasClass('selected')) {
                if (i != all.length - 1) {
                    $(all[i + 1]).addClass('selected');
                    $(all[i]).removeClass('selected');
                } else {
                    $(all[i]).removeClass('selected');
                    $(all[0]).addClass('selected');
                }
                break;
            }
        }
        this.checkScroll(list);
    },
    filterList: function (text, list) {
        if (text.length == 0) {
            $('li', list).removeClass('hidden');
            return;
        }
        $('li', list).each(function () {
            if ($(this).text().toLowerCase().indexOf(text.toLowerCase()) == -1) {
                $(this).addClass('hidden');
            } else {
                $(this).removeClass('hidden');
            }
        })
    },
    ajaxSearch: function (text, url, extraOptions, htmlListContainer, element) {
        var _self = this;
        text = text.trim();
        _self.lastTextOnTheInput = text;
        setTimeout(function () {
            if (text != _self.lastTextOnTheInput || text == _self.lastAjaxSearchedText) { // check for eventual changes
                return 0;
            }
            extraOptions['text'] = text;
            _self.lastAjaxSearchedText = text;
            $.post(url, extraOptions,
                function (result) {
                    htmlListContainer.html('');
                    $.each(result, function (index, value) {
                        htmlListContainer.append("
" + value + "
");
                    });

                    $('li', htmlListContainer).hover(function () {
                        $('li', this.parentNode).removeClass('selected');
                        $(this).addClass('selected');
                    }).click(function () {
                        $(element).val($(this).text());
                        console.log('Click on: ' + $(this).text());
                        _self.selectionWasMade = true;
                        $('#' + $(element).attr('autc_for')).val($(this).text());
                        $('.form-autocomplete-list', element.parentNode).fadeOut();
                        setTimeout(function(){
                            _self.selectionWasMade = false;
                        }, 200);
                        return false;
                    });

                }, 'json');
        }, 200);

    }
};

$.fn.autocomplete = function () {
    this.each(function () {
        MPFForm_AutoComplete.init(this);
    });
};<|endoftext|>


<commit_before>/*
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      This file is part of the Smart Developer Hub Project:
        http://www.smartdeveloperhub.org/
      Center for Open Middleware
            http://www.centeropenmiddleware.com/
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      Copyright (C) 2015 Center for Open Middleware.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      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.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
*/

(function() {

    var normalizeConfig = function normalizeConfig(configuration) {
        if (configuration == null) {
            configuration = {};
        }
        if (typeof configuration.height != "number") {
            configuration.height = 240;
        }
        if (typeof configuration.showFocus != "boolean") {
            configuration.showFocus = true;
        }
        // nvd3 focusHeight is contextHeight
        if (typeof configuration.focusHeight != "number") {
            configuration.focusHeight = configuration.height * 0.5;
            if (!configuration.showFocus) {
                // -30 for leyend
                configuration.focusHeight = configuration.height - 30;
            }
        }
        if (typeof configuration.ownContext != "string") {
            configuration.ownContext = "dafault_rangeNv_Context_id";
        }
        if (typeof configuration.isArea != "boolean") {
            configuration.isArea = false;
        }
        if (typeof configuration.duration != "number") {
            configuration.duration = 250;
        }
        if (typeof configuration.labelFormat != "string") {
            configuration.labelFormat = "%mid%";
        }
        if (typeof configuration.interpolate != "string") {
            configuration.interpolate = "linear";
        }
        if (typeof configuration.background != "string") {
            configuration.background = "rgba(0,0,0,0)";
        }
        if (typeof configuration.axisColor != "string") {
            configuration.axisColor = "#000";
        }
        if (typeof configuration.colors != "object") {
            configuration.colors = undefined;
        }
        if (typeof configuration.showLegend != "boolean") {
            configuration.showLegend = true;
        }return configuration;
    };

    /* rangeNv constructor
     *   element: the DOM element that will contain the rangeNv
     *   data: the data id array
     *   contextId: optional.
     *   configuration: additional chart configuration:
     *      {
     *       ~ donut: boolean - Whether to make a pie graph a donut graph or not.
     *       ~ growOnHover: boolean - For pie/donut charts, whether to increase slice radius on hover or not.
     *       ~ cornerRadius: number - For donut charts only, the corner radius (in pixels) of the slices.
     *       ~ padAngle: number - The percent of the chart that should be spacing between slices.
     *       ~ showLegend: boolean - Whether to display the legend or not.
     *       ~ showLabels: boolean - Show pie/donut chart labels for each slice.
     *       ~ donutRatio: number - Percent of pie radius to cut out of the middle to make the donut. It is multiplied
     *         by the outer radius to calculate the inner radius, thus it should be between 0 and 1.
     *       ~ duration: number - Duration in ms to take when updating chart. For things like bar charts, each bar can
     *         animate by itself but the total time taken should be this value.
     *       ~ labelFormat: string - Format string for the labels. Metric parameters can be used as variables by
     *         surrounding their names with percentages. The metric name can also be accessed with %mid%. For example,
     *         the following is a valid labelFormat: "User: %uid%".
     *      }
     */
    var RangeNv = function RangeNv(element, metrics, contextId, configuration) {

        if(!framework.isReady()) {
            console.error("LinesChart object could not be created because framework is not loaded.");
            return;
        }

        // CHECK D3
        if(typeof d3 === 'undefined') {
            console.error("rangeNv could not be loaded because d3 did not exist.");
            return;
        }

        // CHECK NVD3
        if(typeof nv === 'undefined') {
            console.error("rangeNv could not be loaded because nvd3 did not exist.");
            return;
        }

        // We need relative position for the nvd3 tooltips
        element.style.position = 'inherit';

        this.element = $(element); //Store as jquery object
        this.data = null;
        this.chart = null;
        this.labels = {};
        this.lastExtent = [];
        this.maxY = Number.MIN_VALUE;
        this.minY = Number.MAX_VALUE;
        this.maxT = -8640000000000000;
        this.minT = 8640000000000000;

        // Extending widget
        framework.widgets.CommonWidget.call(this, false, this.element.get(0));

        // Configuration
        this.configuration = normalizeConfig(configuration);

        this.ownContext = configuration.ownContext;

        this.element.append('<svg class="blurable"></svg>');
        this.svg = this.element.children("svg");
        this.svg.get(0).style.minHeight = this.configuration.height + "px";;
        this.svg.get(0).style.backgroundColor = this.configuration.background;

        this.observeCallback = this.commonObserveCallback.bind(this);

        framework.data.observe(metrics, this.observeCallback , contextId);

    };

    RangeNv.prototype = new framework.widgets.CommonWidget(true);

    RangeNv.prototype.updateData = function(framework_data) {

        var normalizedData = getNormalizedData.call(this,framework_data);
        setTimeInfo(this.minT, this.maxT);

        //Update data
        if(this.chart != null) {
            d3.select(this.svg.get(0)).datum(normalizedData);
            this.chart.update();

        } else { // Paint it for first time
            paint.call(this, normalizedData);
        }

    };

    RangeNv.prototype.delete = function() {

        //Stop observing for data changes
        framework.data.stopObserve(this.observeCallback);

        //Remove resize event listener
        if(this.resizeEventHandler != null) {
            $(window).off("resize", this.resizeEventHandler);
            this.resizeEventHandler = null;
        }

        //Clear DOM
        $(this.svg).empty();
        this.element.empty();

        this.svg = null;
        this.chart = null;

    };

    RangeNv.prototype.updateContext = function(d) {
        this.lastExtent = d;
        framework.data.updateContext(this.ownContext, {from: moment(d[0]).format("YYYY-MM-DD"), to: moment(d[1]).format("YYYY-MM-DD")});
        setTimeInfo(d[0], d[1]);
    };

    // PRIVATE METHODS - - - - - - - - - - - - - - - - - - - - - -

    //Function that returns the value to replace with the label variables
    var replacer = function(resourceId, resource, str) {

        //Remove the initial an trailing '%' of the string
        str = str.substring(1, str.length-1);

        //Check if it is a parameter an return its value
        if(str === "resourceId") { //Special command to indicate the name of the resource
            return resourceId;

        } else { // Obtain its value through the object given the path

            var path = str.split(".");
            var subObject = resource;

            for(var p = 0; p < path.length; ++p) {
                if((subObject = subObject[path[p]]) == null)
                    return "";
            }

            return subObject.toString();
        }

    };

    /**
     * Gets a normalized array of data according to the chart expected input from the data returned by the framework.
     * @param framework_data
     * @returns {Array} Contains objects with 'label' and 'value'.
     */
    var getNormalizedData = function getNormalizedData(framework_data) {
        var labelVariable = /%(\w|\.)+%/g; //Regex that matches all the "variables" of the label such as %mid%, %pid%...

        var series = [];
        this.labels = {};
        //var colors = ['#ff7f0e','#2ca02c','#7777ff','#D53E4F','#9E0142'];
        //Data is represented as an array of {x,y} pairs.
        for (var metricId in framework_data) {
            for (var m in framework_data[metricId]) {

                var metric = framework_data[metricId][m];
                var metricData = metric['data'];

                if(metricData.interval.from < this.minT) {
                    this.minT = metricData.interval.from;
                }
                if(metric.data.interval.to > this.maxT) {
                    this.maxT = metricData.interval.to;
                }

                var timePoint = metricData.interval.from - metricData.step;
                var yserie = metricData.values;

                // Create a replacer for this metric
                var metricReplacer = replacer.bind(null, metricId, metric);

                var genLabel = function genLabel(i) {
                  var lab = this.configuration.labelFormat.replace(labelVariable,metricReplacer);
                  if (i > 0) {
                    lab = lab + '(' + i + ')';
                  }
                  if (lab in this.labels) {
                    lab = genLabel.call(this, ++i);
                  }
                  this.labels[lab] = null;
                  return lab;
                };

                // Generate the label by replacing the variables
                var label = genLabel.call(this, 0);

                // Metric dataset
                var dat = yserie.map(function(dat, index) {
                    timePoint += metricData.step;
                    if(dat > this.maxY) {
                        this.maxY = dat;
                    }
                    if (dat < this.minY) {
                        this.minY = dat;
                    }
                    return {'x': new Date(timePoint), 'y': dat};
                }.bind(this));
                series.push({
                    values: dat,      //values - represents the array of {x,y} data points
                    key: label, //key  - the name of the series.
                    area: this.configuration.isArea
                });
                if (series.length == 1) {
                    series[0]['bar'] = true
                }
            }
        }

        //Line chart data should be sent as an array of series objects.
        return series;
    };

    var paint = function paint(data) {

        var width = this.element.get(0).getBoundingClientRect().width;
        var height = this.element.get(0).getBoundingClientRect().height;

        nv.addGraph(function() {
            var chart = nv.models.lineWithFocusChart()
                .focusHeight(this.configuration.focusHeight)
                .interpolate(this.configuration.interpolate)
                .color(this.configuration.colors)
                .duration(this.configuration.duration)
                .showLegend(this.configuration.showLegend);
                // only affect to focus .How can i force Y axis in context chart?
                // ... i don't know ...
                //.forceY([this.maxY + 10, this.minY]);
            this.chart = chart;

            chart.margin({"top":10,"bottom":14});

            chart.xAxis.tickFormat(function(d) {
                return d3.time.format('%x')(new Date(d));
                });
            chart.x2Axis.tickFormat(function(d) {
                return d3.time.format('%x')(new Date(d))
                });

            chart.yAxis.tickFormat(function(d) {
                if (d >= 1000 || d <= -1000) {
                    return Math.abs(d/1000) + " K";
                } else {
                    return Math.abs(d);
                }
            });

            chart.y2Axis.tickFormat(function(d) {
                if (d >= 1000 || d <= -1000) {
                    return Math.abs(d/1000) + " K";
                } else {
                    return Math.abs(d);
                }
            });

            d3.select(this.svg.get(0))
                .datum(data)
                .call(chart);

            var timer = null;
            chart.dispatch.on('brush', function(extent){
                if(JSON.stringify(this.lastExtent) == JSON.stringify(extent.extent)){
                    // Resize event causes a unwanted brush event in this chart
                    return;
                }
                if (timer) {
                    clearTimeout(timer); //cancel the previous timer.
                    timer = null;
                }
                timer = setTimeout(function() {
                    this.updateContext(extent.extent);
                }.bind(this), 400);
            }.bind(this));

            //Update the chart when window resizes.
            this.resizeEventHandler = function() { chart.update() };
            $(window).resize(this.resizeEventHandler);

            if (!this.configuration.showFocus) {
                $(".nv-focus").attr("class", "nv-focus hidden");
            }
            // axis color
            $(this.svg).find("[class~=nv-axisG]").attr('style', 'fill:' + this.configuration.axisColor + ';')
            // leyend color
            $(this.svg).find("[class~=nv-legend-text]").attr('style', 'fill:' + this.configuration.axisColor + ';')

            // bigger brush cover
            $(this.svg).find("[class~=nv-brushBackground] rect").attr('height', 98);
            $(this.svg).find("[class~=nv-brushBackground] rect").attr('transform', 'translate(0,-4)');

            //Call update to update the chart and threfore the context
            chart.update();

            return chart;
        }.bind(this));

    };

    window.framework.widgets.RangeNv = RangeNv;

    // AMD compliant
    if ( typeof define === "function" && define.amd) {
        define( [], function () { return RangeNv; } );
    }

})();
<commit_msg>[Widget][Range] Added CONTEXT_UPDATED event.<commit_after>/*
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      This file is part of the Smart Developer Hub Project:
        http://www.smartdeveloperhub.org/
      Center for Open Middleware
            http://www.centeropenmiddleware.com/
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      Copyright (C) 2015 Center for Open Middleware.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
      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.
    #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
*/

(function() {

    var normalizeConfig = function normalizeConfig(configuration) {
        if (configuration == null) {
            configuration = {};
        }
        if (typeof configuration.height != "number") {
            configuration.height = 240;
        }
        if (typeof configuration.showFocus != "boolean") {
            configuration.showFocus = true;
        }
        // nvd3 focusHeight is contextHeight
        if (typeof configuration.focusHeight != "number") {
            configuration.focusHeight = configuration.height * 0.5;
            if (!configuration.showFocus) {
                // -30 for leyend
                configuration.focusHeight = configuration.height - 30;
            }
        }
        if (typeof configuration.ownContext != "string") {
            configuration.ownContext = "dafault_rangeNv_Context_id";
        }
        if (typeof configuration.isArea != "boolean") {
            configuration.isArea = false;
        }
        if (typeof configuration.duration != "number") {
            configuration.duration = 250;
        }
        if (typeof configuration.labelFormat != "string") {
            configuration.labelFormat = "%mid%";
        }
        if (typeof configuration.interpolate != "string") {
            configuration.interpolate = "linear";
        }
        if (typeof configuration.background != "string") {
            configuration.background = "rgba(0,0,0,0)";
        }
        if (typeof configuration.axisColor != "string") {
            configuration.axisColor = "#000";
        }
        if (typeof configuration.colors != "object") {
            configuration.colors = undefined;
        }
        if (typeof configuration.showLegend != "boolean") {
            configuration.showLegend = true;
        }return configuration;
    };

    /* rangeNv constructor
     *   element: the DOM element that will contain the rangeNv
     *   data: the data id array
     *   contextId: optional.
     *   configuration: additional chart configuration:
     *      {
     *       ~ donut: boolean - Whether to make a pie graph a donut graph or not.
     *       ~ growOnHover: boolean - For pie/donut charts, whether to increase slice radius on hover or not.
     *       ~ cornerRadius: number - For donut charts only, the corner radius (in pixels) of the slices.
     *       ~ padAngle: number - The percent of the chart that should be spacing between slices.
     *       ~ showLegend: boolean - Whether to display the legend or not.
     *       ~ showLabels: boolean - Show pie/donut chart labels for each slice.
     *       ~ donutRatio: number - Percent of pie radius to cut out of the middle to make the donut. It is multiplied
     *         by the outer radius to calculate the inner radius, thus it should be between 0 and 1.
     *       ~ duration: number - Duration in ms to take when updating chart. For things like bar charts, each bar can
     *         animate by itself but the total time taken should be this value.
     *       ~ labelFormat: string - Format string for the labels. Metric parameters can be used as variables by
     *         surrounding their names with percentages. The metric name can also be accessed with %mid%. For example,
     *         the following is a valid labelFormat: "User: %uid%".
     *      }
     *  Events:
     *      ~ CONTEXT_UPDATED: triggered when the owncontext is updated.
     */
    var RangeNv = function RangeNv(element, metrics, contextId, configuration) {

        if(!framework.isReady()) {
            console.error("LinesChart object could not be created because framework is not loaded.");
            return;
        }

        // CHECK D3
        if(typeof d3 === 'undefined') {
            console.error("rangeNv could not be loaded because d3 did not exist.");
            return;
        }

        // CHECK NVD3
        if(typeof nv === 'undefined') {
            console.error("rangeNv could not be loaded because nvd3 did not exist.");
            return;
        }

        // We need relative position for the nvd3 tooltips
        element.style.position = 'inherit';

        this.element = $(element); //Store as jquery object
        this.data = null;
        this.chart = null;
        this.labels = {};
        this.lastExtent = [];
        this.maxY = Number.MIN_VALUE;
        this.minY = Number.MAX_VALUE;
        this.maxT = -8640000000000000;
        this.minT = 8640000000000000;

        // Extending widget
        framework.widgets.CommonWidget.call(this, false, this.element.get(0));

        // Configuration
        this.configuration = normalizeConfig(configuration);

        this.ownContext = configuration.ownContext;

        this.element.append('<svg class="blurable"></svg>');
        this.svg = this.element.children("svg");
        this.svg.get(0).style.minHeight = this.configuration.height + "px";;
        this.svg.get(0).style.backgroundColor = this.configuration.background;

        this.observeCallback = this.commonObserveCallback.bind(this);

        framework.data.observe(metrics, this.observeCallback , contextId);

    };

    RangeNv.prototype = new framework.widgets.CommonWidget(true);

    RangeNv.prototype.updateData = function(framework_data) {

        var normalizedData = getNormalizedData.call(this,framework_data);
        setTimeInfo(this.minT, this.maxT);

        //Update data
        if(this.chart != null) {
            d3.select(this.svg.get(0)).datum(normalizedData);
            this.chart.update();

        } else { // Paint it for first time
            paint.call(this, normalizedData);
        }

    };

    RangeNv.prototype.delete = function() {

        //Stop observing for data changes
        framework.data.stopObserve(this.observeCallback);

        //Remove resize event listener
        if(this.resizeEventHandler != null) {
            $(window).off("resize", this.resizeEventHandler);
            this.resizeEventHandler = null;
        }

        //Clear DOM
        $(this.svg).empty();
        this.element.empty();

        this.svg = null;
        this.chart = null;

    };

    RangeNv.prototype.updateContext = function(d) {
        this.lastExtent = d;
        framework.data.updateContext(this.ownContext, {from: moment(d[0]).format("YYYY-MM-DD"), to: moment(d[1]).format("YYYY-MM-DD")});
        setTimeInfo(d[0], d[1]);
        $(this).trigger("CONTEXT_UPDATED");

    };

    // PRIVATE METHODS - - - - - - - - - - - - - - - - - - - - - -

    //Function that returns the value to replace with the label variables
    var replacer = function(resourceId, resource, str) {

        //Remove the initial an trailing '%' of the string
        str = str.substring(1, str.length-1);

        //Check if it is a parameter an return its value
        if(str === "resourceId") { //Special command to indicate the name of the resource
            return resourceId;

        } else { // Obtain its value through the object given the path

            var path = str.split(".");
            var subObject = resource;

            for(var p = 0; p < path.length; ++p) {
                if((subObject = subObject[path[p]]) == null)
                    return "";
            }

            return subObject.toString();
        }

    };

    /**
     * Gets a normalized array of data according to the chart expected input from the data returned by the framework.
     * @param framework_data
     * @returns {Array} Contains objects with 'label' and 'value'.
     */
    var getNormalizedData = function getNormalizedData(framework_data) {
        var labelVariable = /%(\w|\.)+%/g; //Regex that matches all the "variables" of the label such as %mid%, %pid%...

        var series = [];
        this.labels = {};
        //var colors = ['#ff7f0e','#2ca02c','#7777ff','#D53E4F','#9E0142'];
        //Data is represented as an array of {x,y} pairs.
        for (var metricId in framework_data) {
            for (var m in framework_data[metricId]) {

                var metric = framework_data[metricId][m];
                var metricData = metric['data'];

                if(metricData.interval.from < this.minT) {
                    this.minT = metricData.interval.from;
                }
                if(metric.data.interval.to > this.maxT) {
                    this.maxT = metricData.interval.to;
                }

                var timePoint = metricData.interval.from - metricData.step;
                var yserie = metricData.values;

                // Create a replacer for this metric
                var metricReplacer = replacer.bind(null, metricId, metric);

                var genLabel = function genLabel(i) {
                  var lab = this.configuration.labelFormat.replace(labelVariable,metricReplacer);
                  if (i > 0) {
                    lab = lab + '(' + i + ')';
                  }
                  if (lab in this.labels) {
                    lab = genLabel.call(this, ++i);
                  }
                  this.labels[lab] = null;
                  return lab;
                };

                // Generate the label by replacing the variables
                var label = genLabel.call(this, 0);

                // Metric dataset
                var dat = yserie.map(function(dat, index) {
                    timePoint += metricData.step;
                    if(dat > this.maxY) {
                        this.maxY = dat;
                    }
                    if (dat < this.minY) {
                        this.minY = dat;
                    }
                    return {'x': new Date(timePoint), 'y': dat};
                }.bind(this));
                series.push({
                    values: dat,      //values - represents the array of {x,y} data points
                    key: label, //key  - the name of the series.
                    area: this.configuration.isArea
                });
                if (series.length == 1) {
                    series[0]['bar'] = true
                }
            }
        }

        //Line chart data should be sent as an array of series objects.
        return series;
    };

    var paint = function paint(data) {

        var width = this.element.get(0).getBoundingClientRect().width;
        var height = this.element.get(0).getBoundingClientRect().height;

        nv.addGraph(function() {
            var chart = nv.models.lineWithFocusChart()
                .focusHeight(this.configuration.focusHeight)
                .interpolate(this.configuration.interpolate)
                .color(this.configuration.colors)
                .duration(this.configuration.duration)
                .showLegend(this.configuration.showLegend);
                // only affect to focus .How can i force Y axis in context chart?
                // ... i don't know ...
                //.forceY([this.maxY + 10, this.minY]);
            this.chart = chart;

            chart.margin({"top":10,"bottom":14});

            chart.xAxis.tickFormat(function(d) {
                return d3.time.format('%x')(new Date(d));
                });
            chart.x2Axis.tickFormat(function(d) {
                return d3.time.format('%x')(new Date(d))
                });

            chart.yAxis.tickFormat(function(d) {
                if (d >= 1000 || d <= -1000) {
                    return Math.abs(d/1000) + " K";
                } else {
                    return Math.abs(d);
                }
            });

            chart.y2Axis.tickFormat(function(d) {
                if (d >= 1000 || d <= -1000) {
                    return Math.abs(d/1000) + " K";
                } else {
                    return Math.abs(d);
                }
            });

            d3.select(this.svg.get(0))
                .datum(data)
                .call(chart);

            var timer = null;
            chart.dispatch.on('brush', function(extent){
                if(JSON.stringify(this.lastExtent) == JSON.stringify(extent.extent)){
                    // Resize event causes a unwanted brush event in this chart
                    return;
                }
                if (timer) {
                    clearTimeout(timer); //cancel the previous timer.
                    timer = null;
                }
                timer = setTimeout(function() {
                    this.updateContext(extent.extent);
                }.bind(this), 400);
            }.bind(this));

            //Update the chart when window resizes.
            this.resizeEventHandler = function() { chart.update() };
            $(window).resize(this.resizeEventHandler);

            if (!this.configuration.showFocus) {
                $(".nv-focus").attr("class", "nv-focus hidden");
            }
            // axis color
            $(this.svg).find("[class~=nv-axisG]").attr('style', 'fill:' + this.configuration.axisColor + ';')
            // leyend color
            $(this.svg).find("[class~=nv-legend-text]").attr('style', 'fill:' + this.configuration.axisColor + ';')

            // bigger brush cover
            $(this.svg).find("[class~=nv-brushBackground] rect").attr('height', 98);
            $(this.svg).find("[class~=nv-brushBackground] rect").attr('transform', 'translate(0,-4)');

            //Call update to update the chart and threfore the context
            chart.update();

            return chart;
        }.bind(this));

    };

    window.framework.widgets.RangeNv = RangeNv;

    // AMD compliant
    if ( typeof define === "function" && define.amd) {
        define( [], function () { return RangeNv; } );
    }

})();
<|endoftext|>


<commit_before>/*
==================================================================================================
  Jasy - JavaScript Tooling Framework
  Copyright 2010-2011 Sebastian Werner
--------------------------------------------------------------------------------------------------
  Inspired by: http://www.phpied.com/when-is-a-stylesheet-really-loaded/
==================================================================================================
*/

(function(global, doc)
{
	var completed = {};
	var loading = {};
	var dynamicUri = "?r=" + Date.now();

	/**
	 * Stylesheet (CSS) loader and manager. Tracks status of all loaded files.
	 */
	Module("jasy.io.StyleSheet",
	{
		/**
		 * Whether the given stylesheet is loaded.
		 *
		 * @param url {String} URL pointing to the stylesheet
		 * @return {Boolean} Whether the stylesheet is loaded
		 */
		isLoaded : function(url) {
			return !!completed[url];
		},

		/**
		 * Loads all given stylesheets and execute the given callback after
		 * they have been loaded. Stylesheets are requested in order of the list and
		 * should be applied in the same order.
		 *
		 * @param uris {Array} URLs pointing to all stylesheets
		 * @param callback {Function} Callback that fires when stylesheet is loaded
		 * @param context {Object} Context in which the callback is being executed. Defaults to global context.
		 * @param nocache {Boolean?false} Appends a dynamic parameter to each script to force a fresh copy
		 */
		loadAll: function(uris, callback, context, nocache)
		{
			if (jasy.Env.isSet("debug"))
			{
				jasy.Test.assertArray(uris);

				if (callback != null) {
					jasy.Test.assertFunction(callback);
				}

				if (context != null) {
					jasy.Test.assertObject(context);
				}

				if (nocache != null) {
					jasy.Test.assertBoolean(nocache);
				}
			}

			var keys = {};
			for (var i=0, l=uris.length; i<l; i++)
			{
				var uri = uris[i];

				if (!completed[uri]) {
					keys[uri] = true;
				}
			}

			var caller = function(uri)
			{
				return function()
				{
					delete keys[uri];

					if (Object.empty(keys)) {
						callback.call(context||global);
					}
				}
			};

			for (var i=0, l=uris.length; i<l; i++) {
				this.load(uris[i], caller(uris[i]), null, nocache);
			}
		},

		/**
		 * Loads a stylesheet and fires a callback when the stylesheet is applied to the page.
		 *
		 * Inspired by:
		 * http://www.phpied.com/when-is-a-stylesheet-really-loaded/
		 *
		 * @param url {String} URL pointing to the stylesheet
		 * @param callback {Function} Callback that fires when stylesheet is loaded
		 * @param context {Object} Context in which the callback is being executed. Defaults to global context.
		 * @param nocache {Boolean?false} Appends a dynamic parameter to each script to force a fresh copy
		 */
		load: function(url, callback, context, nocache)
		{
			if (jasy.Env.isSet("debug"))
			{
				jasy.Test.assertString(url);

				if (callback != null) {
					jasy.Test.assertFunction(callback);
				}

				if (context != null) {
					jasy.Test.assertObject(context);
				}

				if (nocache != null) {
					jasy.Test.assertBoolean(nocache);
				}
			}

			var head = doc.getElementsByTagName('head')[0];

			if (jasy.Env.isSet("debug")) {
				nocache = true;
			}

			context = context || global;

			if (loading[url]) {
				throw new Error("Stylesheet is already loading: " + url);
			}

			loading[url] = true;

			var load = function load()
			{
				completed[url] = true;
				delete loading[url];

				if (callback) {
					callback.call(context);
				}
			};

			// Use listener to stylesheet list and compare elements
			if (jasy.Env.isSet("engine", "webkit"))
			{
				var link = doc.createElement('link');
				var sheets = doc.styleSheets;

				handle = setInterval(function()
				{
					for (var i = 0, l = sheets.length; i < l; i++)
					{
						if (sheets[i].ownerNode === link)
						{
							clearInterval(handle);
							load();
						}
					}
				}, 50);

				link.rel = "stylesheet";
				link.type = "text/css";
				link.href = url + (nocache ? dynamicUri : "");

				head.appendChild(link);
			}

			// Use style import fallback for buggy GECKO
			else if (jasy.Env.isSet("engine", "gecko"))
			{
				var style = doc.createElement("style");
				style.textContent = "@import '" + url + (nocache ? dynamicUri : "") + "'";

				var handle = setInterval(function()
				{
					try
					{
						// MAGIC: only populated when file is loaded
						style.sheet.cssRules;
						clearInterval(handle);
						load();
					} catch(e) {}
				}, 50);

				head.appendChild(style);
			}

			// Load event only supported by MSIE and OPERA
			else
			{
				var link = doc.createElement("link");
				link.onload = function()
				{
					link.onload = null;
					load();
				};

				link.rel = "stylesheet";
				link.type = "text/css";
				link.href = url + (nocache ? dynamicUri : "");

				head.appendChild(link);
			}
		}
	});
})(this, document);
<commit_msg>Simplified multi load<commit_after>/*
==================================================================================================
  Jasy - JavaScript Tooling Framework
  Copyright 2010-2011 Sebastian Werner
--------------------------------------------------------------------------------------------------
  Inspired by: http://www.phpied.com/when-is-a-stylesheet-really-loaded/
==================================================================================================
*/

(function(global, doc)
{
	var completed = {};
	var loading = {};
	var dynamicUri = "?r=" + Date.now();

	/**
	 * Stylesheet (CSS) loader and manager. Tracks status of all loaded files.
	 */
	Module("jasy.io.StyleSheet",
	{
		/**
		 * Whether the given stylesheet is loaded.
		 *
		 * @param url {String} URL pointing to the stylesheet
		 * @return {Boolean} Whether the stylesheet is loaded
		 */
		isLoaded : function(url) {
			return !!completed[url];
		},

		/**
		 * Loads all given stylesheets and execute the given callback after
		 * they have been loaded. Stylesheets are requested in order of the list and
		 * should be applied in the same order.
		 *
		 * @param uris {Array} URLs pointing to all stylesheets
		 * @param callback {Function} Callback that fires when stylesheet is loaded
		 * @param context {Object} Context in which the callback is being executed. Defaults to global context.
		 * @param nocache {Boolean?false} Appends a dynamic parameter to each script to force a fresh copy
		 */
		loadAll: function(uris, callback, context, nocache)
		{
			if (jasy.Env.isSet("debug"))
			{
				jasy.Test.assertArray(uris);

				if (callback != null) {
					jasy.Test.assertFunction(callback);
				}

				if (context != null) {
					jasy.Test.assertObject(context);
				}

				if (nocache != null) {
					jasy.Test.assertBoolean(nocache);
				}
			}

			var keys = {};
			for (var i=0, l=uris.length; i<l; i++)
			{
				var uri = uris[i];

				if (!completed[uri]) {
					keys[uri] = true;
				}
			}

			for (var i=0, l=uris.length; i<l; i++)
			{
				this.load(uris[i], function(url)
				{
					delete keys[url];

					if (Object.empty(keys)) {
						callback.call(context||global);
					}
				}, null, nocache);
			}
		},

		/**
		 * Loads a stylesheet and fires a callback when the stylesheet is applied to the page.
		 *
		 * Inspired by:
		 * http://www.phpied.com/when-is-a-stylesheet-really-loaded/
		 *
		 * @param url {String} URL pointing to the stylesheet
		 * @param callback {Function} Callback that fires when stylesheet is loaded
		 * @param context {Object} Context in which the callback is being executed. Defaults to global context.
		 * @param nocache {Boolean?false} Appends a dynamic parameter to each script to force a fresh copy
		 */
		load: function(url, callback, context, nocache)
		{
			if (jasy.Env.isSet("debug"))
			{
				jasy.Test.assertString(url);

				if (callback != null) {
					jasy.Test.assertFunction(callback);
				}

				if (context != null) {
					jasy.Test.assertObject(context);
				}

				if (nocache != null) {
					jasy.Test.assertBoolean(nocache);
				}
			}

			var head = doc.getElementsByTagName('head')[0];

			if (jasy.Env.isSet("debug")) {
				nocache = true;
			}

			context = context || global;

			if (loading[url]) {
				throw new Error("Stylesheet is already loading: " + url);
			}

			loading[url] = true;

			var load = function load()
			{
				completed[url] = true;
				delete loading[url];

				if (callback) {
					callback.call(context, url);
				}
			};

			// Use listener to stylesheet list and compare elements
			if (jasy.Env.isSet("engine", "webkit"))
			{
				var link = doc.createElement('link');
				var sheets = doc.styleSheets;

				handle = setInterval(function()
				{
					for (var i = 0, l = sheets.length; i < l; i++)
					{
						if (sheets[i].ownerNode === link)
						{
							clearInterval(handle);
							load();
						}
					}
				}, 50);

				link.rel = "stylesheet";
				link.type = "text/css";
				link.href = url + (nocache ? dynamicUri : "");

				head.appendChild(link);
			}

			// Use style import fallback for buggy GECKO
			else if (jasy.Env.isSet("engine", "gecko"))
			{
				var style = doc.createElement("style");
				style.textContent = "@import '" + url + (nocache ? dynamicUri : "") + "'";

				var handle = setInterval(function()
				{
					try
					{
						// MAGIC: only populated when file is loaded
						style.sheet.cssRules;
						clearInterval(handle);
						load();
					} catch(e) {}
				}, 50);

				head.appendChild(style);
			}

			// Load event only supported by MSIE and OPERA
			else
			{
				var link = doc.createElement("link");
				link.onload = function()
				{
					link.onload = null;
					load();
				};

				link.rel = "stylesheet";
				link.type = "text/css";
				link.href = url + (nocache ? dynamicUri : "");

				head.appendChild(link);
			}
		}
	});
})(this, document);
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var questionSchema = new mongoose.Schema({
    author: {
        type:mongoose.Schema.Types.ObjectId,
        ref:'User'
    },
    makeWith: String,
    language: String,
    question: [{
        type: {type: String},
        questionText: {type: String},
        image: {type: String},
        answers: [{
            text: String,
            url: String,
            attributesForTForMultiple: {
                isItRight: Boolean
            },
            attributesForSorting: {
                position: Number
            },
            attributesForLinking: {
                text1: String,
                text2: String,
                url1: String,
                url2: String
            },
            attributesForClickableArea: {
                x: Number,
                y: Number
            },
            attributesForEmptySpaces: {
                wordNumber: Number
            }
        }],
    }],
    keywords: [String],
    level: Number,
    totalAnswers: Number,
    correctAnswers: Number
});

questionSchema.statics.getQuestion=function(questionId,callback) {
    return this.findOne({'_id': {$in: questionId}}, callback);
}

questionSchema.statics.createQuestion=function(author,question, callback){
    question.author = author
    var new_question = new this(question);
    return new_question.save(callback);
};

questionSchema.statics.editQuestion=function(question,callback){
    return this.findOneAndUpdate(question._id, question, callback)
}

questionSchema.statics.updateLevel=function(questionId,userLevel,isCorrected,callback){
    this.findOne({_id:questionId}, function(err,question){
        if(err){
            return next(err);
        }
        var oldDifficult = question.level;
        var differentLevel = oldDifficult - userLevel;
        // caso in cui la domanda sia piÃ¹ difficile dell'abilitÃ  dell'utente:
        if(differentLevel <= 100 && differentLevel > 0){
            var scarto = oldDifficult - userLevel;
            if(isCorrected == true){
                if(scarto > 90){
                    question.level = oldDifficult + 11;
                }else if(scarto > 80){
                    question.level = oldDifficult + 10;
                }else if(scarto > 70){
                    question.level = oldDifficult + 9;
                }else if(scarto > 60){
                    question.level = oldDifficult + 8;
                }else if(scarto > 50){
                    question.level = oldDifficult + 7;
                }else if(scarto > 40){
                    question.level = oldDifficult + 6;
                }else if(scarto > 30){
                    question.level = oldDifficult + 5;
                }else if(scarto > 20){
                    question.level = oldDifficult + 4;
                }else if(scarto > 10){
                    question.level = oldDifficult + 3;
                }else if(scarto > 0){
                    question.level = oldDifficult + 2;
                }
            } else{
                if(scarto > 90){
                    question.level = oldDifficult - 2;
                }else if(scarto > 80){
                    question.level = oldDifficult - 3;
                }else if(scarto > 70){
                    question.level = oldDifficult - 4;
                }else if(scarto > 60){
                    question.level = oldDifficult - 5;
                }else if(scarto > 50){
                    question.level = oldDifficult - 6;
                }else if(scarto > 40){
                    question.level = oldDifficult - 7;
                }else if(scarto > 30){
                    question.level = oldDifficult - 8;
                }else if(scarto > 20){
                    question.level = oldDifficult - 9;
                }else if(scarto > 10){
                    question.level = oldDifficult - 10;
                }else if(scarto > 0){
                    question.level = oldDifficult - 11;
                }
            }
        }
        else{ // caso in cui la domanda sia piÃ¹ facile dell'abilitÃ  dell'utente
            var scarto = userLevel - oldDifficult;
            if(isCorrected == true){
                if(scarto > 90){
                    question.level = oldDifficult - 2;
                }else if(scarto > 80){
                    question.level = oldDifficult - 3;
                }else if(scarto > 70){
                    question.level = oldDifficult - 4;
                }else if(scarto > 60){
                    question.level = oldDifficult - 5;
                }else if(scarto > 50){
                    question.level = oldDifficult - 6;
                }else if(scarto > 40){
                    question.level = oldDifficult - 7;
                }else if(scarto > 30){
                    question.level = oldDifficult - 8;
                }else if(scarto > 20){
                    question.level = oldDifficult - 9;
                }else if(scarto > 10){
                    question.level = oldDifficult - 10;
                }else if(scarto > 0){
                    question.level = oldDifficult - 11;
                }else if(scarto == 0){
                    question.level = oldDifficult - 1;
                }
            } else{
                if(scarto > 90){
                    question.level = oldDifficult + 11;
                }else if(scarto > 80){
                    question.level = oldDifficult + 10;
                }else if(scarto > 70){
                    question.level = oldDifficult + 9;
                }else if(scarto > 60){
                    question.level = oldDifficult + 8;
                }else if(scarto > 50){
                    question.level = oldDifficult + 7;
                }else if(scarto > 40){
                    question.level = oldDifficult + 6;
                }else if(scarto > 30){
                    question.level = oldDifficult + 5;
                }else if(scarto > 20){
                    question.level = oldDifficult + 4;
                }else if(scarto > 10){
                    question.level = oldDifficult + 3;
                }else if(scarto > 0){
                    question.level = oldDifficult + 2;
                }else if(scarto == 0){
                    question.level = oldDifficult + 1;
                }
            }
        }
        // controllo che il livello non sia oltre i limiti
        if(question.level > 1000){
            question.level = 1000;
        }
        else if(question.level < 0){
            question.level = 0;
        }
        return question.save(callback);
    })

}

questionSchema.statics.addKeyword=function(questionId,keyword,callback){

}

questionSchema.statics.addCorrect=function(questionId){
    this.findOne({_id: questionId}, function(err, question){
        if (err) { return next(err); }
        question.correctAnswers += 1;
        return question.save()
    });
}

questionSchema.statics.addTotal=function(questionId){
    this.findOne({_id: questionId}, function(err, question){
        if (err) { return next(err); }
        question.totalAnswers += 1;
        return question.save()
    });
}

questionSchema.methods.getQuestion=function(questionId,callback){
    return Quiz.findOne({'_id':{$in:questionId}},callback);
};

module.exports = mongoose.model('Question', questionSchema);<commit_msg>Question controller e model updated<commit_after>var mongoose = require('mongoose');
var questionSchema = new mongoose.Schema({
    author: {
        type:mongoose.Schema.Types.ObjectId,
        ref:'User'
    },
    makeWith: String,
    language: String,
    question: [{
        type: {type: String},
        questionText: {type: String},
        image: {type: String},
        answers: [{
            text: String,
            url: String,
            attributesForTForMultiple: {
                isItRight: Boolean
            },
            attributesForSorting: {
                position: Number
            },
            attributesForLinking: {
                text1: String,
                text2: String,
                url1: String,
                url2: String
            },
            attributesForClickableArea: {
                x: Number,
                y: Number
            },
            attributesForEmptySpaces: {
                wordNumber: Number
            }
        }],
    }],
    keywords: [String],
    level: {type:Number, default: 500},
    totalAnswers: {type:Number, default: 0},
    correctAnswers: {type:Number, default: 0},
});

questionSchema.statics.getQuestion=function(questionId,callback) {
    return this.findOne({'_id': {$in: questionId}}, callback);
}

questionSchema.statics.createQuestion=function(author,question, callback){
    question.author = author
    var new_question = new this(question);
    return new_question.save(callback);
};

questionSchema.statics.editQuestion=function(question,callback){
    return this.findOneAndUpdate(question._id, question, callback)
}

questionSchema.statics.updateLevel=function(questionId,userLevel,isCorrected,callback){
    this.findOne({_id:questionId}, function(err,question){
        if(err){
            return next(err);
        }
        var oldDifficult = question.level;
        var differentLevel = oldDifficult - userLevel;
        // caso in cui la domanda sia piÃ¹ difficile dell'abilitÃ  dell'utente:
        if(differentLevel <= 100 && differentLevel > 0){
            var scarto = oldDifficult - userLevel;
            if(isCorrected == true){
                if(scarto > 90){
                    question.level = oldDifficult + 11;
                }else if(scarto > 80){
                    question.level = oldDifficult + 10;
                }else if(scarto > 70){
                    question.level = oldDifficult + 9;
                }else if(scarto > 60){
                    question.level = oldDifficult + 8;
                }else if(scarto > 50){
                    question.level = oldDifficult + 7;
                }else if(scarto > 40){
                    question.level = oldDifficult + 6;
                }else if(scarto > 30){
                    question.level = oldDifficult + 5;
                }else if(scarto > 20){
                    question.level = oldDifficult + 4;
                }else if(scarto > 10){
                    question.level = oldDifficult + 3;
                }else if(scarto > 0){
                    question.level = oldDifficult + 2;
                }
            } else{
                if(scarto > 90){
                    question.level = oldDifficult - 2;
                }else if(scarto > 80){
                    question.level = oldDifficult - 3;
                }else if(scarto > 70){
                    question.level = oldDifficult - 4;
                }else if(scarto > 60){
                    question.level = oldDifficult - 5;
                }else if(scarto > 50){
                    question.level = oldDifficult - 6;
                }else if(scarto > 40){
                    question.level = oldDifficult - 7;
                }else if(scarto > 30){
                    question.level = oldDifficult - 8;
                }else if(scarto > 20){
                    question.level = oldDifficult - 9;
                }else if(scarto > 10){
                    question.level = oldDifficult - 10;
                }else if(scarto > 0){
                    question.level = oldDifficult - 11;
                }
            }
        }
        else{ // caso in cui la domanda sia piÃ¹ facile dell'abilitÃ  dell'utente
            var scarto = userLevel - oldDifficult;
            if(isCorrected == true){
                if(scarto > 90){
                    question.level = oldDifficult - 2;
                }else if(scarto > 80){
                    question.level = oldDifficult - 3;
                }else if(scarto > 70){
                    question.level = oldDifficult - 4;
                }else if(scarto > 60){
                    question.level = oldDifficult - 5;
                }else if(scarto > 50){
                    question.level = oldDifficult - 6;
                }else if(scarto > 40){
                    question.level = oldDifficult - 7;
                }else if(scarto > 30){
                    question.level = oldDifficult - 8;
                }else if(scarto > 20){
                    question.level = oldDifficult - 9;
                }else if(scarto > 10){
                    question.level = oldDifficult - 10;
                }else if(scarto > 0){
                    question.level = oldDifficult - 11;
                }else if(scarto == 0){
                    question.level = oldDifficult - 1;
                }
            } else{
                if(scarto > 90){
                    question.level = oldDifficult + 11;
                }else if(scarto > 80){
                    question.level = oldDifficult + 10;
                }else if(scarto > 70){
                    question.level = oldDifficult + 9;
                }else if(scarto > 60){
                    question.level = oldDifficult + 8;
                }else if(scarto > 50){
                    question.level = oldDifficult + 7;
                }else if(scarto > 40){
                    question.level = oldDifficult + 6;
                }else if(scarto > 30){
                    question.level = oldDifficult + 5;
                }else if(scarto > 20){
                    question.level = oldDifficult + 4;
                }else if(scarto > 10){
                    question.level = oldDifficult + 3;
                }else if(scarto > 0){
                    question.level = oldDifficult + 2;
                }else if(scarto == 0){
                    question.level = oldDifficult + 1;
                }
            }
        }
        // controllo che il livello non sia oltre i limiti
        if(question.level > 1000){
            question.level = 1000;
        }
        else if(question.level < 0){
            question.level = 0;
        }
        return question.save(callback);
    })

}

questionSchema.statics.addKeyword=function(questionId,keyword,callback){

}

questionSchema.statics.addCorrect=function(questionId){
    this.findOne({_id: questionId}, function(err, question){
        if (err) { return next(err); }
        question.correctAnswers += 1;
        return question.save()
    });
}

questionSchema.statics.addTotal=function(questionId){
    this.findOne({_id: questionId}, function(err, question){
        if (err) { return next(err); }
        question.totalAnswers += 1;
        return question.save()
    });
}

questionSchema.methods.getQuestion=function(questionId,callback){
    return Quiz.findOne({'_id':{$in:questionId}},callback);
};

module.exports = mongoose.model('Question', questionSchema);<|endoftext|>