<commit_before>/*
 *   Copyright 2015 OSBI
 *
 *   Created by Andreas Verleysen
 */

/**
 * Changes the locale of the cube
 */
var ChangeLocale = Backbone.View.extend({

    initialize: function (args) {
        this.visible = false;

        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("changeLocale_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "show", "handleClick", "processDatasourcesWithoutAddingNewTab", "toggleLocaleScreen", "setUserFeedback");

        this.datasources = new Connections({}, { dialog: this });

        // Add change locale button to saiku toolbar
        this.add_button();
        this.workspace.toolbar.changeLocale = this.show;

        // Create locale screen
        this.localeOptionsScreen = $("
  " +
            "
" +
            "
         <button class='button' id='en_US' > English </button>
 " +
            "
   	    <button class='button' id='nl_BE' > Dutch </button>
 " +
            "
   	    <button class='button' id='fr_FR' > French </button>
" +
            "
" +
            "
 " +
            " Choose your language ")
        ;

        // attach event handler
        this.localeOptionsScreen.find('button').click(this.handleClick);

        //function for menu
        $("#nav ul ").css({display: "none"}); // Opera Fix
        $("#nav li").hover(function () {
            $(this).find('ul:first').css({visibility: "visible", display: "none"}).show(400);
        }, function () {
            $(this).find('ul:first').css({visibility: "hidden"});
        });

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.localeOptionsScreen.hide());

    },

    add_button: function () {
        var $chart_button =
            $('')
                .css({  'background-image': "url('js/saiku/plugins/ChangeLocale/images/change_locale.png')",
                    'background-repeat': 'no-repeat',
                    'background-position': '7px 7px'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function (event, ui) {
        this.toggleWorkspaceWithLocaleScreen();
//        if ($(event.target).hasClass('on')) {
//            this.render();
//        } else {
//            this.workspace.table.render({ data: this.workspace.query.result.lastresult() });
//        }
    },

    toggleWorkspaceWithLocaleScreen: function () {
        this.toggleWorkSpace();
        this.toggleLocaleScreen();
    },

    toggleWorkSpace: function () {
        $(this.workspace.table.el).toggle();
        $(this.el).toggle();
    },

    toggleLocaleScreen: function () {
        if (this.visible) {
            this.setUserFeedback("Choose language");
            $(this.workspace.el).find('#languageOptions').addClass('hide');
            $(this.workspace.el).find('#feedback').addClass('hide');
            this.workspace.toolbar.run_query();
        }
        else {
            $(this.workspace.el).find('#languageOptions').removeClass('hide').show();
            $(this.workspace.el).find('#feedback').removeClass('hide').show();
        }
        $(event.target).toggleClass('on');
        this.visible = !this.visible;
    },

    handleClick: function (event) {
        // Keep a reference to the main plugin object.
        var this_p = this;
        this_p.setUserFeedback("Changing locale...");
        // get selected locale
        var newLocale = $(event.target).attr('id');

        // get current selected connection name (without URL data)
        var selectedCube = $(".cubes option:selected").val();
        var selectedConnectionName = selectedCube.substring(0, selectedCube.indexOf("/"));

        // Get all connections from back-end
        var getUrl = Settings.REST_URL + "admin" + "/datasources";
        $.get(getUrl, function (data) {
            var allConnections = data;
            // match
            var selectedConnection = _.find(allConnections, function (connection) {
                return connection.connectionname == selectedConnectionName
            });

            var selectedDataSource = new DataSource(selectedConnection);

            var localeChanged = this_p.changeLocale(selectedConnection, selectedDataSource, newLocale);
            if (localeChanged) {
                this_p.setUserFeedback("Adding locale to data source...");
                this_p.saveDataSource(selectedDataSource);
                this_p.setUserFeedback("Refreshing data source with new locale...");
                this_p.refreshDatasources();
            }
        });
        return false;
    },

    changeLocale: function (selectedConnection, selectedDataSource, newLocale) {
        var localeChanged;
        if (selectedConnection.advanced == null) {
            this.setUserFeedback("Change the URL connection string to advanced");
            localeChanged = false;
        } else {
            var referenceText = "locale=";
            var start = selectedConnection.advanced.toLowerCase().indexOf(referenceText);
            if (start == -1) {
                this.setUserFeedback("no locale defined in connection string of data source");
                localeChanged = false;
            }
            else {
                start = start + referenceText.length;
                var end = selectedConnection.advanced.indexOf(";", start);
                var oldLocale = selectedConnection.advanced.substring(start, end);
                selectedDataSource.set({"advanced": selectedConnection.advanced.replace(oldLocale, newLocale)});
                localeChanged = true;
            }
        }
        return localeChanged;
    },

    saveDataSource: function (selectedDataSource) {
        var this_plugin = this;
        selectedDataSource.save({}, {
                data: JSON.stringify(selectedDataSource.attributes),
                contentType: "application/json",
                success: function (model, respose, options) {
//                    this_plugin.setUserFeedback("The model has been updated to the server");
                },
                error: function (model, xhr, options) {
                    this_plugin.setUserFeedback("Something went wrong while updating the model: " + xhr.responseText);
                }
            }
        );
    },

    refreshDatasources: function () {
        // keep reference to this plugin
        var this_plugin = this;
        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.clear();
        }

        Saiku.session.sessionworkspace.clear();

        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.setItem('saiku-version', Settings.VERSION);
        }

        Saiku.session.sessionworkspace.fetch({success: this_plugin.processDatasourcesWithoutAddingNewTab}, {});
    },

    processDatasourcesWithoutAddingNewTab: function (model, response) {
        // Save session in localStorage for other tabs to use
        if (typeof localStorage !== "undefined" && localStorage && localStorage.getItem('session') === null) {
            localStorage.setItem('session', JSON.stringify(response));

            // Set expiration on localStorage to one day in the future
            var expires = (new Date()).getTime() + Settings.LOCALSTORAGE_EXPIRATION;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem('expiration', expires);
            }
        }

        // Generate cube navigation for reuse
        Saiku.session.sessionworkspace.cube_navigation = _.template($("#template-cubes").html())({
            connections: response
        });

        // Create cube objects
        Saiku.session.sessionworkspace.cube = {};
        Saiku.session.sessionworkspace.connections = response;
        _.delay(Saiku.session.sessionworkspace.prefetch_dimensions, 20);

        this.show();
    },

    setUserFeedback: function (msg) {
        $("#feedback").html(msg);
    }
});

var DataSource = Backbone.Model.extend({
//    url: "admin/datasources",
    urlRoot: "admin/datasources",
    refresh: function () {
        $.ajax({
            type: 'GET',
            url: Settings.REST_URL + "admin" + "/datasources/" + this.get("connectionname") + "/refresh"
        });
    }
});

/**
 * Start Plugin
 */
Saiku.events.bind('session:new', function (session) {

    function new_workspace(args) {
        // Add stats element
        if (typeof args.workspace.changeLocale == "undefined") {
            args.workspace.changeLocale = new ChangeLocale({ workspace: args.workspace });
        }
    };

    function clear_workspace(args) {
        if (typeof args.workspace.changeLocale != "undefined") {
            $(args.workspace.changeLocale.localeOptionsScreen).hide();
            $(args.workspace.changeLocale.el).parents().find('.workspace_results table').show();
            $(args.workspace.changeLocale.el).hide();
        }
    };

    // Attach stats to existing tabs
    for (var i = 0; i < Saiku.tabs._tabs.length; i++) {
        var tab = Saiku.tabs._tabs[i];
        new_workspace({
            workspace: tab.content
        });
    }
    ;

    // Attach stats to future tabs
    Saiku.session.bind("workspace:new", new_workspace);
    Saiku.session.bind("workspace:clear", clear_workspace);
});
<commit_msg>ChangeLocale: adapt plugin to use new back-end method<commit_after>/*
 *   Copyright 2015 OSBI
 *
 *   Created by Andreas Verleysen
 */

/**
 * Changes the locale of the cube
 */
var ChangeLocale = Backbone.View.extend({

    initialize: function (args) {
        this.visible = false;

        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("changeLocale_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "show", "handleClick", "processDatasourcesWithoutAddingNewTab", "toggleLocaleScreen", "setUserFeedback");

        this.datasources = new Connections({}, { dialog: this });

        // Add change locale button to saiku toolbar
        this.add_button();
        this.workspace.toolbar.changeLocale = this.show;

        // Create locale screen
        this.localeOptionsScreen = $("
  " +
            "
" +
            "
         <button class='button' id='en_US' > English </button>
 " +
            "
   	    <button class='button' id='nl_BE' > Dutch </button>
 " +
            "
   	    <button class='button' id='fr_FR' > French </button>
" +
            "
" +
            "
 " +
            " Choose your language ")
        ;

        // attach event handler
        this.localeOptionsScreen.find('button').click(this.handleClick);

        //function for menu
        $("#nav ul ").css({display: "none"}); // Opera Fix
        $("#nav li").hover(function () {
            $(this).find('ul:first').css({visibility: "visible", display: "none"}).show(400);
        }, function () {
            $(this).find('ul:first').css({visibility: "hidden"});
        });

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.localeOptionsScreen.hide());

    },

    add_button: function () {
        var $chart_button =
            $('')
                .css({  'background-image': "url('js/saiku/plugins/ChangeLocale/images/change_locale.png')",
                    'background-repeat': 'no-repeat',
                    'background-position': '7px 7px'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function (event, ui) {
        this.toggleWorkspaceWithLocaleScreen();
//        if ($(event.target).hasClass('on')) {
//            this.render();
//        } else {
//            this.workspace.table.render({ data: this.workspace.query.result.lastresult() });
//        }
    },

    toggleWorkspaceWithLocaleScreen: function () {
        this.toggleWorkSpace();
        this.toggleLocaleScreen();
    },

    toggleWorkSpace: function () {
        $(this.workspace.table.el).toggle();
        $(this.el).toggle();
    },

    toggleLocaleScreen: function () {
        if (this.visible) {
            this.setUserFeedback("Choose language");
            $(this.workspace.el).find('#languageOptions').addClass('hide');
            $(this.workspace.el).find('#feedback').addClass('hide');
            this.workspace.toolbar.run_query();
        }
        else {
            $(this.workspace.el).find('#languageOptions').removeClass('hide').show();
            $(this.workspace.el).find('#feedback').removeClass('hide').show();
        }
        $(event.target).toggleClass('on');
        this.visible = !this.visible;
    },

    handleClick: function (event) {
        // Keep a reference to the main plugin object.
        var this_p = this;
        this_p.setUserFeedback("Changing locale...");
        // get selected locale
        var newLocale = $(event.target).attr('id');

        // get current selected connection name (without URL data)
        var selectedCube = $(".cubes option:selected").val();
        var selectedConnectionName = selectedCube.substring(0, selectedCube.indexOf("/"));

        // Get all connections from back-end
        var getUrl = Settings.REST_URL + "admin" + "/datasources";
        $.get(getUrl, function (data) {
            var allConnections = data;
            // match
            var selectedConnection = _.find(allConnections, function (connection) {
                return connection.connectionname == selectedConnectionName
            });

            var selectedDataSource = new DataSource(selectedConnection);

            this_p.setUserFeedback("Persisting locale...");
            this_p.persistLocaleOfDataSource(selectedDataSource, newLocale);
            this_p.setUserFeedback("Refreshing data source with new locale...");
            this_p.refreshDatasources();

        });
        return false;
    },

    persistLocaleOfDataSource: function (selectedDataSource, newLocale) {
        putUrl = Settings.REST_URL + "admin/datasources/" + selectedDataSource.id + "/locale";

        var xhr = new XMLHttpRequest();
        xhr.open('PUT', putUrl);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(newLocale);
    },

    refreshDatasources: function () {
        // keep reference to this plugin
        var this_plugin = this;
        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.clear();
        }

        Saiku.session.sessionworkspace.clear();

        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.setItem('saiku-version', Settings.VERSION);
        }

        Saiku.session.sessionworkspace.fetch({success: this_plugin.processDatasourcesWithoutAddingNewTab}, {});
    },

    processDatasourcesWithoutAddingNewTab: function (model, response) {
        // Save session in localStorage for other tabs to use
        if (typeof localStorage !== "undefined" && localStorage && localStorage.getItem('session') === null) {
            localStorage.setItem('session', JSON.stringify(response));

            // Set expiration on localStorage to one day in the future
            var expires = (new Date()).getTime() + Settings.LOCALSTORAGE_EXPIRATION;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem('expiration', expires);
            }
        }

        // Generate cube navigation for reuse
        Saiku.session.sessionworkspace.cube_navigation = _.template($("#template-cubes").html())({
            connections: response
        });

        // Create cube objects
        Saiku.session.sessionworkspace.cube = {};
        Saiku.session.sessionworkspace.connections = response;
        _.delay(Saiku.session.sessionworkspace.prefetch_dimensions, 20);

        this.show();
    },

    setUserFeedback: function (msg) {
        $("#feedback").html(msg);
    }
});

var DataSource = Backbone.Model.extend({
//    url: "admin/datasources",
    urlRoot: "admin/datasources",
    refresh: function () {
        $.ajax({
            type: 'GET',
            url: Settings.REST_URL + "admin" + "/datasources/" + this.get("connectionname") + "/refresh"
        });
    }
});

/**
 * Start Plugin
 */
Saiku.events.bind('session:new', function (session) {

    function new_workspace(args) {
        // Add stats element
        if (typeof args.workspace.changeLocale == "undefined") {
            args.workspace.changeLocale = new ChangeLocale({ workspace: args.workspace });
        }
    };

    function clear_workspace(args) {
        if (typeof args.workspace.changeLocale != "undefined") {
            $(args.workspace.changeLocale.localeOptionsScreen).hide();
            $(args.workspace.changeLocale.el).parents().find('.workspace_results table').show();
            $(args.workspace.changeLocale.el).hide();
        }
    };

    // Attach stats to existing tabs
    for (var i = 0; i < Saiku.tabs._tabs.length; i++) {
        var tab = Saiku.tabs._tabs[i];
        new_workspace({
            workspace: tab.content
        });
    }
    ;

    // Attach stats to future tabs
    Saiku.session.bind("workspace:new", new_workspace);
    Saiku.session.bind("workspace:clear", clear_workspace);
});
<|endoftext|>


<commit_before>const express = require('express')

const port = process.env.PORT || 8080
const basedir = __dirname + '/public'

const app = express()

// HTML5 pushState: serve static files or index.html for everything else
app.use(express.static(basedir))
app.get('*', (req, res) => res.sendFile('index.html', { root: basedir }))

// Start listening
app.listen(port)
module.exports.getApp = app;
console.log(`Server started at port ${port}`)
<commit_msg>without .getApp in server.js<commit_after>const express = require('express')

const port = process.env.PORT || 8080
const basedir = __dirname + '/public'

const app = express()

// HTML5 pushState: serve static files or index.html for everything else
app.use(express.static(basedir))
app.get('*', (req, res) => res.sendFile('index.html', { root: basedir }))

// Start listening
app.listen(port)
// module.exports.getApp = app
console.log(`Server started at port ${port}`)
<|endoftext|>


<commit_before>import styled from 'styled-components'

import {Button, declareFont, scale, theme} from '../index'
import {interactiveStyling} from '../utilStyles'

export const StyledIconWrapper = styled.div`
  margin-left: 4px;
  margin-right: -3px;
`

export const StyledIconButtonWrapper = styled(Button)`
  padding-left: .9rem;
  padding-right: .9rem;
`

export const StyledPopper = styled.div`
  box-shadow: 0 0 5px rgba(0, 0, 0, .3);
  border: 1px solid ${theme.color('secondaryLight')};
  z-index: 1001;
`

export const StyledMenuItem = styled.div`
  min-width: 200px;
  max-width: 300px;
  background-color: ${theme.color('secondaryLight')};
`

export const StyledItemLabel = styled.div`
  cursor: ${({hasOnClick}) => hasOnClick ? 'pointer' : 'default'};
  ${declareFont()}
  ${interactiveStyling}
  padding:
    ${scale.space(-2)}
    ${scale.space(-0.5)}
    ${scale.space(-2)}
    calc(${scale.space(-0.5)} + ${({level}) => (level || 0) * 5 + 'px'});
  font-weight: ${({isGroup}) => isGroup ? theme.fontWeight('bold') : theme.fontWeight('regular')};
  box-shadow: none;
`
<commit_msg>fix(tocco-ui): fix menu in action<commit_after>import styled from 'styled-components'

import {Button, declareFont, scale, theme} from '../index'
import {interactiveStyling} from '../utilStyles'

export const StyledIconWrapper = styled.div`
  margin-left: 4px;
  margin-right: -3px;
`

export const StyledIconButtonWrapper = styled(Button)`
  padding-left: .9rem;
  padding-right: .9rem;
`

export const StyledPopper = styled.div`
  box-shadow: 0 0 5px rgba(0, 0, 0, .3);
  border: 1px solid ${theme.color('secondaryLight')};
  z-index: 99999;
`

export const StyledMenuItem = styled.div`
  min-width: 200px;
  max-width: 300px;
  background-color: ${theme.color('secondaryLight')};
`

export const StyledItemLabel = styled.div`
  cursor: ${({hasOnClick}) => hasOnClick ? 'pointer' : 'default'};
  ${declareFont()}
  ${interactiveStyling}
  padding:
    ${scale.space(-2)}
    ${scale.space(-0.5)}
    ${scale.space(-2)}
    calc(${scale.space(-0.5)} + ${({level}) => (level || 0) * 5 + 'px'});
  font-weight: ${({isGroup}) => isGroup ? theme.fontWeight('bold') : theme.fontWeight('regular')};
  box-shadow: none;
`
<|endoftext|>


<commit_before>var controllers = angular.module('controllers');

controllers.controller('MapCtrl', ['$scope', '$rootScope', '$location', '$translate', 'SiteSpecificConfig', 'Search','_', 'Language', 'SectorList', function ($scope, $rootScope, $location, $translate, SiteSpecificConfig, Search, _, Language, SectorList) {
    // Mapbox doesn't need its own var - it automatically attaches to Leaflet's L.
    require('mapbox.js');
    // Use Awesome Markers lib to produce font-icon map markers
    require('../../../src/leaflet.awesome-markers.js');
    // Marker clustering
    require('../../../node_modules/leaflet.markercluster/dist/leaflet.markercluster.js');

    // Initialize the map, using Affinity Bridge's mapbox account.
    map = L.mapbox.map('mapContainer');

    if (SiteSpecificConfig.mapTileAPI == null) {
        L.mapbox.tileLayer('affinitybridge.ia7h38nj').addTo(map);
    } else {
        L.tileLayer(SiteSpecificConfig.mapTileAPI).addTo(map);
    }

    map.locate()
        .on('locationfound', function(e) {
            L.circleMarker(e.latlng).addTo(map);
        })
        .on('locationerror', function(e) {
            console.error(e.message);
        });

    /* TODO: Make a inputs dynamic
    *
    *   1. Need users location input
    *   2. Need proximity radius
    */

        // Geolocation object to input user's location and the selected locations
        // var geoLocationObject = {
        //     latitude: 35.7333333333333,
        //     longitude: 30.2,
        //     radius: 1000000
        // }

        // Filter by proximity
        // Search.filterByProxmity(geoLocationObject);

    // Initialize the empty layer for the markers, and add it to the map.
    // TODO: don't use global var here
    clusterLayer = new L.MarkerClusterGroup({
        zoomToBoundsOnClick: false,
        spiderfyDistanceMultiplier: 2,
        showCoverageOnHover: false
    }).addTo(map);
    // When user clicks on a cluster, zoom directly to its bounds.  If we don't do this,
    // they have to click repeatedly to zoom in enough for the cluster to spiderfy.
    clusterLayer.on('clusterclick', function (a) {
        // Close any popups that are open already. This helps if we came via "show on map" link,
        // which spawns an unbound popup.
        map.closePopup();
        // If the markers in this cluster are all in the same place, spiderfy on click.
        var bounds = a.layer.getBounds();
        if (bounds._northEast.equals(bounds._southWest)) {
            a.layer.spiderfy();
        } else {
            // If the markers in this cluster are NOT all in the same place, zoom in on them.
            a.layer.zoomToBounds();
        }
    });

    // TODO: don't make global but needed now for use in search controller
    polygonLayer = L.geoJson();

    // TODO temporarily removed.
    if (SiteSpecificConfig.includePolygons) {
        map.addLayer(polygonLayer);
    }

    jQuery.getJSON( "src/polygons.json", function( polygonData ) {
        // Create the polygon layer and add to the map.
        polygonLayer.addData(polygonData);

        polygonLayer.getLayers().forEach(function(f) {
            f.setStyle({
                opacity: 0.3
            });

            L.DomEvent.addListener(f, 'mouseover', function(e) {
                //if(this._activeFilters.indexOf(polygonLayer.getLayerId(e.target)) < 0) {
                    e.target.setStyle({
                        opacity: 0.5
                    });
                //}
            }, this);

            L.DomEvent.addListener(f, 'mouseout', function(e) {
                //if(this._activeFilters.indexOf(polygonLayer.getLayerId(e.target)) < 0) {
                    e.target.setStyle({
                        opacity: 0.3
                    });
                //}
            }, this);
        });
    });

    var onChange = function(event) {
        var results = Search.currResults();

        // Clear all the map markers.
        clusterLayer.clearLayers();

        // Initialize the list-view output.
        //var listOutput = '
Services
';

        // Initialize a list where we'll store the current markers for easy reference when
        // building the "show on map" functionality.  TODO: can we streamline this out?
        var markers = {};

        // Loop through the filtered results, adding the markers back to the map.
        results.forEach( function (feature) {
            // Add the filtered markers back to the map's data layer
            clusterLayer.addLayer(feature.marker);
            // Store the marker for easy reference.
            markers[feature.id] = feature.marker;
            // Build the output for the filtered list view
        } );

        map.fitBounds(clusterLayer.getBounds());

        // TODO: do this zooming when someone clicks into the service detailed view

        // Bind "show on map" behavior.  Do this here because now the list exists.
        //$(".show-on-map").click(function(e) {
        //    // Get the unique ID of this service.
        //    var id = e.target.id;
        //    // Close any popups that are open already.
        //    map.closePopup();
        //    // Fire the map/list toggler click event, to switch to viewing the map.
        //    $("#map-list-toggler").click();
        //    // Pan and zoom the map.
        //    map.panTo(markers[id]._latlng);
        //    if (map.getZoom() < 12) { map.setZoom(12); }
        //    // Clone the popup for this marker.  We'll show it at the correct lat-long, but
        //    // unbound from the marker.  We do this in case the marker is in a cluster.
        //    var unboundPopup = markers[id].getPopup();
        //    // Send the service's unique ID as the className of the popup, so that the "Show
        //    // details" binding will work as usual when the popupopen event fires; also, offset
        //    // the Y position so the popup is a little bit above the marker or cluster.
        //    map.openPopup(L.popup({className:id, offset: new L.Point(0,-25)})
        //        .setLatLng(markers[id]._latlng)
        //        .setContent(markers[id].getPopup()._content));
        //});
    };

    $rootScope.$on('FILTER_CHANGED', onChange);

    // Doing some stuff for the results views here because this controller is active
    // for the whole application
    var mc = $('#mapContainer');

    // if the user is on mobile and has the map only partly showing, when they start to scroll
    // we want to hide the map and show the whole results container since it's too small to try to user
    // when the map is showing
    $("#serviceList").scroll(_.throttle(function() {
        if (!mc.hasClass('map-hide')) {
            toggleMap();
        }
    }, 10));

    // HACK: using a global here so we can use an onclick="toggleMap()"
    toggleMap = function() {
        mc.toggleClass('map-hide');
        map.invalidateSize();
    }
}]);
<commit_msg>See #71. Limit minZoom so world doesnt repeat.<commit_after>var controllers = angular.module('controllers');

controllers.controller('MapCtrl', ['$scope', '$rootScope', '$location', '$translate', 'SiteSpecificConfig', 'Search','_', 'Language', 'SectorList', function ($scope, $rootScope, $location, $translate, SiteSpecificConfig, Search, _, Language, SectorList) {
    // Mapbox doesn't need its own var - it automatically attaches to Leaflet's L.
    require('mapbox.js');
    // Use Awesome Markers lib to produce font-icon map markers
    require('../../../src/leaflet.awesome-markers.js');
    // Marker clustering
    require('../../../node_modules/leaflet.markercluster/dist/leaflet.markercluster.js');

    // Initialize the map, using Affinity Bridge's mapbox account.
    map = L.mapbox.map('mapContainer', null, { minZoom: 3 });

    if (SiteSpecificConfig.mapTileAPI == null) {
        L.mapbox.tileLayer('affinitybridge.ia7h38nj').addTo(map);
    } else {
        L.tileLayer(SiteSpecificConfig.mapTileAPI).addTo(map);
    }

    map.locate()
        .on('locationfound', function(e) {
            L.circleMarker(e.latlng).addTo(map);
        })
        .on('locationerror', function(e) {
            console.error(e.message);
        });

    /* TODO: Make a inputs dynamic
    *
    *   1. Need users location input
    *   2. Need proximity radius
    */

        // Geolocation object to input user's location and the selected locations
        // var geoLocationObject = {
        //     latitude: 35.7333333333333,
        //     longitude: 30.2,
        //     radius: 1000000
        // }

        // Filter by proximity
        // Search.filterByProxmity(geoLocationObject);

    // Initialize the empty layer for the markers, and add it to the map.
    // TODO: don't use global var here
    clusterLayer = new L.MarkerClusterGroup({
        zoomToBoundsOnClick: false,
        spiderfyDistanceMultiplier: 2,
        showCoverageOnHover: false
    }).addTo(map);
    // When user clicks on a cluster, zoom directly to its bounds.  If we don't do this,
    // they have to click repeatedly to zoom in enough for the cluster to spiderfy.
    clusterLayer.on('clusterclick', function (a) {
        // Close any popups that are open already. This helps if we came via "show on map" link,
        // which spawns an unbound popup.
        map.closePopup();
        // If the markers in this cluster are all in the same place, spiderfy on click.
        var bounds = a.layer.getBounds();
        if (bounds._northEast.equals(bounds._southWest)) {
            a.layer.spiderfy();
        } else {
            // If the markers in this cluster are NOT all in the same place, zoom in on them.
            a.layer.zoomToBounds();
        }
    });

    // TODO: don't make global but needed now for use in search controller
    polygonLayer = L.geoJson();

    // TODO temporarily removed.
    if (SiteSpecificConfig.includePolygons) {
        map.addLayer(polygonLayer);
    }

    jQuery.getJSON( "src/polygons.json", function( polygonData ) {
        // Create the polygon layer and add to the map.
        polygonLayer.addData(polygonData);

        polygonLayer.getLayers().forEach(function(f) {
            f.setStyle({
                opacity: 0.3
            });

            L.DomEvent.addListener(f, 'mouseover', function(e) {
                //if(this._activeFilters.indexOf(polygonLayer.getLayerId(e.target)) < 0) {
                    e.target.setStyle({
                        opacity: 0.5
                    });
                //}
            }, this);

            L.DomEvent.addListener(f, 'mouseout', function(e) {
                //if(this._activeFilters.indexOf(polygonLayer.getLayerId(e.target)) < 0) {
                    e.target.setStyle({
                        opacity: 0.3
                    });
                //}
            }, this);
        });
    });

    var onChange = function(event) {
        var results = Search.currResults();

        // Clear all the map markers.
        clusterLayer.clearLayers();

        // Initialize the list-view output.
        //var listOutput = '
Services
';

        // Initialize a list where we'll store the current markers for easy reference when
        // building the "show on map" functionality.  TODO: can we streamline this out?
        var markers = {};

        // Loop through the filtered results, adding the markers back to the map.
        results.forEach( function (feature) {
            // Add the filtered markers back to the map's data layer
            clusterLayer.addLayer(feature.marker);
            // Store the marker for easy reference.
            markers[feature.id] = feature.marker;
            // Build the output for the filtered list view
        } );

        map.fitBounds(clusterLayer.getBounds());

        // TODO: do this zooming when someone clicks into the service detailed view

        // Bind "show on map" behavior.  Do this here because now the list exists.
        //$(".show-on-map").click(function(e) {
        //    // Get the unique ID of this service.
        //    var id = e.target.id;
        //    // Close any popups that are open already.
        //    map.closePopup();
        //    // Fire the map/list toggler click event, to switch to viewing the map.
        //    $("#map-list-toggler").click();
        //    // Pan and zoom the map.
        //    map.panTo(markers[id]._latlng);
        //    if (map.getZoom() < 12) { map.setZoom(12); }
        //    // Clone the popup for this marker.  We'll show it at the correct lat-long, but
        //    // unbound from the marker.  We do this in case the marker is in a cluster.
        //    var unboundPopup = markers[id].getPopup();
        //    // Send the service's unique ID as the className of the popup, so that the "Show
        //    // details" binding will work as usual when the popupopen event fires; also, offset
        //    // the Y position so the popup is a little bit above the marker or cluster.
        //    map.openPopup(L.popup({className:id, offset: new L.Point(0,-25)})
        //        .setLatLng(markers[id]._latlng)
        //        .setContent(markers[id].getPopup()._content));
        //});
    };

    $rootScope.$on('FILTER_CHANGED', onChange);

    // Doing some stuff for the results views here because this controller is active
    // for the whole application
    var mc = $('#mapContainer');

    // if the user is on mobile and has the map only partly showing, when they start to scroll
    // we want to hide the map and show the whole results container since it's too small to try to user
    // when the map is showing
    $("#serviceList").scroll(_.throttle(function() {
        if (!mc.hasClass('map-hide')) {
            toggleMap();
        }
    }, 10));

    // HACK: using a global here so we can use an onclick="toggleMap()"
    toggleMap = function() {
        mc.toggleClass('map-hide');
        map.invalidateSize();
    }
}]);
<|endoftext|>


<commit_before>function onRequest(context) {
    // var log = new Log("user-listing");
    var userModule = require("/modules/user.js").userModule;
    var allUsers = userModule.getUsers();
    if (allUsers.length == 1) {
        context.users = [];
        context.listUsersStatus = "Oops, Sorry, No other Users found.";
    } else {
        var i, filteredUserList = [];
        for (i = 0; i < allUsers.length; i++) {
            //if (String(allUsers[i].username) != "admin") {
                filteredUserList.push(allUsers[i]);
            //}
        }
        context.users = filteredUserList;
        context.listUsersStatus = "Total number of Users found : " + filteredUserList.length;
    }
    context.permissions = userModule.getUIPermissions();
    return context;
}<commit_msg>User Listing update<commit_after>function onRequest(context) {
    // var log = new Log("user-listing");
    var userModule = require("/modules/user.js").userModule;
    var allUsers = userModule.getUsers();
    if (allUsers.length == 0) {
        context.users = [];
        context.listUsersStatus = "Oops, Sorry, No other Users found.";
    } else {
        var i, filteredUserList = [];
        for (i = 0; i < allUsers.length; i++) {
            //if (String(allUsers[i].username) != "admin") {
                filteredUserList.push(allUsers[i]);
            //}
        }
        context.users = filteredUserList;
        context.listUsersStatus = "Total number of Users found : " + filteredUserList.length;
    }
    context.permissions = userModule.getUIPermissions();
    return context;
}<|endoftext|>


<commit_before>/*
Add this file to $(ipython locate)/nbextensions/vim.js
And load it with:

require(["nbextensions/vim"], function (vim_extension) {
    console.log('vim extension loaded');
    vim_extension.load_extension();
});

*/
define( function () {
    var token_name = "vim_github_token";

    var load_extension = function () {
        $([IPython.events]).on("notebook_loaded.Notebook", function () {
          console.log("notebook loaded event");
        });
    };

    return {
        load_extension : load_extension,
    };
});

// Monkey patch: KeyboardManager.handle_keydown
// Diff: disable this handler

IPython.KeyboardManager.prototype.handle_keydown = function (event) {
  return;
}

// Monkey patch: KeyboardManager.register_events
// Diff: disable this handler
IPython.KeyboardManager.prototype.register_events = function (e) {
  return;
}
// Monkey patch insert_cell_below
// Diff: Select cell after insert
IPython.Notebook.prototype.insert_cell_below = function (type, index) {
    index = this.index_or_selected(index);
    var cell = this.insert_cell_at_index(type, index+1);
    this.select(this.find_cell_index(cell));
    return cell;
};

// Monkey patch insert_cell_above
// Diff: Select cell after insert
IPython.Notebook.prototype.insert_cell_above = function (type, index) {
    index = this.index_or_selected(index);
    var cell = this.insert_cell_at_index(type, index);
    this.select(this.find_cell_index(cell));
    return cell;
};

// Monkey patch: execute_cell
// Diff: don't switch to command mode
IPython.Notebook.prototype.execute_cell = function () {
    var cell = this.get_selected_cell();
    var cell_index = this.find_cell_index(cell);
    cell.execute();
    this.set_dirty(true);
};

// Focus editor on select
IPython.CodeCell.prototype.select = function () {
    var cont = IPython.Cell.prototype.select.apply(this);
    if (cont) {
        this.code_mirror.refresh();
        this.focus_editor();
        this.auto_highlight();
    }
    return cont;
};

// Override CodeCell codemirror
old_handle_keyevent = IPython.CodeCell.prototype._handle_codemirror_keyevent ;
IPython.CodeCell.prototype._base_handle_codemirror_keyevent = old_handle_keyevent;

IPython.CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
  var key   = IPython.utils.keycodes;
  if (event.which !== key.ESC) {
    var ret =  this._base_handle_codemirror_keyevent(editor, event);

    if(ret != null) {
      return ret;
    }
  }

  if(event.type == 'keydown') {
    ret = IPython.VIM.keyDown(this.notebook, event);
    return ret;
  }
  return false;
}

// Override TextCell keydown
// Really just here to handle the render/editing of text cells
// Might need to consider also using codemirror keyevent
old_handle_keydown = IPython.TextCell.prototype.handle_keydown ;
IPython.TextCell.prototype._base_handle_keydown = old_handle_keydown;

IPython.TextCell.prototype.handle_keydown = function (event) {
  var ret =  this._base_handle_keydown(event);
  if(ret != null) {
    return ret;
  }

  if( event.type == 'keydown') {
    ret = IPython.VIM.keyDown(this.notebook, event);
    return ret;
  }
  return false;
}

IPython.Notebook.prototype.setVIMode = function (mode) {
  var cell = this.get_selected_cell();
  cm = cell.code_mirror;
  if(cm) {
    if(mode == 'INSERT') {
      CodeMirror.keyMap.vim.I(cm);
    }
  }
}

var IPython = (function (IPython) {

  var NormalMode = {};
  var InsertMode = {};

  var VIM = function() {;};

  VIM.prototype.keyDown = function(that, event) {
    var cell = that.get_selected_cell();
    var vim_mode = cell.code_mirror.getOption('keyMap');

    ret = false;

    if(vim_mode == 'vim') {
      ret = NormalMode.keyDown(that, event);
    }

    if(vim_mode == 'vim-insert') {
      ret = InsertMode.keyDown(that, event);
    }

    if(ret) {
      event.preventDefault();
      return true;
    }
  };

  NormalMode.keyDown = function(that, event) {
    var cell = that.get_selected_cell();
    var cell_type = cell.cell_type;
    var textcell = cell instanceof IPython.TextCell;

    // ` : enable console
    if (event.which === 192) {
      $(IPython.console_book.element).toggle();
      IPython.console_book.focus_selected();
      return true;
    }

    // K: up cell
    if (event.which === 75 && (event.shiftKey || event.metaKey))
    {
        that.select_prev();
        return true;
    }
    // k: up
    if (event.which === 75 && !event.shiftKey)
    {
        var cursor = cell.code_mirror.getCursor();
        if (cursor.line === 0) {
          that.select_prev();
          var new_cell = that.get_selected_cell();
          if(new_cell.code_mirror) { // bottom line, same ch position
            var last_line = new_cell.code_mirror.lastLine();
            // NOTE: a current code_mirror bug doesn't respect the new cursor opsition
            // https://github.com/marijnh/CodeMirror/issues/2289
            new_cell.code_mirror.setCursor(last_line, cursor.ch);
          }
          return true;
        }
    }
    // J: down cell
    if (event.which === 74 && (event.shiftKey || event.metaKey)) {
        that.select_next();
        return true;
    }
    // j: down
    if (event.which === 74 && !event.shiftKey) {
      var cursor = cell.code_mirror.getCursor();
      var ch = cursor.ch;
      if (cursor.line === (cell.code_mirror.lineCount()-1)) {
        that.select_next();
        var new_cell = that.get_selected_cell();
        if(new_cell.code_mirror) { // bottom line, same ch position
          // NOTE: a current code_mirror bug doesn't respect the new cursor opsition
          // https://github.com/marijnh/CodeMirror/issues/2289
          new_cell.code_mirror.setCursor(0, cursor.ch);
        }
        return true;
      };
    }
    // Y: copy cell
    if (event.which === 89 && event.shiftKey) {
        that.copy_cell();
        return true;
    }
    // D: delete cell / cut
    if (event.which === 68 && event.shiftKey) {
        that.cut_cell();
        return true;
    }
    // P: paste cell
    if (event.which === 80 && event.shiftKey) {
        that.paste_cell();
        return true;
    }
    // B: open new cell below
    if (event.which === 66 && event.shiftKey) {
        that.insert_cell_below('code');
        that.setVIMode('INSERT');
        return true;
    }
    // shift+O or apple + O: open new cell below
    // I know this is wrong but i hate hitting A
    if (event.which === 79 && (event.metaKey || event.shiftKey)) {
        that.insert_cell_below('code');
        that.setVIMode('INSERT');
        return true;
    }
    // A: open new cell above
    if (event.which === 65 && event.shiftKey) {
      that.insert_cell_above('code');
      that.setVIMode('INSERT');
      return true;
    }
    // control/apple E: execute (apple - E is easier than shift E)
    if ((event.ctrlKey || event.metaKey) && event.keyCode==69) {
      that.execute_cell();
      return true;
    }
    // E:  execute
    if (event.which === 69 && event.shiftKey) {
      that.execute_cell();
      return true;
    }
    // F: toggle output
    if (event.which === 70 && event.shiftKey) {
      that.toggle_output();
      return true;
    }
    // M: markdown
    if (event.which === 77 && event.shiftKey) {
      that.to_markdown();
      return true;
    }
    // C: codecell
    if (event.which === 77 && event.shiftKey) {
      that.to_code();
      return true;
    }
    // i: insert. only relevant on textcell
    var rendered = cell.rendered;
    if (textcell && rendered && event.which === 73 && !event.shiftKey) {
      cell.edit();
      return false;
    }

    // esc: get out of insert and render textcell
    if (textcell && !rendered && event.which === 27 && !event.shiftKey) {
        cell.render();
        return false;
    }
  };

  InsertMode.keyDown = function(that, event) {
    var cell = that.get_selected_cell();
    var cell_type = cell.cell_type;
    var textcell = cell instanceof IPython.TextCell;

    // control/apple E: execute (apple - E is easier than shift E)
    if ((event.ctrlKey || event.metaKey) && event.keyCode==69) {
      that.execute_cell();
      return true;
    }
    if (event.which === 74 && (event.metaKey)) {
        that.select_next();
        return true;
    }
      if (event.which === 75 && (event.metaKey)) {
          that.select_prev();
          return true;
      }
  };

  IPython.VIM = new VIM();
  return IPython;

}(IPython));
<commit_msg>fix on our side for ttps://github.com/marijnh/CodeMirror/issues/2289<commit_after>/*
Add this file to $(ipython locate)/nbextensions/vim.js
And load it with:

require(["nbextensions/vim"], function (vim_extension) {
    console.log('vim extension loaded');
    vim_extension.load_extension();
});

*/
define( function () {
    var token_name = "vim_github_token";

    var load_extension = function () {
        $([IPython.events]).on("notebook_loaded.Notebook", function () {
          console.log("notebook loaded event");
        });
    };

    return {
        load_extension : load_extension,
    };
});

// Monkey patch: KeyboardManager.handle_keydown
// Diff: disable this handler

IPython.KeyboardManager.prototype.handle_keydown = function (event) {
  return;
}

// Monkey patch: KeyboardManager.register_events
// Diff: disable this handler
IPython.KeyboardManager.prototype.register_events = function (e) {
  return;
}
// Monkey patch insert_cell_below
// Diff: Select cell after insert
IPython.Notebook.prototype.insert_cell_below = function (type, index) {
    index = this.index_or_selected(index);
    var cell = this.insert_cell_at_index(type, index+1);
    this.select(this.find_cell_index(cell));
    return cell;
};

// Monkey patch insert_cell_above
// Diff: Select cell after insert
IPython.Notebook.prototype.insert_cell_above = function (type, index) {
    index = this.index_or_selected(index);
    var cell = this.insert_cell_at_index(type, index);
    this.select(this.find_cell_index(cell));
    return cell;
};

// Monkey patch: execute_cell
// Diff: don't switch to command mode
IPython.Notebook.prototype.execute_cell = function () {
    var cell = this.get_selected_cell();
    var cell_index = this.find_cell_index(cell);
    cell.execute();
    this.set_dirty(true);
};

// Focus editor on select
IPython.CodeCell.prototype.select = function () {
    var cont = IPython.Cell.prototype.select.apply(this);
    if (cont) {
        this.code_mirror.refresh();
        this.focus_editor();
        this.auto_highlight();
    }
    return cont;
};

// Override CodeCell codemirror
old_handle_keyevent = IPython.CodeCell.prototype._handle_codemirror_keyevent ;
IPython.CodeCell.prototype._base_handle_codemirror_keyevent = old_handle_keyevent;

IPython.CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
  var key   = IPython.utils.keycodes;
  if (event.which !== key.ESC) {
    var ret =  this._base_handle_codemirror_keyevent(editor, event);

    if(ret != null) {
      return ret;
    }
  }

  if(event.type == 'keydown') {
    ret = IPython.VIM.keyDown(this.notebook, event);
    return ret;
  }
  return false;
}

// Override TextCell keydown
// Really just here to handle the render/editing of text cells
// Might need to consider also using codemirror keyevent
old_handle_keydown = IPython.TextCell.prototype.handle_keydown ;
IPython.TextCell.prototype._base_handle_keydown = old_handle_keydown;

IPython.TextCell.prototype.handle_keydown = function (event) {
  var ret =  this._base_handle_keydown(event);
  if(ret != null) {
    return ret;
  }

  if( event.type == 'keydown') {
    ret = IPython.VIM.keyDown(this.notebook, event);
    return ret;
  }
  return false;
}

IPython.Notebook.prototype.setVIMode = function (mode) {
  var cell = this.get_selected_cell();
  cm = cell.code_mirror;
  if(cm) {
    if(mode == 'INSERT') {
      CodeMirror.keyMap.vim.I(cm);
    }
  }
}

var IPython = (function (IPython) {

  var NormalMode = {};
  var InsertMode = {};

  var VIM = function() {;};

  VIM.prototype.keyDown = function(that, event) {
    var cell = that.get_selected_cell();
    var vim_mode = cell.code_mirror.getOption('keyMap');

    ret = false;

    if(vim_mode == 'vim') {
      ret = NormalMode.keyDown(that, event);
    }

    if(vim_mode == 'vim-insert') {
      ret = InsertMode.keyDown(that, event);
    }

    if(ret) {
      event.preventDefault();
      return true;
    }
  };

  NormalMode.keyDown = function(that, event) {
    var cell = that.get_selected_cell();
    var cell_type = cell.cell_type;
    var textcell = cell instanceof IPython.TextCell;

    // ` : enable console
    if (event.which === 192) {
      $(IPython.console_book.element).toggle();
      IPython.console_book.focus_selected();
      return true;
    }

    // K: up cell
    if (event.which === 75 && (event.shiftKey || event.metaKey))
    {
        that.select_prev();
        return true;
    }
    // k: up
    if (event.which === 75 && !event.shiftKey)
    {
        var cursor = cell.code_mirror.getCursor();
        if (cursor.line === 0) {
          that.select_prev();
          var new_cell = that.get_selected_cell();
          if(new_cell.code_mirror) { // bottom line, same ch position
            var last_line = new_cell.code_mirror.lastLine();
            // NOTE: a current code_mirror bug doesn't respect the new cursor opsition
            // https://github.com/marijnh/CodeMirror/issues/2289
            if(new_cell.code_mirror.state.vim) {
              new_cell.code_mirror.state.vim.lastHPos = cursor.ch;
            }
            new_cell.code_mirror.setCursor(last_line, cursor.ch);
          }
          return true;
        }
    }
    // J: down cell
    if (event.which === 74 && (event.shiftKey || event.metaKey)) {
        that.select_next();
        return true;
    }
    // j: down
    if (event.which === 74 && !event.shiftKey) {
      var cursor = cell.code_mirror.getCursor();
      var ch = cursor.ch;
      if (cursor.line === (cell.code_mirror.lineCount()-1)) {
        that.select_next();
        var new_cell = that.get_selected_cell();
        if(new_cell.code_mirror) { // bottom line, same ch position
          // NOTE: a current code_mirror bug doesn't respect the new cursor opsition
          // https://github.com/marijnh/CodeMirror/issues/2289
          new_cell.code_mirror.setCursor(0, cursor.ch);
          if(new_cell.code_mirror.state.vim) {
            new_cell.code_mirror.state.vim.lastHPos = cursor.ch;
          }
        }
        return true;
      };
    }
    // Y: copy cell
    if (event.which === 89 && event.shiftKey) {
        that.copy_cell();
        return true;
    }
    // D: delete cell / cut
    if (event.which === 68 && event.shiftKey) {
        that.cut_cell();
        return true;
    }
    // P: paste cell
    if (event.which === 80 && event.shiftKey) {
        that.paste_cell();
        return true;
    }
    // B: open new cell below
    if (event.which === 66 && event.shiftKey) {
        that.insert_cell_below('code');
        that.setVIMode('INSERT');
        return true;
    }
    // shift+O or apple + O: open new cell below
    // I know this is wrong but i hate hitting A
    if (event.which === 79 && (event.metaKey || event.shiftKey)) {
        that.insert_cell_below('code');
        that.setVIMode('INSERT');
        return true;
    }
    // A: open new cell above
    if (event.which === 65 && event.shiftKey) {
      that.insert_cell_above('code');
      that.setVIMode('INSERT');
      return true;
    }
    // control/apple E: execute (apple - E is easier than shift E)
    if ((event.ctrlKey || event.metaKey) && event.keyCode==69) {
      that.execute_cell();
      return true;
    }
    // E:  execute
    if (event.which === 69 && event.shiftKey) {
      that.execute_cell();
      return true;
    }
    // F: toggle output
    if (event.which === 70 && event.shiftKey) {
      that.toggle_output();
      return true;
    }
    // M: markdown
    if (event.which === 77 && event.shiftKey) {
      that.to_markdown();
      return true;
    }
    // C: codecell
    if (event.which === 77 && event.shiftKey) {
      that.to_code();
      return true;
    }
    // i: insert. only relevant on textcell
    var rendered = cell.rendered;
    if (textcell && rendered && event.which === 73 && !event.shiftKey) {
      cell.edit();
      return false;
    }

    // esc: get out of insert and render textcell
    if (textcell && !rendered && event.which === 27 && !event.shiftKey) {
        cell.render();
        return false;
    }
  };

  InsertMode.keyDown = function(that, event) {
    var cell = that.get_selected_cell();
    var cell_type = cell.cell_type;
    var textcell = cell instanceof IPython.TextCell;

    // control/apple E: execute (apple - E is easier than shift E)
    if ((event.ctrlKey || event.metaKey) && event.keyCode==69) {
      that.execute_cell();
      return true;
    }
    if (event.which === 74 && (event.metaKey)) {
        that.select_next();
        return true;
    }
      if (event.which === 75 && (event.metaKey)) {
          that.select_prev();
          return true;
      }
  };

  IPython.VIM = new VIM();
  return IPython;

}(IPython));
<|endoftext|>


<commit_before>/**
 * @preserve EventEmitter v1.0.0
 *
 * Copyright 2011, Oliver Caldwell (flowdev.co.uk)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * https://github.com/Wolfy87/Listen
 */

// Initialise the class
function EventEmitter() {}

// Initialise the storage variables
EventEmitter.prototype._events = {};
EventEmitter.prototype._listeners = [];

// Has to be an array to stop it being moved out of the prototype on change
EventEmitter.prototype._maxListeners = [10];

/**
 * Adds a listener for a specified event
 *
 * @param {String} name Name of the event
 * @param {Function} listener Run when the event is emitted
 * @param {Boolean} once If true, the listener will only be run once, use EventEmitter.once instead, this is mainly for internal use
 */
EventEmitter.prototype.addListener = function(name, listener, once) {
	// Grab the index of the listener
	var index = this._listeners.length;

	// Add the listener
	this._listeners.push({
		listener: listener,
		once: (once) ? true : false
	});

	// Add the event to the events object if required
	if(typeof this._events[name] === 'undefined') {
		this._events[name] = [];
	}

	// Add the listeners index to the event
	this._events[name].push(index);

	// Check if we have exceeded the max listeners
	if(this._events[name].length === this._maxListeners[0]) {
		// We have, let the developer know
		console.log('Maximum number of listeners (' + this._maxListeners[0] + ') reached for the "' + name + '" event!');
	}
};

/**
 * Adds a listener for a specified event (alias of EventEmitter.addListener)
 *
 * @param {String} name Name of the event
 * @param {Function} listener Run when the event is emitted
 * @param {Boolean} once If true, the listener will only be run once, use EventEmitter.once instead, this is mainly for internal use
 */
EventEmitter.prototype.on = EventEmitter.prototype.addListener;

/**
 * Adds a listener for a specified event that will only be called once
 *
 * @param {String} name Name of the event
 * @param {Function} listener Run when the event is emitted
 */
EventEmitter.prototype.once = function(name, listener) {
	this.addListener(name, listener, true);
};

/**
 * Removes a listener for a specified event
 *
 * @param {String} name Name of the event
 * @param {Function} listener Reference to the listener function
 */
EventEmitter.prototype.removeListener = function(name, listener) {
	// Initialise any required variables
	var i = null,
		indexes = null;

	// Make sure the event exists
	if(this._events[name] instanceof Array) {
		// Grab the listeners indexes
		indexes = this._events[name];

		// Loop through all of the indexes
		for(i = 0; i < indexes.length; i++) {
			// Check if we have found the listener
			if(this._listeners[indexes[i]].listener === listener) {
				// It is, remove it and return
				indexes.splice(i, 1);
			}
		}
	}
};

/**
 * Removes all the listeners for a specified event
 *
 * @param {String} name Name of the event
 */
EventEmitter.prototype.removeAllListeners = function(name) {
	this._events[name] = [];
};

/**
 * Sets the max number of listeners before a message is displayed
 * If it is set to 0 then there is no limit
 *
 * @param {Number} n Max number of listeners before a message is displayed
 */
EventEmitter.prototype.setMaxListeners = function(n) {
	this._maxListeners[0] = n;
};

/**
 * Returns an array of listeners for the specified event
 *
 * @param {String} name Name of the event
 * @returns {Array} An array of the assigned listeners
 */
EventEmitter.prototype.listeners = function(name) {
	// Initialise any required variables
	var i = null,
		built = [];

	// Make sure the event exists
	if(this._events[name] instanceof Array) {
		// Grab the listeners indexes
		indexes = this._events[name];

		// Loop through all of the indexes
		for(i = 0; i < indexes.length; i++) {
			// Add it to the array
			built.push(this._listeners[indexes[i]].listener);
		}
	}

	// Return the found listeners
	return built;
};

/**
 * Emits the specified event with optional arguments
 *
 * @param {String} name Name of the event to be emitted
 * @param {Mixed} An argument to be passed to the listeners, you can have as many of these as you want
 */
EventEmitter.prototype.emit = function(name) {

};
<commit_msg>Added the code for the emit function<commit_after>/**
 * @preserve EventEmitter v1.0.0
 *
 * Copyright 2011, Oliver Caldwell (flowdev.co.uk)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * https://github.com/Wolfy87/Listen
 */

// Initialise the class
function EventEmitter() {}

// Initialise the storage variables
EventEmitter.prototype._events = {};
EventEmitter.prototype._listeners = [];

// Has to be an array to stop it being moved out of the prototype on change
EventEmitter.prototype._maxListeners = [10];

/**
 * Adds a listener for a specified event
 *
 * @param {String} name Name of the event
 * @param {Function} listener Run when the event is emitted
 * @param {Boolean} once If true, the listener will only be run once, use EventEmitter.once instead, this is mainly for internal use
 */
EventEmitter.prototype.addListener = function(name, listener, once) {
	// Grab the index of the listener
	var index = this._listeners.length;

	// Add the listener
	this._listeners.push({
		listener: listener,
		once: (once) ? true : false
	});

	// Add the event to the events object if required
	if(typeof this._events[name] === 'undefined') {
		this._events[name] = [];
	}

	// Add the listeners index to the event
	this._events[name].push(index);

	// Check if we have exceeded the max listeners
	if(this._events[name].length === this._maxListeners[0]) {
		// We have, let the developer know
		console.log('Maximum number of listeners (' + this._maxListeners[0] + ') reached for the "' + name + '" event!');
	}
};

/**
 * Adds a listener for a specified event (alias of EventEmitter.addListener)
 *
 * @param {String} name Name of the event
 * @param {Function} listener Run when the event is emitted
 * @param {Boolean} once If true, the listener will only be run once, use EventEmitter.once instead, this is mainly for internal use
 */
EventEmitter.prototype.on = EventEmitter.prototype.addListener;

/**
 * Adds a listener for a specified event that will only be called once
 *
 * @param {String} name Name of the event
 * @param {Function} listener Run when the event is emitted
 */
EventEmitter.prototype.once = function(name, listener) {
	this.addListener(name, listener, true);
};

/**
 * Removes a listener for a specified event
 *
 * @param {String} name Name of the event
 * @param {Function} listener Reference to the listener function
 */
EventEmitter.prototype.removeListener = function(name, listener) {
	// Initialise any required variables
	var i = null,
		indexes = null;

	// Make sure the event exists
	if(this._events[name] instanceof Array) {
		// Grab the listeners indexes
		indexes = this._events[name];

		// Loop through all of the indexes
		for(i = 0; i < indexes.length; i++) {
			// Check if we have found the listener
			if(this._listeners[indexes[i]].listener === listener) {
				// It is, remove it and return
				indexes.splice(i, 1);
			}
		}
	}
};

/**
 * Removes all the listeners for a specified event
 *
 * @param {String} name Name of the event
 */
EventEmitter.prototype.removeAllListeners = function(name) {
	this._events[name] = [];
};

/**
 * Sets the max number of listeners before a message is displayed
 * If it is set to 0 then there is no limit
 *
 * @param {Number} n Max number of listeners before a message is displayed
 */
EventEmitter.prototype.setMaxListeners = function(n) {
	this._maxListeners[0] = n;
};

/**
 * Returns an array of listeners for the specified event
 *
 * @param {String} name Name of the event
 * @returns {Array} An array of the assigned listeners
 */
EventEmitter.prototype.listeners = function(name) {
	// Initialise any required variables
	var i = null,
		built = [];

	// Make sure the event exists
	if(this._events[name] instanceof Array) {
		// Grab the listeners indexes
		indexes = this._events[name];

		// Loop through all of the indexes
		for(i = 0; i < indexes.length; i++) {
			// Add it to the array
			built.push(this._listeners[indexes[i]].listener);
		}
	}

	// Return the found listeners
	return built;
};

/**
 * Emits the specified event with optional arguments
 *
 * @param {String} name Name of the event to be emitted
 * @param {Mixed} An argument to be passed to the listeners, you can have as many of these as you want
 */
EventEmitter.prototype.emit = function(name) {
	// Initialise any required variables
	var i = null,
		args = Array.prototype.slice.call(arguments),
		listeners = this.listeners(name);

	// Splice out the first argument
	args.splice(0, 1);

	// Loop through the listeners
	for(i = 0; i < listeners.length; i++) {
		// Call the function
		listeners[i].apply(null, args);
	}
};
<|endoftext|>


<commit_before>/*
 * DB connection and schema management class
 */
Scholar.DB = new function(){
	// Private members
	var _connection;
	var _transactionRollback;
	var _transactionNestingLevel = 0;

	// Privileged methods
	this.query = query;
	this.valueQuery = valueQuery;
	this.rowQuery = rowQuery;
	this.columnQuery = columnQuery;
	this.statementQuery = statementQuery;
	this.getColumns = getColumns;
	this.getColumnHash = getColumnHash;
	this.beginTransaction = beginTransaction;
	this.commitTransaction = commitTransaction;
	this.rollbackTransaction = rollbackTransaction;
	this.transactionInProgress = transactionInProgress;
	this.tableExists = tableExists;

	/////////////////////////////////////////////////////////////////
	//
	// Privileged methods
	//
	/////////////////////////////////////////////////////////////////

	/*
	 * Run an SQL query
	 *
	 *  Optional _params_ is an array of bind parameters in the form
	 *		[{'int':2},{'string':'foobar'}]
	 *
	 * 	Returns:
	 *  	 - Associative array (similar to mysql_fetch_assoc) for SELECT's
	 *	 - lastInsertId for INSERT's
	 *	 - TRUE for other successful queries
	 *	 - FALSE on error
	 */
	function query(sql,params){
		var db = _getDBConnection();

		try {
			// Parse out the SQL command being used
			var op = sql.match(/^[^a-z]*[^ ]+/i);
			if (op){
				op = op.toString().toLowerCase();
			}

			// If SELECT statement, return result
			if (op=='select'){
				// Until the native dataset methods work (or at least exist),
				// we build a multi-dimensional associative array manually

				var statement = statementQuery(sql,params);

				var dataset = new Array();
				while (statement.executeStep()){
					var row = new Array();
					for(var i=0; i<statement.columnCount; i++) {
						row[statement.getColumnName(i)] = statement.getUTF8String(i);
					}
					dataset.push(row);
				}
				statement.reset();

				return dataset.length ? dataset : false;
			}
			else {
				if (params){
					var statement = statementQuery(sql,params);
					statement.execute();
				}
				else {
					Scholar.debug(sql,5);
					db.executeSimpleSQL(sql);
				}

				if (op=='insert'){
					return db.lastInsertRowID;
				}
				// DEBUG: Can't get affected rows for UPDATE or DELETE?
				else {
					return true;
				}
			}
		}
		catch (e){
			throw(e + ' [QUERY: ' + sql + '] [ERROR: ' + db.lastErrorString + ']');
		}
	}

	/*
	 * Query a single value and return it
	 */
	function valueQuery(sql,params){
		var db = _getDBConnection();
		try {
			var statement = statementQuery(sql,params);
		}
		catch (e){
			throw(db.lastErrorString);
		}

		// No rows
		if (!statement.executeStep()){
			statement.reset();
			return false;
		}
		if (sql.indexOf('SELECT COUNT(*)') > -1){
			var value = statement.getInt32(0);
		}
		else {
			var value = statement.getUTF8String(0);
		}
		statement.reset();
		return value;
	}

	/*
	 * Run a query and return the first row
	 */
	function rowQuery(sql,params){
		var result = query(sql,params);
		if (result){
			return result[0];
		}
	}

	/*
	 * Run a query and return the first column as a numerically-indexed array
	 */
	function columnQuery(sql,params){
		var statement = statementQuery(sql,params);

		if (statement){
			var column = new Array();
			while (statement.executeStep()){
				column.push(statement.getUTF8String(0));
			}
			statement.reset();
			return column.length ? column : false;
		}
		return false;
	}

	/*
	 * Run a query, returning a mozIStorageStatement for direct manipulation
	 *
	 *  Optional _params_ is an array of bind parameters in the form
	 *		[{'int':2},{'string':'foobar'}]
	 */
	function statementQuery(sql,params){
		var db = _getDBConnection();

		try {
			Scholar.debug(sql,5);
			var statement = db.createStatement(sql);
		}
		catch (e){
			throw('[QUERY: ' + sql + '] [ERROR: ' + db.lastErrorString + ']');
		}

		if (statement && params){
			for (var i=0; i<params.length; i++){
				// Int
				if (typeof params[i]['int'] != 'undefined'){
					Scholar.debug('Binding parameter ' + (i+1) + ' of type int: ' +
						params[i]['int'],5);
					statement.bindInt32Parameter(i,params[i]['int']);
				}
				// String
				else if (typeof params[i]['string'] != 'undefined'){
					Scholar.debug('Binding parameter ' + (i+1) + ' of type string: "' +
						params[i]['string'] + '"',5);
					statement.bindUTF8StringParameter(i,params[i]['string']);
				}
				// Null
				else if (typeof params[i]['null'] != 'undefined'){
					Scholar.debug('Binding parameter ' + (i+1) + ' of type NULL', 5);
					statement.bindNullParameter(i);
				}
			}
		}
		return statement;
	}

	function beginTransaction(){
		var db = _getDBConnection();

		if (db.transactionInProgress){
			_transactionNestingLevel++;
			Scholar.debug('Transaction in progress -- increasing level to '
				+ _transactionNestingLevel, 5);
		}
		else {
			Scholar.debug('Beginning DB transaction', 5);
			db.beginTransaction();
		}
	}

	function commitTransaction(){
		var db = _getDBConnection();

		if (_transactionNestingLevel){
			_transactionNestingLevel--;
			Scholar.debug('Decreasing transaction level to ' + _transactionNestingLevel, 5);
		}
		else if (_transactionRollback){
			Scholar.debug('Rolling back previously flagged transaction', 5);
			db.rollbackTransaction();
		}
		else {
			Scholar.debug('Committing transaction',5);
			db.commitTransaction();
		}
	}

	function rollbackTransaction(){
		var db = _getDBConnection();

		if (_transactionNestingLevel){
			Scholar.debug('Flagging nested transaction for rollback', 5);
			_transactionRollback = true;
		}
		else {
			Scholar.debug('Rolling back transaction', 5);
			_transactionRollback = false;
			db.rollbackTransaction();
		}
	}

	function transactionInProgress(){
		var db = _getDBConnection();
		return db.transactionInProgress;
	}

	function tableExists(table){
		return _getDBConnection().tableExists(table);
	}

	function getColumns(table){
		var db = _getDBConnection();

		try {
			var sql = "SELECT * FROM " + table + " LIMIT 1";
			var statement = statementQuery(sql);

			var cols = new Array();
			for (var i=0,len=statement.columnCount; i<len; i++){
				cols.push(statement.getColumnName(i));
			}
			return cols;
		}
		catch (e){
			Scholar.debug(e,1);
			return false;
		}
	}

	function getColumnHash(table){
		var cols = getColumns(table);
		var hash = new Array();
		if (cols.length){
			for (var i=0; i<cols.length; i++){
				hash[cols[i]] = true;
			}
		}
		return hash;
	}

	/////////////////////////////////////////////////////////////////
	//
	// Private methods
	//
	/////////////////////////////////////////////////////////////////

	/*
	 * Retrieve a link to the data store
	 */
	function _getDBConnection(){
		if (_connection){
			return _connection;
		}

		// Get the storage service
		var store = Components.classes["@mozilla.org/storage/service;1"].
			getService(Components.interfaces.mozIStorageService);

		// Get the profile directory
		var file = Components.classes["@mozilla.org/file/directory_service;1"]
			.getService(Components.interfaces.nsIProperties)
			.get("ProfD", Components.interfaces.nsILocalFile);

		// This makes file point to PROFILE_DIR/<scholar database file>
		file.append(SCHOLAR_CONFIG['DB_FILE']);

		_connection = store.openDatabase(file);

		return _connection;
	}
}
<commit_msg>Change the DB query functions to return values of the actual types they are, rather than to return everything as strings -- this will prevent the need for parseInt on COUNT(*) values, etc. and is generally better<commit_after>/*
 * DB connection and schema management class
 */
Scholar.DB = new function(){
	// Private members
	var _connection;
	var _transactionRollback;
	var _transactionNestingLevel = 0;

	// Privileged methods
	this.query = query;
	this.valueQuery = valueQuery;
	this.rowQuery = rowQuery;
	this.columnQuery = columnQuery;
	this.statementQuery = statementQuery;
	this.getColumns = getColumns;
	this.getColumnHash = getColumnHash;
	this.beginTransaction = beginTransaction;
	this.commitTransaction = commitTransaction;
	this.rollbackTransaction = rollbackTransaction;
	this.transactionInProgress = transactionInProgress;
	this.tableExists = tableExists;

	/////////////////////////////////////////////////////////////////
	//
	// Privileged methods
	//
	/////////////////////////////////////////////////////////////////

	/*
	 * Run an SQL query
	 *
	 *  Optional _params_ is an array of bind parameters in the form
	 *		[{'int':2},{'string':'foobar'}]
	 *
	 * 	Returns:
	 *  	 - Associative array (similar to mysql_fetch_assoc) for SELECT's
	 *	 - lastInsertId for INSERT's
	 *	 - TRUE for other successful queries
	 *	 - FALSE on error
	 */
	function query(sql,params){
		var db = _getDBConnection();

		try {
			// Parse out the SQL command being used
			var op = sql.match(/^[^a-z]*[^ ]+/i);
			if (op){
				op = op.toString().toLowerCase();
			}

			// If SELECT statement, return result
			if (op=='select'){
				// Until the native dataset methods work (or at least exist),
				// we build a multi-dimensional associative array manually

				var statement = statementQuery(sql,params);

				var dataset = new Array();
				while (statement.executeStep()){
					var row = new Array();

					for(var i=0; i<statement.columnCount; i++) {
						row[statement.getColumnName(i)] = _getTypedValue(statement, i);
					}
					dataset.push(row);
				}
				statement.reset();

				return dataset.length ? dataset : false;
			}
			else {
				if (params){
					var statement = statementQuery(sql,params);
					statement.execute();
				}
				else {
					Scholar.debug(sql,5);
					db.executeSimpleSQL(sql);
				}

				if (op=='insert'){
					return db.lastInsertRowID;
				}
				// DEBUG: Can't get affected rows for UPDATE or DELETE?
				else {
					return true;
				}
			}
		}
		catch (e){
			throw(e + ' [QUERY: ' + sql + '] [ERROR: ' + db.lastErrorString + ']');
		}
	}

	/*
	 * Query a single value and return it
	 */
	function valueQuery(sql,params){
		var db = _getDBConnection();
		try {
			var statement = statementQuery(sql,params);
		}
		catch (e){
			throw(db.lastErrorString);
		}

		// No rows
		if (!statement.executeStep()){
			statement.reset();
			return false;
		}

		return _getTypedValue(statement, 0);
	}

	/*
	 * Run a query and return the first row
	 */
	function rowQuery(sql,params){
		var result = query(sql,params);
		if (result){
			return result[0];
		}
	}

	/*
	 * Run a query and return the first column as a numerically-indexed array
	 */
	function columnQuery(sql,params){
		var statement = statementQuery(sql,params);

		if (statement){
			var column = new Array();
			while (statement.executeStep()){
				column.push(_getTypedValue(statement, 0));
			}
			statement.reset();
			return column.length ? column : false;
		}
		return false;
	}

	/*
	 * Run a query, returning a mozIStorageStatement for direct manipulation
	 *
	 *  Optional _params_ is an array of bind parameters in the form
	 *		[{'int':2},{'string':'foobar'}]
	 */
	function statementQuery(sql,params){
		var db = _getDBConnection();

		try {
			Scholar.debug(sql,5);
			var statement = db.createStatement(sql);
		}
		catch (e){
			throw('[QUERY: ' + sql + '] [ERROR: ' + db.lastErrorString + ']');
		}

		if (statement && params){
			for (var i=0; i<params.length; i++){
				// Int
				if (typeof params[i]['int'] != 'undefined'){
					Scholar.debug('Binding parameter ' + (i+1) + ' of type int: ' +
						params[i]['int'],5);
					statement.bindInt32Parameter(i,params[i]['int']);
				}
				// String
				else if (typeof params[i]['string'] != 'undefined'){
					Scholar.debug('Binding parameter ' + (i+1) + ' of type string: "' +
						params[i]['string'] + '"',5);
					statement.bindUTF8StringParameter(i,params[i]['string']);
				}
				// Null
				else if (typeof params[i]['null'] != 'undefined'){
					Scholar.debug('Binding parameter ' + (i+1) + ' of type NULL', 5);
					statement.bindNullParameter(i);
				}
			}
		}
		return statement;
	}

	function beginTransaction(){
		var db = _getDBConnection();

		if (db.transactionInProgress){
			_transactionNestingLevel++;
			Scholar.debug('Transaction in progress -- increasing level to '
				+ _transactionNestingLevel, 5);
		}
		else {
			Scholar.debug('Beginning DB transaction', 5);
			db.beginTransaction();
		}
	}

	function commitTransaction(){
		var db = _getDBConnection();

		if (_transactionNestingLevel){
			_transactionNestingLevel--;
			Scholar.debug('Decreasing transaction level to ' + _transactionNestingLevel, 5);
		}
		else if (_transactionRollback){
			Scholar.debug('Rolling back previously flagged transaction', 5);
			db.rollbackTransaction();
		}
		else {
			Scholar.debug('Committing transaction',5);
			db.commitTransaction();
		}
	}

	function rollbackTransaction(){
		var db = _getDBConnection();

		if (_transactionNestingLevel){
			Scholar.debug('Flagging nested transaction for rollback', 5);
			_transactionRollback = true;
		}
		else {
			Scholar.debug('Rolling back transaction', 5);
			_transactionRollback = false;
			db.rollbackTransaction();
		}
	}

	function transactionInProgress(){
		var db = _getDBConnection();
		return db.transactionInProgress;
	}

	function tableExists(table){
		return _getDBConnection().tableExists(table);
	}

	function getColumns(table){
		var db = _getDBConnection();

		try {
			var sql = "SELECT * FROM " + table + " LIMIT 1";
			var statement = statementQuery(sql);

			var cols = new Array();
			for (var i=0,len=statement.columnCount; i<len; i++){
				cols.push(statement.getColumnName(i));
			}
			return cols;
		}
		catch (e){
			Scholar.debug(e,1);
			return false;
		}
	}

	function getColumnHash(table){
		var cols = getColumns(table);
		var hash = new Array();
		if (cols.length){
			for (var i=0; i<cols.length; i++){
				hash[cols[i]] = true;
			}
		}
		return hash;
	}

	/////////////////////////////////////////////////////////////////
	//
	// Private methods
	//
	/////////////////////////////////////////////////////////////////

	/*
	 * Retrieve a link to the data store
	 */
	function _getDBConnection(){
		if (_connection){
			return _connection;
		}

		// Get the storage service
		var store = Components.classes["@mozilla.org/storage/service;1"].
			getService(Components.interfaces.mozIStorageService);

		// Get the profile directory
		var file = Components.classes["@mozilla.org/file/directory_service;1"]
			.getService(Components.interfaces.nsIProperties)
			.get("ProfD", Components.interfaces.nsILocalFile);

		// This makes file point to PROFILE_DIR/<scholar database file>
		file.append(SCHOLAR_CONFIG['DB_FILE']);

		_connection = store.openDatabase(file);

		return _connection;
	}

	function _getTypedValue(statement, i){
		var type = statement.getTypeOfIndex(i);
		switch (type){
			case statement.VALUE_TYPE_INTEGER:
				var func = statement.getInt32;
				break;
			case statement.VALUE_TYPE_TEXT:
				var func = statement.getUTF8String;
				break;
			case statement.VALUE_TYPE_NULL:
				return null;
			case statement.VALUE_TYPE_FLOAT:
				var func = statement.getDouble;
				break;
			case statement.VALUE_TYPE_BLOB:
				var func = statement.getBlob;
				break;
		}

		return func(i);
	}
}
<|endoftext|>


<commit_before>Package.describe({
  summary: "Send email messages",
  version: "1.2.0"
});

Npm.depends({
  mailcomposer: "4.0.1",
  // Using smtp-connection@2 (instead of latest) because it shares
  // nodemailer-shared with mailcomposer@4:
  "smtp-connection": "2.12.2",
  "stream-buffers": "0.2.5"});

Package.onUse(function (api) {
  api.use('underscore', 'server');
  api.export(['Email', 'EmailInternals'], 'server');
  api.export('EmailTest', 'server', {testOnly: true});
  api.addFiles('email.js', 'server');
});

Package.onTest(function (api) {
  api.use('email', 'server');
  api.use('tinytest');
  api.addFiles('email_tests.js', 'server');
});
<commit_msg>Add `-rc.n` suffix to `email` before republishing.<commit_after>Package.describe({
  summary: "Send email messages",
  version: "1.2.0-rc.2"
});

Npm.depends({
  mailcomposer: "4.0.1",
  // Using smtp-connection@2 (instead of latest) because it shares
  // nodemailer-shared with mailcomposer@4:
  "smtp-connection": "2.12.2",
  "stream-buffers": "0.2.5"});

Package.onUse(function (api) {
  api.use('underscore', 'server');
  api.export(['Email', 'EmailInternals'], 'server');
  api.export('EmailTest', 'server', {testOnly: true});
  api.addFiles('email.js', 'server');
});

Package.onTest(function (api) {
  api.use('email', 'server');
  api.use('tinytest');
  api.addFiles('email_tests.js', 'server');
});
<|endoftext|>