<commit_before>/**
 * OpenLayers 3 Draw Control
 * @param opt_options
 * @constructor
 * @extends ol.control.Control
 */
ol.control.DrawButtons = function (opt_options) {

    // Get options
    var options = opt_options || {};
    options.draw.Ending = true;

    this.selectedLayers = options.selectedLayer;
    var this_ = this;

    if (options.style_buttons == undefined) {
        options.style_buttons = "default";
    }

    if (options.popup_form == true) {
        this.popup = document.getElementById('popup');
    }

    // Default values
    this.typeSelect = 'Point';
    this.map = this.getMap();
    this.flagDraw = new Boolean(false);
    this.setFlagDraw(this.flagDraw);

    // Classes CSS
    this.olClassName = 'ol-unselectable ol-control';
    this.drawContainer = 'toggle-control';

    this.drawClassName = this.olClassName + ' ' + this.drawContainer;

    this.olGroupClassName = 'ol-control-group';

    // Boutons
    var elementDrawButtons = new ol.Collection();
    var elementDrawControls = new ol.Collection();

    // Events listeners
    var handleButtonsClick = function (e)
    {
        e = e || window.event;

        // Disabled Controls buttons
        var divsChildren = this_.element.getElementsByClassName('div-controls')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;
        }

        // Disable Draws controls
        var divsChildren = this_.element.getElementsByClassName('div-draw')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;

            if (divsChildren.item(i).type_control == 'ending') {
                divsChildren.item(i).classList.remove('hidden');
                divsChildren.item(i).disabled = false;
            }
        }

        // Enable the actual button
        e.target.classList.toggle('enable');
        e.target.disabled = false;

        this_.drawOnMap(e);

        e.preventDefault();
    };

    // handling control mode
    var handleControlsClick = function (e)
    {
        e = e || window.event;

        // Disabled Controls buttons
        var divsChildren = this_.element.getElementsByClassName('div-controls')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;

            if (divsChildren.item(i).type_control == 'ending') {
                divsChildren.item(i).classList.remove('hidden');
                divsChildren.item(i).disabled = false;
            }
        }

        // Disable Draws controls
        var divsChildren = this_.element.getElementsByClassName('div-draw')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;
        }

        // Enable the actual button
        e.target.classList.toggle('enable');
        e.target.disabled = false;

        switch (e.target.type_control) {
            case 'edit' :
                this_.controlEditOnMap(e);
                break;
            case 'delete' :
                this_.controlDelOnMap(e);
                break;
        }

        e.preventDefault();
    };

    // Endind draw/control mode
    var handleGroupEnd = function (e)
    {
        var divsChildren = this_.element.querySelectorAll('.div-controls button, .div-draw button');
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).disabled = false;

            if (divsChildren.item(i).type_control == 'ending') {
                if (!divsChildren.item(i).classList.contains('hidden')) {
                    divsChildren.item(i).classList.toggle('hidden');
                }
            }
        }

        // Removing interaction
        this_.map.removeInteraction(this_.draw);
        this_.setFlagDraw(false); // Desactivation of drawing flag
        e.preventDefault();
    };

    // Marker
    var buttonPoint = this.buttonPoint = document.createElement('button');
    buttonPoint.setAttribute('title', 'Draw point');
    buttonPoint.id = buttonPoint.draw = 'Point';
    buttonPoint.type_control = 'draw';
    buttonPoint.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonPoint);

    // Line
    var buttonLine = this.buttonLine = document.createElement('button');
    buttonLine.setAttribute('title', 'Draw line');
    buttonLine.id = buttonLine.draw = 'LineString';
    buttonLine.type_control = 'draw';
    buttonLine.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonLine);

    // Square
    var buttonSquare = this.buttonCircle = document.createElement('button');
    buttonSquare.setAttribute('title', 'Draw square');
    buttonSquare.id = buttonSquare.draw = 'Square';
    buttonSquare.type_control = 'draw';
    buttonSquare.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonSquare);

    // Circle
    var buttonCircle = this.buttonCircle = document.createElement('button');
    buttonCircle.setAttribute('title', 'Draw circle');
    buttonCircle.id = buttonCircle.draw = 'Circle';
    buttonCircle.type_control = 'draw';
    buttonCircle.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonCircle);

    // Polygone
    var buttonPolygone = this.buttonPolygone = document.createElement('button');
    buttonPolygone.setAttribute('title', 'Draw polygone');
    buttonPolygone.id = buttonPolygone.draw = 'Polygon';
    buttonPolygone.type_control = 'draw';
    buttonPolygone.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonPolygone);

    // Record add items
    var buttonDrawEnd = this.buttonDrawEnd = document.createElement('button');
    buttonDrawEnd.setAttribute('title', 'Ending draw mode');
    buttonDrawEnd.id = buttonDrawEnd.draw = 'Ending';
    buttonDrawEnd.type_control = 'ending';
    buttonDrawEnd.addEventListener('click', handleGroupEnd, false);
    elementDrawButtons.push(buttonDrawEnd);

    // Edit
    var buttonEdit = this.buttonEdit = document.createElement('button');
    buttonEdit.setAttribute('title', 'Edit feature');
    buttonEdit.id = 'Edit';
    buttonEdit.type_control = 'edit';
    buttonEdit.addEventListener('click', handleControlsClick, false);
    elementDrawControls.push(buttonEdit);

    // Delete
    var buttonDel = this.buttonEdit = document.createElement('button');
    buttonDel.setAttribute('title', 'Delete feature');
    buttonDel.id = 'Delete';
    buttonDel.type_control = 'delete';
    buttonDel.addEventListener('click', handleControlsClick, false);
    elementDrawControls.push(buttonDel);

    var buttonControlEnd = this.buttonControlEnd = document.createElement('button');
    buttonControlEnd.setAttribute('title', 'Ending control mode');
    buttonControlEnd.id = 'Ending';
    buttonControlEnd.type_control = 'ending';
    buttonControlEnd.addEventListener('click', handleGroupEnd, false);
    elementDrawControls.push(buttonControlEnd);

    // /!\ if you want to use glyphicon, you must have Bootstrap
    if (options.style_buttons == "glyphicon") {
        buttonPoint.className = 'glyphicon glyphicon-map-marker';
        buttonLine.className = 'glyphicon glyphicon-vector-path-line';
        buttonSquare.className = 'glyphicon glyphicon-vector-path-square';
        buttonCircle.className = 'glyphicon glyphicon-vector-path-circle';
        buttonPolygone.className = 'glyphicon glyphicon-vector-path-polygon';
        buttonDrawEnd.className = 'glyphicon glyphicon-ok hidden';

        buttonEdit.className = 'glyphicon glyphicon-pencil';
        buttonDel.className = 'glyphicon glyphicon-trash';
        buttonControlEnd.className = 'glyphicon glyphicon-ok hidden';

    } else {
        buttonPoint.className = 'glyphicon-vector-path-point';
        buttonLine.className = 'glyphicon-vector-path-line';
        buttonSquare.className = 'glyphicon-vector-path-square';
        buttonCircle.className = 'glyphicon-vector-path-circle';
        buttonPolygone.className = 'glyphicon-vector-path-polygon';
        buttonDrawEnd.className = 'glyphicon-vector-path-ok hidden';

        buttonEdit.className = 'glyphicon-vector-path-pencil';
        buttonDel.className = 'glyphicon-vector-path-trash';
        buttonControlEnd.className = 'glyphicon-vector-path-ok hidden';
    }

    // Containers
    var divDraw = document.createElement('div');
    divDraw.className = 'div-draw ' + this.olGroupClassName;
    elementDrawButtons.forEach(function(button) {
        if(options.draw[button.draw] == true) {
            divDraw.appendChild(button);
        }
    });

    var divControls = document.createElement('div');
    divControls.className = 'div-controls ' + this.olGroupClassName;
    elementDrawControls.forEach(function(button) {
        divControls.appendChild(button);
    });

    // Container
    var element = document.createElement('div');
    element.className = this.drawClassName;
    element.appendChild(divDraw);
    element.appendChild(divControls);

    ol.control.Control.call(this, {
        element: element,
        target: options.target
    });
};

ol.inherits(ol.control.DrawButtons, ol.control.Control);

/**
 * Drawing on map
 * @param evt
 */
ol.control.DrawButtons.prototype.drawOnMap = function(evt)
{
    this.map = this.getMap();

    if (!this.getSelectedLayer()) {
        this.setFlagDraw(false);
    } else {
        this.setFlagDraw(true)
    }

    if (this.getFlagDraw() == true) {
        var geometryFctDraw;
        var typeSelect = evt.target.draw;

        // Specific for square
        if (typeSelect == 'Square') {
            typeSelect = 'Circle';
            geometryFctDraw = ol.interaction.Draw.createRegularPolygon(4);
        }

        // Draw new item
        var draw = this.draw = new ol.interaction.Draw({
            //features: features,
            source : this.getSelectedLayer().getSource(),
            features : new ol.Collection(),
            type: /** @type {ol.geom.GeometryType} */ (typeSelect),
            geometryFunction : geometryFctDraw,
            style : this.styleAdd()
        });

        draw.on('drawend', this.drawEndFeature, this);

        this.map.addInteraction(draw);
    }

};

/**
 * Edit or delete a feature
 * @param evt
 */
ol.control.DrawButtons.prototype.controlEditOnMap = function(evt) {
    if (!this.getSelectedLayer()) {
        this.setFlagDraw(false)
    } else {
        this.setFlagDraw(true);
    }

    if (this.getFlagDraw() == true) {
        this.map = this.getMap();

        // Select Interaction
        var selectedLayer = this.getSelectedLayer();
        var selectInteraction = new ol.interaction.Select({
            condition: ol.events.condition.click,
        });
        this.map.addInteraction(selectInteraction);

        // Gestion des event sur la feature
        selectInteraction.getFeatures().addEventListener('add', function (e) {
            var feature = e.element;
            feature.addEventListener('change', function(e) {
                console.log(feature.getGeometry());
            });
            console.log(feature.getGeometry());
        });

        // Modify interaction
        var mod = new ol.interaction.Modify({
            features: selectInteraction.getFeatures(),
            style: this.styleEdit()
        });
        this.map.addInteraction(mod);
    }
};

/**
 * Delete a feature from map
 * @param evt
 */
ol.control.DrawButtons.prototype.controlDelOnMap = function (evt)
{
    if (!this.getSelectedLayer()) {
        this.setFlagDraw(false)
    } else {
        this.setFlagDraw(true);
    }

    if (this.getFlagDraw() == true) {
        this.map = this.getMap();

        // Select Interaction
        var selectInteraction = new ol.interaction.Select({
            condition: ol.events.condition.click,
            source : function(layer) {
                if (layer == this.getSelectedLayer()) {
                    return layer
                }
            }
        });
        var this_ = this;
        selectInteraction.getFeatures().addEventListener('add', function(e) {
            var feature = e.element;
            if(confirm('Are you sure you want to delete this feature ?')) {
                // remove from selected Layer
                this_.getSelectedLayer().getSource().removeFeature(feature);

                // Here, override for deleting from your database

            } else {
                selectInteraction.getFeatures().remove(feature);
            }
            e.preventDefault();
        });

        this.map.addInteraction(selectInteraction);
    }
};

/**
 * Styles of selected layer
 */
ol.control.DrawButtons.prototype.styleAdd = function()
{
    var style = new ol.style.Style({
        fill: new ol.style.Fill({
            color: [69, 175, 157, 0.4] //#45B29D
        }),
        stroke: new ol.style.Stroke({
            color: [0, 75, 82, 0.75], //#004B52
            width: 1.5
        }),
        image: new ol.style.Circle({
            radius: 7,
            fill: new ol.style.Fill({
                color: [60, 255, 100, 0.4]
            }),
            stroke: new ol.style.Stroke({
                color: [255, 255, 255, 0.75],
                width: 1.5
            })
        }),
        zIndex: 100000
    });

    return style;
};

ol.control.DrawButtons.prototype.styleEdit = function()
{
    var style = new ol.style.Style({
        fill: new ol.style.Fill({
            color: [4, 100, 128, 0.4] //#046380
        }),
        stroke: new ol.style.Stroke({
            color: [0, 64, 28, 0.75], //#004080
            width: 1.5
        }),
        image: new ol.style.Circle({
            radius: 7,
            fill: new ol.style.Fill({
                color: [4, 100, 128, 0.4]
            }),
            stroke: new ol.style.Stroke({
                color: [0, 64, 28, 0.75],
                width: 1.5
            })
        }),
        zIndex: 100000
    });
    return style;
};

// Endind drawing feature
ol.control.DrawButtons.prototype.drawEndFeature = function(evt)
{
    var feature = evt.feature;
    var parser = new ol.format.GeoJSON();

    // Problem with recuperation of a circle geometry : https://github.com/openlayers/ol3/pull/3434
    if ('Circle' == feature.type) {
        //var parserCircle = parser.writeCircleGeometry_()
    } else {
        var featureGeoJSON = parser.writeFeatureObject(feature);
    }

    //console.log(feature.getGeometry().getCoordinates());
    console.log(featureGeoJSON);
};

// Getters/setters of selected layer :
// Set your layer according to your need :)
ol.control.DrawButtons.prototype.setSelectedLayer = function(layer)
{
    this.selectedLayers = layer;
};

ol.control.DrawButtons.prototype.getSelectedLayer = function()
{
    return this.selectedLayers;
};

/**
 * Add a flag if Mode draw or not
 * @param flagDraw
 */
ol.control.DrawButtons.prototype.setFlagDraw = function(/** @type {boolean} */flagDraw)
{
    this.flagDraw = flagDraw;
};

ol.control.DrawButtons.prototype.getFlagDraw = function()
{
    return this.flagDraw;
};
<commit_msg>Remove feature from interaction and layer<commit_after>/**
 * OpenLayers 3 Draw Control
 * @param opt_options
 * @constructor
 * @extends ol.control.Control
 */
ol.control.DrawButtons = function (opt_options) {

    // Get options
    var options = opt_options || {};
    options.draw.Ending = true;

    this.selectedLayers = options.selectedLayer;
    var this_ = this;

    if (options.style_buttons == undefined) {
        options.style_buttons = "default";
    }

    if (options.popup_form == true) {
        this.popup = document.getElementById('popup');
    }

    // Default values
    this.typeSelect = 'Point';
    this.map = this.getMap();
    this.flagDraw = new Boolean(false);
    this.setFlagDraw(this.flagDraw);

    // Classes CSS
    this.olClassName = 'ol-unselectable ol-control';
    this.drawContainer = 'toggle-control';

    this.drawClassName = this.olClassName + ' ' + this.drawContainer;

    this.olGroupClassName = 'ol-control-group';

    // Boutons
    var elementDrawButtons = new ol.Collection();
    var elementDrawControls = new ol.Collection();

    // Events listeners
    var handleButtonsClick = function (e)
    {
        e = e || window.event;

        // Disabled Controls buttons
        var divsChildren = this_.element.getElementsByClassName('div-controls')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;
        }

        // Disable Draws controls
        var divsChildren = this_.element.getElementsByClassName('div-draw')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;

            if (divsChildren.item(i).type_control == 'ending') {
                divsChildren.item(i).classList.remove('hidden');
                divsChildren.item(i).disabled = false;
            }
        }

        // Enable the actual button
        e.target.classList.toggle('enable');
        e.target.disabled = false;

        this_.drawOnMap(e);

        e.preventDefault();
    };

    // handling control mode
    var handleControlsClick = function (e)
    {
        e = e || window.event;

        // Disabled Controls buttons
        var divsChildren = this_.element.getElementsByClassName('div-controls')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;

            if (divsChildren.item(i).type_control == 'ending') {
                divsChildren.item(i).classList.remove('hidden');
                divsChildren.item(i).disabled = false;
            }
        }

        // Disable Draws controls
        var divsChildren = this_.element.getElementsByClassName('div-draw')[0].children;
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).classList.remove('enable');
            divsChildren.item(i).disabled = true;
        }

        // Enable the actual button
        e.target.classList.toggle('enable');
        e.target.disabled = false;

        switch (e.target.type_control) {
            case 'edit' :
                this_.controlEditOnMap(e);
                break;
            case 'delete' :
                this_.controlDelOnMap(e);
                break;
        }

        e.preventDefault();
    };

    // Endind draw/control mode
    var handleGroupEnd = function (e)
    {
        var divsChildren = this_.element.querySelectorAll('.div-controls button, .div-draw button');
        for(var i = 0; i < divsChildren.length; i++) {
            divsChildren.item(i).disabled = false;

            if (divsChildren.item(i).type_control == 'ending') {
                if (!divsChildren.item(i).classList.contains('hidden')) {
                    divsChildren.item(i).classList.toggle('hidden');
                }
            }
        }

        // Removing interaction
        this_.map.removeInteraction(this_.draw);
        this_.setFlagDraw(false); // Desactivation of drawing flag
        e.preventDefault();
    };

    // Marker
    var buttonPoint = this.buttonPoint = document.createElement('button');
    buttonPoint.setAttribute('title', 'Draw point');
    buttonPoint.id = buttonPoint.draw = 'Point';
    buttonPoint.type_control = 'draw';
    buttonPoint.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonPoint);

    // Line
    var buttonLine = this.buttonLine = document.createElement('button');
    buttonLine.setAttribute('title', 'Draw line');
    buttonLine.id = buttonLine.draw = 'LineString';
    buttonLine.type_control = 'draw';
    buttonLine.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonLine);

    // Square
    var buttonSquare = this.buttonCircle = document.createElement('button');
    buttonSquare.setAttribute('title', 'Draw square');
    buttonSquare.id = buttonSquare.draw = 'Square';
    buttonSquare.type_control = 'draw';
    buttonSquare.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonSquare);

    // Circle
    var buttonCircle = this.buttonCircle = document.createElement('button');
    buttonCircle.setAttribute('title', 'Draw circle');
    buttonCircle.id = buttonCircle.draw = 'Circle';
    buttonCircle.type_control = 'draw';
    buttonCircle.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonCircle);

    // Polygone
    var buttonPolygone = this.buttonPolygone = document.createElement('button');
    buttonPolygone.setAttribute('title', 'Draw polygone');
    buttonPolygone.id = buttonPolygone.draw = 'Polygon';
    buttonPolygone.type_control = 'draw';
    buttonPolygone.addEventListener('click', handleButtonsClick, false);
    elementDrawButtons.push(buttonPolygone);

    // Record add items
    var buttonDrawEnd = this.buttonDrawEnd = document.createElement('button');
    buttonDrawEnd.setAttribute('title', 'Ending draw mode');
    buttonDrawEnd.id = buttonDrawEnd.draw = 'Ending';
    buttonDrawEnd.type_control = 'ending';
    buttonDrawEnd.addEventListener('click', handleGroupEnd, false);
    elementDrawButtons.push(buttonDrawEnd);

    // Edit
    var buttonEdit = this.buttonEdit = document.createElement('button');
    buttonEdit.setAttribute('title', 'Edit feature');
    buttonEdit.id = 'Edit';
    buttonEdit.type_control = 'edit';
    buttonEdit.addEventListener('click', handleControlsClick, false);
    elementDrawControls.push(buttonEdit);

    // Delete
    var buttonDel = this.buttonEdit = document.createElement('button');
    buttonDel.setAttribute('title', 'Delete feature');
    buttonDel.id = 'Delete';
    buttonDel.type_control = 'delete';
    buttonDel.addEventListener('click', handleControlsClick, false);
    elementDrawControls.push(buttonDel);

    var buttonControlEnd = this.buttonControlEnd = document.createElement('button');
    buttonControlEnd.setAttribute('title', 'Ending control mode');
    buttonControlEnd.id = 'Ending';
    buttonControlEnd.type_control = 'ending';
    buttonControlEnd.addEventListener('click', handleGroupEnd, false);
    elementDrawControls.push(buttonControlEnd);

    // /!\ if you want to use glyphicon, you must have Bootstrap
    if (options.style_buttons == "glyphicon") {
        buttonPoint.className = 'glyphicon glyphicon-map-marker';
        buttonLine.className = 'glyphicon glyphicon-vector-path-line';
        buttonSquare.className = 'glyphicon glyphicon-vector-path-square';
        buttonCircle.className = 'glyphicon glyphicon-vector-path-circle';
        buttonPolygone.className = 'glyphicon glyphicon-vector-path-polygon';
        buttonDrawEnd.className = 'glyphicon glyphicon-ok hidden';

        buttonEdit.className = 'glyphicon glyphicon-pencil';
        buttonDel.className = 'glyphicon glyphicon-trash';
        buttonControlEnd.className = 'glyphicon glyphicon-ok hidden';

    } else {
        buttonPoint.className = 'glyphicon-vector-path-point';
        buttonLine.className = 'glyphicon-vector-path-line';
        buttonSquare.className = 'glyphicon-vector-path-square';
        buttonCircle.className = 'glyphicon-vector-path-circle';
        buttonPolygone.className = 'glyphicon-vector-path-polygon';
        buttonDrawEnd.className = 'glyphicon-vector-path-ok hidden';

        buttonEdit.className = 'glyphicon-vector-path-pencil';
        buttonDel.className = 'glyphicon-vector-path-trash';
        buttonControlEnd.className = 'glyphicon-vector-path-ok hidden';
    }

    // Containers
    var divDraw = document.createElement('div');
    divDraw.className = 'div-draw ' + this.olGroupClassName;
    elementDrawButtons.forEach(function(button) {
        if(options.draw[button.draw] == true) {
            divDraw.appendChild(button);
        }
    });

    var divControls = document.createElement('div');
    divControls.className = 'div-controls ' + this.olGroupClassName;
    elementDrawControls.forEach(function(button) {
        divControls.appendChild(button);
    });

    // Container
    var element = document.createElement('div');
    element.className = this.drawClassName;
    element.appendChild(divDraw);
    element.appendChild(divControls);

    ol.control.Control.call(this, {
        element: element,
        target: options.target
    });
};

ol.inherits(ol.control.DrawButtons, ol.control.Control);

/**
 * Drawing on map
 * @param evt
 */
ol.control.DrawButtons.prototype.drawOnMap = function(evt)
{
    this.map = this.getMap();

    if (!this.getSelectedLayer()) {
        this.setFlagDraw(false);
    } else {
        this.setFlagDraw(true)
    }

    if (this.getFlagDraw() == true) {
        var geometryFctDraw;
        var typeSelect = evt.target.draw;

        // Specific for square
        if (typeSelect == 'Square') {
            typeSelect = 'Circle';
            geometryFctDraw = ol.interaction.Draw.createRegularPolygon(4);
        }

        // Draw new item
        var draw = this.draw = new ol.interaction.Draw({
            //features: features,
            source : this.getSelectedLayer().getSource(),
            features : new ol.Collection(),
            type: /** @type {ol.geom.GeometryType} */ (typeSelect),
            geometryFunction : geometryFctDraw,
            style : this.styleAdd()
        });

        draw.on('drawend', this.drawEndFeature, this);

        this.map.addInteraction(draw);
    }

};

/**
 * Edit or delete a feature
 * @param evt
 */
ol.control.DrawButtons.prototype.controlEditOnMap = function(evt) {
    if (!this.getSelectedLayer()) {
        this.setFlagDraw(false)
    } else {
        this.setFlagDraw(true);
    }

    if (this.getFlagDraw() == true) {
        this.map = this.getMap();

        // Select Interaction
        var selectedLayer = this.getSelectedLayer();
        var selectInteraction = new ol.interaction.Select({
            condition: ol.events.condition.click,
        });
        this.map.addInteraction(selectInteraction);

        // Gestion des event sur la feature
        selectInteraction.getFeatures().addEventListener('add', function (e) {
            var feature = e.element;
            feature.addEventListener('change', function(e) {
                console.log(feature.getGeometry());
            });
            console.log(feature.getGeometry());

            // ---------------------------------------------- //
            // Here, override for updating into your database //
            // ---------------------------------------------- //
        });

        // Modify interaction
        var mod = new ol.interaction.Modify({
            features: selectInteraction.getFeatures(),
            style: this.styleEdit()
        });
        this.map.addInteraction(mod);
    }
};

/**
 * Delete a feature from map
 * @param evt
 */
ol.control.DrawButtons.prototype.controlDelOnMap = function (evt)
{
    if (!this.getSelectedLayer()) {
        this.setFlagDraw(false)
    } else {
        this.setFlagDraw(true);
    }

    if (this.getFlagDraw() == true) {
        this.map = this.getMap();

        // Select Interaction
        var selectInteraction = new ol.interaction.Select({
            condition: ol.events.condition.click,
            source : function(layer) {
                if (layer == this.getSelectedLayer()) {
                    return layer
                }
            }
        });
        var this_ = this;
        selectInteraction.getFeatures().addEventListener('add', function(e) {
            var feature = e.element;
            if(confirm('Are you sure you want to delete this feature ?')) {
                // remove from selected Layer
                this_.getSelectedLayer().getSource().removeFeature(feature);

                // Remove from interaction
                selectInteraction.getFeatures().remove(feature);

                // ---------------------------------------------- //
                // Here, override for deleting from your database //
                // ---------------------------------------------- //
            } else {
                selectInteraction.getFeatures().remove(feature);
            }
            e.preventDefault();
        });

        this.map.addInteraction(selectInteraction);
    }
};

/**
 * Styles of selected layer
 */
ol.control.DrawButtons.prototype.styleAdd = function()
{
    var style = new ol.style.Style({
        fill: new ol.style.Fill({
            color: [69, 175, 157, 0.4] //#45B29D
        }),
        stroke: new ol.style.Stroke({
            color: [0, 75, 82, 0.75], //#004B52
            width: 1.5
        }),
        image: new ol.style.Circle({
            radius: 7,
            fill: new ol.style.Fill({
                color: [60, 255, 100, 0.4]
            }),
            stroke: new ol.style.Stroke({
                color: [255, 255, 255, 0.75],
                width: 1.5
            })
        }),
        zIndex: 100000
    });

    return style;
};

ol.control.DrawButtons.prototype.styleEdit = function()
{
    var style = new ol.style.Style({
        fill: new ol.style.Fill({
            color: [4, 100, 128, 0.4] //#046380
        }),
        stroke: new ol.style.Stroke({
            color: [0, 64, 28, 0.75], //#004080
            width: 1.5
        }),
        image: new ol.style.Circle({
            radius: 7,
            fill: new ol.style.Fill({
                color: [4, 100, 128, 0.4]
            }),
            stroke: new ol.style.Stroke({
                color: [0, 64, 28, 0.75],
                width: 1.5
            })
        }),
        zIndex: 100000
    });
    return style;
};

// Endind drawing feature
ol.control.DrawButtons.prototype.drawEndFeature = function(evt)
{
    var feature = evt.feature;
    var parser = new ol.format.GeoJSON();

    // Problem with recuperation of a circle geometry : https://github.com/openlayers/ol3/pull/3434
    if ('Circle' == feature.type) {
        //var parserCircle = parser.writeCircleGeometry_()
    } else {
        var featureGeoJSON = parser.writeFeatureObject(feature);
    }

    //console.log(feature.getGeometry().getCoordinates());
    console.log(featureGeoJSON);

    // -------------------------------------------- //
    // Here, override for adding into your database //
    // -------------------------------------------- //
};

// Getters/setters of selected layer :
// Set your layer according to your need :)
ol.control.DrawButtons.prototype.setSelectedLayer = function(layer)
{
    this.selectedLayers = layer;
};

ol.control.DrawButtons.prototype.getSelectedLayer = function()
{
    return this.selectedLayers;
};

/**
 * Add a flag if Mode draw or not
 * @param flagDraw
 */
ol.control.DrawButtons.prototype.setFlagDraw = function(/** @type {boolean} */flagDraw)
{
    this.flagDraw = flagDraw;
};

ol.control.DrawButtons.prototype.getFlagDraw = function()
{
    return this.flagDraw;
};
<|endoftext|>


<commit_before>// Google Analytics
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-43498910-1', 'gounsubscribe.me');
ga('send', 'pageview');

// Gogle +1
window.___gcfg = {lang: 'en-GB'};
(function() {
    var po = document.createElement('script');
    po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/plusone.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(po, s);
})();

// Facebook
(function() {
    var fb1 = document.createElement('script');
    fb1.type = 'text/javascript';
    fb1.async = true;
    fb1.src = 'http://connect.facebook.net/en_GB/all.js#xfbml=1', 'facebook-jssdk';
    var fb2 = document.getElementsByTagName('script')[0];
    fb2.parentNode.insertBefore(fb1, fb2);
})();

// Twitter
(function() {
    var twitterScriptTag = document.createElement('script');
    twitterScriptTag.type = 'text/javascript';
    twitterScriptTag.async = true;
    twitterScriptTag.src = 'http://platform.twitter.com/widgets.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(twitterScriptTag, s);
})();

// menu
(function(){
    var url = window.location;
    $('ul.nav a').filter(function() {
        return this.href == url;
    }).parent().addClass('active');
})();

// Rest of the site
function search() {
    location.href="#"+String(document.getElementById("siteName").value).toLowerCase().replace(/[^ a-z0-9]/g, '').replace(/\s+/g, '-');
}

openLink = function(link){
    window.open(link);
}

var websites = []
var elements = document.getElementsByTagName("h2");
for (var i = 0; i < elements.length; i++){
    websites.push(elements[i].innerText);
}

$( "#siteName" ).autocomplete({
    source: websites,
    delay: 0,
    messages: {
        noResults: '',
        results: function() {}
    },
    select: function (event, ui) { search() },
}).keydown(function(e){
    if (e.keyCode === 13){
        search();
    }
});
<commit_msg>Fix Facebook like button<commit_after>// Google Analytics
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-43498910-1', 'gounsubscribe.me');
ga('send', 'pageview');

// Gogle +1
window.___gcfg = {lang: 'en-GB'};
(function() {
    var po = document.createElement('script');
    po.type = 'text/javascript'; po.async = true;
    po.src = 'https://apis.google.com/js/plusone.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(po, s);
})();

// Facebook
(function() {
    var fb1 = document.createElement('script');
    fb1.type = 'text/javascript';
    fb1.async = true;
    fb1.src = 'http://connect.facebook.net/en_GB/all.js#xfbml=1&status=0', 'facebook-jssdk';
    var fb2 = document.getElementsByTagName('script')[0];
    fb2.parentNode.insertBefore(fb1, fb2);
})();

// Twitter
(function() {
    var twitterScriptTag = document.createElement('script');
    twitterScriptTag.type = 'text/javascript';
    twitterScriptTag.async = true;
    twitterScriptTag.src = 'http://platform.twitter.com/widgets.js';
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(twitterScriptTag, s);
})();

// menu
(function(){
    var url = window.location;
    $('ul.nav a').filter(function() {
        return this.href == url;
    }).parent().addClass('active');
})();

// Rest of the site
function search() {
    location.href="#"+String(document.getElementById("siteName").value).toLowerCase().replace(/[^ a-z0-9]/g, '').replace(/\s+/g, '-');
}

openLink = function(link){
    window.open(link);
}

var websites = []
var elements = document.getElementsByTagName("h2");
for (var i = 0; i < elements.length; i++){
    websites.push(elements[i].innerText);
}

$( "#siteName" ).autocomplete({
    source: websites,
    delay: 0,
    messages: {
        noResults: '',
        results: function() {}
    },
    select: function (event, ui) { search() },
}).keydown(function(e){
    if (e.keyCode === 13){
        search();
    }
});
<|endoftext|>


<commit_before>/**
 *
 * Tab Page Classes (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-06-17
 *
 */

window.ST = window['ST'] || {};

(function (NS) {

	const SELECTOR_TARGET    = '.tab-page';
	const CLS_TAB_LIST       = 'stile-tab-page-tab-list';
	const CLS_TAB_LIST_BELOW = 'stile-tab-page-tab-list-below';
	const ST_STATE_CURRENT   = 'current';

	const PAGE_HEIGHT_WINDOW_HEIGHT_RATIO = 0.8;

	NS.addInit(4, () => {
		const tabPages = [];
		const tps = document.querySelectorAll(SELECTOR_TARGET);
		for (let i = 0; i < tps.length; i += 1) {
			const tabPage = createTabPage(tps[i]);
			if (tabPage) tabPages.push(tabPage);
		}
		NS.onResize(() => { onResize(tabPages); });
		setTimeout(function () { onResize(tabPages) }, 200);  // Delay
	});

	function createTabPage(container) {
		const fh = getFirstHeading(container);
		if (!fh) return false;
		const tabH = fh.tagName;

		const pages = [], htmls = [];
		const cs = [].slice.call(container.children);
		let curPage = null;

		for (let i = 0; i < cs.length; i += 1) {
			const c = cs[i];
			if (c.tagName === tabH) {
				if (curPage) pages.push(curPage);
				curPage = document.createElement('div');
				container.removeChild(c);
				htmls.push(c.innerHTML);
			} else {
				if (curPage) curPage.appendChild(c);
			}
		}
		if (curPage) pages.push(curPage);

		const tp = { pages, container, currentIdx: 0, isAccordion: false };
		createTab(htmls, tp);

		container.insertBefore(tp.tabUl, container.firstChild);
		for (let i = 0; i < pages.length; i += 1) container.appendChild(pages[i]);
		container.appendChild(tp.tabUl2);

		return tp;
	}

	function createTab(htmls, tp) {
		tp.tabUl = document.createElement('ul');
		for (let i = 0; i < htmls.length; i += 1) {
			const li = document.createElement('li');
			li.innerHTML = htmls[i];
			tp.tabUl.appendChild(li);
		}
		tp.tabUl.className = CLS_TAB_LIST;
		tp.tabs = [].slice.call(tp.tabUl.children);

		tp.tabUl2 = tp.tabUl.cloneNode(true);
		tp.tabUl2.className = CLS_TAB_LIST_BELOW;
		tp.tabs2 = [].slice.call(tp.tabUl2.children);
		addTabEvent(tp);

		setTimeout(() => {
			tp.isAccordion = getComputedStyle(tp.tabUl2).flexDirection === 'column';
			onTabClick(tp, tp.isAccordion ? -1 : 0);
		}, 10);
	}

	function getFirstHeading(container) {
		const cs = container.children;
		for (let i = 0; i < cs.length; i += 1) {
			if (/^H[1-6]$/.test(cs[i].tagName)) return cs[i];
		}
		return null;
	}

	function addTabEvent(tp) {
		const ts = tp.tabs, ts2 = tp.tabs2;

		for (let i = 0; i < ts.length; i += 1) {
			const f = (function (idx) {
				return function (e) {
					e.preventDefault();
					onTabClick(tp, idx);
					if (!tp.isAccordion) onResizeOne(tp, true);
				};
			})(i);
			ts[i].addEventListener('click', f);
			ts2[i].addEventListener('click', f);
		}
	}

	function onTabClick(tp, idx) {
		if (tp.isAccordion) {
			idx = tp.currentIdx === idx ? -1 : idx;
			updateAccordionTabState(tp, idx);
		}
		changeCurrentTab(tp, idx);
	}

	function changeCurrentTab(tp, idx) {
		const ts = tp.tabs, ts2 = tp.tabs2;
		const ps = tp.pages;

		for (let i = 0; i < ts.length; i += 1) {
			if (i === idx) {
				NS.addStile(ts[i], ST_STATE_CURRENT);
				NS.addStile(ts2[i], ST_STATE_CURRENT);
				NS.addStile(ps[i], ST_STATE_CURRENT);
			} else {
				NS.removeStile(ts[i], ST_STATE_CURRENT);
				NS.removeStile(ts2[i], ST_STATE_CURRENT);
				NS.removeStile(ps[i], ST_STATE_CURRENT);
			}
			ts[i].className = '';
			ts2[i].className = '';
			ps[i].className = '';
		}
		tp.currentIdx = idx;
		if (idx === -1) return;

		setTimeout(() => {
			const bcr = tp.tabUl2.getBoundingClientRect();
			if (bcr.top < 0) NS.jumpToElement(tp.tabUl, 200, false);
		}, 10);
	}

	function updateAccordionTabState(tp, idx) {
		const ts  = tp.tabs, ts2 = tp.tabs2;

		if (idx === -1) {
			for (let i = 0; i < ts.length; i += 1) {
				ts[i].style.display  = '';
				ts2[i].style.display = 'none';
			}
		} else {
			for (let i = 0; i < ts.length; i += 1) {
				ts[i].style.display  = (i <= idx) ? '' : 'none';
				ts2[i].style.display = (i > idx)  ? 'flex' : 'none';
			}
		}
	}

	function onResize(tps) {
		for (let i = 0; i < tps.length; i += 1) {
			onResizeOne(tps[i]);
		}
	}

	function onResizeOne(tp, suppressTabClick = false) {
		const cont = tp.container;
		const prevIsAccordion = tp.isAccordion;
		tp.isAccordion = getComputedStyle(tp.tabUl2).flexDirection === 'column';

		if (tp.isAccordion) {
			cont.style.minHeight = '';
			cont.style.height    = '';
			if (prevIsAccordion !== tp.isAccordion) changeCurrentTab(tp, -1);
			updateAccordionTabState(tp, tp.currentIdx);
		} else {
			const minH = getMinHeight(tp);
			if (minH < window.innerHeight * PAGE_HEIGHT_WINDOW_HEIGHT_RATIO) {
				cont.style.minHeight = minH + 'px';
				cont.style.height    = minH + 'px';
			} else {
				cont.style.minHeight = '';
				cont.style.height    = '';
			}
			const ts = tp.tabs, ts2 = tp.tabs2;

			for (let i = 0; i < ts.length; i += 1) {
				ts[i].style.display = '';
				ts2[i].style.display = '';
			}
			if (tp.currentIdx === -1) tp.currentIdx = 0;
			if (!suppressTabClick) onTabClick(tp, tp.currentIdx);
		}
	}

	function getMinHeight(tp) {
		const tabUl  = tp.tabUl;
		const tabUl2 = tp.tabUl2;
		const pages  = tp.pages;

		let marginBtm = parseInt(getComputedStyle(tabUl).marginBottom);
		let marginTop = parseInt(getComputedStyle(tabUl2).marginTop);
		let height = 0;

		for (let i = 0; i < pages.length; i += 1) {
			const p = pages[i];
			const ps = getComputedStyle(p);
			const mt = parseInt(ps.marginTop);
			const mb = parseInt(ps.marginBottom);
			const h = p.getBoundingClientRect().height;
			marginBtm = Math.max(marginBtm, mt);
			marginTop = Math.max(marginTop, mb);
			height = Math.max(height, h);
		}
		return tabUl.offsetHeight + tabUl2.offsetHeight + marginBtm + marginTop + height;
	}

})(window.ST);
<commit_msg>Add hash transition<commit_after>/**
 *
 * Tab Page Classes (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-12-16
 *
 */

window.ST = window['ST'] || {};

(function (NS) {

	const SELECTOR_TARGET    = '.tab-page';
	const CLS_TAB_LIST       = 'stile-tab-page-tab-list';
	const CLS_TAB_LIST_BELOW = 'stile-tab-page-tab-list-below';
	const ST_STATE_CURRENT   = 'current';

	const PAGE_WINDOW_HEIGHT_RATIO = 0.8;

	const HASH_PREFIX = 'tab:';

	NS.addInit(4, () => {
		const tabPages = [];
		const tps = document.querySelectorAll(SELECTOR_TARGET);
		for (let i = 0; i < tps.length; i += 1) {
			const tabPage = createTabPage(tps[i], i);
			tabPages.push(tabPage);
		}
		NS.onResize(() => { onResizeAll(tabPages); }, true);
		setTimeout(() => {  // Delay
			onResizeAll(tabPages);
			onHash(tabPages);
		}, 200);
		window.addEventListener('hashchange', () => { onHash(tabPages); });
	});

	function createTabPage(container, contIdx) {
		const fh = getFirstHeading(container);
		if (!fh) return false;
		const tabH = fh.tagName;

		const pages = [], htmls = [];
		const cs = [].slice.call(container.children);
		let curPage = null;

		for (let i = 0; i < cs.length; i += 1) {
			const c = cs[i];
			if (c.tagName === tabH) {
				if (curPage) pages.push(curPage);
				curPage = document.createElement('div');
				container.removeChild(c);
				htmls.push(c.innerHTML);
			} else {
				if (curPage) curPage.appendChild(c);
			}
		}
		if (curPage) pages.push(curPage);

		const tp = { pages, container, currentIdx: 0, isAccordion: false, contIdx };
		createTab(htmls, tp);

		container.insertBefore(tp.tabUl, container.firstChild);
		for (let i = 0; i < pages.length; i += 1) container.appendChild(pages[i]);
		container.appendChild(tp.tabUl2);

		return tp;
	}

	function createTab(htmls, tp) {
		tp.tabUl = document.createElement('ul');
		for (let i = 0; i < htmls.length; i += 1) {
			const li = document.createElement('li');
			li.innerHTML = htmls[i];
			tp.tabUl.appendChild(li);
		}
		tp.tabUl.className = CLS_TAB_LIST;
		tp.tabs = [].slice.call(tp.tabUl.children);

		tp.tabUl2 = tp.tabUl.cloneNode(true);
		tp.tabUl2.className = CLS_TAB_LIST_BELOW;
		tp.tabs2 = [].slice.call(tp.tabUl2.children);
		addTabEvent(tp);

		setTimeout(() => {
			tp.isAccordion = getComputedStyle(tp.tabUl2).flexDirection === 'column';
			update(tp, tp.isAccordion ? -1 : 0);
		}, 10);
	}

	function addTabEvent(tp) {
		const ts = tp.tabs, ts2 = tp.tabs2;

		for (let i = 0; i < ts.length; i += 1) {
			const f = (function (idx) { return (e) => { onClick(e, tp, idx); }; })(i);
			ts[i].addEventListener('click', f);
			ts2[i].addEventListener('click', f);
		}
	}

	function onHash(tps) {
		const [hCont, hPage] = extractIndexFromHash(window.location.hash);
		if (hCont !== null) {
			update(tps[hCont], hPage);
			scrollToTab(tps[hCont]);
		}
	}

	function onClick(e, tp, idx) {
		e.preventDefault();
		update(tp, idx);
		scrollToTab(tp);
		pushTabState(tp, idx);
		if (!tp.isAccordion) resizeTab(tp, true);
	}

	function scrollToTab(tp) {
		if (tp.currentIdx === -1) return;
		setTimeout(() => {
			const bcr = tp.tabUl2.getBoundingClientRect();
			if (bcr.top < 0) NS.jumpToElement(tp.tabUl, 200, false);
		}, 10);
	}

	// -------------------------------------------------------------------------

	function getFirstHeading(container) {
		const cs = container.children;
		for (let i = 0; i < cs.length; i += 1) {
			if (/^H[1-6]$/.test(cs[i].tagName)) return cs[i];
		}
		return null;
	}

	// -------------------------------------------------------------------------

	function extractIndexFromHash(hash) {
		if (hash.indexOf('#tab:') !== 0) return [null, null];
		const cp = hash.replace('#tab:', '').split('-');
		if (cp.length !== 2) return [null, null];
		return [parseInt(cp[0]) - 1, parseInt(cp[1]) - 1];
	}

	function pushTabState(tp, idx) {
		let hash = '';
		if (0 <= idx) hash = '#' + HASH_PREFIX + (tp.contIdx + 1) + '-' + (idx + 1);
		history.pushState(null, null, hash);
		window.location.hash = hash;
	}

	// -------------------------------------------------------------------------

	function update(tp, idx) {
		if (tp.isAccordion) {
			idx = tp.currentIdx === idx ? -1 : idx;
			updateAccordion(tp, idx);
		}
		updateTab(tp, idx);
	}

	function updateAccordion(tp, idx) {
		const ts = tp.tabs, ts2 = tp.tabs2;

		if (idx === -1) {
			for (let i = 0; i < ts.length; i += 1) {
				ts[i].style.display  = '';
				ts2[i].style.display = 'none';
			}
		} else {
			for (let i = 0; i < ts.length; i += 1) {
				ts[i].style.display  = (i <= idx) ? '' : 'none';
				ts2[i].style.display = (i > idx)  ? 'flex' : 'none';
			}
		}
	}

	function updateTab(tp, idx) {
		const ts = tp.tabs, ts2 = tp.tabs2;
		const ps = tp.pages;

		for (let i = 0; i < ts.length; i += 1) {
			if (i === idx) {
				NS.addStile(ts[i],  ST_STATE_CURRENT);
				NS.addStile(ts2[i], ST_STATE_CURRENT);
				NS.addStile(ps[i],  ST_STATE_CURRENT);
			} else {
				NS.removeStile(ts[i],  ST_STATE_CURRENT);
				NS.removeStile(ts2[i], ST_STATE_CURRENT);
				NS.removeStile(ps[i],  ST_STATE_CURRENT);
			}
			ts[i].className  = '';
			ts2[i].className = '';
			ps[i].className  = '';
		}
		tp.currentIdx = idx;
	}

	// -------------------------------------------------------------------------

	function onResizeAll(tps) {
		for (let i = 0; i < tps.length; i += 1) {
			if (!tps[i]) continue;
			resizeOne(tps[i]);
		}
	}

	function resizeOne(tp) {
		const prevIsAccordion = tp.isAccordion;
		tp.isAccordion = getComputedStyle(tp.tabUl2).flexDirection === 'column';

		if (tp.isAccordion) {
			resizeAccordion(tp, prevIsAccordion);
		} else {
			resizeTab(tp);
		}
	}

	function resizeAccordion(tp, prevIsAccordion) {
		const cont = tp.container;
		cont.style.minHeight = '';
		cont.style.height    = '';
		if (prevIsAccordion !== tp.isAccordion) updateTab(tp, -1);
		updateAccordion(tp, tp.currentIdx);
	}

	function resizeTab(tp, suppressTabClick = false) {
		const cont = tp.container;
		const minH = getMinHeight(tp);

		const h = (minH < window.innerHeight * PAGE_WINDOW_HEIGHT_RATIO) ? (minH + 'px') : '';
		cont.style.minHeight = h;
		cont.style.height    = h;

		const ts = tp.tabs, ts2 = tp.tabs2;

		for (let i = 0; i < ts.length; i += 1) {
			ts[i].style.display  = '';
			ts2[i].style.display = '';
		}
		if (tp.currentIdx === -1) tp.currentIdx = 0;
		if (!suppressTabClick) update(tp, tp.currentIdx);
	}

	function getMinHeight(tp) {
		const tabUl  = tp.tabUl;
		const tabUl2 = tp.tabUl2;
		const pages  = tp.pages;

		let marginBtm = parseInt(getComputedStyle(tabUl).marginBottom);
		let marginTop = parseInt(getComputedStyle(tabUl2).marginTop);
		let height = 0;

		for (let i = 0; i < pages.length; i += 1) {
			const p  = pages[i];
			const ps = getComputedStyle(p);
			const mt = parseInt(ps.marginTop);
			const mb = parseInt(ps.marginBottom);
			const h  = p.getBoundingClientRect().height;

			marginBtm = Math.max(marginBtm, mt);
			marginTop = Math.max(marginTop, mb);
			height    = Math.max(height, h);
		}
		return tabUl.offsetHeight + tabUl2.offsetHeight + marginBtm + marginTop + height;
	}

})(window.ST);
<|endoftext|>


<commit_before>(function( root, $, undefined ) {
	"use strict";
	var currentPanelIndex = 0;
	var resizeTOHandler;

	$(function () {

		/*
		// DOM ready, take it away
		var options = {
			$menu: $(".elevator-items-list"),
			menuSelector: '.elevator-item',
			panelSelector: '.project',
			namespace: '.panelSnap',
			onSnapStart: function(event){
				console.log("start  "+event[0].dataset.panel);
			},
			onSnapFinish: function(event){
				//double call here ??
				console.log("finish  "+event[0].dataset.panel);
				currentPanelIndex = parseInt(event[0].dataset.panel.substring(2));
				selectElevator(currentPanelIndex);
			},
			onActivate: function(event){
			},
			directionThreshold: 1000,
			slideSpeed: 300,
			easing: 'swing',
			offset: 0,
			navigation: {
				keys: {
				  nextKey: 40,
				  prevKey: 38,
				},
				buttons: {
				  $nextButton: $(".down-arrow"),
				  $prevButton: $(".up-arrow"),
				},
				wrapAround: false
			}
		};

		$('body').panelSnap(options);

		*/

		$('.down-arrow').click(function(event){
			console.log("down arrow");
			if(currentPanelIndex >= $('article').length-1)
				return;

			currentPanelIndex++;
			updatePosition(currentPanelIndex);
		});
		$('.up-arrow').click(function(event){
			// console.log("up arrow");
			if(currentPanelIndex <= 0)
				return;

			currentPanelIndex--;
			updatePosition(currentPanelIndex);

		});
		$('.elevator').on('click', '.elevator-item', function(event) {
			// console.log("elevator item");
			currentPanelIndex = event.currentTarget.dataset.panel.substring(2);
			updatePosition(currentPanelIndex);
		});

		$( window ).resize(function() {
			clearTimeout(resizeTOHandler);
			resizeTOHandler = setTimeout(function(){
				updatePosition(currentPanelIndex);
			}, 500);

		});

		init();

	// End	$(function)
	});

	function init(){
		var acticleNbr = $('article').length;
		for(var k=0;k<acticleNbr;k++)
			$(".elevator-items-list").append('
'+eval(k+1)+'
');

		if(window.location.hash != "" && typeof $(window.location.hash)[0] != "undefined"){
			currentPanelIndex = parseInt($(window.location.hash)[0].dataset.panel.substring(2));
		}
		scrollToPanel("article[data-panel='dp"+currentPanelIndex+"']");
		selectElevator(currentPanelIndex);
		animateText(currentPanelIndex);
	}

	function updatePosition(index){
		scrollToPanel("article[data-panel='dp"+index+"']",function(){
			if(window.location.hash != $("article[data-panel='dp"+index+"']").attr("id"))
				window.location.hash = $("article[data-panel='dp"+index+"']").attr("id");
		});

		scrollBackToTop(index);
		selectElevator(index);
		animateText(index);
		//window.location.hash = $("article[data-panel='dp"+index+"']").attr("id");
	}

	function scrollToPanel(selector,completecb){
		var aTag = $(selector);
		$('html,body').animate({scrollTop: aTag.offset().top},{duration:0,easing:"swing",complete:completecb}); // changed duration 400 to 0
	}

	function selectElevator(index){
		// unselect current
		$(".selected-item").removeClass("selected-item");
		$($(".elevator-item")[index]).addClass("selected-item");
	}

	function scrollBackToTop(index){
		var imgDiv = $("article[data-panel='dp"+index+"']").children(".image-column");
		imgDiv.animate({scrollTop:0},{duration:0} );
	}

	function animateText(index){

		var articleDiv = $("article[data-panel='dp"+index+"']");
		var legend = articleDiv.children(".legend");
		var fixHeight = legend.height();
		legend.css('height', fixHeight );
		// $('.text-column').hide();
		// $('.legend').hide();
		// legend.show();

		var num = legend.children('.legend-line-1').children('.lgd-numbering');
		var title =	legend.children('.legend-line-1').children('.lgd-title');
		var commanditaire = legend.children('.legend-line-2').children('.lgd-commanditaire');
		var lieu = legend.children('.legend-line-3').children('.lgd-lieu');
		var annee = legend.children('.legend-line-3').children('.lgd-annee');

		// var allDivs = [num,title, commanditaire,lieu,annee];
		// var allTexts = [num.html(),title.html(), commanditaire.html(),lieu.html(),annee.html()];
		var allDivs = [title, commanditaire,lieu,annee];
		var allTexts = [title.html(), commanditaire.html(),lieu.html(),annee.html()];
		type(allDivs,allTexts,index);
	}

	function type(divs,texts,indexElevator){

		if (divs.length > 0){
			$.each(divs,function(index,div){
				div.html('');
				div.data('typed', null);
			});
			var div = divs.shift();
			var text = texts.shift();
			div.typed({
						strings: [text],
						typeSpeed: 5,
						contentType: 'text',
						showCursor: false,
						cursorChar: "|",
						callback: function() { div.html(text); type(divs,texts);}
				});
		}
		else{
			// console.log("done");
			// $('.legend').show();
			// $('.text-column').show();
		}
	}

} ( this, jQuery ));
<commit_msg>keyboard up&down<commit_after>(function( root, $, undefined ) {
	"use strict";
	var currentPanelIndex = 0;
	var resizeTOHandler;

	$(function () {

		/*
		// DOM ready, take it away
		var options = {
			$menu: $(".elevator-items-list"),
			menuSelector: '.elevator-item',
			panelSelector: '.project',
			namespace: '.panelSnap',
			onSnapStart: function(event){
				console.log("start  "+event[0].dataset.panel);
			},
			onSnapFinish: function(event){
				//double call here ??
				console.log("finish  "+event[0].dataset.panel);
				currentPanelIndex = parseInt(event[0].dataset.panel.substring(2));
				selectElevator(currentPanelIndex);
			},
			onActivate: function(event){
			},
			directionThreshold: 1000,
			slideSpeed: 300,
			easing: 'swing',
			offset: 0,
			navigation: {
				keys: {
				  nextKey: 40,
				  prevKey: 38,
				},
				buttons: {
				  $nextButton: $(".down-arrow"),
				  $prevButton: $(".up-arrow"),
				},
				wrapAround: false
			}
		};

		$('body').panelSnap(options);

		*/

		$('.down-arrow').click(function(event){
			goDown();
		});
		$('.up-arrow').click(function(event){
			goUp();
		});

		$(document).keyup(function(e){
			if(e.keyCode == 38){
				goUp();
			 }
			 if(e.keyCode == 40){
				 goDown();
			 }
		});

		$('.elevator').on('click', '.elevator-item', function(event) {
			// console.log("elevator item");
			currentPanelIndex = event.currentTarget.dataset.panel.substring(2);
			updatePosition(currentPanelIndex);
		});

		$( window ).resize(function() {
			clearTimeout(resizeTOHandler);
			resizeTOHandler = setTimeout(function(){
				updatePosition(currentPanelIndex);
			}, 500);

		});

		init();

	// End	$(function)
	});

	function init(){
		var acticleNbr = $('article').length;
		for(var k=0;k<acticleNbr;k++)
			$(".elevator-items-list").append('
'+eval(k+1)+'
');

		if(window.location.hash != "" && typeof $(window.location.hash)[0] != "undefined"){
			currentPanelIndex = parseInt($(window.location.hash)[0].dataset.panel.substring(2));
		}
		scrollToPanel("article[data-panel='dp"+currentPanelIndex+"']");
		selectElevator(currentPanelIndex);
		animateText(currentPanelIndex);
	}

	function goUp(){
		if(currentPanelIndex <= 0)
			return;
		currentPanelIndex--;
		updatePosition(currentPanelIndex);
	}

	function goDown(){
		if(currentPanelIndex >= $('article').length-1)
			return;
		currentPanelIndex++;
		updatePosition(currentPanelIndex);
	}

	function updatePosition(index){
		scrollToPanel("article[data-panel='dp"+index+"']",function(){
			if(window.location.hash != $("article[data-panel='dp"+index+"']").attr("id"))
				window.location.hash = $("article[data-panel='dp"+index+"']").attr("id");
		});

		scrollBackToTop(index);
		selectElevator(index);
		animateText(index);
		//window.location.hash = $("article[data-panel='dp"+index+"']").attr("id");
	}

	function scrollToPanel(selector,completecb){
		var aTag = $(selector);
		$('html,body').animate({scrollTop: aTag.offset().top},{duration:0,easing:"swing",complete:completecb}); // changed duration 400 to 0
	}

	function selectElevator(index){
		// unselect current
		$(".selected-item").removeClass("selected-item");
		$($(".elevator-item")[index]).addClass("selected-item");
	}

	function scrollBackToTop(index){
		var imgDiv = $("article[data-panel='dp"+index+"']").children(".image-column");
		imgDiv.animate({scrollTop:0},{duration:0} );
	}

	function animateText(index){

		var articleDiv = $("article[data-panel='dp"+index+"']");
		var legend = articleDiv.children(".legend");
		var fixHeight = legend.height();
		legend.css('height', fixHeight );
		// $('.text-column').hide();
		// $('.legend').hide();
		// legend.show();

		var num = legend.children('.legend-line-1').children('.lgd-numbering');
		var title =	legend.children('.legend-line-1').children('.lgd-title');
		var commanditaire = legend.children('.legend-line-2').children('.lgd-commanditaire');
		var lieu = legend.children('.legend-line-3').children('.lgd-lieu');
		var annee = legend.children('.legend-line-3').children('.lgd-annee');

		// var allDivs = [num,title, commanditaire,lieu,annee];
		// var allTexts = [num.html(),title.html(), commanditaire.html(),lieu.html(),annee.html()];
		var allDivs = [title, commanditaire,lieu,annee];
		var allTexts = [title.html(), commanditaire.html(),lieu.html(),annee.html()];
		type(allDivs,allTexts,index);
	}

	function type(divs,texts,indexElevator){

		if (divs.length > 0){
			$.each(divs,function(index,div){
				div.html('');
				div.data('typed', null);
			});
			var div = divs.shift();
			var text = texts.shift();
			div.typed({
						strings: [text],
						typeSpeed: 5,
						contentType: 'text',
						showCursor: false,
						cursorChar: "|",
						callback: function() { div.html(text); type(divs,texts);}
				});
		}
		else{
			// console.log("done");
			// $('.legend').show();
			// $('.text-column').show();
		}
	}

} ( this, jQuery ));
<|endoftext|>


<commit_before>// Logger is a `console` replacement which is capable of sending its output to
// multiple places, like the browser console, REST APIs, or a Zaqar queue.
//
// Adding new log destinations consists of creating an adapter.  An adapter in
// this context is a subclass of Adapter.
//
// The Logger class reads the tripleOUiConfig object to determine which logger
// adapters should be activated.  By default, only the console adapter is used.
//
// Usage:
//
//   import logger from 'src/js/services/logger';
//   logger.log('Hello world!');

class Adapter {
  debug(...args) {}

  info(...args) {}

  warn(...args) {}

  error(...args) {}

  group(...args) {}

  groupCollapsed(...args) {}

  groupEnd(...args) {}

  log(...args) {}
}

class ConsoleAdapter extends Adapter {
  debug(...args) {
    console.debug(...args); // eslint-disable-line no-console
  }

  info(...args) {
    console.info(...args); // eslint-disable-line no-console
  }

  warn(...args) {
    console.warn(...args); // eslint-disable-line no-console
  }

  error(...args) {
    console.error(...args); // eslint-disable-line no-console
  }

  group(...args) {
    console.group(...args); // eslint-disable-line no-console
  }

  groupCollapsed(...args) {
    console.groupCollapsed(...args); // eslint-disable-line no-console
  }

  groupEnd(...args) {
    console.groupEnd(...args); // eslint-disable-line no-console
  }

  log(...args) {
    console.log(...args); // eslint-disable-line no-console
  }
}

class ZaqarAdapter extends Adapter {}

const AVAILABLE_ADAPTERS = {
  console: new ConsoleAdapter(),
  zaqar: new ZaqarAdapter()
};

class Logger {
  AVAILABLE_FUNCTIONS = [
    'debug',
    'info',
    'warn',
    'error',
    'group',
    'groupCollapsed',
    'groupEnd',
    'log'
  ];

  constructor() {
    this.adapters = [];

    if (window.tripleOUiConfig !== undefined) {
      this.loadAdapters();
    }

    this.AVAILABLE_FUNCTIONS.forEach(fn => {
      this[fn] = function(...args) {
        return this.dispatch(fn, ...args);
      };
    });
  }

  loadAdapters() {
    if (this.adapters.length) {
      return;
    }

    if (window.tripleOUiConfig === undefined) {
      return;
    }

    let enabledAdapters = window.tripleOUiConfig.loggers || ['console'];

    enabledAdapters.forEach(adapter => {
      let instance = AVAILABLE_ADAPTERS[adapter];

      if (instance === undefined) {
        throw Error(`Adapter ${adapter} not defined`);
      }

      this.adapters.push(instance);
    });
  }

  dispatch(fn, ...args) {
    this.loadAdapters();

    this.adapters.forEach(adapter => {
      let f = adapter[fn];

      if (f === undefined) {
        throw Error(`Function ${fn} not defined in ${adapter}`);
      }

      return adapter[fn](...args);
    });
  }
}

export default new Logger();
<commit_msg>Add a global error handler<commit_after>// Logger is a `console` replacement which is capable of sending its output to
// multiple places, like the browser console, REST APIs, or a Zaqar queue.
//
// Adding new log destinations consists of creating an adapter.  An adapter in
// this context is a subclass of Adapter.
//
// The Logger class reads the tripleOUiConfig object to determine which logger
// adapters should be activated.  By default, only the console adapter is used.
//
// Usage:
//
//   import logger from 'src/js/services/logger';
//   logger.log('Hello world!');

class Adapter {
  debug(...args) {}

  info(...args) {}

  warn(...args) {}

  error(...args) {}

  group(...args) {}

  groupCollapsed(...args) {}

  groupEnd(...args) {}

  log(...args) {}
}

class ConsoleAdapter extends Adapter {
  debug(...args) {
    console.debug(...args); // eslint-disable-line no-console
  }

  info(...args) {
    console.info(...args); // eslint-disable-line no-console
  }

  warn(...args) {
    console.warn(...args); // eslint-disable-line no-console
  }

  error(...args) {
    console.error(...args); // eslint-disable-line no-console
  }

  group(...args) {
    console.group(...args); // eslint-disable-line no-console
  }

  groupCollapsed(...args) {
    console.groupCollapsed(...args); // eslint-disable-line no-console
  }

  groupEnd(...args) {
    console.groupEnd(...args); // eslint-disable-line no-console
  }

  log(...args) {
    console.log(...args); // eslint-disable-line no-console
  }
}

class ZaqarAdapter extends Adapter {}

const AVAILABLE_ADAPTERS = {
  console: new ConsoleAdapter(),
  zaqar: new ZaqarAdapter()
};

class Logger {
  AVAILABLE_FUNCTIONS = [
    'debug',
    'info',
    'warn',
    'error',
    'group',
    'groupCollapsed',
    'groupEnd',
    'log'
  ];

  constructor() {
    this.adapters = [];

    if (window.tripleOUiConfig !== undefined) {
      this.loadAdapters();
    }

    this.AVAILABLE_FUNCTIONS.forEach(fn => {
      this[fn] = function(...args) {
        return this.dispatch(fn, ...args);
      };
    });

    this.registerGlobalErrorHandler();
  }

  loadAdapters() {
    if (this.adapters.length) {
      return;
    }

    if (window.tripleOUiConfig === undefined) {
      return;
    }

    let enabledAdapters = window.tripleOUiConfig.loggers || ['console'];

    enabledAdapters.forEach(adapter => {
      let instance = AVAILABLE_ADAPTERS[adapter];

      if (instance === undefined) {
        throw Error(`Adapter ${adapter} not defined`);
      }

      this.adapters.push(instance);
    });
  }

  dispatch(fn, ...args) {
    this.loadAdapters();

    this.adapters.forEach(adapter => {
      let f = adapter[fn];

      if (f === undefined) {
        throw Error(`Function ${fn} not defined in ${adapter}`);
      }

      return adapter[fn](...args);
    });
  }

  registerGlobalErrorHandler() {
    window.onerror = (messageOrEvent, source, lineno, colno, e) => {
      this.error(e);
      return true;
    };
  }
}

export default new Logger();
<|endoftext|>


<commit_before>import io from 'socket.io-client'

const socket = io({
  'reconnection': true,
  'reconnectionDelay': 1000,
  'reconnectionDelayMax': 5000,
  'reconnectionAttempts': 10
})

export default socket
<commit_msg>Ping the websocket server to prevent timeouts.<commit_after>import io from 'socket.io-client'

const socket = io({
  'reconnection': true,
  'reconnectionDelay': 1000,
  'reconnectionDelayMax': 5000,
  'reconnectionAttempts': 10
})

setInterval(() => {
  if (socket.connected) {
    socket.emit('ping')
  }
}, 5000)

export default socket
<|endoftext|>


<commit_before>var slimScroller = {
    scroll: function (target, horizontal, time, callback) {
        'use strict';

        var easeInOutCubic = function (time) {
            return (time < 0.5) ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1;
        };
        var position = function (start, end, elapsed, duration) {
            return (elapsed > duration) ? end : start + (end - start) * easeInOutCubic(elapsed / duration);
        };

        var duration = time || 300;
        var start = (horizontal) ? window.pageXOffset : window.pageYOffset;
        var total = (horizontal) ? document.body.offsetWidth - window.innerWidth : document.body.offsetHeight - window.innerHeight;

        var targetPosition = (horizontal) ? document.querySelector(target).offsetLeft : document.querySelector(target).offsetTop;

        var clock = Date.now();
        var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;

        var step = function () {
            var elapsed = Date.now() - clock;
            var finalPosition = position(start, targetPosition, elapsed, duration);
            if(horizontal){window.scroll(finalPosition, 0);}else{window.scroll(0, finalPosition);}

            if (elapsed > duration) {
                if (callback) {
                    callback(window.scrollY);
                }
            } else {
                requestAnimationFrame(step);
            }
        };
        step();
    }
};<commit_msg>Recreated small version<commit_after>var slimScroller = function (){
    'use strict';

    var targetPosition,horizontal,clock,elapsed,duration,startPosition,total,callback;
    var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;

    var scroll = function (target, horizontal, durationTime, callbackFunction) {
        duration = durationTime || 500;
        horizontal = horizontal || false;
        callback = callbackFunction || false;
        startPosition = (horizontal) ? window.pageXOffset : window.pageYOffset;
        total = (horizontal) ? document.body.offsetWidth - window.innerWidth : document.body.offsetHeight - window.innerHeight;
        targetPosition = (target > total) ? total : target;

        clock = Date.now();
        step();
    };
    var easeInOutCubic = function (time) {
        return (time < 0.5) ? 4 * time * time * time : (time - 1) * (2 * time - 2) * (2 * time - 2) + 1;
    };
    var calculatePosition = function () {
        return (elapsed > duration) ? targetPosition : startPosition + (targetPosition - startPosition) * easeInOutCubic(elapsed / duration);
    };
    var step = function () {
        elapsed = Date.now() - clock;
        var stepPosition = calculatePosition();

        if (horizontal){
            window.scroll(stepPosition, 0);
        }else{
            window.scroll(0, stepPosition);
        }
        if (elapsed > duration) {
            if (callback) {
                callback([window.scrollX,window.scrollY]);
            }
        } else {
            requestAnimationFrame(step);
        }
    };
    return {
        scroll: scroll
    };
}();<|endoftext|>


<commit_before>var uuid = require('node-uuid')
  , models = require('../models')
  , async = require('async')
  , util = require('util')
  , crypto = require('crypto')

exports.addModel = function(db) {
  var statisticsSerializer = {
    select: ['userId', 'posts', 'likes', 'discussions', 'subscribers', 'subscriptions']
  }

  function Group(params) {
//    Group.super_.call(this, params);
    this.id = params.id
    this.username = params.username
    this.createdAt = params.createdAt
    this.updatedAt = params.updatedAt
    this.admins = params.admins
    this.type = "group"
  }

  util.inherits(Group, models.User)

  Group.getAttributes = function() {
    return ['id', 'username', 'subscribers', 'createdAt', 'updatedAt', 'admins', 'type']
  }

  Group.findById = function(groupId, callback) {
    db.hgetall('user:' + groupId, function(err, attrs) {
      if (attrs === null)
        return callback(1, null)

      attrs.id = groupId
      var newGroup = new Group(attrs)

      callback(err, newGroup)
    })
  }

  Group.destroy = function(groupId, callback) {
    var destroyAllPosts = function(group, callback) {
      group.getPostsTimeline({start: 0}, function(err, timeline) {
        if (err)
          return callback(err)

        var deletePartOfPosts = function(start, count) {
          timeline.getPostsIds(start, count, function(err, postsIds) {
            if (err)
              return callback(err)

            async.forEach(postsIds, function(postId, done) {
                models.Post.destroy(postId, function(err) {
                  done(err)
                })
              },
              function(err) {
                if (postsIds.length < 25)
                  return callback(err)

                deletePartOfPosts(start + count, count)
              })
          })
        }(0, 25)
      })
    }

    var unsubscribeAllUsers = function(group, callback) {
      group.getPostsTimeline({start: 0}, function(err, timeline) {
        if (err)
          return callback(err)

        timeline.getSubscribers(function(err, subscribers) {
          if (err)
            return callback(err)

          async.forEach(subscribers, function(subscriber, done) {
              subscriber.unsubscribeTo(timeline.id, function(err) {
                done(err)
              })
            },
            function(err) {
              callback(err)
            })
        })
      })
    }

    var destroyAllTimelines = function(group, callback) {
      group.getTimelinesIds(function(err, timelinesIds) {
        if (err)
          return callback(err)

        var ids = []
        for (var id in timelinesIds) {
          ids.push(timelinesIds[id])
        }

        async.forEach(ids, function(timelineId, done) {
          db.keys('timeline:' + timelineId + '*', function(err, keys) {
            async.forEach(keys, function(key, done) {
              db.del(key, function(err, res) {
                done(err)
              })
            }, function(err) {
              done(err)
            })
          })
        }, function(err) {
          callback(err)
        })
      })
    }

    var deleteStats = function(group, callback) {
      db.del('stats:' + group.id, function(err, res) {
        callback(err)
      })
    }

    var deleteGroup = function(group, callback) {
      async.parallel([
        function(done) {
          db.keys('user:' + group.id + '*', function(err, keys) {
            async.forEach(keys, function(key, done) {
              db.del(key, function(err, res) {
                done(err)
              })
            },
            function(err) {
              done(err)
            })
          })
        },
        function(done) {
          db.del('username:' + group.username + ':uid', function(err, res) {
            done(err)
          })
        }],
        function(err) {
          callback(err)
        })
    }

    models.User.findById(groupId, function(err, group) {
      if (err)
        return callback(err)

      unsubscribeAllUsers(group, function(err) {
        if (err)
          return callback(err)

        destroyAllPosts(group, function(err) {
          if (err)
            return callback(err)

          async.parallel([
            function(done) {
              destroyAllTimelines(group, function(err) {
                if (err)
                  return done(err)

                deleteGroup(group, done)
              })
          },
          function(done) {
            deleteStats(group, done)
          }],
          function(err) {
            callback(err)
          })
        })
      })
    })
  }

  Group.prototype.validate = function(callback) {
    var that = this

    // TODO: review this code
    async.parallel([
      function(done) {
        db.exists('user:' + this.id, function(err, groupExists) {
          done(err, groupExists === 0 &&
                   that.username && that.username.length > 1)
        })
      },
      function(done) {
        db.exists('username:' + that.username + ':uid', function(err, usernameExists) {
          done(err, usernameExists === 0 &&
            that.username && that.username.length > 1)
        })
      }],
      function(err, res) {
        callback(res.indexOf(false) == -1)
      })
  }

  // TODO: review design on passing ownerId as a parameter
  Group.prototype.create = function(ownerId, callback) {
    var that = this

    this.createdAt = new Date().getTime()
    this.updatedAt = new Date().getTime()
    this.id = uuid.v4()

    var subscribeOwner = function(callback) {
      models.FeedFactory.findById(that.id, function(err, groupFeed) {
        groupFeed.getPostsTimelineId(function(err, timelineId) {
          models.FeedFactory.findById(ownerId, function(err, ownerFeed) {
            ownerFeed.subscribeTo(timelineId, function(err, res) {
              callback(err, res)
            })
          })
        })
      })
    }

    this.validate(function(valid) {
      if (!valid)
        return callback(1, that)

      db.exists('user:' + that.id, function(err, res) {
        if (res !== 0)
          return callback(err, res)

        async.parallel([
          function(done) {
            db.hmset('user:' + that.id,
              { 'username': that.username,
                'createdAt': that.createdAt.toString(),
                'updatedAt': that.updatedAt.toString(),
                'type': that.type
              }, function(err, res) {
                  done(err, res)
              })
          },
          function(done) {
            db.set('username:' + that.username + ':uid', that.id, function(err, res) {
              done(err, res)
            })
          },
          function(done) {
            var stats = new models.Stats({
              userId: that.id
            })
            stats.create(function(err, stats) {
              done(err, stats)
            })
          },
          function(done) {
            db.zadd('user:' + that.id + ':administrators', new Date().getTime().toString(), ownerId, function(err, res) {
              done(err, res)
            })
          }
        ], function(err, res) {
          subscribeOwner(function(err, res) {
            callback(err, that)
          })
        })
      })
    })
  }

  Group.prototype.update = function(callback) {
    var that = this

    this.updatedAt = new Date().getTime()

    this.validate(function(valid) {
      if (!valid)
        return callback(1, that)

      db.exists('user:' + that.id, function(err, res) {
        if (res !== 1)
          return callback(err, res)

        db.hmset('user:' + that.id,
          { 'username': that.username,
            'updatedAt': that.updatedAt.toString()
          }, function(err, res) {
            if (err)
              return callback(err, that)

            callback(null, that)
          })
      })
    })
  }

  Group.prototype.getAdministratorsIds = function(callback) {
    var that = this

    db.zrevrange('user:' + that.id + ':administrators', 0, -1, function(err, res) {
      callback(err, res)
    })
  }

  Group.prototype.addAdministrator = function(feedId, callback) {
    var that = this

    db.zadd('user:' + that.id + ':administrators', new Date().getTime().toString(), feedId, function(err, res) {
      callback(err, res)
    })
  }

  Group.prototype.removeAdministrator = function(feedId, callback) {
    var that = this

    that.getAdministratorsIds(function(err, administratorsIds) {
      if (err)
        return callback(err)

      if (administratorsIds.indexOf(feedId) == -1)
        return callback(null)

      if (administratorsIds.length === 1)
        return callback(1)

      db.zrem('user:' + that.id + ':administrators', feedId, function(err, res) {
        callback(err, res)
      })
    })
  }

  Group.prototype.toJSON = function(params, callback) {
    var that = this
      , json = {}
      , select = params.select ||
        models.Group.getAttributes()

    var returnJSON = function(err) {
      var isReady = true
      if(select.indexOf('admins') != -1) {
        isReady = isReady && json.admins !== undefined
      }
      if(select.indexOf('statistics') != -1) {
        isReady = isReady && json.statistics !== undefined
      }
      if(select.indexOf('subscribers') != -1) {
        isReady = isReady && json.subscribers !== undefined
      }

      if(isReady) {
        callback(err, json)
      }
    }

    if (select.indexOf('id') != -1)
      json.id = that.id

    if (select.indexOf('username') != -1)
      json.username = that.username

    if (select.indexOf('createdAt') != -1)
      json.createdAt = that.createdAt

    if (select.indexOf('updatedAt') != -1)
      json.updatedAt = that.updatedAt

    if (select.indexOf('type') != -1)
      json.type = that.type

    if (select.indexOf('admins') != -1) {
      that.getAdministratorsIds(function(err, administratorsIds) {
        async.map(administratorsIds, function(administratorId, callback) {
          models.FeedFactory.findById(administratorId, function(err, user) {
            user.toJSON({ select: ['id', 'username'] }, function(err, json) {
              callback(err, json)
            })
          })
        }, function(err, administratorsJSON) {
          json.admins = administratorsJSON

          returnJSON(err)
        })
      })
    }

    if (select.indexOf('statistics') != -1) {
      models.Stats.findByUserId(that.id, function(err, stats) {
        if (stats) {
          stats.toJSON(statisticsSerializer, function(err, statistics) {
            json.statistics = statistics
            returnJSON(err)
          })
        } else {
          json.statistics = null
          returnJSON(null)
        }
      })
    }

    if (select.indexOf('subscribers') != -1) {
      var subscribersIds = []
      var subscribersJSON = []
      that.getTimelines( {start: 0}, function(err, timelines) {
        async.forEach(timelines, function(timeline, done) {
            timeline.getSubscribers(function(err, subscribers) {
              async.forEach(subscribers, function(subscriber, done) {
                  if (subscribersIds.indexOf(subscriber.id) != -1)
                    return done(null)

                  subscribersIds.push(subscriber.id)
                  subscriber.toJSON(params.subscribers || {},function(err, subscriberJSON) {
                    subscribersJSON.push(subscriberJSON)
                    done(err)
                  })
                },
                function(err) {
                  done(err)
                })
            })
          },
          function(err) {
            json.subscribers = subscribersJSON
            returnJSON(err)
          })
      })
    }

    returnJSON(null)
  }

  return Group;
}
<commit_msg>Skips json generation for broken administrators.<commit_after>var uuid = require('node-uuid')
  , models = require('../models')
  , async = require('async')
  , util = require('util')
  , crypto = require('crypto')

exports.addModel = function(db) {
  var statisticsSerializer = {
    select: ['userId', 'posts', 'likes', 'discussions', 'subscribers', 'subscriptions']
  }

  function Group(params) {
//    Group.super_.call(this, params);
    this.id = params.id
    this.username = params.username
    this.createdAt = params.createdAt
    this.updatedAt = params.updatedAt
    this.admins = params.admins
    this.type = "group"
  }

  util.inherits(Group, models.User)

  Group.getAttributes = function() {
    return ['id', 'username', 'subscribers', 'createdAt', 'updatedAt', 'admins', 'type']
  }

  Group.findById = function(groupId, callback) {
    db.hgetall('user:' + groupId, function(err, attrs) {
      if (attrs === null)
        return callback(1, null)

      attrs.id = groupId
      var newGroup = new Group(attrs)

      callback(err, newGroup)
    })
  }

  Group.destroy = function(groupId, callback) {
    var destroyAllPosts = function(group, callback) {
      group.getPostsTimeline({start: 0}, function(err, timeline) {
        if (err)
          return callback(err)

        var deletePartOfPosts = function(start, count) {
          timeline.getPostsIds(start, count, function(err, postsIds) {
            if (err)
              return callback(err)

            async.forEach(postsIds, function(postId, done) {
                models.Post.destroy(postId, function(err) {
                  done(err)
                })
              },
              function(err) {
                if (postsIds.length < 25)
                  return callback(err)

                deletePartOfPosts(start + count, count)
              })
          })
        }(0, 25)
      })
    }

    var unsubscribeAllUsers = function(group, callback) {
      group.getPostsTimeline({start: 0}, function(err, timeline) {
        if (err)
          return callback(err)

        timeline.getSubscribers(function(err, subscribers) {
          if (err)
            return callback(err)

          async.forEach(subscribers, function(subscriber, done) {
              subscriber.unsubscribeTo(timeline.id, function(err) {
                done(err)
              })
            },
            function(err) {
              callback(err)
            })
        })
      })
    }

    var destroyAllTimelines = function(group, callback) {
      group.getTimelinesIds(function(err, timelinesIds) {
        if (err)
          return callback(err)

        var ids = []
        for (var id in timelinesIds) {
          ids.push(timelinesIds[id])
        }

        async.forEach(ids, function(timelineId, done) {
          db.keys('timeline:' + timelineId + '*', function(err, keys) {
            async.forEach(keys, function(key, done) {
              db.del(key, function(err, res) {
                done(err)
              })
            }, function(err) {
              done(err)
            })
          })
        }, function(err) {
          callback(err)
        })
      })
    }

    var deleteStats = function(group, callback) {
      db.del('stats:' + group.id, function(err, res) {
        callback(err)
      })
    }

    var deleteGroup = function(group, callback) {
      async.parallel([
        function(done) {
          db.keys('user:' + group.id + '*', function(err, keys) {
            async.forEach(keys, function(key, done) {
              db.del(key, function(err, res) {
                done(err)
              })
            },
            function(err) {
              done(err)
            })
          })
        },
        function(done) {
          db.del('username:' + group.username + ':uid', function(err, res) {
            done(err)
          })
        }],
        function(err) {
          callback(err)
        })
    }

    models.User.findById(groupId, function(err, group) {
      if (err)
        return callback(err)

      unsubscribeAllUsers(group, function(err) {
        if (err)
          return callback(err)

        destroyAllPosts(group, function(err) {
          if (err)
            return callback(err)

          async.parallel([
            function(done) {
              destroyAllTimelines(group, function(err) {
                if (err)
                  return done(err)

                deleteGroup(group, done)
              })
          },
          function(done) {
            deleteStats(group, done)
          }],
          function(err) {
            callback(err)
          })
        })
      })
    })
  }

  Group.prototype.validate = function(callback) {
    var that = this

    // TODO: review this code
    async.parallel([
      function(done) {
        db.exists('user:' + this.id, function(err, groupExists) {
          done(err, groupExists === 0 &&
                   that.username && that.username.length > 1)
        })
      },
      function(done) {
        db.exists('username:' + that.username + ':uid', function(err, usernameExists) {
          done(err, usernameExists === 0 &&
            that.username && that.username.length > 1)
        })
      }],
      function(err, res) {
        callback(res.indexOf(false) == -1)
      })
  }

  // TODO: review design on passing ownerId as a parameter
  Group.prototype.create = function(ownerId, callback) {
    var that = this

    this.createdAt = new Date().getTime()
    this.updatedAt = new Date().getTime()
    this.id = uuid.v4()

    var subscribeOwner = function(callback) {
      models.FeedFactory.findById(that.id, function(err, groupFeed) {
        groupFeed.getPostsTimelineId(function(err, timelineId) {
          models.FeedFactory.findById(ownerId, function(err, ownerFeed) {
            ownerFeed.subscribeTo(timelineId, function(err, res) {
              callback(err, res)
            })
          })
        })
      })
    }

    this.validate(function(valid) {
      if (!valid)
        return callback(1, that)

      db.exists('user:' + that.id, function(err, res) {
        if (res !== 0)
          return callback(err, res)

        async.parallel([
          function(done) {
            db.hmset('user:' + that.id,
              { 'username': that.username,
                'createdAt': that.createdAt.toString(),
                'updatedAt': that.updatedAt.toString(),
                'type': that.type
              }, function(err, res) {
                  done(err, res)
              })
          },
          function(done) {
            db.set('username:' + that.username + ':uid', that.id, function(err, res) {
              done(err, res)
            })
          },
          function(done) {
            var stats = new models.Stats({
              userId: that.id
            })
            stats.create(function(err, stats) {
              done(err, stats)
            })
          },
          function(done) {
            db.zadd('user:' + that.id + ':administrators', new Date().getTime().toString(), ownerId, function(err, res) {
              done(err, res)
            })
          }
        ], function(err, res) {
          subscribeOwner(function(err, res) {
            callback(err, that)
          })
        })
      })
    })
  }

  Group.prototype.update = function(callback) {
    var that = this

    this.updatedAt = new Date().getTime()

    this.validate(function(valid) {
      if (!valid)
        return callback(1, that)

      db.exists('user:' + that.id, function(err, res) {
        if (res !== 1)
          return callback(err, res)

        db.hmset('user:' + that.id,
          { 'username': that.username,
            'updatedAt': that.updatedAt.toString()
          }, function(err, res) {
            if (err)
              return callback(err, that)

            callback(null, that)
          })
      })
    })
  }

  Group.prototype.getAdministratorsIds = function(callback) {
    var that = this

    db.zrevrange('user:' + that.id + ':administrators', 0, -1, function(err, res) {
      callback(err, res)
    })
  }

  Group.prototype.addAdministrator = function(feedId, callback) {
    var that = this

    db.zadd('user:' + that.id + ':administrators', new Date().getTime().toString(), feedId, function(err, res) {
      callback(err, res)
    })
  }

  Group.prototype.removeAdministrator = function(feedId, callback) {
    var that = this

    that.getAdministratorsIds(function(err, administratorsIds) {
      if (err)
        return callback(err)

      if (administratorsIds.indexOf(feedId) == -1)
        return callback(null)

      if (administratorsIds.length === 1)
        return callback(1)

      db.zrem('user:' + that.id + ':administrators', feedId, function(err, res) {
        callback(err, res)
      })
    })
  }

  Group.prototype.toJSON = function(params, callback) {
    var that = this
      , json = {}
      , select = params.select ||
        models.Group.getAttributes()

    var returnJSON = function(err) {
      var isReady = true
      if(select.indexOf('admins') != -1) {
        isReady = isReady && json.admins !== undefined
      }
      if(select.indexOf('statistics') != -1) {
        isReady = isReady && json.statistics !== undefined
      }
      if(select.indexOf('subscribers') != -1) {
        isReady = isReady && json.subscribers !== undefined
      }

      if(isReady) {
        callback(err, json)
      }
    }

    if (select.indexOf('id') != -1)
      json.id = that.id

    if (select.indexOf('username') != -1)
      json.username = that.username

    if (select.indexOf('createdAt') != -1)
      json.createdAt = that.createdAt

    if (select.indexOf('updatedAt') != -1)
      json.updatedAt = that.updatedAt

    if (select.indexOf('type') != -1)
      json.type = that.type

    if (select.indexOf('admins') != -1) {
      that.getAdministratorsIds(function(err, administratorsIds) {
        async.map(administratorsIds, function(administratorId, callback) {
          models.FeedFactory.findById(administratorId, function(err, user) {
            if (!user) return callback(1, null)

            user.toJSON({ select: ['id', 'username'] }, function(err, json) {
              callback(err, json)
            })
          })
        }, function(err, administratorsJSON) {
          json.admins = administratorsJSON

          returnJSON(err)
        })
      })
    }

    if (select.indexOf('statistics') != -1) {
      models.Stats.findByUserId(that.id, function(err, stats) {
        if (stats) {
          stats.toJSON(statisticsSerializer, function(err, statistics) {
            json.statistics = statistics
            returnJSON(err)
          })
        } else {
          json.statistics = null
          returnJSON(null)
        }
      })
    }

    if (select.indexOf('subscribers') != -1) {
      var subscribersIds = []
      var subscribersJSON = []
      that.getTimelines( {start: 0}, function(err, timelines) {
        async.forEach(timelines, function(timeline, done) {
            timeline.getSubscribers(function(err, subscribers) {
              async.forEach(subscribers, function(subscriber, done) {
                  if (subscribersIds.indexOf(subscriber.id) != -1)
                    return done(null)

                  subscribersIds.push(subscriber.id)
                  subscriber.toJSON(params.subscribers || {},function(err, subscriberJSON) {
                    subscribersJSON.push(subscriberJSON)
                    done(err)
                  })
                },
                function(err) {
                  done(err)
                })
            })
          },
          function(err) {
            json.subscribers = subscribersJSON
            returnJSON(err)
          })
      })
    }

    returnJSON(null)
  }

  return Group;
}
<|endoftext|>