<commit_before>editor = {
    feature_types: {},
    feature_types_order: [],
    _highlight_layer: null,

    init: function () {
        // Init Map
        editor.map = L.map('map', {
            zoom: 2,
            maxZoom: 10,
            minZoom: 1,
            crs: L.CRS.Simple,
            editable: true,
            closePopupOnClick: false
        });

        L.control.scale({imperial: false}).addTo(editor.map);
        editor._highlight_layer = L.layerGroup().addTo(editor.map);

        $('#show_map').click(function() {
            $('body').removeClass('controls');
        });
        $('#show_details').click(function() {
            $('body').addClass('controls');
        });

        editor.get_feature_types();
        editor.get_packages();
        editor.get_sources();
    },

    get_feature_types: function () {
        $.getJSON('/api/featuretypes/', function (feature_types) {
            var feature_type;
            var editcontrols = $('#mapeditlist');
            for (var i = 0; i < feature_types.length; i++) {
                feature_type = feature_types[i];
                editor.feature_types[feature_type.name] = feature_type;
                feature_type.weight = 1;
                feature_type.fillOpacity = 0.6;
                editor.feature_types_order.push(feature_type.name);
                editcontrols.append(
                    $('<fieldset class="feature_list">').attr('name', feature_type.name).append(
                        $('<legend>').text(feature_type.title_plural).append(
                            $('<button class="btn btn-default btn-xs pull-right start-drawing"></button>')
                        )
                    )
                );
            }
            editor.get_levels();
        });
    },

    packages: {},
    get_packages: function () {
        $.getJSON('/api/packages/', function (packages) {
            var bounds = [[0, 0], [0, 0]];
            var pkg;
            for (var i = 0; i < packages.length; i++) {
                pkg = packages[i];
                editor.packages[pkg.name] = pkg;
                if (pkg.bounds === null) continue;
                bounds = [[Math.min(bounds[0][0], pkg.bounds[0][0]), Math.min(bounds[0][1], pkg.bounds[0][1])],
                    [Math.max(bounds[1][0], pkg.bounds[1][0]), Math.max(bounds[1][1], pkg.bounds[1][1])]];
            }
            editor.map.setMaxBounds(bounds);
            editor.map.fitBounds(bounds, {padding: [30, 50]});
        });
    },

    sources: {},
    get_sources: function () {
        $.getJSON('/api/sources/', function (sources) {
            var layers = {};
            var source;
            for (var i = 0; i < sources.length; i++) {
                source = sources[i];
                editor.sources[source.name] = source;
                source.layer = L.imageOverlay('/api/sources/' + source.name + '/image/', source.bounds);
                layers[source.name] = source.layer;
            }
            L.control.layers([], layers).addTo(editor.map);
        });
    },

    levels: {},
    _level: null,
    level_feature_layers: {},
    get_levels: function () {
        $.getJSON('/api/levels/?ordering=-altitude', function (levels) {
            L.LevelControl = L.Control.extend({
                options: {
                    position: 'bottomright'
                },
                onAdd: function () {
                    var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-levels'), link;
                    var level;
                    for (var i = 0; i < levels.length; i++) {
                        level = levels[i];
                        link = L.DomUtil.create('a', (i == levels.length - 1) ? 'current' : '', container);
                        link.name = level.name;
                        link.innerHTML = level.name;
                        link.href = '';
                    }
                    return container;
                }
            });
            editor.map.addControl(new L.LevelControl());

            $('.leaflet-levels').on('click', 'a', function (e) {
                e.preventDefault();
                editor.set_current_level($(this).attr('name'));
            });

            var level, feature_type;
            for (var i = 0; i < levels.length; i++) {
                level = levels[i];
                editor.levels[level.name] = level;
                editor.level_feature_layers[level.name] = {};
                for (var j = 0; j < editor.feature_types_order.length; j++) {
                    feature_type = editor.feature_types_order[j];
                    editor.level_feature_layers[level.name][feature_type] = L.layerGroup();
                    $('.feature_list[name='+feature_type+']').append(
                        $('
').attr('data-level', level.name)
                    );
                }
            }
            editor.set_current_level(levels[levels.length - 1].name);
            editor.init_features();
        });
    },
    set_current_level: function(level_name) {
        if (editor._creating !== null || editor._editing !== null) return;
        for (var i = 0; i < editor.feature_types_order.length; i++) {
            if (editor._level !== null) {
                editor.level_feature_layers[editor._level][editor.feature_types_order[i]].remove();
            }
            editor.level_feature_layers[level_name][editor.feature_types_order[i]].addTo(editor.map);
        }
        editor._level = level_name;
        $('.leaflet-levels .current').removeClass('current');
        $('.leaflet-levels a[name='+level_name+']').addClass('current');
        $('.feature_level_list').hide();
        $('.feature_level_list[data-level='+level_name+']').show();
    },

    _creating: null,
    _editing: null,
    init_features: function () {
        // Add drawing new features
        L.DrawControl = L.Control.extend({
            options: {
                position: 'topleft'
            },
            onAdd: function () {
                var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-drawbar');
                $('').appendTo(container).text('cancel').attr({
                    href: '#',
                    title: 'cancel drawing',
                    name: ''
                }).on('click', function (e) {
                    e.preventDefault();
                    editor.cancel_creating();
                });
                return container;
            }
        });
        editor.map.addControl(new L.DrawControl());

        $('#mapeditlist').on('click', '.start-drawing', editor._click_start_drawing)
                         .on('mouseenter', '.feature_level_list li', editor._hover_feature_detail)
                         .on('mouseleave', '.feature_level_list li', editor._unhover_feature_detail)
                         .on('click', '.feature_level_list li', editor._click_feature_detail);

        editor.map.on('editable:drawing:commit', editor.done_creating);
        editor.map.on('editable:editing', editor.update_editing);
        editor.map.on('editable:drawing:cancel', editor._canceled_creating);

        $('#mapeditdetail').on('click', '#btn_editing_cancel', editor.cancel_editing)
                           .on('click', 'button[type=submit]', editor.submit_editing_btn_click)
                           .on('submit', 'form', editor.submit_editing)

        editor.get_features();
    },

    features: {},
    get_features: function () {
        $.getJSON('/api/features/', function(all_features) {
            $('.feature_level_list li').remove();
            var feature_type, features, feature, layergroup;
            for (var j = 0; j < editor.feature_types_order.length; j++) {
                feature_type = editor.feature_types_order[j];
                for (var level in editor.levels) {
                    editor.level_feature_layers[level][feature_type].clearLayers();
                }
                features = all_features[editor.feature_types[feature_type].endpoint]

                for (var i = 0; i < features.length; i++) {
                    feature = features[i];
                    console.log(feature);
                    layergroup = L.geoJSON({
                        type: 'Feature',
                        geometry: feature.geometry,
                        properties: {
                            name: feature.name,
                            feature_type: feature_type
                        }
                    }, {
                        style: editor._get_feature_style
                    }).on('mouseover', editor._hover_feature_layer)
                      .on('mouseout', editor._unhover_feature_layer)
                      .on('click', editor._click_feature_layer)
                      .addTo(editor.level_feature_layers[feature.level][feature_type]);
                    feature.layer = layergroup.getLayers()[0];
                    editor.features[feature.name] = feature;

                    $('.feature_list[name=' + feature_type + '] > [data-level=' + feature.level + ']').append(
                        $('
').attr('name', feature.name).append(
                            $('
').text(feature.title).append(' ').append(
                                $('').text(feature.name)
                            )
                        )
                    );
                }
            }
            $('.start-drawing').show();
            $('#mapeditcontrols').addClass('list');
            editor.set_current_level(editor._level);
        });
    },
    _get_feature_style: function (feature) {
        return editor.feature_types[feature.properties.feature_type];
    },

    _click_start_drawing: function (e) {
        editor.start_creating($(this).closest('fieldset').attr('name'));
    },
    _hover_feature_detail: function (e) {
        editor._highlight_layer.clearLayers();
        L.geoJSON(editor.features[$(this).attr('name')].geometry, {
            style: function() {
                return {
                    color: '#FFFFDD',
                    weight: 3,
                    opacity: 0.7,
                    fillOpacity: 0,
                    className: 'c3nav-highlight'
                };
            }
        }).addTo(editor._highlight_layer);
    },
    _unhover_feature_detail: function () {
        editor._highlight_layer.clearLayers();
    },
    _click_feature_detail: function() {
        editor.start_editing($(this).attr('name'));
    },

    _hover_feature_layer: function (e) {
        editor._unhover_feature_layer();
        if (editor._editing === null && editor._creating === null) {
            editor._highlight_layer.clearLayers();
            L.geoJSON(e.layer.toGeoJSON(), {
                style: function() {
                    return {
                        color: '#FFFFDD',
                        weight: 3,
                        opacity: 0.7,
                        fillOpacity: 0,
                        className: 'c3nav-highlight'
                    };
                }
            }).addTo(editor._highlight_layer);
        }
        $('.feature_list li[name='+e.layer.feature.properties.name+']').addClass('hover');
    },
    _unhover_feature_layer: function (e) {
        editor._highlight_layer.clearLayers();
        $('.feature_list .hover').removeClass('hover');
    },
    _click_feature_layer: function(e) {
        editor.start_editing(e.layer.feature.properties.name);
        if ((e.originalEvent.ctrlKey || e.originalEvent.metaKey) && this.editEnabled()) {
            if (e.layer.feature.properties.geomtype == 'polygon') {
                this.editor.newHole(e.latlng);
            }
        }
    },

    start_creating: function (feature_type) {
        if (editor._creating !== null || editor._editing !== null) return;
        editor._highlight_layer.clearLayers();
        editor._creating = feature_type;
        var options = editor.feature_types[feature_type];
        if (options.geomtype == 'polygon') {
            editor.map.editTools.startPolygon(null, options);
        } else if (options.geomtype == 'polyline') {
            editor.map.editTools.startPolyline(null, options);
        }
        $('.leaflet-drawbar').show();
        $('.start-drawing').hide();
        $('body').removeClass('controls');
    },
    cancel_creating: function () {
        if (editor._creating === null || editor._editing !== null) return;
        editor.map.editTools.stopDrawing();
        editor._creating = null;
        $('.leaflet-drawbar').hide();
    },
    _canceled_creating: function (e) {
        if (editor._creating !== null && editor._editing === null) {
            e.layer.remove();
            $('.start-drawing').show();
        }
    },
    done_creating: function(e) {
        if (editor._creating !== null && editor._editing === null) {
            editor._editing = e.layer;
            editor._editing.disableEdit();
            editor.map.fitBounds(editor._editing.getBounds());

            $('.leaflet-drawbar').hide();
            var path = '/editor/features/' + editor._creating + '/add/';
            $('#mapeditcontrols').removeClass('list');
            $('body').addClass('controls');
            $('#mapeditdetail').load(path, editor.edit_form_loaded);
        }
    },

    start_editing: function (name) {
        if (editor._creating !== null || editor._editing !== null) return;
        editor._highlight_layer.clearLayers();
        editor._editing = editor.features[name].layer;
        var path = '/editor/features/'+ editor._editing.feature.properties.feature_type +'/edit/' + name + '/';
        $('#mapeditcontrols').removeClass('list');
        $('#mapeditdetail').load(path, editor.edit_form_loaded);
        $('body').addClass('controls');
    },
    edit_form_loaded: function() {
        $('#mapeditcontrols').addClass('detail');
        $('#id_level').val(editor._level);
        if ($('#id_geometry').length) {
            $('#id_geometry').val(JSON.stringify(editor._editing.toGeoJSON().geometry));
            editor._editing.enableEdit();
        }
        if (editor._editing.options.geomtype == 'polygon') {
            editor._editing.on('click', function (e) {
                if ((e.originalEvent.ctrlKey || e.originalEvent.metaKey) && this.editEnabled()) {
                    this.editor.newHole(e.latlng);
                }
            });
        }
    },
    update_editing: function () {
        if (editor._editing !== null) {
            $('#id_geometry').val(JSON.stringify(editor._editing.toGeoJSON().geometry));
        }
    },
    cancel_editing: function() {
        if (editor._editing !== null) {
            if (editor._creating !== null) {
                editor._editing.remove();
            }
            editor._editing = null;
            editor._creating = null;
            $('#mapeditcontrols').removeClass('detail');
            $('#mapeditdetail').html('');
            editor.get_features();
        }
    },
    submit_editing_btn_click: function(e) {
        $(this).closest('form').data('btn', $(this)).clearQueue().delay(300).queue(function() {
            $(this).data('button', null);
        });
    },
    submit_editing: function(e) {
        if ($(this).attr('name') == 'redirect') return;
        e.preventDefault();
        var data = $(this).serialize();
        var btn = $(this).data('btn');
        if (btn !== undefined && btn !== null && $(btn).is('[name]')) {
            data += '&'+$('<input>').attr('name', $(btn).attr('name')).val($(btn).val()).serialize();
        }
        var action = $(this).attr('action');
        $('#mapeditcontrols').removeClass('detail');
        $('#mapeditdetail').html('');
        editor._editing.disableEdit();
        $.post(action, data, function (data) {
            var content = $(data);
            if ($('
').append(content).find('form').length > 0) {
                $('#mapeditcontrols').addClass('detail');
                $('#mapeditdetail').html(content).find('form[name=redirect]').submit();
                if ($('#id_geometry').length) {
                    editor._editing.enableEdit();
                }
            } else {
                if (editor._creating !== null) {
                    editor._editing.remove();
                }
                editor._editing = null;
                editor._creating = null;
                editor.get_features();
                $('body').removeClass('controls');
            }
        });
    }
};

if ($('#mapeditlist').length) {
    editor.init();
}
<commit_msg>sort feature by name in editor<commit_after>editor = {
    feature_types: {},
    feature_types_order: [],
    _highlight_layer: null,

    init: function () {
        // Init Map
        editor.map = L.map('map', {
            zoom: 2,
            maxZoom: 10,
            minZoom: 1,
            crs: L.CRS.Simple,
            editable: true,
            closePopupOnClick: false
        });

        L.control.scale({imperial: false}).addTo(editor.map);
        editor._highlight_layer = L.layerGroup().addTo(editor.map);

        $('#show_map').click(function() {
            $('body').removeClass('controls');
        });
        $('#show_details').click(function() {
            $('body').addClass('controls');
        });

        editor.get_feature_types();
        editor.get_packages();
        editor.get_sources();
    },

    get_feature_types: function () {
        $.getJSON('/api/featuretypes/', function (feature_types) {
            var feature_type;
            var editcontrols = $('#mapeditlist');
            for (var i = 0; i < feature_types.length; i++) {
                feature_type = feature_types[i];
                editor.feature_types[feature_type.name] = feature_type;
                feature_type.weight = 1;
                feature_type.fillOpacity = 0.6;
                editor.feature_types_order.push(feature_type.name);
                editcontrols.append(
                    $('<fieldset class="feature_list">').attr('name', feature_type.name).append(
                        $('<legend>').text(feature_type.title_plural).append(
                            $('<button class="btn btn-default btn-xs pull-right start-drawing"></button>')
                        )
                    )
                );
            }
            editor.get_levels();
        });
    },

    packages: {},
    get_packages: function () {
        $.getJSON('/api/packages/', function (packages) {
            var bounds = [[0, 0], [0, 0]];
            var pkg;
            for (var i = 0; i < packages.length; i++) {
                pkg = packages[i];
                editor.packages[pkg.name] = pkg;
                if (pkg.bounds === null) continue;
                bounds = [[Math.min(bounds[0][0], pkg.bounds[0][0]), Math.min(bounds[0][1], pkg.bounds[0][1])],
                    [Math.max(bounds[1][0], pkg.bounds[1][0]), Math.max(bounds[1][1], pkg.bounds[1][1])]];
            }
            editor.map.setMaxBounds(bounds);
            editor.map.fitBounds(bounds, {padding: [30, 50]});
        });
    },

    sources: {},
    get_sources: function () {
        $.getJSON('/api/sources/', function (sources) {
            var layers = {};
            var source;
            for (var i = 0; i < sources.length; i++) {
                source = sources[i];
                editor.sources[source.name] = source;
                source.layer = L.imageOverlay('/api/sources/' + source.name + '/image/', source.bounds);
                layers[source.name] = source.layer;
            }
            L.control.layers([], layers).addTo(editor.map);
        });
    },

    levels: {},
    _level: null,
    level_feature_layers: {},
    get_levels: function () {
        $.getJSON('/api/levels/?ordering=-altitude', function (levels) {
            L.LevelControl = L.Control.extend({
                options: {
                    position: 'bottomright'
                },
                onAdd: function () {
                    var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-levels'), link;
                    var level;
                    for (var i = 0; i < levels.length; i++) {
                        level = levels[i];
                        link = L.DomUtil.create('a', (i == levels.length - 1) ? 'current' : '', container);
                        link.name = level.name;
                        link.innerHTML = level.name;
                        link.href = '';
                    }
                    return container;
                }
            });
            editor.map.addControl(new L.LevelControl());

            $('.leaflet-levels').on('click', 'a', function (e) {
                e.preventDefault();
                editor.set_current_level($(this).attr('name'));
            });

            var level, feature_type;
            for (var i = 0; i < levels.length; i++) {
                level = levels[i];
                editor.levels[level.name] = level;
                editor.level_feature_layers[level.name] = {};
                for (var j = 0; j < editor.feature_types_order.length; j++) {
                    feature_type = editor.feature_types_order[j];
                    editor.level_feature_layers[level.name][feature_type] = L.layerGroup();
                    $('.feature_list[name='+feature_type+']').append(
                        $('
').attr('data-level', level.name)
                    );
                }
            }
            editor.set_current_level(levels[levels.length - 1].name);
            editor.init_features();
        });
    },
    set_current_level: function(level_name) {
        if (editor._creating !== null || editor._editing !== null) return;
        for (var i = 0; i < editor.feature_types_order.length; i++) {
            if (editor._level !== null) {
                editor.level_feature_layers[editor._level][editor.feature_types_order[i]].remove();
            }
            editor.level_feature_layers[level_name][editor.feature_types_order[i]].addTo(editor.map);
        }
        editor._level = level_name;
        $('.leaflet-levels .current').removeClass('current');
        $('.leaflet-levels a[name='+level_name+']').addClass('current');
        $('.feature_level_list').hide();
        $('.feature_level_list[data-level='+level_name+']').show();
    },

    _creating: null,
    _editing: null,
    init_features: function () {
        // Add drawing new features
        L.DrawControl = L.Control.extend({
            options: {
                position: 'topleft'
            },
            onAdd: function () {
                var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-drawbar');
                $('').appendTo(container).text('cancel').attr({
                    href: '#',
                    title: 'cancel drawing',
                    name: ''
                }).on('click', function (e) {
                    e.preventDefault();
                    editor.cancel_creating();
                });
                return container;
            }
        });
        editor.map.addControl(new L.DrawControl());

        $('#mapeditlist').on('click', '.start-drawing', editor._click_start_drawing)
                         .on('mouseenter', '.feature_level_list li', editor._hover_feature_detail)
                         .on('mouseleave', '.feature_level_list li', editor._unhover_feature_detail)
                         .on('click', '.feature_level_list li', editor._click_feature_detail);

        editor.map.on('editable:drawing:commit', editor.done_creating);
        editor.map.on('editable:editing', editor.update_editing);
        editor.map.on('editable:drawing:cancel', editor._canceled_creating);

        $('#mapeditdetail').on('click', '#btn_editing_cancel', editor.cancel_editing)
                           .on('click', 'button[type=submit]', editor.submit_editing_btn_click)
                           .on('submit', 'form', editor.submit_editing)

        editor.get_features();
    },

    features: {},
    get_features: function () {
        $.getJSON('/api/features/?ordering=name', function(all_features) {
            $('.feature_level_list li').remove();
            var feature_type, features, feature, layergroup;
            for (var j = 0; j < editor.feature_types_order.length; j++) {
                feature_type = editor.feature_types_order[j];
                for (var level in editor.levels) {
                    editor.level_feature_layers[level][feature_type].clearLayers();
                }
                features = all_features[editor.feature_types[feature_type].endpoint]

                for (var i = 0; i < features.length; i++) {
                    feature = features[i];
                    console.log(feature);
                    layergroup = L.geoJSON({
                        type: 'Feature',
                        geometry: feature.geometry,
                        properties: {
                            name: feature.name,
                            feature_type: feature_type
                        }
                    }, {
                        style: editor._get_feature_style
                    }).on('mouseover', editor._hover_feature_layer)
                      .on('mouseout', editor._unhover_feature_layer)
                      .on('click', editor._click_feature_layer)
                      .addTo(editor.level_feature_layers[feature.level][feature_type]);
                    feature.layer = layergroup.getLayers()[0];
                    editor.features[feature.name] = feature;

                    $('.feature_list[name=' + feature_type + '] > [data-level=' + feature.level + ']').append(
                        $('
').attr('name', feature.name).append(
                            $('
').text(feature.title).append(' ').append(
                                $('').text(feature.name)
                            )
                        )
                    );
                }
            }
            $('.start-drawing').show();
            $('#mapeditcontrols').addClass('list');
            editor.set_current_level(editor._level);
        });
    },
    _get_feature_style: function (feature) {
        return editor.feature_types[feature.properties.feature_type];
    },

    _click_start_drawing: function (e) {
        editor.start_creating($(this).closest('fieldset').attr('name'));
    },
    _hover_feature_detail: function (e) {
        editor._highlight_layer.clearLayers();
        L.geoJSON(editor.features[$(this).attr('name')].geometry, {
            style: function() {
                return {
                    color: '#FFFFDD',
                    weight: 3,
                    opacity: 0.7,
                    fillOpacity: 0,
                    className: 'c3nav-highlight'
                };
            }
        }).addTo(editor._highlight_layer);
    },
    _unhover_feature_detail: function () {
        editor._highlight_layer.clearLayers();
    },
    _click_feature_detail: function() {
        editor.start_editing($(this).attr('name'));
    },

    _hover_feature_layer: function (e) {
        editor._unhover_feature_layer();
        if (editor._editing === null && editor._creating === null) {
            editor._highlight_layer.clearLayers();
            L.geoJSON(e.layer.toGeoJSON(), {
                style: function() {
                    return {
                        color: '#FFFFDD',
                        weight: 3,
                        opacity: 0.7,
                        fillOpacity: 0,
                        className: 'c3nav-highlight'
                    };
                }
            }).addTo(editor._highlight_layer);
        }
        $('.feature_list li[name='+e.layer.feature.properties.name+']').addClass('hover');
    },
    _unhover_feature_layer: function (e) {
        editor._highlight_layer.clearLayers();
        $('.feature_list .hover').removeClass('hover');
    },
    _click_feature_layer: function(e) {
        editor.start_editing(e.layer.feature.properties.name);
        if ((e.originalEvent.ctrlKey || e.originalEvent.metaKey) && this.editEnabled()) {
            if (e.layer.feature.properties.geomtype == 'polygon') {
                this.editor.newHole(e.latlng);
            }
        }
    },

    start_creating: function (feature_type) {
        if (editor._creating !== null || editor._editing !== null) return;
        editor._highlight_layer.clearLayers();
        editor._creating = feature_type;
        var options = editor.feature_types[feature_type];
        if (options.geomtype == 'polygon') {
            editor.map.editTools.startPolygon(null, options);
        } else if (options.geomtype == 'polyline') {
            editor.map.editTools.startPolyline(null, options);
        }
        $('.leaflet-drawbar').show();
        $('.start-drawing').hide();
        $('body').removeClass('controls');
    },
    cancel_creating: function () {
        if (editor._creating === null || editor._editing !== null) return;
        editor.map.editTools.stopDrawing();
        editor._creating = null;
        $('.leaflet-drawbar').hide();
    },
    _canceled_creating: function (e) {
        if (editor._creating !== null && editor._editing === null) {
            e.layer.remove();
            $('.start-drawing').show();
        }
    },
    done_creating: function(e) {
        if (editor._creating !== null && editor._editing === null) {
            editor._editing = e.layer;
            editor._editing.disableEdit();
            editor.map.fitBounds(editor._editing.getBounds());

            $('.leaflet-drawbar').hide();
            var path = '/editor/features/' + editor._creating + '/add/';
            $('#mapeditcontrols').removeClass('list');
            $('body').addClass('controls');
            $('#mapeditdetail').load(path, editor.edit_form_loaded);
        }
    },

    start_editing: function (name) {
        if (editor._creating !== null || editor._editing !== null) return;
        editor._highlight_layer.clearLayers();
        editor._editing = editor.features[name].layer;
        var path = '/editor/features/'+ editor._editing.feature.properties.feature_type +'/edit/' + name + '/';
        $('#mapeditcontrols').removeClass('list');
        $('#mapeditdetail').load(path, editor.edit_form_loaded);
        $('body').addClass('controls');
    },
    edit_form_loaded: function() {
        $('#mapeditcontrols').addClass('detail');
        $('#id_level').val(editor._level);
        if ($('#id_geometry').length) {
            $('#id_geometry').val(JSON.stringify(editor._editing.toGeoJSON().geometry));
            editor._editing.enableEdit();
        }
        if (editor._editing.options.geomtype == 'polygon') {
            editor._editing.on('click', function (e) {
                if ((e.originalEvent.ctrlKey || e.originalEvent.metaKey) && this.editEnabled()) {
                    this.editor.newHole(e.latlng);
                }
            });
        }
    },
    update_editing: function () {
        if (editor._editing !== null) {
            $('#id_geometry').val(JSON.stringify(editor._editing.toGeoJSON().geometry));
        }
    },
    cancel_editing: function() {
        if (editor._editing !== null) {
            if (editor._creating !== null) {
                editor._editing.remove();
            }
            editor._editing = null;
            editor._creating = null;
            $('#mapeditcontrols').removeClass('detail');
            $('#mapeditdetail').html('');
            editor.get_features();
        }
    },
    submit_editing_btn_click: function(e) {
        $(this).closest('form').data('btn', $(this)).clearQueue().delay(300).queue(function() {
            $(this).data('button', null);
        });
    },
    submit_editing: function(e) {
        if ($(this).attr('name') == 'redirect') return;
        e.preventDefault();
        var data = $(this).serialize();
        var btn = $(this).data('btn');
        if (btn !== undefined && btn !== null && $(btn).is('[name]')) {
            data += '&'+$('<input>').attr('name', $(btn).attr('name')).val($(btn).val()).serialize();
        }
        var action = $(this).attr('action');
        $('#mapeditcontrols').removeClass('detail');
        $('#mapeditdetail').html('');
        editor._editing.disableEdit();
        $.post(action, data, function (data) {
            var content = $(data);
            if ($('
').append(content).find('form').length > 0) {
                $('#mapeditcontrols').addClass('detail');
                $('#mapeditdetail').html(content).find('form[name=redirect]').submit();
                if ($('#id_geometry').length) {
                    editor._editing.enableEdit();
                }
            } else {
                if (editor._creating !== null) {
                    editor._editing.remove();
                }
                editor._editing = null;
                editor._creating = null;
                editor.get_features();
                $('body').removeClass('controls');
            }
        });
    }
};

if ($('#mapeditlist').length) {
    editor.init();
}
<|endoftext|>


<commit_before>/* Copyright (C) 2017 Canonical Ltd. */
'use strict';

const PropTypes = require('prop-types');
const React = require('react');

const ButtonRow = require('../button-row/button-row');
const InspectorHeader = require('../inspector/header/header');

class LocalInspector extends React.Component {
  constructor(props) {
    super(props);
    this.state = this._generateState(this.props);
  }

  componentWillReceiveProps(nextProps) {
    this.setState(this._generateState(nextProps));
  }

  /**
    Change the state to reflect the chosen component.

    @method _changeActiveComponent
    @param {String} newComponent The component to switch to.
  */
  _changeActiveComponent(newComponent) {
    var nextProps = this.state;
    nextProps.activeComponent = newComponent;
    this.setState(this._generateState(nextProps));
  }

  /**
    Generates the state for the local inspector based on the app state.

    @method _generateState
    @param {Object} nextProps The props which were sent to the component.
    @return {Object} A generated state object which can be passed to setState.
  */
  _generateState(nextProps) {
    return {
      activeComponent: nextProps.activeComponent || nextProps.localType ||
        this.props.localType
    };
  }

  /**
    Generates the active component based on the state.

    @method _generateComponent
    @param {Object} activeComponent The component to display.
    @return {Object} A generated component.
  */
  _generateComponent(activeComponent) {
    var component;
    switch (activeComponent) {
      case 'new':
        var series = this.props.series;
        var seriesOptions = Object.keys(series).map((key) => {
          return (
            <option value={key} key={key}>
              {series[key].name}
            </option>);
        });
        component = (

Choose a series to deploy this charm

            <select ref="series" defaultValue="trusty"
              className="local-inspector__series"
              disabled={this.props.acl.isReadOnly()}>
              {seriesOptions}
            </select>

        );
        break;
      case 'update':
        component = (

            {this._generateServiceList()}

        );
        break;
    }
    return component;
  }

  /**
    Generate a list of services

    @method _generateServiceList
  */
  _generateServiceList() {
    var props = this.props;
    var services = props.services.toArray();
    if (services.length === 0) {
      return
No existing services
;
    }
    var items = [];
    services.forEach(function(service) {
      var serviceId = service.get('id');
      items.push(

            <input type="checkbox" data-id={serviceId}
              disabled={props.acl.isReadOnly()}
              ref={'service-' + serviceId} />
            {service.get('name')}

      );
    });
    return items;
  }

  /**
    Handle closing the local inspector.

    @method _close
  */
  _close() {
    this.props.changeState({
      gui: {
        inspector: null
      }});
  }

  /**
    Handle uploading the charm.

    @method _handleUpload
  */
  _handleUpload() {
    this.props.uploadLocalCharm(this.refs.series.value, this.props.file);
  }

  /**
    Handle updating services.

    @method _handleUpdate
  */
  _handleUpdate() {
    var refs = this.refs;
    var services = this.props.services;
    var selectedServices = Object.keys(refs).filter((ref) => {
      var input = refs[ref];
      if (ref.split('-')[0] === 'service' && input.checked) {
        return true;
      }
      return false;
    });
    if (selectedServices.length > 0) {
      var serviceList = selectedServices.map((serviceId) => {
        return services.getById(serviceId.split('-')[1]);
      });
      this.props.upgradeServiceUsingLocalCharm(serviceList, this.props.file);
      this._close();
    }
  }

  render() {
    var isReadOnly = this.props.acl.isReadOnly();
    var localType = this.props.localType;
    var file = this.props.file;
    var size = (file.size / 1024).toFixed(2);
    var buttons = [{
      title: 'Cancel',
      action: this._close.bind(this),
      type: 'base'
    }, {
      title: 'Upload',
      action: this.state.activeComponent === 'new' ?
        this._handleUpload.bind(this) : this._handleUpdate.bind(this),
      disabled: isReadOnly,
      type: 'neutral'
    }];
    return (

        <InspectorHeader
          backCallback={this._close.bind(this)}
          title="Local charm" />

File: {file.name}

Size: {size}kb

                <input type="radio" name="action"
                  defaultChecked={localType === 'new'}
                  disabled={isReadOnly}
                  onChange={this._changeActiveComponent.bind(this, 'new')} />
                Deploy local

                <input type="radio" name="action"
                  defaultChecked={localType === 'update'}
                  disabled={isReadOnly}
                  onChange={
                    this._changeActiveComponent.bind(this, 'update')} />
                Upgrade local

          {this._generateComponent(this.state.activeComponent)}

        <ButtonRow
          buttons={buttons} />

    );
  }
};

LocalInspector.propTypes = {
  acl: PropTypes.object.isRequired,
  changeState: PropTypes.func.isRequired,
  file: PropTypes.object.isRequired,
  localType: PropTypes.string.isRequired,
  series: PropTypes.object.isRequired,
  services: PropTypes.object.isRequired,
  upgradeServiceUsingLocalCharm: PropTypes.func.isRequired,
  uploadLocalCharm: PropTypes.func.isRequired
};

module.exports = LocalInspector;
<commit_msg>Fix the handleUpdate method when upgradng with a local charm.<commit_after>/* Copyright (C) 2017 Canonical Ltd. */
'use strict';

const PropTypes = require('prop-types');
const React = require('react');

const ButtonRow = require('../button-row/button-row');
const InspectorHeader = require('../inspector/header/header');

class LocalInspector extends React.Component {
  constructor(props) {
    super(props);
    this.state = this._generateState(this.props);
  }

  componentWillReceiveProps(nextProps) {
    this.setState(this._generateState(nextProps));
  }

  /**
    Change the state to reflect the chosen component.

    @method _changeActiveComponent
    @param {String} newComponent The component to switch to.
  */
  _changeActiveComponent(newComponent) {
    var nextProps = this.state;
    nextProps.activeComponent = newComponent;
    this.setState(this._generateState(nextProps));
  }

  /**
    Generates the state for the local inspector based on the app state.

    @method _generateState
    @param {Object} nextProps The props which were sent to the component.
    @return {Object} A generated state object which can be passed to setState.
  */
  _generateState(nextProps) {
    return {
      activeComponent: nextProps.activeComponent || nextProps.localType ||
        this.props.localType
    };
  }

  /**
    Generates the active component based on the state.

    @method _generateComponent
    @param {Object} activeComponent The component to display.
    @return {Object} A generated component.
  */
  _generateComponent(activeComponent) {
    var component;
    switch (activeComponent) {
      case 'new':
        var series = this.props.series;
        var seriesOptions = Object.keys(series).map((key) => {
          return (
            <option value={key} key={key}>
              {series[key].name}
            </option>);
        });
        component = (

Choose a series to deploy this charm

            <select ref="series" defaultValue="trusty"
              className="local-inspector__series"
              disabled={this.props.acl.isReadOnly()}>
              {seriesOptions}
            </select>

        );
        break;
      case 'update':
        component = (

            {this._generateServiceList()}

        );
        break;
    }
    return component;
  }

  /**
    Generate a list of services

    @method _generateServiceList
  */
  _generateServiceList() {
    var props = this.props;
    var services = props.services.toArray();
    if (services.length === 0) {
      return
No existing services
;
    }
    var items = [];
    services.forEach(function(service) {
      var serviceId = service.get('id');
      items.push(

            <input type="checkbox" data-id={serviceId}
              disabled={props.acl.isReadOnly()}
              ref={'service-' + serviceId} />
            {service.get('name')}

      );
    });
    return items;
  }

  /**
    Handle closing the local inspector.

    @method _close
  */
  _close() {
    this.props.changeState({
      gui: {
        inspector: null
      }});
  }

  /**
    Handle uploading the charm.

    @method _handleUpload
  */
  _handleUpload() {
    this.props.uploadLocalCharm(this.refs.series.value, this.props.file);
  }

  /**
    Handle updating services.
  */
  _handleUpdate() {
    const refs = this.refs;
    const selectedServices = Object.keys(refs).filter((ref) => {
      const input = refs[ref];
      if (ref.split('-')[0] === 'service' && input.checked) {
        return true;
      }
      return false;
    });
    if (selectedServices.length > 0) {
      const serviceList = selectedServices.map((serviceId) =>
        this.props.services.getById(serviceId.split('-').splice(1).join('-')));
      this.props.upgradeServiceUsingLocalCharm(serviceList, this.props.file);
      this._close();
    }
  }

  render() {
    var isReadOnly = this.props.acl.isReadOnly();
    var localType = this.props.localType;
    var file = this.props.file;
    var size = (file.size / 1024).toFixed(2);
    var buttons = [{
      title: 'Cancel',
      action: this._close.bind(this),
      type: 'base'
    }, {
      title: 'Upload',
      action: this.state.activeComponent === 'new' ?
        this._handleUpload.bind(this) : this._handleUpdate.bind(this),
      disabled: isReadOnly,
      type: 'neutral'
    }];
    return (

        <InspectorHeader
          backCallback={this._close.bind(this)}
          title="Local charm" />

File: {file.name}

Size: {size}kb

                <input type="radio" name="action"
                  defaultChecked={localType === 'new'}
                  disabled={isReadOnly}
                  onChange={this._changeActiveComponent.bind(this, 'new')} />
                Deploy local

                <input type="radio" name="action"
                  defaultChecked={localType === 'update'}
                  disabled={isReadOnly}
                  onChange={
                    this._changeActiveComponent.bind(this, 'update')} />
                Upgrade local

          {this._generateComponent(this.state.activeComponent)}

        <ButtonRow
          buttons={buttons} />

    );
  }
};

LocalInspector.propTypes = {
  acl: PropTypes.object.isRequired,
  changeState: PropTypes.func.isRequired,
  file: PropTypes.object.isRequired,
  localType: PropTypes.string.isRequired,
  series: PropTypes.object.isRequired,
  services: PropTypes.object.isRequired,
  upgradeServiceUsingLocalCharm: PropTypes.func.isRequired,
  uploadLocalCharm: PropTypes.func.isRequired
};

module.exports = LocalInspector;
<|endoftext|>


<commit_before>(function() {

    var i18n = require('./dep/i18next-1.1')
      , url = require('url')
      , Cookies = require('cookies')
      , wrapper = {};

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = wrapper;
    }

    // overriding detect language function
    var detectLanguage = function(req, res) {
        var querystring, cookies, headers, query, cookie;

        if (typeof req === 'object') {
            querystring = url.parse(req.url, true);
            cookies = new Cookies(req, res);
            headers = req.headers;

            query = querystring.query['setLng'];
            cookie = cookies.get('i18next');
        }

        var locales = [];

        var locale = (query || cookie);

        if (locale) {
            locales.push(locale);
        } else {
            locales = _extractLocales(headers);
        }

        return locales;
    };

    var _extractLocales = function(headers) {
        var locales = [];

        if (!headers) {
            locales.push(i18n.fallbackLng);
            return locales;
        }

        var acceptLanguage = headers['accept-language'];

        if (acceptLanguage) {
            var lngs = [];

            // associate language tags by their 'q' value (between 1 and 0)
            acceptLanguage.split(',').forEach(function(l) {
                var parts = l.split(';'); // 'en-GB;q=0.8' -> ['en-GB', 'q=0.8']

                // get the language tag qvalue: 'q=0.8' -> 0.8
                var qvalue = 1; // default qvalue
                var i;
                for (i = 0; i < parts.length; i++) {
                    var part = parts[i].split('=');
                    if (part[0] === 'q' && !isNaN(part[1])) {
                        qvalue = Number(part[1]);
                        break;
                    }
                }

                // add the tag and primary subtag to the qvalue associations
                lngs.push({lng: parts[0], q: qvalue});
            });

            lngs.sort(function(a,b) {
                return a.q - b.q;
            });

            for (i = 0; i < lngs.length; i++) {
                locales.push(lngs[i].lng);
            }

        } else {
            locales.push(i18n.fallbackLng);
        }

        return locales;
    };

    // overriding for the functions in i18next.js
    var f = {

        extend: function(target, source) {
            if (!source || typeof source === 'function') {
                return target;
            }

            for (var attr in source) { target[attr] = source[attr]; }
            return target;
        },

        each: function( object, callback, args ) {
            var name, i = 0,
                length = object.length,
                isObj = length === undefined || typeof object === "function";

            if ( args ) {
                if ( isObj ) {
                    for ( name in object ) {
                        if ( callback.apply( object[ name ], args ) === false ) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if ( callback.apply( object[ i++ ], args ) === false ) {
                            break;
                        }
                    }
                }

            // A special, fast, case for the most common use of each
            } else {
                if ( isObj ) {
                    for ( name in object ) {
                        if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
                            break;
                        }
                    }
                }
            }

            return object;
        },

        ajax: function() { return null; },

        detectLanguage: detectLanguage
    };

    f.extend(i18n.functions, f);
    f.extend(wrapper, i18n);
    wrapper.detectLanguage = f.detectLanguage;

})();<commit_msg>fixed lng prio order<commit_after>(function() {

    var i18n = require('./dep/i18next-1.1')
      , url = require('url')
      , Cookies = require('cookies')
      , wrapper = {};

    if (typeof module !== 'undefined' && module.exports) {
        module.exports = wrapper;
    }

    // overriding detect language function
    var detectLanguage = function(req, res) {
        var querystring, cookies, headers, query, cookie;

        if (typeof req === 'object') {
            querystring = url.parse(req.url, true);
            cookies = new Cookies(req, res);
            headers = req.headers;

            query = querystring.query['setLng'];
            cookie = cookies.get('i18next');
        }

        var locales = [];

        var locale = (query || cookie);

        if (locale) {
            locales.push(locale);
        } else {
            locales = _extractLocales(headers);
        }

        return locales;
    };

    var _extractLocales = function(headers) {
        var locales = [];

        if (!headers) {
            locales.push(i18n.fallbackLng);
            return locales;
        }

        var acceptLanguage = headers['accept-language'];

        if (acceptLanguage) {
            var lngs = [];

            // associate language tags by their 'q' value (between 1 and 0)
            acceptLanguage.split(',').forEach(function(l) {
                var parts = l.split(';'); // 'en-GB;q=0.8' -> ['en-GB', 'q=0.8']

                // get the language tag qvalue: 'q=0.8' -> 0.8
                var qvalue = 1; // default qvalue
                var i;
                for (i = 0; i < parts.length; i++) {
                    var part = parts[i].split('=');
                    if (part[0] === 'q' && !isNaN(part[1])) {
                        qvalue = Number(part[1]);
                        break;
                    }
                }

                // add the tag and primary subtag to the qvalue associations
                lngs.push({lng: parts[0], q: qvalue});
            });

            lngs.sort(function(a,b) {
                return b.q - a.q;
            });

            for (i = 0; i < lngs.length; i++) {
                locales.push(lngs[i].lng);
            }

        } else {
            locales.push(i18n.fallbackLng);
        }

        return locales;
    };

    // overriding for the functions in i18next.js
    var f = {

        extend: function(target, source) {
            if (!source || typeof source === 'function') {
                return target;
            }

            for (var attr in source) { target[attr] = source[attr]; }
            return target;
        },

        each: function( object, callback, args ) {
            var name, i = 0,
                length = object.length,
                isObj = length === undefined || typeof object === "function";

            if ( args ) {
                if ( isObj ) {
                    for ( name in object ) {
                        if ( callback.apply( object[ name ], args ) === false ) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if ( callback.apply( object[ i++ ], args ) === false ) {
                            break;
                        }
                    }
                }

            // A special, fast, case for the most common use of each
            } else {
                if ( isObj ) {
                    for ( name in object ) {
                        if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
                            break;
                        }
                    }
                } else {
                    for ( ; i < length; ) {
                        if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
                            break;
                        }
                    }
                }
            }

            return object;
        },

        ajax: function() { return null; },

        detectLanguage: detectLanguage
    };

    f.extend(i18n.functions, f);
    f.extend(wrapper, i18n);
    wrapper.detectLanguage = f.detectLanguage;

})();<|endoftext|>


<commit_before>var fs = require( 'fs' ),
    knox = require( 'knox' ),
    Path = require( 'path' );

// Make sure the dir exists, and its parent. Create if not.
function ensurePathExistsSync( path ) {
  var parent = Path.dirname( path );

  // Build paths above too, if not present. Check for relative or absolute paths
  if ( parent !== "." && parent !== "/" ) {
    ensurePathExistsSync( parent );
  }

  if ( !fs.existsSync( path ) ) {
    fs.mkdirSync( path );
  }
}

var BaseFileStore = {

  // By default, we use no prefix/suffix unless user-supplied.
  namePrefix: '',
  nameSuffix: '',

  // If we have a root directory, we need to work with the FS
  get requiresFileSystem() {
    return !!this.root;
  },

  // Expand a name to include the namePrefix
  expand: function( name ) {
    // Make sure name is a string, since non-strings are ignored by join
    name = name + '';
    return Path.join( this.namePrefix, name ) + this.nameSuffix;
  }
};

/**
 * LocalFileStore - stores data in local file system using root dir
 */
function LocalFileStore( options ) {
  // A root path for all filenames
  if( !options.root ) {
    throw 'LocalFileStore Error: expected root';
  }
  this.root = options.root;

  // An optional prefix for all filenames.  Will be joined with /
  // For example: filename=foo namePrefix=v becomes v/foo
  if( options.namePrefix ) {
    this.namePrefix = options.namePrefix;
  }
  ensurePathExistsSync( Path.join( this.root, this.namePrefix ) );

  // An optional suffix for all filenames.  Will be joined directly
  // For example: filename=foo nameSuffix=.html becomes foo.html
  if( options.nameSuffix ) {
    this.nameSuffix = options.nameSuffix;
  }

  // If embedHostname isn't appropriate, a hostname can be provided to override it.
  this.hostname = options.hostname;
}

LocalFileStore.prototype = Object.create( BaseFileStore );

LocalFileStore.prototype.write = function( path, data, callback ) {
  path = Path.join( this.root, this.expand( path ) );
  ensurePathExistsSync( Path.dirname( path ) );

  fs.writeFile( path, data, function( err ) {
    if (err) {
      callback( err );
    } else {
      callback();
    }
  });
};

LocalFileStore.prototype.remove = function( path, callback ) {
  callback = callback || function(){};
  path = Path.join( this.root, this.expand( path ) );
  fs.unlink( path, callback );
};

/**
 * S3FileStore - store data using Amazon S3
 */
function S3FileStore( options ) {
  // Amazon S3 credentials for accessing a bucket
  this.client = knox.createClient({
    key: options.key,
    secret: options.secret,
    bucket: options.bucket
  });

  // An optional prefix for all keys.  Will be joined with /
  // For example: key=foo namePrefix=v becomes v/foo
  if( options.namePrefix ) {
    this.namePrefix = options.namePrefix;
  }

  // An optional suffix for all filenames.  Will be joined directly
  // For example: filename=foo nameSuffix=.html becomes foo.html
  if( options.nameSuffix ) {
    this.nameSuffix = options.nameSuffix;
  }

  // An optional mime type for the files to be written.  Defaults to
  // text/plain if none given.
  this.contentType = options.contentType || 'text/plain';

  // If embedHostname isn't appropriate, a hostname can be provided to override it.
  this.hostname = options.hostname;

  // Append any additional headers to send with a write request
  this.headers = {};
  if ( options.headers ) {
    this.headers = options.headers;
  }
}

S3FileStore.prototype = Object.create( BaseFileStore );

S3FileStore.prototype.write = function( key, data, callback ) {
  var headers = {
    'x-amz-acl': 'public-read',
    'Content-Length': data.length,
    'Content-Type': this.contentType
  };

  Object.keys( this.headers ).forEach(function( key ) {
    headers[ key ] = this.headers[ key ];
  });

  this.client.put( this.expand( key ), headers )
  .on( 'response', function( res ) {
    if( res.statusCode === 200 ) {
      callback();
    } else {
      callback( res.statusCode );
    }
  })
  .end( data );
};

S3FileStore.prototype.remove = function( key, callback ) {
  callback = callback || function(){};
  this.client.del( this.expand( key ) )
  .on( 'response', function( res ) {
    if( res.statusCode === 200 ) {
      callback();
    } else {
      callback( res.statusCode );
    }
  })
  .end();
};

var FileStores = {
  'S3': S3FileStore,
  'LOCAL': LocalFileStore
};

module.exports = {
  // Pass a type (one of 'local' or 's3') to build a FileStore object.
  create: function( type, options ) {
    options = options || {};

    var Constructor = FileStores[ type.toUpperCase() ];
    if( !Constructor ) {
      throw 'Unknown FileStore type: ' + type;
    }

    return new Constructor( options );
  }
};
<commit_msg>No Bug - Use proper `this` context in headers<commit_after>var fs = require( 'fs' ),
    knox = require( 'knox' ),
    Path = require( 'path' );

// Make sure the dir exists, and its parent. Create if not.
function ensurePathExistsSync( path ) {
  var parent = Path.dirname( path );

  // Build paths above too, if not present. Check for relative or absolute paths
  if ( parent !== "." && parent !== "/" ) {
    ensurePathExistsSync( parent );
  }

  if ( !fs.existsSync( path ) ) {
    fs.mkdirSync( path );
  }
}

var BaseFileStore = {

  // By default, we use no prefix/suffix unless user-supplied.
  namePrefix: '',
  nameSuffix: '',

  // If we have a root directory, we need to work with the FS
  get requiresFileSystem() {
    return !!this.root;
  },

  // Expand a name to include the namePrefix
  expand: function( name ) {
    // Make sure name is a string, since non-strings are ignored by join
    name = name + '';
    return Path.join( this.namePrefix, name ) + this.nameSuffix;
  }
};

/**
 * LocalFileStore - stores data in local file system using root dir
 */
function LocalFileStore( options ) {
  // A root path for all filenames
  if( !options.root ) {
    throw 'LocalFileStore Error: expected root';
  }
  this.root = options.root;

  // An optional prefix for all filenames.  Will be joined with /
  // For example: filename=foo namePrefix=v becomes v/foo
  if( options.namePrefix ) {
    this.namePrefix = options.namePrefix;
  }
  ensurePathExistsSync( Path.join( this.root, this.namePrefix ) );

  // An optional suffix for all filenames.  Will be joined directly
  // For example: filename=foo nameSuffix=.html becomes foo.html
  if( options.nameSuffix ) {
    this.nameSuffix = options.nameSuffix;
  }

  // If embedHostname isn't appropriate, a hostname can be provided to override it.
  this.hostname = options.hostname;
}

LocalFileStore.prototype = Object.create( BaseFileStore );

LocalFileStore.prototype.write = function( path, data, callback ) {
  path = Path.join( this.root, this.expand( path ) );
  ensurePathExistsSync( Path.dirname( path ) );

  fs.writeFile( path, data, function( err ) {
    if (err) {
      callback( err );
    } else {
      callback();
    }
  });
};

LocalFileStore.prototype.remove = function( path, callback ) {
  callback = callback || function(){};
  path = Path.join( this.root, this.expand( path ) );
  fs.unlink( path, callback );
};

/**
 * S3FileStore - store data using Amazon S3
 */
function S3FileStore( options ) {
  // Amazon S3 credentials for accessing a bucket
  this.client = knox.createClient({
    key: options.key,
    secret: options.secret,
    bucket: options.bucket
  });

  // An optional prefix for all keys.  Will be joined with /
  // For example: key=foo namePrefix=v becomes v/foo
  if( options.namePrefix ) {
    this.namePrefix = options.namePrefix;
  }

  // An optional suffix for all filenames.  Will be joined directly
  // For example: filename=foo nameSuffix=.html becomes foo.html
  if( options.nameSuffix ) {
    this.nameSuffix = options.nameSuffix;
  }

  // An optional mime type for the files to be written.  Defaults to
  // text/plain if none given.
  this.contentType = options.contentType || 'text/plain';

  // If embedHostname isn't appropriate, a hostname can be provided to override it.
  this.hostname = options.hostname;

  // Append any additional headers to send with a write request
  this.headers = {};
  if ( options.headers ) {
    this.headers = options.headers;
  }
}

S3FileStore.prototype = Object.create( BaseFileStore );

S3FileStore.prototype.write = function( key, data, callback ) {
  var headers = {
    'x-amz-acl': 'public-read',
    'Content-Length': data.length,
    'Content-Type': this.contentType
  };

  Object.keys( this.headers ).forEach(function( key ) {
    headers[ key ] = this.headers[ key ];
  }, this );

  this.client.put( this.expand( key ), headers )
  .on( 'response', function( res ) {
    if( res.statusCode === 200 ) {
      callback();
    } else {
      callback( res.statusCode );
    }
  })
  .end( data );
};

S3FileStore.prototype.remove = function( key, callback ) {
  callback = callback || function(){};
  this.client.del( this.expand( key ) )
  .on( 'response', function( res ) {
    if( res.statusCode === 200 ) {
      callback();
    } else {
      callback( res.statusCode );
    }
  })
  .end();
};

var FileStores = {
  'S3': S3FileStore,
  'LOCAL': LocalFileStore
};

module.exports = {
  // Pass a type (one of 'local' or 's3') to build a FileStore object.
  create: function( type, options ) {
    options = options || {};

    var Constructor = FileStores[ type.toUpperCase() ];
    if( !Constructor ) {
      throw 'Unknown FileStore type: ' + type;
    }

    return new Constructor( options );
  }
};
<|endoftext|>


<commit_before>// Dependencies
var cacheFiles = require('../index');
var CacheFiles = require('../index').Prototype;
var chai = require('chai');
var gutil = require('gulp-util');
var fs = require('fs');
var path = require('path');
var gulp = require('gulp');
var through2 = require('through2').obj;

var assert = chai.assert;
var expect = chai.expect;
var PLUGIN_NAME = 'gulp-cache-files';

// Utility Functions
var throwErr = function (err) {
  if (err) throw err;
};

var rmFileWithDir = function(filepath){
  fs.unlink(filepath, function(err) {
    throwErr(err);
    fs.rmdir(path.dirname(filepath), throwErr);
  });
};

describe(PLUGIN_NAME, function () {

  describe('cache file creation', function () {
    // After - Clean up test directory
    after(function () {
      rmFileWithDir('./' + PLUGIN_NAME + '/manifest.json');
      fs.unlink('./manifest.json', throwErr);
      rmFileWithDir('./custom/foo.json');
    });

    it('Default file should be created', function(done) {
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter())
        .pipe(cacheFiles.manifest())
        .on('finish', function() {
          setTimeout(function(){
            assert(fs.existsSync('./' + PLUGIN_NAME + '/manifest.json'), 'File is not created');
            done();
          }, 100);
        });
    });

    it('Default file should be created when only manifest func called', function(done) {
      var cacheFiles = new CacheFiles();
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.manifest())
        .pipe(gulp.dest('./'));

        setTimeout(function(){
          assert(fs.existsSync('./manifest.json'), 'File is not created');
          done();
        }, 100);
    });

    it('Custom file should be created', function(done) {
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter('./custom/foo.json'))
        .pipe(cacheFiles.manifest());

        setTimeout(function(){
          assert(fs.existsSync('./custom/foo.json'), 'File is not created');
          done();
        }, 100);
    });
  });

  describe('filter function tests', function() {
    var manifestPath = './filter/manifest.json';
    var cacheFiles = new CacheFiles();

    beforeEach(function(){
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter(manifestPath))
        .pipe(cacheFiles.manifest());
    });

    // After - Clean up test directory
    afterEach(function() {
      rmFileWithDir(manifestPath);
    });

    it('should drop all cached files from stream', function(done) {
      var filesAmount = 0;
      //wait when file will be created in before
      setTimeout(function () {
        gulp.src('./test/fixtures/*', {read: false})
          .pipe(cacheFiles.filter(manifestPath))
          .on('data', function(){
            filesAmount++;
          })
          .pipe(cacheFiles.manifest())
          .on('finish', function(){
            assert.equal(filesAmount, 0);
            done();
          });
      }, 200);
    });

    it('should add file into stream if it was modified', function(done) {
      var filesAmount = 0;
      //wait when file will be created in before
      setTimeout(function () {
        gulp.src('./test/fixtures/*', {read: false})
          .on('data', function(file){
            if (file.relative === 'bar.jpg'){
              file.stat.mtime = new Date();
            }
          })
          .pipe(cacheFiles.filter(manifestPath))
          .on('data', function(){
            filesAmount++;
          })
          .pipe(cacheFiles.manifest())
          .on('finish', function(){
            assert.equal(filesAmount, 1);
            done();
          });
      }, 200);
    });
  });

  describe('manifest file content test', function() {
    var manifestPath = './manifest-content/manifest.json';
    var cachePath = './cache/cache.json';
    var mtimes = {};
    // Before - find timestamps
    before(function() {
      gulp.src('./test/fixtures/*')
        .on('data', function(file) {
          mtimes[file.relative] = file.stat.mtime.getTime();
        });
    });
    // After - Clean up test directory
    after(function() {
      rmFileWithDir(manifestPath);
      rmFileWithDir(cachePath);
    });

    it('should write correct object with relative path as key and mtime as value', function(done) {
      var cacheFiles = new CacheFiles();
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter(manifestPath))
        .pipe(cacheFiles.manifest())
        .on('finish', function(){
          setTimeout(function(){
            fs.readFile(manifestPath,'utf8', function(err, data) {
              throwErr(err);
              expect(JSON.parse(data)).to.deep.equal(mtimes);
              done();
            });
          }, 300);
        });
    });

    it('should write correct mtime as value if file was changed', function(done) {
      var cacheFiles = new CacheFiles();
      var changeTime;
      gulp.src('./test/fixtures-changed/*', {read: false})
        .pipe(cacheFiles.filter(cachePath))
        .on('data', function (file) {
          if(file.relative === 'baz.jpg'){
            var time = new Date();
            console.log(time);
            changeTime = time.getTime();
            console.log(changeTime);
            fs.utimes(file.path, time, time, function(){});
          }
        })
        .pipe(cacheFiles.manifest())
        .on('finish', function(){
          setTimeout(function(){
            fs.readFile(cachePath,'utf8', function(err, data) {
              throwErr(err);
              console.log(changeTime, JSON.parse(data)['baz.jpg']);
              console.log(JSON.parse(data));
              assert.equal(JSON.parse(data)['baz.jpg'], changeTime);
              done();
            });
          }, 400);
        });
    });
  });
});
<commit_msg>added logs to find what is with travis cli<commit_after>// Dependencies
var cacheFiles = require('../index');
var CacheFiles = require('../index').Prototype;
var chai = require('chai');
var gutil = require('gulp-util');
var fs = require('fs');
var path = require('path');
var gulp = require('gulp');
var through2 = require('through2').obj;

var assert = chai.assert;
var expect = chai.expect;
var PLUGIN_NAME = 'gulp-cache-files';

// Utility Functions
var throwErr = function (err) {
  if (err) throw err;
};

var rmFileWithDir = function(filepath){
  fs.unlink(filepath, function(err) {
    throwErr(err);
    fs.rmdir(path.dirname(filepath), throwErr);
  });
};

describe(PLUGIN_NAME, function () {

  describe('cache file creation', function () {
    // After - Clean up test directory
    after(function () {
      rmFileWithDir('./' + PLUGIN_NAME + '/manifest.json');
      fs.unlink('./manifest.json', throwErr);
      rmFileWithDir('./custom/foo.json');
    });

    it('Default file should be created', function(done) {
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter())
        .pipe(cacheFiles.manifest())
        .on('finish', function() {
          setTimeout(function(){
            assert(fs.existsSync('./' + PLUGIN_NAME + '/manifest.json'), 'File is not created');
            done();
          }, 100);
        });
    });

    it('Default file should be created when only manifest func called', function(done) {
      var cacheFiles = new CacheFiles();
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.manifest())
        .pipe(gulp.dest('./'));

        setTimeout(function(){
          assert(fs.existsSync('./manifest.json'), 'File is not created');
          done();
        }, 100);
    });

    it('Custom file should be created', function(done) {
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter('./custom/foo.json'))
        .pipe(cacheFiles.manifest());

        setTimeout(function(){
          assert(fs.existsSync('./custom/foo.json'), 'File is not created');
          done();
        }, 100);
    });
  });

  describe('filter function tests', function() {
    var manifestPath = './filter/manifest.json';
    var cacheFiles = new CacheFiles();

    beforeEach(function(){
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter(manifestPath))
        .pipe(cacheFiles.manifest());
    });

    // After - Clean up test directory
    afterEach(function() {
      rmFileWithDir(manifestPath);
    });

    it('should drop all cached files from stream', function(done) {
      var filesAmount = 0;
      //wait when file will be created in before
      setTimeout(function () {
        gulp.src('./test/fixtures/*', {read: false})
          .pipe(cacheFiles.filter(manifestPath))
          .on('data', function(){
            filesAmount++;
          })
          .pipe(cacheFiles.manifest())
          .on('finish', function(){
            assert.equal(filesAmount, 0);
            done();
          });
      }, 200);
    });

    it('should add file into stream if it was modified', function(done) {
      var filesAmount = 0;
      //wait when file will be created in before
      setTimeout(function () {
        gulp.src('./test/fixtures/*', {read: false})
          .on('data', function(file){
            if (file.relative === 'bar.jpg'){
              file.stat.mtime = new Date();
            }
          })
          .pipe(cacheFiles.filter(manifestPath))
          .on('data', function(){
            filesAmount++;
          })
          .pipe(cacheFiles.manifest())
          .on('finish', function(){
            assert.equal(filesAmount, 1);
            done();
          });
      }, 200);
    });
  });

  describe('manifest file content test', function() {
    var manifestPath = './manifest-content/manifest.json';
    var cachePath = './cache/cache.json';
    var mtimes = {};
    // Before - find timestamps
    before(function() {
      gulp.src('./test/fixtures/*')
        .on('data', function(file) {
          mtimes[file.relative] = file.stat.mtime.getTime();
        });
    });
    // After - Clean up test directory
    after(function() {
      rmFileWithDir(manifestPath);
      rmFileWithDir(cachePath);
    });

    it('should write correct object with relative path as key and mtime as value', function(done) {
      var cacheFiles = new CacheFiles();
      gulp.src('./test/fixtures/*', {read: false})
        .pipe(cacheFiles.filter(manifestPath))
        .pipe(cacheFiles.manifest())
        .on('finish', function(){
          setTimeout(function(){
            fs.readFile(manifestPath,'utf8', function(err, data) {
              throwErr(err);
              expect(JSON.parse(data)).to.deep.equal(mtimes);
              done();
            });
          }, 300);
        });
    });

    it('should write correct mtime as value if file was changed', function(done) {
      var cacheFiles = new CacheFiles();
      var changeTime;
      gulp.src('./test/fixtures-changed/*', {read: false})
        .pipe(cacheFiles.filter(cachePath))
        .on('data', function (file) {
          if(file.relative === 'baz.jpg'){
            var time = new Date();
            console.log(time);
            changeTime = time;
            console.log(changeTime);
            fs.utimes(file.path, time, time, function(){});
          }
        })
        .pipe(cacheFiles.manifest())
        .on('finish', function(){
          setTimeout(function(){
            fs.readFile(cachePath,'utf8', function(err, data) {
              throwErr(err);
              console.log(changeTime, JSON.parse(data)['baz.jpg']);
              console.log(JSON.parse(data));
              assert.equal(JSON.parse(data)['baz.jpg'], changeTime.getTime());
              done();
            });
          }, 400);
        });
    });
  });
});
<|endoftext|>


<commit_before>var mime = require('mime'),
    fs = require('fs'),
    gm = require('gm'),
    lockFile    = require('lockfile');

var imageModifier = {
    modifyImage: function (processOptions, options, appConfig){ //callback) {
        //console.log("Modifying image...");
        return new Promise(function(resolve, reject){
            var srcPath = processOptions.filepath;
            var dstPath = processOptions.modifiedFilePath;

            var lockFileOptions = {
                wait: 1000,
                stale: 10000,
                retries: 3,
                retryWait: 250
            };

            var fileType = mime.lookup(srcPath);
            if(appConfig.allowedMimeTypes.indexOf(fileType) > -1){
                try {
                    //console.log("Trying to identify image...");
                    gm(srcPath).identify(function (err, imageData) {
                        if (err || !(imageData && imageData.size && imageData.size.width)) {
                            //console.log("--Couldn't get image information", err);
                            reject(err);
                            return;
                        }

                        var origWidth  = imageData.size.width;
                        var origHeight = imageData.size.height;
                        //console.log("Setting up with these options: ", options);
                        //if we have no width or height defines, just output the full size image.
                        if(!options.width && !options.height){
                            options.width = origWidth;
                            options.height = origHeight;
                        }else if(!options.width){
                            //no width specified, calculate from height
                            options.width = imageModifier.ratioCalculator(options.height, origHeight, origWidth);
                        }else if(!options.height){
                            //no height specified, calculate from width
                            options.height = imageModifier.ratioCalculator(options.width, origWidth, origHeight);
                        }
                        //console.log("options are now: ", options);
                        var Canvas = require('canvas'),
                            SmartCrop = require('smartcrop');

                        var img = new Canvas.Image();
                        var canvasOptions = {}; canvasOptions.canvasFactory = function(w, h) { return new Canvas(w, h); };

                        img.src = fs.readFileSync(srcPath);

                        canvasOptions.width = options.width;
                        canvasOptions.height = options.height;
                        if(options.minScale){
                            canvasOptions.minScale = options.minScale;
                        }
                        canvasOptions.dstPath = dstPath;
                        //console.log("Using smartCrop with ", canvasOptions);
                        SmartCrop.crop(img, canvasOptions, function(result) {
                            //console.log(JSON.stringify(result.topCrop, null, '  '));
                            var rt = result.topCrop;

                            lockFile.lock(srcPath + ".lock", lockFileOptions, function(err) {
                                if (err) {
                                    //console.log(err);
                                    return reject(err);
                                }
                                //console.log("Resizing and writing to" + dstPath + "...");
                                //console.log("Cropping to", rt.width, rt.height, rt.x, rt.y);
                                //console.log("Resizing to", options.width, options.height);
                                //console.log("Source path: "+srcPath);
                                gm(srcPath)
                                    .crop(rt.width, rt.height, rt.x, rt.y)
                                    .resize(options.width, options.height, "!")
                                    .gravity('Center')
                                    .quality(options.quality)
                                    .stream(function(err, stdout, stderr){
                                        //console.log("--unlocking file");
                                        lockFile.unlock(srcPath + ".lock", function (unlockErr) {
                                            if (unlockErr) {
                                                //console.log(unlockErr);
                                                reject(unlockErr);
                                            } else {
                                                if (!err) {
                                                    //console.log("--Writing file stream to "+dstPath);
                                                    var writeStream = fs.createWriteStream(dstPath);

                                                    stdout.pipe(writeStream);
                                                    writeStream.on('close', function () {
                                                        resolve(dstPath);
                                                    });
                                                } else {
                                                    //console.log("gm error", err);
                                                    reject(err);
                                                }
                                            }
                                        });
                                    });
                            });
                        });
                    });
                }catch(err){
                    console.log("gm error", err);
                    reject(err);
                }
            }else{
                var err = {
                    responseCode: 403,
                    message: "Incorrect Mime Type!"
                };
                console.log(err, fileType);
                reject(err);
            }
        });
    },

    modifyImageProcess: function(processOptions, options, appConfig){
        return new Promise(function(resolve, reject){
            var spawn = require('child_process').spawn;
            var resolved = false;
            var env = {
                processOptions: JSON.stringify(processOptions),
                options: JSON.stringify(options),
                appConfig: JSON.stringify(appConfig)
            };

            var child = spawn('node',
                [
                    __dirname+'/image-modifier.js',
                    'processOptions', env.processOptions,
                    'options', env.options,
                    'appConfig', env.appConfig
                ]
            );
            console.log(node, __dirname+'/image-modifier.js', '--processOptions', env.processOptions, '--options', env.options, '--appConfig', env.appConfig);

            // Listen for stdout data
            child.stdout.on('data', function (data) {
                console.log("-------------"+data);
                var out = (""+data).replace(/\n$/, '');
                try {
                    out = JSON.parse(out);
                }catch(err){}

                if(out.dstPath){
                    resolved = true;
                    resolve(out.dstPath);
                }else{
                    console.log(out);
                    resolved = true;
                    reject(out);
                }
            });

            // Listen for an exit event:
            child.on('error', function (err) {
                if(err){
                    console.log("--child error", err);
                    reject(err);
                }
            });

            // Listen for an exit event:
            child.on('exit', function (exitCode, signal) {
                if(!resolved){
                    console.log("--Not resolved, aborting child", signal);
                    reject(exitCode);
                }
                console.log("Child exited with code: " + exitCode, signal);
            });
        });
    },

    /**
     * Detect targetHeight for a proportional resizing, when only width is indicated.
     *
     * @param targetWidth
     * @param origWidth
     * @param origHeight
     */
    ratioCalculator: function (targetKnown, origTargetKnownEquiv, origTargetUnknownEquiv) {
        return origTargetUnknownEquiv * targetKnown / origTargetKnownEquiv;
    }
};

//Check to see if we've been executed as a process
var runOptions = {};
for(var i = 0; i < process.argv.length; i++){
    if(['processOptions', 'options', 'appConfig'].indexOf(process.argv[i]) > -1){
        runOptions[process.argv[i]] = process.argv[i+1];
    }
}
if(runOptions.processOptions){
    processOptions = JSON.parse(runOptions.processOptions);
    options = JSON.parse(runOptions.options);
    appConfig = JSON.parse(runOptions.appConfig);

    imageModifier.modifyImage(processOptions, options, appConfig).then(function(dstPath){
        console.log(JSON.stringify({ dstPath: dstPath }));

    }).catch(function(err){
        console.log(JSON.stringify({
            responseCode: 404,
            message: err
        }));
    });
}

module.exports = imageModifier;

<commit_msg>logging more info<commit_after>var mime = require('mime'),
    fs = require('fs'),
    gm = require('gm'),
    lockFile    = require('lockfile');

var imageModifier = {
    modifyImage: function (processOptions, options, appConfig){ //callback) {
        //console.log("Modifying image...");
        return new Promise(function(resolve, reject){
            var srcPath = processOptions.filepath;
            var dstPath = processOptions.modifiedFilePath;

            var lockFileOptions = {
                wait: 1000,
                stale: 10000,
                retries: 3,
                retryWait: 250
            };

            var fileType = mime.lookup(srcPath);
            if(appConfig.allowedMimeTypes.indexOf(fileType) > -1){
                try {
                    //console.log("Trying to identify image...");
                    gm(srcPath).identify(function (err, imageData) {
                        if (err || !(imageData && imageData.size && imageData.size.width)) {
                            //console.log("--Couldn't get image information", err);
                            reject(err);
                            return;
                        }

                        var origWidth  = imageData.size.width;
                        var origHeight = imageData.size.height;
                        //console.log("Setting up with these options: ", options);
                        //if we have no width or height defines, just output the full size image.
                        if(!options.width && !options.height){
                            options.width = origWidth;
                            options.height = origHeight;
                        }else if(!options.width){
                            //no width specified, calculate from height
                            options.width = imageModifier.ratioCalculator(options.height, origHeight, origWidth);
                        }else if(!options.height){
                            //no height specified, calculate from width
                            options.height = imageModifier.ratioCalculator(options.width, origWidth, origHeight);
                        }
                        //console.log("options are now: ", options);
                        var Canvas = require('canvas'),
                            SmartCrop = require('smartcrop');

                        var img = new Canvas.Image();
                        var canvasOptions = {}; canvasOptions.canvasFactory = function(w, h) { return new Canvas(w, h); };

                        img.src = fs.readFileSync(srcPath);

                        canvasOptions.width = options.width;
                        canvasOptions.height = options.height;
                        if(options.minScale){
                            canvasOptions.minScale = options.minScale;
                        }
                        canvasOptions.dstPath = dstPath;
                        //console.log("Using smartCrop with ", canvasOptions);
                        SmartCrop.crop(img, canvasOptions, function(result) {
                            //console.log(JSON.stringify(result.topCrop, null, '  '));
                            var rt = result.topCrop;

                            lockFile.lock(srcPath + ".lock", lockFileOptions, function(err) {
                                if (err) {
                                    //console.log(err);
                                    return reject(err);
                                }
                                //console.log("Resizing and writing to" + dstPath + "...");
                                //console.log("Cropping to", rt.width, rt.height, rt.x, rt.y);
                                //console.log("Resizing to", options.width, options.height);
                                //console.log("Source path: "+srcPath);
                                gm(srcPath)
                                    .crop(rt.width, rt.height, rt.x, rt.y)
                                    .resize(options.width, options.height, "!")
                                    .gravity('Center')
                                    .quality(options.quality)
                                    .stream(function(err, stdout, stderr){
                                        //console.log("--unlocking file");
                                        lockFile.unlock(srcPath + ".lock", function (unlockErr) {
                                            if (unlockErr) {
                                                //console.log(unlockErr);
                                                reject(unlockErr);
                                            } else {
                                                if (!err) {
                                                    //console.log("--Writing file stream to "+dstPath);
                                                    var writeStream = fs.createWriteStream(dstPath);

                                                    stdout.pipe(writeStream);
                                                    writeStream.on('close', function () {
                                                        resolve(dstPath);
                                                    });
                                                } else {
                                                    //console.log("gm error", err);
                                                    reject(err);
                                                }
                                            }
                                        });
                                    });
                            });
                        });
                    });
                }catch(err){
                    console.log("gm error", err);
                    reject(err);
                }
            }else{
                var err = {
                    responseCode: 403,
                    message: "Incorrect Mime Type!"
                };
                console.log(err, fileType);
                reject(err);
            }
        });
    },

    modifyImageProcess: function(processOptions, options, appConfig){
        return new Promise(function(resolve, reject){
            var spawn = require('child_process').spawn;
            var resolved = false;
            var env = {
                processOptions: JSON.stringify(processOptions),
                options: JSON.stringify(options),
                appConfig: JSON.stringify(appConfig)
            };

            var child = spawn('node',
                [
                    __dirname+'/image-modifier.js',
                    'processOptions', env.processOptions,
                    'options', env.options,
                    'appConfig', env.appConfig
                ]
            );
            console.log('node', __dirname+'/image-modifier.js', '--processOptions', env.processOptions, '--options', env.options, '--appConfig', env.appConfig);

            // Listen for stdout data
            child.stdout.on('data', function (data) {
                console.log("-------------"+data);
                var out = (""+data).replace(/\n$/, '');
                try {
                    out = JSON.parse(out);
                }catch(err){}

                if(out.dstPath){
                    resolved = true;
                    resolve(out.dstPath);
                }else{
                    console.log(out);
                    resolved = true;
                    reject(out);
                }
            });

            // Listen for an exit event:
            child.on('error', function (err) {
                if(err){
                    console.log("--child error", err);
                    reject(err);
                }
            });

            // Listen for an exit event:
            child.on('exit', function (exitCode, signal) {
                if(!resolved){
                    console.log("--Not resolved, aborting child", signal);
                    reject(exitCode);
                }
                console.log("Child exited with code: " + exitCode, signal);
            });
        });
    },

    /**
     * Detect targetHeight for a proportional resizing, when only width is indicated.
     *
     * @param targetWidth
     * @param origWidth
     * @param origHeight
     */
    ratioCalculator: function (targetKnown, origTargetKnownEquiv, origTargetUnknownEquiv) {
        return origTargetUnknownEquiv * targetKnown / origTargetKnownEquiv;
    }
};

//Check to see if we've been executed as a process
var runOptions = {};
for(var i = 0; i < process.argv.length; i++){
    if(['processOptions', 'options', 'appConfig'].indexOf(process.argv[i]) > -1){
        runOptions[process.argv[i]] = process.argv[i+1];
    }
}
if(runOptions.processOptions){
    processOptions = JSON.parse(runOptions.processOptions);
    options = JSON.parse(runOptions.options);
    appConfig = JSON.parse(runOptions.appConfig);

    imageModifier.modifyImage(processOptions, options, appConfig).then(function(dstPath){
        console.log(JSON.stringify({ dstPath: dstPath }));

    }).catch(function(err){
        console.log(JSON.stringify({
            responseCode: 404,
            message: err
        }));
    });
}

module.exports = imageModifier;

<|endoftext|>


<commit_before>module.exports = function (remainingRepositories, resultsFileName, ghClient, indexed) {
  indexed = indexed || {};
  var totalIndexedSinceLastSave = 0;
  index(10000);

  function index(startFrom) {
    var repoName = remainingRepositories[startFrom].name;
    ghClient.findFollowers(repoName)
      .then(function (followers) {
        indexed[repoName] = followers;
        totalIndexedSinceLastSave += followers.length;
        if (totalIndexedSinceLastSave > 1000) {
          save();
        }

        if (remainingRepositories.length < startFrom) {
          save();
          console.log('Finished processing');
        } else {
          index(startFrom + 1);
        }
      });
  }

  function save() {
    var fs = require('fs');
    console.log('Saving current results to', resultsFileName);
    try {
      fs.writeFileSync(resultsFileName, JSON.stringify(indexed));
    } catch (e) {
      console.log("Error: ", e);
      throw e;

    }
    console.log('Added', totalIndexedSinceLastSave, 'followers info to the file');
    totalIndexedSinceLastSave = 0;
  }
};
<commit_msg>Added info about remaining<commit_after>module.exports = function (remainingRepositories, resultsFileName, ghClient, indexed) {
  indexed = indexed || {};
  var totalIndexedSinceLastSave = 0;
  index(10000);

  function index(startFrom) {
    var repoName = remainingRepositories[startFrom].name;
    ghClient.findFollowers(repoName)
      .then(function (followers) {
        indexed[repoName] = followers;
        totalIndexedSinceLastSave += followers.length;
        if (totalIndexedSinceLastSave > 1000) {
          save();
        }

        if (remainingRepositories.length < startFrom) {
          save();
          console.log('Finished processing');
        } else {
          index(startFrom + 1);
        }
      });
  }

  function save() {
    var fs = require('fs');
    console.log('Saving current results to', resultsFileName);
    try {
      fs.writeFileSync(resultsFileName, JSON.stringify(indexed));
    } catch (e) {
      console.log("Error: ", e);
      throw e;

    }
    console.log('Added', totalIndexedSinceLastSave, 'followers info to the file');
    console.log('Remaining', remainingRepositories.length - Object.keys(indexed).length);
    totalIndexedSinceLastSave = 0;
  }
};
<|endoftext|>