<commit_before>import _extend from 'lodash-es/extend';
import _find from 'lodash-es/find';
import _forEach from 'lodash-es/forEach';

import rbush from 'rbush';

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { json as d3_json } from 'd3-request';
import { request as d3_request } from 'd3-request';

import { geoExtent, geoVecAdd } from '../geo';
import { iOsmError } from '../osm';
import { services } from './index';
import { t } from '../util/locale';
import { utilRebind, utilTiler, utilQsString } from '../util';

var tiler = utilTiler();
var dispatch = d3_dispatch('loaded');

var _erCache;
var _erZoom = 14;

var _impOsmUrls = {
    ow: 'http://directionofflow.skobbler.net/directionOfFlowService',
    mr: 'http://missingroads.skobbler.net/missingGeoService',
    tr: 'http://turnrestrictionservice.skobbler.net/turnRestrictionService'
};

function abortRequest(i) {
    _forEach(i, function(v) {
        if (v) {
            v.abort();
        }
    });
}

function abortUnwantedRequests(cache, tiles) {
    _forEach(cache.inflightTile, function(v, k) {
        var wanted = _find(tiles, function(tile) {
            return k === tile.id;
        });
        if (!wanted) {
            abortRequest(v);
            delete cache.inflightTile[k];
        }
    });
}

function encodeErrorRtree(d) {
    return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
}

// replace or remove error from rtree
function updateRtree(item, replace) {
    _erCache.rtree.remove(item, function isEql(a, b) {
        return a.data.id === b.data.id;
    });

    if (replace) {
        _erCache.rtree.insert(item);
    }
}

function linkErrorObject(d) {
    return '' + d + '';
}

function linkEntity(d) {
    return '' + d + '';
}

function pointAverage(points) {
    var x = 0;
    var y = 0;

    _forEach(points, function(v) {
        x += v.lon;
        y += v.lat;
    });

    x /= points.length;
    y /= points.length;

    return [x, y];
}

function relativeBearing(p1, p2) {
    var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
    if (angle < 0) {
        angle += 2 * Math.PI;
    }

    // Return degrees
    return angle * 180 / Math.PI;
}

// Assuming range [0,360)
function cardinalDirection(bearing) {
    var dir = 45 * Math.round(bearing / 45);
    var compass = {
        0: 'north',
        45: 'northeast',
        90: 'east',
        135: 'southeast',
        180: 'south',
        225: 'southwest',
        270: 'west',
        315: 'northwest',
        360: 'north'
    };

    return t('QA.improveOSM.directions.' + compass[dir]);
}

// Errors shouldn't obscure eachother
function preventCoincident(loc, bumpUp) {
    var coincident = false;
    do {
        // first time, move marker up. after that, move marker right.
        var delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
        loc = geoVecAdd(loc, delta);
        var bbox = geoExtent(loc).bbox();
        coincident = _erCache.rtree.search(bbox).length;
    } while (coincident);

    return loc;
}

export default {
    init: function() {
        if (!_erCache) {
            this.reset();
        }

        this.event = utilRebind(this, dispatch, 'on');
    },

    reset: function() {
        if (_erCache) {
            _forEach(_erCache.inflightTile, abortRequest);
        }
        _erCache = {
            data: {},
            loadedTile: {},
            inflightTile: {},
            inflightPost: {},
            closed: {},
            rtree: rbush()
        };
    },

    loadErrors: function(projection) {
        var options = {
            client: 'iD',
            confidenceLevel: 'C1', // most confident only, still have false positives
            status: 'OPEN',
            type: 'PARKING,ROAD,BOTH,PATH', // exclude WATER as it doesn't seem useful
            zoom: '19' // Use a high zoom so that clusters aren't returned
        };

        // determine the needed tiles to cover the view
        var tiles = tiler
            .zoomExtent([_erZoom, _erZoom])
            .getTiles(projection);

        // abort inflight requests that are no longer needed
        abortUnwantedRequests(_erCache, tiles);

        // issue new requests..
        tiles.forEach(function(tile) {
            if (_erCache.loadedTile[tile.id] || _erCache.inflightTile[tile.id]) return;

            var rect = tile.extent.rectangle();
            var params = _extend({}, options, { east: rect[0], south: rect[3], west: rect[2], north: rect[1] });

            // 3 separate requests to store for each tile
            var requests = {};

            _forEach(_impOsmUrls, function(v, k) {
                var url = v + '/search?' + utilQsString(params);

                requests[k] = d3_json(url,
                    function(err, data) {
                        delete _erCache.inflightTile[tile.id];

                        if (err) return;
                        _erCache.loadedTile[tile.id] = true;

                        // Road segments at high zoom == oneways
                        if (data.roadSegments) {
                            data.roadSegments.forEach(function(feature) {
                                // Position error at the approximate middle of the segment
                                var points = feature.points;
                                var mid = points.length / 2;
                                var loc;

                                // Even number of points, find midpoint of the middle two
                                // Odd number of points, use position of very middle point
                                if (mid % 1 === 0) {
                                    loc = pointAverage([points[mid - 1], points[mid]]);
                                } else {
                                    mid = points[Math.floor(mid)];
                                    loc = [mid.lon, mid.lat];
                                }

                                // One-ways can land on same segment in opposite direction
                                loc = preventCoincident(loc, false);

                                var d = new iOsmError({
                                    loc: loc,
                                    comments: null,
                                    error_subtype: '',
                                    error_type: k,
                                    icon: 'fas-arrow-circle-up', //TODO: Change arrow based on direction?
                                    identifier: { // this is used to post changes to the error
                                        wayId: feature.wayId,
                                        fromNodeId: feature.fromNodeId,
                                        toNodeId: feature.toNodeId
                                    },
                                    object_id: feature.wayId,
                                    object_type: 'way',
                                    status: feature.status
                                });

                                // Variables used in the description
                                d.replacements = {
                                    percentage: feature.percentOfTrips,
                                    num_trips: feature.numberOfTrips,
                                    highway: linkErrorObject(t('QA.keepRight.error_parts.highway')),
                                    from_node: linkEntity('n' + feature.fromNodeId),
                                    to_node: linkEntity('n' + feature.toNodeId)
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }

                        // Tiles at high zoom == missing roads
                        if (data.tiles) {
                            data.tiles.forEach(function(feature) {
                                // Average of recorded points should land on the missing geometry
                                var loc = pointAverage(feature.points);

                                // Missing geometry could happen to land on another error
                                loc = preventCoincident(loc, false);

                                var geoType = feature.type.toLowerCase();
                                var geoIcons = {
                                    road: 'maki-car',
                                    parking: 'maki-parking',
                                    both: 'maki-car',
                                    path: 'maki-shoe'
                                };

                                var d = new iOsmError({
                                    loc: loc,
                                    comments: null,
                                    error_subtype: geoType,
                                    error_type: k,
                                    icon: geoIcons[geoType],
                                    identifier: { x: feature.x, y: feature.y },
                                    status: feature.status
                                });

                                d.replacements = {
                                    num_trips: feature.numberOfTrips,
                                    geometry_type: t('QA.improveOSM.geometry_types.' + geoType)
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }

                        // Entities at high zoom == turn restrictions
                        if (data.entities) {
                            data.entities.forEach(function(feature) {
                                var loc = feature.point;

                                // Turn restrictions could be missing at same junction
                                // We also want to bump the error up so node is accessible
                                loc = preventCoincident([loc.lon, loc.lat], true);

                                // Elements are presented in a strange way
                                var ids = feature.id.split(',');
                                var from_way = ids[0];
                                var via_node = ids[3];
                                var to_way = ids[2].split(':')[1];

                                // Travel direction along from_way clarifies the turn restriction
                                var p1 = feature.segments[0].points[0];
                                var p2 = feature.segments[0].points[1];

                                var dir_of_travel = cardinalDirection(relativeBearing(p1, p2));

                                var d = new iOsmError({
                                    loc: loc,
                                    comments: null,
                                    error_subtype: '',
                                    error_type: k,
                                    icon: 'temaki-junction',
                                    identifier: feature.id,
                                    object_id: via_node,
                                    object_type: 'node',
                                    status: feature.status
                                });

                                // Variables used in the description
                                d.replacements = {
                                    num_passed: feature.numberOfPasses,
                                    num_trips: feature.segments[0].numberOfTrips,
                                    turn_restriction: feature.turnType.toLowerCase(),
                                    from_way: linkEntity('w' + from_way),
                                    to_way: linkEntity('w' + to_way),
                                    travel_direction: dir_of_travel,
                                    junction: linkErrorObject(t('QA.keepRight.error_parts.this_node'))
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }
                    }
                );
            });

            _erCache.inflightTile[tile.id] = requests;
            dispatch.call('loaded');
        });
    },

    postUpdate: function(d, callback) {
        if (!services.osm.authenticated()) { // Username required in payload
            return callback({ message: 'Not Authenticated', status: -3}, d);
        }
        if (_erCache.inflightPost[d.id]) {
            return callback({ message: 'Error update already inflight', status: -2 }, d);
        }

        var osmUsername = services.osm.userDetails(function(err, user) {
            if (err) return '';

            return user.display_name;
        });

        var that = this;
        var type = d.error_type;
        var payload = {
            username: osmUsername
        };

        // Each error type has different data for identification
        if (type === 'ow') {
            payload.roadSegments = [ d.identifier ];
        } else if (type === 'mr') {
            payload.tiles = [ d.identifier ];
        } else if (type === 'tr') {
            payload.targetIds = [ d.identifier ];
        }

        // Separate requests required to comment and change status
        var url = _impOsmUrls[type] + '/comment';

        // Comments don't currently work
        // if (d.newComment !== undefined) {
        //     payload.text = d.newComment;
        // }

        if (d.newStatus !== d.status) {
            payload.status = d.newStatus;
            payload.text = 'status changed';

            _erCache.inflightPost[d.id] = d3_request(url)
                .header('Content-Type', 'application/json')
                .post(JSON.stringify(payload), function(err) {
                    delete _erCache.inflightPost[d.id];

                    if (d.newStatus === 'INVALID') {
                        that.removeError(d);
                    } else if (d.newStatus === 'SOLVED') {
                        that.removeError(d);

                        //TODO the identifiers are ugly and can't be used frontend, use error position instead?
                        // or perhaps don't track this at all?
                        //_erCache.closed[d.error_type + ':' + d.identifier] = true;
                    }

                    return callback(err, d);
                });
        }
    },

    // get all cached errors covering the viewport
    getErrors: function(projection) {
        var viewport = projection.clipExtent();
        var min = [viewport[0][0], viewport[1][1]];
        var max = [viewport[1][0], viewport[0][1]];
        var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();

        return _erCache.rtree.search(bbox).map(function(d) {
            return d.data;
        });
    },

    // get a single error from the cache
    getError: function(id) {
        return _erCache.data[id];
    },

    // replace a single error in the cache
    replaceError: function(error) {
        if (!(error instanceof iOsmError) || !error.id) return;

        _erCache.data[error.id] = error;
        updateRtree(encodeErrorRtree(error), true); // true = replace
        return error;
    },

    // remove a single error from the cache
    removeError: function(error) {
        if (!(error instanceof iOsmError) || !error.id) return;

        delete _erCache.data[error.id];
        updateRtree(encodeErrorRtree(error), false); // false = remove
    }
};
<commit_msg>Fix incorrect usage of osm.userDetails callback<commit_after>import _extend from 'lodash-es/extend';
import _find from 'lodash-es/find';
import _forEach from 'lodash-es/forEach';

import rbush from 'rbush';

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { json as d3_json } from 'd3-request';
import { request as d3_request } from 'd3-request';

import { geoExtent, geoVecAdd } from '../geo';
import { iOsmError } from '../osm';
import { services } from './index';
import { t } from '../util/locale';
import { utilRebind, utilTiler, utilQsString } from '../util';

var tiler = utilTiler();
var dispatch = d3_dispatch('loaded');

var _erCache;
var _erZoom = 14;

var _impOsmUrls = {
    ow: 'http://directionofflow.skobbler.net/directionOfFlowService',
    mr: 'http://missingroads.skobbler.net/missingGeoService',
    tr: 'http://turnrestrictionservice.skobbler.net/turnRestrictionService'
};

function abortRequest(i) {
    _forEach(i, function(v) {
        if (v) {
            v.abort();
        }
    });
}

function abortUnwantedRequests(cache, tiles) {
    _forEach(cache.inflightTile, function(v, k) {
        var wanted = _find(tiles, function(tile) {
            return k === tile.id;
        });
        if (!wanted) {
            abortRequest(v);
            delete cache.inflightTile[k];
        }
    });
}

function encodeErrorRtree(d) {
    return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
}

// replace or remove error from rtree
function updateRtree(item, replace) {
    _erCache.rtree.remove(item, function isEql(a, b) {
        return a.data.id === b.data.id;
    });

    if (replace) {
        _erCache.rtree.insert(item);
    }
}

function linkErrorObject(d) {
    return '' + d + '';
}

function linkEntity(d) {
    return '' + d + '';
}

function pointAverage(points) {
    var x = 0;
    var y = 0;

    _forEach(points, function(v) {
        x += v.lon;
        y += v.lat;
    });

    x /= points.length;
    y /= points.length;

    return [x, y];
}

function relativeBearing(p1, p2) {
    var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
    if (angle < 0) {
        angle += 2 * Math.PI;
    }

    // Return degrees
    return angle * 180 / Math.PI;
}

// Assuming range [0,360)
function cardinalDirection(bearing) {
    var dir = 45 * Math.round(bearing / 45);
    var compass = {
        0: 'north',
        45: 'northeast',
        90: 'east',
        135: 'southeast',
        180: 'south',
        225: 'southwest',
        270: 'west',
        315: 'northwest',
        360: 'north'
    };

    return t('QA.improveOSM.directions.' + compass[dir]);
}

// Errors shouldn't obscure eachother
function preventCoincident(loc, bumpUp) {
    var coincident = false;
    do {
        // first time, move marker up. after that, move marker right.
        var delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
        loc = geoVecAdd(loc, delta);
        var bbox = geoExtent(loc).bbox();
        coincident = _erCache.rtree.search(bbox).length;
    } while (coincident);

    return loc;
}

export default {
    init: function() {
        if (!_erCache) {
            this.reset();
        }

        this.event = utilRebind(this, dispatch, 'on');
    },

    reset: function() {
        if (_erCache) {
            _forEach(_erCache.inflightTile, abortRequest);
        }
        _erCache = {
            data: {},
            loadedTile: {},
            inflightTile: {},
            inflightPost: {},
            closed: {},
            rtree: rbush()
        };
    },

    loadErrors: function(projection) {
        var options = {
            client: 'iD',
            confidenceLevel: 'C1', // most confident only, still have false positives
            status: 'OPEN',
            type: 'PARKING,ROAD,BOTH,PATH', // exclude WATER as it doesn't seem useful
            zoom: '19' // Use a high zoom so that clusters aren't returned
        };

        // determine the needed tiles to cover the view
        var tiles = tiler
            .zoomExtent([_erZoom, _erZoom])
            .getTiles(projection);

        // abort inflight requests that are no longer needed
        abortUnwantedRequests(_erCache, tiles);

        // issue new requests..
        tiles.forEach(function(tile) {
            if (_erCache.loadedTile[tile.id] || _erCache.inflightTile[tile.id]) return;

            var rect = tile.extent.rectangle();
            var params = _extend({}, options, { east: rect[0], south: rect[3], west: rect[2], north: rect[1] });

            // 3 separate requests to store for each tile
            var requests = {};

            _forEach(_impOsmUrls, function(v, k) {
                var url = v + '/search?' + utilQsString(params);

                requests[k] = d3_json(url,
                    function(err, data) {
                        delete _erCache.inflightTile[tile.id];

                        if (err) return;
                        _erCache.loadedTile[tile.id] = true;

                        // Road segments at high zoom == oneways
                        if (data.roadSegments) {
                            data.roadSegments.forEach(function(feature) {
                                // Position error at the approximate middle of the segment
                                var points = feature.points;
                                var mid = points.length / 2;
                                var loc;

                                // Even number of points, find midpoint of the middle two
                                // Odd number of points, use position of very middle point
                                if (mid % 1 === 0) {
                                    loc = pointAverage([points[mid - 1], points[mid]]);
                                } else {
                                    mid = points[Math.floor(mid)];
                                    loc = [mid.lon, mid.lat];
                                }

                                // One-ways can land on same segment in opposite direction
                                loc = preventCoincident(loc, false);

                                var d = new iOsmError({
                                    loc: loc,
                                    comments: null,
                                    error_subtype: '',
                                    error_type: k,
                                    icon: 'fas-arrow-circle-up', //TODO: Change arrow based on direction?
                                    identifier: { // this is used to post changes to the error
                                        wayId: feature.wayId,
                                        fromNodeId: feature.fromNodeId,
                                        toNodeId: feature.toNodeId
                                    },
                                    object_id: feature.wayId,
                                    object_type: 'way',
                                    status: feature.status
                                });

                                // Variables used in the description
                                d.replacements = {
                                    percentage: feature.percentOfTrips,
                                    num_trips: feature.numberOfTrips,
                                    highway: linkErrorObject(t('QA.keepRight.error_parts.highway')),
                                    from_node: linkEntity('n' + feature.fromNodeId),
                                    to_node: linkEntity('n' + feature.toNodeId)
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }

                        // Tiles at high zoom == missing roads
                        if (data.tiles) {
                            data.tiles.forEach(function(feature) {
                                // Average of recorded points should land on the missing geometry
                                var loc = pointAverage(feature.points);

                                // Missing geometry could happen to land on another error
                                loc = preventCoincident(loc, false);

                                var geoType = feature.type.toLowerCase();
                                var geoIcons = {
                                    road: 'maki-car',
                                    parking: 'maki-parking',
                                    both: 'maki-car',
                                    path: 'maki-shoe'
                                };

                                var d = new iOsmError({
                                    loc: loc,
                                    comments: null,
                                    error_subtype: geoType,
                                    error_type: k,
                                    icon: geoIcons[geoType],
                                    identifier: { x: feature.x, y: feature.y },
                                    status: feature.status
                                });

                                d.replacements = {
                                    num_trips: feature.numberOfTrips,
                                    geometry_type: t('QA.improveOSM.geometry_types.' + geoType)
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }

                        // Entities at high zoom == turn restrictions
                        if (data.entities) {
                            data.entities.forEach(function(feature) {
                                var loc = feature.point;

                                // Turn restrictions could be missing at same junction
                                // We also want to bump the error up so node is accessible
                                loc = preventCoincident([loc.lon, loc.lat], true);

                                // Elements are presented in a strange way
                                var ids = feature.id.split(',');
                                var from_way = ids[0];
                                var via_node = ids[3];
                                var to_way = ids[2].split(':')[1];

                                // Travel direction along from_way clarifies the turn restriction
                                var p1 = feature.segments[0].points[0];
                                var p2 = feature.segments[0].points[1];

                                var dir_of_travel = cardinalDirection(relativeBearing(p1, p2));

                                var d = new iOsmError({
                                    loc: loc,
                                    comments: null,
                                    error_subtype: '',
                                    error_type: k,
                                    icon: 'temaki-junction',
                                    identifier: feature.id,
                                    object_id: via_node,
                                    object_type: 'node',
                                    status: feature.status
                                });

                                // Variables used in the description
                                d.replacements = {
                                    num_passed: feature.numberOfPasses,
                                    num_trips: feature.segments[0].numberOfTrips,
                                    turn_restriction: feature.turnType.toLowerCase(),
                                    from_way: linkEntity('w' + from_way),
                                    to_way: linkEntity('w' + to_way),
                                    travel_direction: dir_of_travel,
                                    junction: linkErrorObject(t('QA.keepRight.error_parts.this_node'))
                                };

                                _erCache.data[d.id] = d;
                                _erCache.rtree.insert(encodeErrorRtree(d));
                            });
                        }
                    }
                );
            });

            _erCache.inflightTile[tile.id] = requests;
            dispatch.call('loaded');
        });
    },

    postUpdate: function(d, callback) {
        if (!services.osm.authenticated()) { // Username required in payload
            return callback({ message: 'Not Authenticated', status: -3}, d);
        }
        if (_erCache.inflightPost[d.id]) {
            return callback({ message: 'Error update already inflight', status: -2 }, d);
        }

        var that = this;

        // Payload can only be sent once username is established
        services.osm.userDetails(sendPayload);

        function sendPayload(err, user) {
            if (err) { return callback(err, d); }

            var type = d.error_type;
            var url = _impOsmUrls[type] + '/comment';
            var payload = {
                username: user.display_name
            };

            // Each error type has different data for identification
            if (type === 'ow') {
                payload.roadSegments = [ d.identifier ];
            } else if (type === 'mr') {
                payload.tiles = [ d.identifier ];
            } else if (type === 'tr') {
                payload.targetIds = [ d.identifier ];
            }

            // Comments don't currently work, if they ever do in future
            // it looks as though they require a separate post
            // if (d.newComment !== undefined) {
            //     payload.text = d.newComment;
            // }

            if (d.newStatus !== d.status) {
                payload.status = d.newStatus;
                payload.text = 'status changed';
            }

            _erCache.inflightPost[d.id] = d3_request(url)
                .header('Content-Type', 'application/json')
                .post(JSON.stringify(payload), function(err) {
                    delete _erCache.inflightPost[d.id];

                    // Unsuccessful response status, keep issue open
                    if (err.status !== 200) { return callback(err, d); }

                    that.removeError(d);

                    if (d.newStatus === 'SOLVED') {
                        //TODO the identifiers are ugly and can't be used frontend, use error position instead?
                        // or perhaps don't track this at all?
                        //_erCache.closed[d.error_type + ':' + d.identifier] = true;
                    }

                    return callback(err, d);
                });
        }
    },

    // get all cached errors covering the viewport
    getErrors: function(projection) {
        var viewport = projection.clipExtent();
        var min = [viewport[0][0], viewport[1][1]];
        var max = [viewport[1][0], viewport[0][1]];
        var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();

        return _erCache.rtree.search(bbox).map(function(d) {
            return d.data;
        });
    },

    // get a single error from the cache
    getError: function(id) {
        return _erCache.data[id];
    },

    // replace a single error in the cache
    replaceError: function(error) {
        if (!(error instanceof iOsmError) || !error.id) return;

        _erCache.data[error.id] = error;
        updateRtree(encodeErrorRtree(error), true); // true = replace
        return error;
    },

    // remove a single error from the cache
    removeError: function(error) {
        if (!(error instanceof iOsmError) || !error.id) return;

        delete _erCache.data[error.id];
        updateRtree(encodeErrorRtree(error), false); // false = remove
    }
};
<|endoftext|>


<commit_before>const db = require('sqlite')
const squel = require('squel')
const debug = require('debug')
const log = debug('app:library:get')

async function getLibrary() {
  let artists = {
    result: [],
    entities: {}
  }
  let songs = {
    result: [],
    entities: {}
  }

  // get all artists
  try {
    const q = squel.select()
      .from('artists')
      .field('artistId, name')
      .order('name')

    // log(q.toString())
    const { text, values } = q.toParam()
    const rows = await db.all(text, values)

    // normalize results
    rows.forEach(function(row){
      artists.result.push(row.artistId)
      artists.entities[row.artistId] = row
      artists.entities[row.artistId].songIds = []
    })
  } catch(err) {
    log(err.message)
    return Promise.reject(err)
  }

  // get all songs
  try {
    const q = squel.select()
      .from('songs')
      .field('songId, artistId, title, duration, provider')
      .order('title')

    // log(q.toString())
    const { text, values } = q.toParam()
    const rows = await db.all(text, values)

    // normalize results
    rows.forEach(function(row){
      songs.result.push(row.songId)
      songs.entities[row.songId] = row

      // used in library view as parent/child LUT
      artists.entities[row.artistId].songIds.push(row.songId)
    })
  } catch(err) {
    log(err.message)
    return Promise.reject(err)
  }

  return { artists, songs }
}

module.exports = getLibrary
<commit_msg>Include star count in song results<commit_after>const db = require('sqlite')
const squel = require('squel')
const debug = require('debug')
const log = debug('app:library:get')

async function getLibrary() {
  let artists = {
    result: [],
    entities: {}
  }
  let songs = {
    result: [],
    entities: {}
  }

  // get all artists
  try {
    const q = squel.select()
      .from('artists')
      .field('artistId, name')
      .order('name')

    // log(q.toString())
    const { text, values } = q.toParam()
    const rows = await db.all(text, values)

    // normalize results
    rows.forEach(function(row){
      artists.result.push(row.artistId)
      artists.entities[row.artistId] = row
      artists.entities[row.artistId].songIds = []
    })
  } catch(err) {
    log(err.message)
    return Promise.reject(err)
  }

  // get all songs
  try {
    const q = squel.select()
      .field('songId, artistId, title, duration, provider')
      .field('COUNT(stars.userId) AS stars')
      .from('songs')
      .left_join('stars USING(songId)')
      .group('songId')
      .order('title')

    // log(q.toString())
    const { text, values } = q.toParam()
    const rows = await db.all(text, values)

    // normalize results
    rows.forEach(function(row){
      songs.result.push(row.songId)
      songs.entities[row.songId] = row

      // used in library view as parent/child LUT
      artists.entities[row.artistId].songIds.push(row.songId)
    })
  } catch(err) {
    log(err.message)
    return Promise.reject(err)
  }

  return { artists, songs }
}

module.exports = getLibrary
<|endoftext|>


<commit_before>L.Control.Pan = L.Control.extend({
	options: {
		position: 'topleft',
		panOffset: 500
	},

	onAdd: function (map) {
		var className = 'leaflet-control-pan',
			container = L.DomUtil.create('div', className),
			off = this.options.panOffset;

		this._panButton('Up'   , className + '-up'
						, container, map, new L.Point(    0 , -off));
		this._panButton('Left' , className + '-left'
						, container, map, new L.Point( -off ,  0));
		this._panButton('Right', className + '-right'
						, container, map, new L.Point(  off ,  0));
		this._panButton('Down' , className + '-down'
						, container, map, new L.Point(    0 ,  off));

		return container;
	},

	_panButton: function (title, className, container, map, offset, text) {
		var wrapper = L.DomUtil.create('div', className + "-wrap", container);
		var link = L.DomUtil.create('a', className, wrapper);
		link.href = '#';
		link.title = title;
		L.DomEvent
			.on(link, 'click', L.DomEvent.stopPropagation)
			.on(link, 'click', L.DomEvent.preventDefault)
			.on(link, 'click', function(){ map.panBy(offset); }, map)
			.on(link, 'dblclick', L.DomEvent.stopPropagation)

		return link;
	}
});

L.Map.mergeOptions({
    panControl: true
});

L.Map.addInitHook(function () {
    if (this.options.panControl) {
		this.panControl = new L.Control.Pan();
		this.addControl(this.panControl);
	}
});

L.control.pan = function (options) {
    return new L.Control.Pan(options);
};
<commit_msg>Pan control defaults to false so it doesn't affect other plugins<commit_after>L.Control.Pan = L.Control.extend({
	options: {
		position: 'topleft',
		panOffset: 500
	},

	onAdd: function (map) {
		var className = 'leaflet-control-pan',
			container = L.DomUtil.create('div', className),
			off = this.options.panOffset;

		this._panButton('Up'   , className + '-up'
						, container, map, new L.Point(    0 , -off));
		this._panButton('Left' , className + '-left'
						, container, map, new L.Point( -off ,  0));
		this._panButton('Right', className + '-right'
						, container, map, new L.Point(  off ,  0));
		this._panButton('Down' , className + '-down'
						, container, map, new L.Point(    0 ,  off));

		return container;
	},

	_panButton: function (title, className, container, map, offset, text) {
		var wrapper = L.DomUtil.create('div', className + "-wrap", container);
		var link = L.DomUtil.create('a', className, wrapper);
		link.href = '#';
		link.title = title;
		L.DomEvent
			.on(link, 'click', L.DomEvent.stopPropagation)
			.on(link, 'click', L.DomEvent.preventDefault)
			.on(link, 'click', function(){ map.panBy(offset); }, map)
			.on(link, 'dblclick', L.DomEvent.stopPropagation)

		return link;
	}
});

L.Map.mergeOptions({
    panControl: false
});

L.Map.addInitHook(function () {
    if (this.options.panControl) {
		this.panControl = new L.Control.Pan();
		this.addControl(this.panControl);
	}
});

L.control.pan = function (options) {
    return new L.Control.Pan(options);
};
<|endoftext|>


<commit_before>const find = (arr, key, val) => arr.find(obj => val ? obj[key] === val : obj[key])

module.exports = function nuxtMeta (_options) {
  // Defaults
  const defaults = {
    name: process.env.npm_package_name,
    description: process.env.npm_package_description,
    charset: 'utf-8',
    viewport: 'width=device-width, initial-scale=1, minimal-ui',
    mobileApp: true,
    favicon: true,
    mobileAppIOS: false,
    appleStatusBarStyle: 'black-translucent',
    theme_color: this.options.loading && this.options.loading.color,
    lang: 'en',
    ogType: 'website',
    ogTitle: true,
    ogDescription: true
  }

  // Combine sources
  const options = Object.assign({}, defaults, this.options.manifest, this.options.meta, _options)

  // Charset
  if (options.charset && !find(this.options.head.meta, 'charset')) {
    this.options.head.meta.push({ charset: options.charset })
  }

  // Viewport
  if (options.viewport && !find(this.options.head.meta, 'name', 'viewport')) {
    this.options.head.meta.push({ name: 'viewport', content: options.viewport })
  }

  // mobileApp
  if (options.mobileApp && !find(this.options.head.meta, 'name', 'mobile-web-app-capable')) {
    this.options.head.meta.push({ name: 'mobile-web-app-capable', content: 'yes' })
  }

  // mobileApp (IOS)
  if (options.mobileAppIOS && !find(this.options.head.meta, 'name', 'apple-mobile-web-app-capable')) {
    this.options.head.meta.push({ name: 'apple-mobile-web-app-capable', content: 'yes' })
  }

  // statusBarStyle (IOS)
  if (options.mobileAppIOS && options.appleStatusBarStyle && !find(this.options.head.meta, 'name', 'apple-mobile-web-app-status-bar-style')) {
    this.options.head.meta.push({ name: 'apple-mobile-web-app-status-bar-style', content: options.appleStatusBarStyle })
  }

  // Icons
  if (options.favicon && options.icons && options.icons.length > 0) {
    const iconSmall = options.icons[0]
    const iconBig = options.icons[options.icons.length - 1]

    if (!find(this.options.head.link, 'rel', 'shortcut icon')) {
      this.options.head.link.push({ rel: 'shortcut icon', href: iconSmall.src })
    }

    if (!find(this.options.head.link, 'rel', 'apple-touch-icon')) {
      this.options.head.link.push({ rel: 'apple-touch-icon', href: iconBig.src, sizes: iconBig.sizes })
    }
  }

  // Title
  if (options.name && !this.options.head.title) {
    this.options.head.title = options.name
  }

  // IOS launch icon title
  const title = options.name || this.options.head.title || false
  if (title && !find(this.options.head.meta, 'name', 'apple-mobile-web-app-title')) {
    this.options.head.meta.push({ name: 'apple-mobile-web-app-title', content: title })
  }

  // description meta
  if (options.description && !find(this.options.head.meta, 'name', 'description')) {
    this.options.head.meta.push({ hid: 'description', name: 'description', content: options.description })
  }

  // theme-color meta
  if (options.theme_color && !find(this.options.head.meta, 'name', 'theme-color')) {
    this.options.head.meta.push({ name: 'theme-color', content: options.theme_color })
  }

  // Add lang to html tag
  if (options.lang && !(this.options.head.htmlAttrs && this.options.head.htmlAttrs.lang)) {
    if (!this.options.head.htmlAttrs) {
      this.options.head.htmlAttrs = {}
    }
    this.options.head.htmlAttrs.lang = options.lang
  }

  // og:type
  if (options.ogType && !find(this.options.head.meta, 'name', 'og:type')) {
    this.options.head.meta.push({ name: 'og:type', content: options.ogType })
  }

  // og:title
  if (options.ogTitle === true) {
    options.ogTitle = options.name
  }
  if (options.ogTitle && !find(this.options.head.meta, 'name', 'og:title')) {
    this.options.head.meta.push({ name: 'og:title', content: options.ogTitle })
  }

  // og:description
  if (options.ogDescription === true) {
    options.ogDescription = options.description
  }
  if (options.ogDescription && !find(this.options.head.meta, 'name', 'og:description')) {
    this.options.head.meta.push({ name: 'og:description', content: options.ogDescription })
  }
}

module.exports.meta = require('./package.json')
<commit_msg>feat(meta): apple-touch-startup-image<commit_after>const find = (arr, key, val) => arr.find(obj => val ? obj[key] === val : obj[key])

module.exports = function nuxtMeta (_options) {
  // Defaults
  const defaults = {
    name: process.env.npm_package_name,
    description: process.env.npm_package_description,
    charset: 'utf-8',
    viewport: 'width=device-width, initial-scale=1, minimal-ui',
    mobileApp: true,
    favicon: true,
    mobileAppIOS: false,
    appleStatusBarStyle: 'black-translucent',
    theme_color: this.options.loading && this.options.loading.color,
    lang: 'en',
    ogType: 'website',
    ogTitle: true,
    ogDescription: true
  }

  // Combine sources
  const options = Object.assign({}, defaults, this.options.manifest, this.options.meta, _options)

  // Charset
  if (options.charset && !find(this.options.head.meta, 'charset')) {
    this.options.head.meta.push({ charset: options.charset })
  }

  // Viewport
  if (options.viewport && !find(this.options.head.meta, 'name', 'viewport')) {
    this.options.head.meta.push({ name: 'viewport', content: options.viewport })
  }

  // mobileApp
  if (options.mobileApp && !find(this.options.head.meta, 'name', 'mobile-web-app-capable')) {
    this.options.head.meta.push({ name: 'mobile-web-app-capable', content: 'yes' })
  }

  // mobileApp (IOS)
  if (options.mobileAppIOS && !find(this.options.head.meta, 'name', 'apple-mobile-web-app-capable')) {
    this.options.head.meta.push({ name: 'apple-mobile-web-app-capable', content: 'yes' })
  }

  // statusBarStyle (IOS)
  if (options.mobileAppIOS && options.appleStatusBarStyle && !find(this.options.head.meta, 'name', 'apple-mobile-web-app-status-bar-style')) {
    this.options.head.meta.push({ name: 'apple-mobile-web-app-status-bar-style', content: options.appleStatusBarStyle })
  }

  // Icons
  if (options.favicon && options.icons && options.icons.length > 0) {
    const iconSmall = options.icons[0]
    const iconBig = options.icons[options.icons.length - 1]

    if (!find(this.options.head.link, 'rel', 'shortcut icon')) {
      this.options.head.link.push({ rel: 'shortcut icon', href: iconSmall.src })
    }

    if (!find(this.options.head.link, 'rel', 'apple-touch-icon')) {
      this.options.head.link.push({ rel: 'apple-touch-icon', href: iconBig.src, sizes: iconBig.sizes })
    }

    // Launch Screen Image (IOS)
    if (options.mobileAppIOS && !find(this.options.head.link, 'rel', 'apple-touch-startup-image')) {
      this.options.head.link.push({ rel: 'apple-touch-startup-image', href: iconBig.src })
    }
  }

  // Title
  if (options.name && !this.options.head.title) {
    this.options.head.title = options.name
  }

  // IOS launch icon title
  const title = options.name || this.options.head.title || false
  if (title && !find(this.options.head.meta, 'name', 'apple-mobile-web-app-title')) {
    this.options.head.meta.push({ name: 'apple-mobile-web-app-title', content: title })
  }

  // description meta
  if (options.description && !find(this.options.head.meta, 'name', 'description')) {
    this.options.head.meta.push({ hid: 'description', name: 'description', content: options.description })
  }

  // theme-color meta
  if (options.theme_color && !find(this.options.head.meta, 'name', 'theme-color')) {
    this.options.head.meta.push({ name: 'theme-color', content: options.theme_color })
  }

  // Add lang to html tag
  if (options.lang && !(this.options.head.htmlAttrs && this.options.head.htmlAttrs.lang)) {
    if (!this.options.head.htmlAttrs) {
      this.options.head.htmlAttrs = {}
    }
    this.options.head.htmlAttrs.lang = options.lang
  }

  // og:type
  if (options.ogType && !find(this.options.head.meta, 'name', 'og:type')) {
    this.options.head.meta.push({ name: 'og:type', content: options.ogType })
  }

  // og:title
  if (options.ogTitle === true) {
    options.ogTitle = options.name
  }
  if (options.ogTitle && !find(this.options.head.meta, 'name', 'og:title')) {
    this.options.head.meta.push({ name: 'og:title', content: options.ogTitle })
  }

  // og:description
  if (options.ogDescription === true) {
    options.ogDescription = options.description
  }
  if (options.ogDescription && !find(this.options.head.meta, 'name', 'og:description')) {
    this.options.head.meta.push({ name: 'og:description', content: options.ogDescription })
  }
}

module.exports.meta = require('./package.json')
<|endoftext|>


<commit_before>const fs = require('fs')
const rfr = require('rfr')
const actionsPath = './actions/'
const Model = require('./model').model
const extend = require('extend')
const ObjectId = require('mongoose').Types.ObjectId
const jimp = require('jimp')
const moment = require('moment')

const jwtHelper = rfr('helpers/jwt')
const errorHandler = rfr('helpers/error')
const FB = rfr('helpers/facebook')
const prerender = rfr('helpers/prerender')
const onesignal = rfr('helpers/onesignal')
const createQueryObject = rfr('helpers/request').createQueryObject

const controllerActions = {}

// Import default actions
const importActions = ['findOneAndUpdate', 'remove']
const createMethods = (element, index) => {
  controllerActions[element] = rfr(actionsPath + element)(Model)
}
importActions.forEach(createMethods)

const clearCacheAndDispatch = (instance) => {
  const eventUrl = `${process.env.EVENTS_URL}${instance.slug}`
  prerender.recache(eventUrl)
  .then(() => {
    createFacebookPost(instance, eventUrl)
    sendNotification(instance, eventUrl)
  })
  .catch(() => {
    errorHandler.sendMail({message: `Erro ao limpar o cache do prerender da url ${eventUrl}.`})
  })
}

const createFacebookPost = (instance, eventUrl) => {
  const data = {
    published: false,
    message: `Eventos! ${instance.title} - Veja mais no link. #Escoteiros #EscoteirosDeMinas`,
    link: eventUrl,
    scheduled_publish_time: Math.round(new Date().getTime() / 1000) + (60 * 5)
  }
  FB.api(`${process.env.FB_PAGE}/feed`, 'post', data, (res) => {
    if (!res || res.error) {
      errorHandler.sendMail({message: `Erro ao postar o evento ${eventUrl}.`, trace: JSON.stringify(res.error)})
      return false
    }

    const query = {_id: instance._id}
    const mod = {$set: {fb_post_id: (res.post_id || res.id)}}
    Model.findOneAndUpdate(query, mod, {new: true}, (err, data) => {
      if (err) {
        errorHandler.sendMail({message: `Erro ao atualizar o evento pós-postagem ${eventUrl}.`, trace: JSON.stringify(err)})
        throw err
      }
    })
  })
}

const sendNotification = (instance, eventUrl) => {
  const options = {
    image: `${process.env.ASSETS_URL}${instance.image}`,
    url: eventUrl,
    title: `Escoteiros de Minas | Eventos`,
    message: `Novo Evento - ${instance.title}`
  }

  onesignal.sendNotification(options)
  .then(() => {
    console.log('Notificação Enviada')
  })
  .catch(() => {
    errorHandler.sendMail({message: `Erro ao enviar a notificação pra OneSignal da notícia ${eventUrl}.`})
  })
}

// Controller custom actions
const customMethods = {
  findByIdOrSlug: (req, res) => {
    const query = (ObjectId.isValid(req.params.id)) ? {_id: req.params.id} : {slug: req.params.id}
    Model.findOne(query, (err, data) => {
      if (err) throw err
      res.status(200).json(data)
    })
  },
  find: (req, res) => {
    let query = createQueryObject(req)

    if (req.query.start_date) {
      query['start_date'] = {$gte: moment(req.query.start_date, 'DD/MM/YYYY')}
    }

    // When searching through events section and hosts, match any of the given filters.
    if (req.query.section && req.query.section.length) {
      query.section = {$in: req.query.section}
    }
    if (req.query.hosts && req.query.hosts.length) {
      query.hosts = {$in: req.query.hosts}
    }

    if (req.query.filter && req.query.filter.length) {
      let regex = new RegExp(req.query.filter, 'i')
      query = Object.assign(query, {
        '$or': [
          {title: regex},
          {description: regex},
          {place: regex},
          {section: regex},
          {hosts: regex},
          {slug: regex}
        ]
      })
    }

    const pagOptions = {
      page: Number.parseInt(req.query.page - 1) || 0,
      limit: Number.parseInt(req.query.limit) || 15
    }

    Model
      .find(query)
      .count()
      .exec((err, count) => {
        if (err) throw err
        const meta = {
          currentPage: (pagOptions.page + 1),
          limit: pagOptions.limit,
          totalPages: Math.ceil(count / pagOptions.limit)
        }
        Model
        .find(query)
        .sort({'start_date': 1})
        .skip(pagOptions.page * pagOptions.limit)
        .limit(pagOptions.limit)
        .populate('last_updated_by')
        .exec((err, data) => {
          if (err) throw err
          const response = {
            events: data,
            meta: meta
          }
          res.status(200).json(response)
        })
      })
  },
  create: (req, res) => {
    const data = req.body
    data['last_updated_by'] = jwtHelper.getUserId(req)
    data.start_date = moment(data.start_date, 'DD/MM/YYYY')
    if (data.end_date && data.end_date.length) data.end_date = moment(data.end_date, 'DD/MM/YYYY')
    const modelInstance = new Model(data)

    if (req.files) {
      const file = req.files.image
      const modelPath = `events/${modelInstance._id}`
      const localPath = `${process.cwd()}/public/${modelPath}`

      if (!fs.existsSync(localPath)) {
        fs.mkdirSync(localPath)
      }

      let fileName = ''
      if (file.mimetype === 'image/jpeg' || 'image/jpg') {
        fileName = `image.jpg`
      } else if (file.mimetype === 'image/png') {
        fileName = `image.png`
      } else {
        return res.status(422).json({format: 'invalid_format'})
      }

      const newFile = localPath + '/' + fileName
      fs.writeFile(newFile, file.data, (err) => {
        if (err) {
          res.status(500).json({error: err})
        }

        modelInstance['image'] = `${modelPath}/${fileName}`
        modelInstance.save((err, data) => {
          if (err) console.log(JSON.stringify(err))
          if (err) throw err
          data
          .populate('last_updated_by', (err, events) => {
            if (err) throw err
            res.status(201).json(events)

            jimp.read(newFile)
            .then((image) => {
              image.resize(480, 480)
              .quality(90)
              .write(newFile)
            })
            .catch((error) => {
              throw error
            })

            setTimeout(() => { clearCacheAndDispatch(data) }, 1000)
          })
        })
      })
    } else {
      modelInstance.save((err, data) => {
        if (err) throw err
        data
        .populate('last_updated_by', (err, events) => {
          if (err) throw err
          res.status(201).json(events)

          setTimeout(() => { clearCacheAndDispatch(data) }, 1000)
        })
      })
    }
  },
  update: (req, res) => {
    const mod = req.body
    mod['last_updated_by'] = jwtHelper.getUserId(req)
    mod.start_date = moment(mod.start_date, 'DD/MM/YYYY')
    if (mod.end_date) mod.end_date = moment(mod.end_date, 'DD/MM/YYYY')

    Model.findByIdAndUpdate(req.params.id, {$set: mod}, {new: true}, (err, modelInstance) => {
      if (err) throw err

      if (!modelInstance) res.status(404).json({error: 'event_not_found'})

      if (req.files && req.files.image) {
        const file = req.files.image
        const modelPath = `events/${modelInstance._id}`
        const localPath = `${process.cwd()}/public/${modelPath}`

        if (!fs.existsSync(localPath)) {
          fs.mkdirSync(localPath)
        }

        let fileName = ''
        if (file.mimetype === 'image/jpeg' || 'image/jpg') {
          fileName = `image.jpg`
        } else if (file.mimetype === 'image/png') {
          fileName = `image.png`
        } else {
          return res.status(422).json({format: 'invalid_format'})
        }

        const newFile = localPath + '/' + fileName
        fs.writeFile(newFile, file.data, (err) => {
          if (err) {
            res.status(500).json({error: err})
          }

          modelInstance['image'] = `${modelPath}/${fileName}`
          modelInstance.save((err, data) => {
            if (err) throw err
            data
            .populate('last_updated_by', (err, events) => {
              if (err) throw err
              res.status(200).json(events)

              jimp.read(newFile)
              .then((image) => {
                image.resize(480, 480)
                .quality(90)
                .write(newFile)
              })
              .catch((error) => {
                throw error
              })
            })
          })
        })
      } else {
        modelInstance.save((err, data) => {
          if (err) throw err
          data
          .populate('last_updated_by', (err, event) => {
            if (err) throw err
            res.status(200).json(event)
          })
        })
      }
    })
  },
  rescrape: (req, res) => {
    const query = {_id: req.params.id}

    Model.findOne(query, (err, events) => {
      if (err) throw err

      const eventUrl = `${process.env.EVENTS_URL}${events.slug}`
      prerender.recache(eventUrl)
      .then(() => {
        const data = {
          id: eventUrl,
          scrape: true
        }

        FB.api('/', 'post', data, (response) => {
          if (!response || response.error) {
            errorHandler.sendMail({message: `Erro ao atualizar o cache do evento ${eventUrl}.`, trace: JSON.stringify(response.error)})
            res.status(500).json({error: response.error})
            return false
          }

          res.status(200).json({success: true})
        })
      })
      .catch((error) => {
        res.status(500).json({error: error})
        errorHandler.sendMail({message: `Erro ao limpar o cache do prerender da url ${eventUrl}. Chamada de atualização de scrape.`})
      })
    })
  }
}

extend(controllerActions, customMethods)
module.exports = controllerActions
<commit_msg>Kinda fixing publish time<commit_after>const fs = require('fs')
const rfr = require('rfr')
const actionsPath = './actions/'
const Model = require('./model').model
const extend = require('extend')
const ObjectId = require('mongoose').Types.ObjectId
const jimp = require('jimp')
const moment = require('moment')

const jwtHelper = rfr('helpers/jwt')
const errorHandler = rfr('helpers/error')
const FB = rfr('helpers/facebook')
const prerender = rfr('helpers/prerender')
const onesignal = rfr('helpers/onesignal')
const createQueryObject = rfr('helpers/request').createQueryObject

const controllerActions = {}

// Import default actions
const importActions = ['findOneAndUpdate', 'remove']
const createMethods = (element, index) => {
  controllerActions[element] = rfr(actionsPath + element)(Model)
}
importActions.forEach(createMethods)

const clearCacheAndDispatch = (instance) => {
  const eventUrl = `${process.env.EVENTS_URL}${instance.slug}`
  prerender.recache(eventUrl)
  .then(() => {
    createFacebookPost(instance, eventUrl)
    sendNotification(instance, eventUrl)
  })
  .catch(() => {
    errorHandler.sendMail({message: `Erro ao limpar o cache do prerender da url ${eventUrl}.`})
  })
}

const createFacebookPost = (instance, eventUrl) => {
  const data = {
    published: false,
    message: `Eventos! ${instance.title} - Veja mais no link. #Escoteiros #EscoteirosDeMinas`,
    link: eventUrl,
    scheduled_publish_time: Math.round(new Date().getTime() / 1000) + (60 * 10)
  }
  FB.api(`${process.env.FB_PAGE}/feed`, 'post', data, (res) => {
    if (!res || res.error) {
      errorHandler.sendMail({message: `Erro ao postar o evento ${eventUrl}.`, trace: JSON.stringify(res.error)})
      return false
    }

    const query = {_id: instance._id}
    const mod = {$set: {fb_post_id: (res.post_id || res.id)}}
    Model.findOneAndUpdate(query, mod, {new: true}, (err, data) => {
      if (err) {
        errorHandler.sendMail({message: `Erro ao atualizar o evento pós-postagem ${eventUrl}.`, trace: JSON.stringify(err)})
        throw err
      }
    })
  })
}

const sendNotification = (instance, eventUrl) => {
  const options = {
    image: `${process.env.ASSETS_URL}${instance.image}`,
    url: eventUrl,
    title: `Escoteiros de Minas | Eventos`,
    message: `Novo Evento - ${instance.title}`
  }

  onesignal.sendNotification(options)
  .then(() => {
    console.log('Notificação Enviada')
  })
  .catch(() => {
    errorHandler.sendMail({message: `Erro ao enviar a notificação pra OneSignal da notícia ${eventUrl}.`})
  })
}

// Controller custom actions
const customMethods = {
  findByIdOrSlug: (req, res) => {
    const query = (ObjectId.isValid(req.params.id)) ? {_id: req.params.id} : {slug: req.params.id}
    Model.findOne(query, (err, data) => {
      if (err) throw err
      res.status(200).json(data)
    })
  },
  find: (req, res) => {
    let query = createQueryObject(req)

    if (req.query.start_date) {
      query['start_date'] = {$gte: moment(req.query.start_date, 'DD/MM/YYYY')}
    }

    // When searching through events section and hosts, match any of the given filters.
    if (req.query.section && req.query.section.length) {
      query.section = {$in: req.query.section}
    }
    if (req.query.hosts && req.query.hosts.length) {
      query.hosts = {$in: req.query.hosts}
    }

    if (req.query.filter && req.query.filter.length) {
      let regex = new RegExp(req.query.filter, 'i')
      query = Object.assign(query, {
        '$or': [
          {title: regex},
          {description: regex},
          {place: regex},
          {section: regex},
          {hosts: regex},
          {slug: regex}
        ]
      })
    }

    const pagOptions = {
      page: Number.parseInt(req.query.page - 1) || 0,
      limit: Number.parseInt(req.query.limit) || 15
    }

    Model
      .find(query)
      .count()
      .exec((err, count) => {
        if (err) throw err
        const meta = {
          currentPage: (pagOptions.page + 1),
          limit: pagOptions.limit,
          totalPages: Math.ceil(count / pagOptions.limit)
        }
        Model
        .find(query)
        .sort({'start_date': 1})
        .skip(pagOptions.page * pagOptions.limit)
        .limit(pagOptions.limit)
        .populate('last_updated_by')
        .exec((err, data) => {
          if (err) throw err
          const response = {
            events: data,
            meta: meta
          }
          res.status(200).json(response)
        })
      })
  },
  create: (req, res) => {
    const data = req.body
    data['last_updated_by'] = jwtHelper.getUserId(req)
    data.start_date = moment(data.start_date, 'DD/MM/YYYY')
    if (data.end_date && data.end_date.length) data.end_date = moment(data.end_date, 'DD/MM/YYYY')
    const modelInstance = new Model(data)

    if (req.files) {
      const file = req.files.image
      const modelPath = `events/${modelInstance._id}`
      const localPath = `${process.cwd()}/public/${modelPath}`

      if (!fs.existsSync(localPath)) {
        fs.mkdirSync(localPath)
      }

      let fileName = ''
      if (file.mimetype === 'image/jpeg' || 'image/jpg') {
        fileName = `image.jpg`
      } else if (file.mimetype === 'image/png') {
        fileName = `image.png`
      } else {
        return res.status(422).json({format: 'invalid_format'})
      }

      const newFile = localPath + '/' + fileName
      fs.writeFile(newFile, file.data, (err) => {
        if (err) {
          res.status(500).json({error: err})
        }

        modelInstance['image'] = `${modelPath}/${fileName}`
        modelInstance.save((err, data) => {
          if (err) console.log(JSON.stringify(err))
          if (err) throw err
          data
          .populate('last_updated_by', (err, events) => {
            if (err) throw err
            res.status(201).json(events)

            jimp.read(newFile)
            .then((image) => {
              image.resize(480, 480)
              .quality(90)
              .write(newFile)
            })
            .catch((error) => {
              throw error
            })

            setTimeout(() => { clearCacheAndDispatch(data) }, 1000)
          })
        })
      })
    } else {
      modelInstance.save((err, data) => {
        if (err) throw err
        data
        .populate('last_updated_by', (err, events) => {
          if (err) throw err
          res.status(201).json(events)

          setTimeout(() => { clearCacheAndDispatch(data) }, 1000)
        })
      })
    }
  },
  update: (req, res) => {
    const mod = req.body
    mod['last_updated_by'] = jwtHelper.getUserId(req)
    mod.start_date = moment(mod.start_date, 'DD/MM/YYYY')
    if (mod.end_date) mod.end_date = moment(mod.end_date, 'DD/MM/YYYY')

    Model.findByIdAndUpdate(req.params.id, {$set: mod}, {new: true}, (err, modelInstance) => {
      if (err) throw err

      if (!modelInstance) res.status(404).json({error: 'event_not_found'})

      if (req.files && req.files.image) {
        const file = req.files.image
        const modelPath = `events/${modelInstance._id}`
        const localPath = `${process.cwd()}/public/${modelPath}`

        if (!fs.existsSync(localPath)) {
          fs.mkdirSync(localPath)
        }

        let fileName = ''
        if (file.mimetype === 'image/jpeg' || 'image/jpg') {
          fileName = `image.jpg`
        } else if (file.mimetype === 'image/png') {
          fileName = `image.png`
        } else {
          return res.status(422).json({format: 'invalid_format'})
        }

        const newFile = localPath + '/' + fileName
        fs.writeFile(newFile, file.data, (err) => {
          if (err) {
            res.status(500).json({error: err})
          }

          modelInstance['image'] = `${modelPath}/${fileName}`
          modelInstance.save((err, data) => {
            if (err) throw err
            data
            .populate('last_updated_by', (err, events) => {
              if (err) throw err
              res.status(200).json(events)

              jimp.read(newFile)
              .then((image) => {
                image.resize(480, 480)
                .quality(90)
                .write(newFile)
              })
              .catch((error) => {
                throw error
              })
            })
          })
        })
      } else {
        modelInstance.save((err, data) => {
          if (err) throw err
          data
          .populate('last_updated_by', (err, event) => {
            if (err) throw err
            res.status(200).json(event)
          })
        })
      }
    })
  },
  rescrape: (req, res) => {
    const query = {_id: req.params.id}

    Model.findOne(query, (err, events) => {
      if (err) throw err

      const eventUrl = `${process.env.EVENTS_URL}${events.slug}`
      prerender.recache(eventUrl)
      .then(() => {
        const data = {
          id: eventUrl,
          scrape: true
        }

        FB.api('/', 'post', data, (response) => {
          if (!response || response.error) {
            errorHandler.sendMail({message: `Erro ao atualizar o cache do evento ${eventUrl}.`, trace: JSON.stringify(response.error)})
            res.status(500).json({error: response.error})
            return false
          }

          res.status(200).json({success: true})
        })
      })
      .catch((error) => {
        res.status(500).json({error: error})
        errorHandler.sendMail({message: `Erro ao limpar o cache do prerender da url ${eventUrl}. Chamada de atualização de scrape.`})
      })
    })
  }
}

extend(controllerActions, customMethods)
module.exports = controllerActions
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu } = Components;

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "Messaging","resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/AndroidLog.jsm", "AndroidLog");

const EXPERIMENTS_CONFIGURATION = "https://firefox.settings.services.mozilla.com/v1/buckets/fennec/collections/experiments/records";

const Experiments = Services.wm.getMostRecentWindow("navigator:browser").Experiments;

document.addEventListener("DOMContentLoaded", initList, false);

function log(msg) {
  Log.d("expt-addon", msg);
}

function initList() {
  let list = document.getElementById("list");
  list.addEventListener("click", toggleOverride, false);

  Promise.all([promiseEnabledExperiments(), promiseExperimentsConfiguration()]).then(values => {
    let enabledExperiments = values[0];
    let configuration = values[1];

    configuration.data.forEach(function(experiment) {
      let item = document.createElement("li");
      item.textContent = experiment.name;
      item.setAttribute("name", experiment.name);
      item.setAttribute("isEnabled", enabledExperiments.indexOf(experiment.name) != -1);
      list.appendChild(item);
    });
  });
}

function toggleOverride(e) {
  let item = e.originalTarget;
  let name = item.getAttribute("name");
  let isEnabled = item.getAttribute("isEnabled") === "true";

  log("toggleOverride: " + name);

  Experiments.setOverride(name, !isEnabled);
  item.setAttribute("isEnabled", !isEnabled);
}

/**
 * Get list of locally enabled experiments
 */
function promiseEnabledExperiments() {
  log("promiseEnabledExperiments");

  return Messaging.sendRequestForResult({
    type: "Experiments:GetActive"
  }).then(experiments => {
    return JSON.parse(experiments);
  });
}

/**
 * Fetch list of experiments from server configuration
 */
function promiseExperimentsConfiguration() {
  log("promiseExperimentsConfiguration");

  return new Promise((resolve, reject) => {
    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);

    try {
      xhr.open("GET", EXPERIMENTS_CONFIGURATION, true);
    } catch (e) {
      reject("Error opening request: " + e);
      return;
    }

    xhr.onerror = function onerror(e) {
      reject("Error making request: " + e.error);
    };

    xhr.onload = function onload(event) {
      if (xhr.status === 200) {
        try {
          resolve(JSON.parse(xhr.responseText));
        } catch (e) {
          reject("Error parsing request: " + e);
        }
      } else {
        reject("Request to " + url + " returned status " + xhr.status);
      }
    };

    xhr.send(null);
  });
}
<commit_msg>Fix issue 9 - Switch to new Messaging API with backward compatibility<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

const { classes: Cc, interfaces: Ci, manager: Cm, utils: Cu } = Components;

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher","resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messaging","resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/AndroidLog.jsm", "AndroidLog");

const EXPERIMENTS_CONFIGURATION = "https://firefox.settings.services.mozilla.com/v1/buckets/fennec/collections/experiments/records";

const Experiments = Services.wm.getMostRecentWindow("navigator:browser").Experiments;

document.addEventListener("DOMContentLoaded", initList, false);

function log(msg) {
  Log.d("expt-addon", msg);
}

function initList() {
  let list = document.getElementById("list");
  list.addEventListener("click", toggleOverride, false);

  Promise.all([promiseEnabledExperiments(), promiseExperimentsConfiguration()]).then(values => {
    let enabledExperiments = values[0];
    let configuration = values[1];

    configuration.data.forEach(function(experiment) {
      let item = document.createElement("li");
      item.textContent = experiment.name;
      item.setAttribute("name", experiment.name);
      item.setAttribute("isEnabled", enabledExperiments.indexOf(experiment.name) != -1);
      list.appendChild(item);
    });
  });
}

function toggleOverride(e) {
  let item = e.originalTarget;
  let name = item.getAttribute("name");
  let isEnabled = item.getAttribute("isEnabled") === "true";

  log("toggleOverride: " + name);

  Experiments.setOverride(name, !isEnabled);
  item.setAttribute("isEnabled", !isEnabled);
}

/**
 * Get list of locally enabled experiments
 */
function promiseEnabledExperiments() {
  log("promiseEnabledExperiments");

  // Check app version for backward compatibility
  let appVersion = AppConstants.MOZ_APP_VERSION;
  let implEventDispatcher;
  if(appVersion.split(".")[0] > 53){
    implEventDispatcher = EventDispatcher.instance;
  } else {
    implEventDispatcher = Messaging;
  }

  return implEventDispatcher.sendRequestForResult({
    type: "Experiments:GetActive"
  }).then(experiments => {
    let result = experiments;

    //  Before firefox 55 data format is json serialized as string
    //  Try to parse json for backward compatibility
    try {
      result = JSON.parse(experiments);
    } catch (e) {
      log("json parse fail; fallback to return object directly");
    }
    return result;
  });
}

/**
 * Fetch list of experiments from server configuration
 */
function promiseExperimentsConfiguration() {
  log("promiseExperimentsConfiguration");

  return new Promise((resolve, reject) => {
    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);

    try {
      xhr.open("GET", EXPERIMENTS_CONFIGURATION, true);
    } catch (e) {
      reject("Error opening request: " + e);
      return;
    }

    xhr.onerror = function onerror(e) {
      reject("Error making request: " + e.error);
    };

    xhr.onload = function onload(event) {
      if (xhr.status === 200) {
        try {
          resolve(JSON.parse(xhr.responseText));
        } catch (e) {
          reject("Error parsing request: " + e);
        }
      } else {
        reject("Request to " + url + " returned status " + xhr.status);
      }
    };

    xhr.send(null);
  });
}
<|endoftext|>


<commit_before>const { sub, isDimension } = require('./Subscript')

module.exports = class LoopIndexVars {
  constructor() {
    this.loopIndexVars = this.loopIndexVarGen()
    this.lhsLoopIndices = {}
  }
  index(subscriptName) {
    let index
    if (isDimension(subscriptName)) {
      let subscript = sub(subscriptName)
      if (subscript) {
        let family = subscript.family
        index = this.lhsLoopIndices[family]
        if (!index) {
          index = this.loopIndexVars.next()
          this.lhsLoopIndices[family] = index
        }
      }
    }
    return index
  }
  marked() {
    return this.loopIndexVars.marked
  }
  loopIndexVarGen() {
    // Return an object supplying C loop index variables.
    // Loop index variables are assigned in order from this list.
    const LOOP_INDEX_VARS = ['i', 'j', 'k', 'l']
    let iter = LOOP_INDEX_VARS[Symbol.iterator]()
    // Array functions like SUM are evaluated with a special inner loop using this loop index variable.
    const ARRAY_FN_LOOP_INDEX_VAR = 'm'
    return {
      next: () => iter.next().value,
      marked: ARRAY_FN_LOOP_INDEX_VAR
    }
  }
}

// Test
// const { Subscript } = require('./Subscript')
// let loopIndices = new LoopIndexVars();
// console.error(loopIndices.marked());
// let s = Subscript('S', ['A1', 'A2', 'A3', 'A4']);
// let t = Subscript('T', ['B1', 'B2', 'B3', 'B4']);
// console.error(isDimension('_s'));
// console.error(loopIndices.index('_s'));
// console.error(loopIndices.index('_t'));
// console.error(loopIndices.index('_s'));
<commit_msg>map loop index vars to subscript name instead of family to enable two dims in the same family<commit_after>const { sub, isDimension } = require('./Subscript')

module.exports = class LoopIndexVars {
  constructor() {
    this.loopIndexVars = this.loopIndexVarGen()
    this.lhsLoopIndices = {}
  }
  index(subscriptName) {
    let index
    if (isDimension(subscriptName)) {
      index = this.lhsLoopIndices[subscriptName]
      if (!index) {
        index = this.loopIndexVars.next()
        this.lhsLoopIndices[subscriptName] = index
      }
    }
    return index
  }
  marked() {
    return this.loopIndexVars.marked
  }
  loopIndexVarGen() {
    // Return an object supplying C loop index variables.
    // Loop index variables are assigned in order from this list.
    const LOOP_INDEX_VARS = ['i', 'j', 'k', 'l']
    let iter = LOOP_INDEX_VARS[Symbol.iterator]()
    // Array functions like SUM are evaluated with a special inner loop using this loop index variable.
    const ARRAY_FN_LOOP_INDEX_VAR = 'm'
    return {
      next: () => iter.next().value,
      marked: ARRAY_FN_LOOP_INDEX_VAR
    }
  }
}

// Test
// const { Subscript } = require('./Subscript')
// let loopIndices = new LoopIndexVars();
// console.error(loopIndices.marked());
// let s = Subscript('S', ['A1', 'A2', 'A3', 'A4']);
// let t = Subscript('T', ['B1', 'B2', 'B3', 'B4']);
// console.error(isDimension('_s'));
// console.error(loopIndices.index('_s'));
// console.error(loopIndices.index('_t'));
// console.error(loopIndices.index('_s'));
<|endoftext|>


<commit_before>var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('mma_data.db');
var bot = null;

exports.init = function(bot_obj) {
    bot = bot_obj;

    db.serialize(function() {
        db.run("CREATE TABLE IF NOT EXISTS mma_data (user_id TEXT PRIMARY KEY NOT NULL, date INTEGER)");
    });

    bot.slack.on('message', slack_on_message);
}

function slack_on_message(message) {
    if(message.type != 'message') return;

    var channel = bot.slack.getChannelGroupOrDMByID(message.channel);
    if(message.text && message.text.indexOf('!전역일 등록 ') === 0) {
        var split_text = message.text.split(' ');
        if(split_text.length != 3) {
             channel.send("형식: !전역일 등록 YYYY-MM-DD");
             return;
        }
        var d = Date.parse(split_text[2]);
        if(!d) {
            chanel.send("날짜 형식이 잘못되었습니다.");
            return;
        }

        db.serialize(function() {
            db.run("INSERT OR REPLACE INTO mma_data VALUES ('" + message.user + "', " + d + ");");
            chanel.send("전역일이 " + new Date(d).toISOString().slice(0,10) + "으로 설정되었습니다");
        });

        return;
    }

    if(message.text && message.text == "!전역일") {
        //TODO : 동시에 여러명 보는 것도
        db.all("SELECT * FROM mma_data WHERE user='" + message.user + "';", function(err, rows) {
            if(rows.length < 1) {
                channel.send("전역일이 등록되어 있지 않습니다. `!전역일 등록 YYYY-MM-DD`");
                return;
            }

            var date = new Date(rows[0].date);
            channel.send("전역일 : " + date.toISOString().slice(0,10) + "\n" + "===>------------------------[10%]");
        });
        return;
    }
}
<commit_msg>전역일 계산기 형식 에러 수정 #6<commit_after>var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('mma_data.db');
var bot = null;

exports.init = function(bot_obj) {
    bot = bot_obj;

    db.serialize(function() {
        db.run("CREATE TABLE IF NOT EXISTS mma_data (user_id TEXT PRIMARY KEY NOT NULL, date INTEGER)");
    });

    bot.slack.on('message', slack_on_message);
}

function slack_on_message(message) {
    if(message.type != 'message') return;

    var channel = bot.slack.getChannelGroupOrDMByID(message.channel);
    if(message.text && message.text.indexOf('!전역일 등록 ') === 0) {
        var split_text = message.text.split(' ');
        if(split_text.length != 3) {
             channel.send("형식: `!전역일 등록 YYYY-MM-DD`");
             return;
        }
        var d = Date.parse(split_text[2]);
        if(!d) {
            channel.send("날짜 형식이 잘못되었습니다. `!전역일 등록 YYYY-MM-DD`");
            return;
        }

        db.serialize(function() {
            db.run("INSERT OR REPLACE INTO mma_data VALUES ('" + message.user + "', " + d + ");");
            channel.send("전역일이 " + new Date(d).toISOString().slice(0,10) + "으로 설정되었습니다");
        });

        return;
    }

    if(message.text && message.text == "!전역일") {
        //TODO : 동시에 여러명 보는 것도
        db.all("SELECT * FROM mma_data WHERE user='" + message.user + "';", function(err, rows) {
            if(rows.length < 1) {
                channel.send("전역일이 등록되어 있지 않습니다. `!전역일 등록 YYYY-MM-DD`");
                return;
            }

            var date = new Date(rows[0].date);
            channel.send("전역일 : " + date.toISOString().slice(0,10) + "\n" + "===>------------------------[10%]");
        });
        return;
    }
}
<|endoftext|>


<commit_before>var LOGNS = 'DeezerKids:';
var APP_ID = '236482';
var CHANNEL_URL = 'http://www.beup2date.com/DeezerKids/channel.html';

var DeezerKids = angular.module('DeezerKids', ['firebase']);

DeezerKids.controller('AppController', function AppController($scope, $rootScope, $http, $firebaseObject) {

	// Init config
	$scope.login = false;

	// when landing on the page, get all accounts and show them
	$http.get('/api/accounts')
		.success(function(data) {
			if (data.length == 1) {
				console.log(LOGNS, 'Account already loggedin');
				$scope.account = data[0];
				$scope.login = true;
			} else {
				console.log(LOGNS, 'Account not loggedin yet');
				$scope.login = false;
			}
			console.log(LOGNS, data);
		})
		.error(function(data) {
			console.log(LOGNS, 'Error: ' + data);
		});

	// Global for test purpose
	rootScope = $rootScope;
	scope = $scope;

	DZ.init({
		appId: APP_ID,
		channelUrl: CHANNEL_URL
	});

	console.log(LOGNS, 'Deezer-API initialiazed successfully');

	// --------------------------------------------------- Methods

	$scope.doLogin = function() {
		$http.get('http://beup2date.com/DeezerKids/devices/123')
			.success(function(data) {
				console.log(LOGNS, data);
			})
			.error(function(data) {
				console.log(LOGNS, 'Error: ' + data);
			});
	};

	$scope.doLogin2 = function() {
		console.log(LOGNS, 'login clicked');

		DZ.login(function(response) {
			if (response.authResponse) {
				console.log(LOGNS, 'successfully logged in with Token ', response.authResponse.accessToken);
				$scope.account.accessToken = response.authResponse.accessToken;
				$scope.account.expire = response.authResponse.expire;
				$scope.account.userID = response.userID;
				createAccount();
			} else {
				console.log(LOGNS, 'login aborded by user');
				$scope.login = false;
			}
		}, {scope: 'basic_access,email,offline_access,manage_library'});
	};

	$scope.doLogout = function() {
		console.log(LOGNS, 'logout clicked');
	};

	// save the account after login
	function createAccount() {
		$http.post('/api/accounts', $scope.account)
			.success(function(data) {
				console.log(LOGNS, 'Account saved to database', data);
				$scope.login = true;
			})
			.error(function(data) {
				console.log(LOGNS, 'Error while saving account: ' + data);
				$scope.login = false;
			});
	};

	// delete the account after logout
	function deleteAccount(id) {
		$http.delete('/api/accounts/' + id)
			.success(function(data) {
				$scope.account = { };
				console.log(LOGNS, 'Account successfully deleted', data);
				$scope.login = false;
			})
			.error(function(data) {
				console.log(LOGNS, 'Error while deleting account: ' + data);
			});
	};
});
<commit_msg>Update core.js<commit_after>var LOGNS = 'DeezerKids:';
var APP_ID = '236482';
var CHANNEL_URL = 'http://www.beup2date.com/DeezerKids/channel.html';

var DeezerKids = angular.module('DeezerKids', ['firebase']);

DeezerKids.controller('AppController', function($scope, $rootScope, $http, $firebaseObject) {

	// Init config
	$scope.login = false;

	// when landing on the page, get all accounts and show them
	$http.get('/api/accounts')
		.success(function(data) {
			if (data.length == 1) {
				console.log(LOGNS, 'Account already loggedin');
				$scope.account = data[0];
				$scope.login = true;
			} else {
				console.log(LOGNS, 'Account not loggedin yet');
				$scope.login = false;
			}
			console.log(LOGNS, data);
		})
		.error(function(data) {
			console.log(LOGNS, 'Error: ' + data);
		});

	// Global for test purpose
	rootScope = $rootScope;
	scope = $scope;

	DZ.init({
		appId: APP_ID,
		channelUrl: CHANNEL_URL
	});

	console.log(LOGNS, 'Deezer-API initialiazed successfully');

	// --------------------------------------------------- Methods

	$scope.doLogin = function() {
		$http.get('http://beup2date.com/DeezerKids/devices/123')
			.success(function(data) {
				console.log(LOGNS, data);
			})
			.error(function(data) {
				console.log(LOGNS, 'Error: ' + data);
			});
	};

	$scope.doLogin2 = function() {
		console.log(LOGNS, 'login clicked');

		DZ.login(function(response) {
			if (response.authResponse) {
				console.log(LOGNS, 'successfully logged in with Token ', response.authResponse.accessToken);
				$scope.account.accessToken = response.authResponse.accessToken;
				$scope.account.expire = response.authResponse.expire;
				$scope.account.userID = response.userID;
				createAccount();
			} else {
				console.log(LOGNS, 'login aborded by user');
				$scope.login = false;
			}
		}, {scope: 'basic_access,email,offline_access,manage_library'});
	};

	$scope.doLogout = function() {
		console.log(LOGNS, 'logout clicked');
	};

	// save the account after login
	function createAccount() {
		$http.post('/api/accounts', $scope.account)
			.success(function(data) {
				console.log(LOGNS, 'Account saved to database', data);
				$scope.login = true;
			})
			.error(function(data) {
				console.log(LOGNS, 'Error while saving account: ' + data);
				$scope.login = false;
			});
	};

	// delete the account after logout
	function deleteAccount(id) {
		$http.delete('/api/accounts/' + id)
			.success(function(data) {
				$scope.account = { };
				console.log(LOGNS, 'Account successfully deleted', data);
				$scope.login = false;
			})
			.error(function(data) {
				console.log(LOGNS, 'Error while deleting account: ' + data);
			});
	};
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Internet of Protocols Alliance (IOPA)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const util = require('util'),
    iopaStream = require('iopa-common-stream');

const constants = require('iopa').constants,
    IOPA = constants.IOPA,
    SERVER = constants.SERVER

const MESSAGE_LOGGER = {
     CAPABILITY: "urn:io.iopa:MessageLogger",
     HOOKED: "messageLogger.Hooked"
     }

 const packageVersion = require('../package.json').version;

/**
 * IOPA Middleware
 *
 * @class MessageLogger
 * @this app.properties  the IOPA AppBuilder Properties Dictionary, used to add server.capabilities
 * @constructor
 * @public
 */
function MessageLogger(app) {
     app.properties[SERVER.Capabilities][MESSAGE_LOGGER.CAPABILITY] = {};
     app.properties[SERVER.Capabilities][MESSAGE_LOGGER.CAPABILITY][SERVER.Version] = packageVersion;
}

/**
 * @method invoke
 * @param context IOPA context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
MessageLogger.prototype.channel = function MessageLogger_channel(context, next) {

  // context.log.info("[IOPA] REQUEST IN " + _requestLog(context));
        return next();
};

/**
 * @method invoke
 * @param context IOPA context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
MessageLogger.prototype.invoke = function MessageLogger_invoke(context, next) {
        context.response[SERVER.RawStream] = new iopaStream.OutgoingStreamTransform(_writeResponse.bind(this, context.response, context.response[SERVER.RawStream]));
        context.log.info("[IOPA] REQUEST IN " + _requestLog(context))
        return next();

};

/**
 * @method invoke
 * @param context IOPA context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
MessageLogger.prototype.connect = function MessageLogger_connect(context, next) {
     context[IOPA.Events].on(IOPA.EVENTS.Response, _invokeOnParentResponse.bind(this, context));

        return next();

};

/**
 * @method connect
 * @this context IOPA context dictionary
 */
MessageLogger.prototype.dispatch = function MessageLogger_dispatch(context, next) {
            context[SERVER.RawStream] = new iopaStream.OutgoingStreamTransform(_writeRequest.bind(this, context, context[SERVER.RawStream]));
        return next();
}

/**
 * @method _invokeOnParentResponse
 * @this CacheMatch
 * @param channelContext IOPA parent context dictionary
 * @param context IOPA childResponse context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
function _invokeOnParentResponse(parentContext, response) {
   response.log.info("[IOPA] RESPONSE IN " + _responseLog(response))

       // HOOK INTO RESPONSE STREAM
   response.response[SERVER.RawStream] = new iopaStream.OutgoingStreamTransform(_writeResponse.bind(this, response.response, response.response[SERVER.RawStream]));

};

/**
 * @method _write
 * @param context IOPA context dictionary
 * @param nextStream Stream The raw stream saved that is next in chain for writing
 * @param chunk     String | Buffer The data to write
 * @param encoding String The encoding, if chunk is a String
 * @param callback Function Callback for when this chunk of data is flushed
 * @private
*/
function _writeResponse(context, nextStream, chunk, encoding, callback) {
      nextStream.write(chunk, encoding, callback);
       context.log.info("[IOPA] RESPONSE OUT " + _responseLog(context));
};

/**
 * @method _write
 * @param context IOPA context dictionary
 * @param nextStream Stream The raw stream saved that is next in chain for writing
 * @param chunk     String | Buffer The data to write
 * @param encoding String The encoding, if chunk is a String
 * @param callback Function Callback for when this chunk of data is flushed
 * @private
*/
function _writeRequest(context, nextStream, chunk, encoding, callback) {
    nextStream.write(chunk, encoding, callback);
      context.log.info("[IOPA] REQUEST OUT " + _requestLog(context));
};

function _url(context) {
    return context[IOPA.Scheme]
        + "//" + context[SERVER.RemoteAddress]
        + ":" + context[SERVER.RemotePort]
        + context[IOPA.Path]
        + (context[IOPA.QueryString] ? + context[IOPA.QueryString] : "");
}

function _requestLog(context) {
    return context[IOPA.Method] + " " + context[IOPA.MessageId] + ":" + context[IOPA.Seq] + " "
        + _url(context)
       + "  " + ((context[IOPA.Body] !== null) ? context[IOPA.Body].toString() : "");
}

function _responseLog(response, chunk) {

    return response[IOPA.Method] + " " + response[IOPA.MessageId] + ":" + response[IOPA.Seq] + " "
        + response[IOPA.StatusCode] + "/"
        + response[IOPA.ReasonPhrase]
        + " [" + response[SERVER.RemoteAddress]
        + ":" + response[SERVER.RemotePort] + "]" + "  "
        + ((response[IOPA.Body] !== null) ? response[IOPA.Body].toString() : "");
}

module.exports = MessageLogger;
<commit_msg>dev test<commit_after>/*
 * Copyright (c) 2015 Internet of Protocols Alliance (IOPA)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const util = require('util'),
    iopaStream = require('iopa-common-stream');

const constants = require('iopa').constants,
    IOPA = constants.IOPA,
    SERVER = constants.SERVER

const MESSAGE_LOGGER = {
     CAPABILITY: "urn:io.iopa:MessageLogger",
     HOOKED: "messageLogger.Hooked"
     }

 const packageVersion = require('../package.json').version;

/**
 * IOPA Middleware
 *
 * @class MessageLogger
 * @this app.properties  the IOPA AppBuilder Properties Dictionary, used to add server.capabilities
 * @constructor
 * @public
 */
function MessageLogger(app) {
     app.properties[SERVER.Capabilities][MESSAGE_LOGGER.CAPABILITY] = {};
     app.properties[SERVER.Capabilities][MESSAGE_LOGGER.CAPABILITY][SERVER.Version] = packageVersion;
}

/**
 * @method invoke
 * @param context IOPA context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
MessageLogger.prototype.channel = function MessageLogger_channel(channelContext, next) {
     channelContext[IOPA.Events].on(IOPA.EVENTS.Response, _invokeOnParentResponse.bind(this, channelContext));
     return next();
};

/**
 * @method invoke
 * @param context IOPA context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
MessageLogger.prototype.invoke = function MessageLogger_invoke(context, next) {
        context.response[SERVER.RawStream] = new iopaStream.OutgoingStreamTransform(_writeResponse.bind(this, context.response, context.response[SERVER.RawStream]));
        context.log.info("[IOPA] REQUEST IN " + _requestLog(context))
        return next();

};

/**
 * @method invoke
 * @param context IOPA context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
MessageLogger.prototype.connect = function MessageLogger_connect(context, next) {
     context[IOPA.Events].on(IOPA.EVENTS.Response, _invokeOnParentResponse.bind(this, context));
     return next();

};

/**
 * @method connect
 * @this context IOPA context dictionary
 */
MessageLogger.prototype.dispatch = function MessageLogger_dispatch(context, next) {
       context[SERVER.RawStream] = new iopaStream.OutgoingStreamTransform(_writeRequest.bind(this, context, context[SERVER.RawStream]));
      return next();
}

/**
 * @method _invokeOnParentResponse
 * @this CacheMatch
 * @param channelContext IOPA parent context dictionary
 * @param context IOPA childResponse context dictionary
 * @param next   IOPA application delegate for the remainder of the pipeline
 */
function _invokeOnParentResponse(parentContext, response) {
   response.log.info("[IOPA] RESPONSE IN " + _responseLog(response))

       // HOOK INTO RESPONSE STREAM
   response.response[SERVER.RawStream] = new iopaStream.OutgoingStreamTransform(_writeResponse.bind(this, response.response, response.response[SERVER.RawStream]));

};

/**
 * @method _write
 * @param context IOPA context dictionary
 * @param nextStream Stream The raw stream saved that is next in chain for writing
 * @param chunk     String | Buffer The data to write
 * @param encoding String The encoding, if chunk is a String
 * @param callback Function Callback for when this chunk of data is flushed
 * @private
*/
function _writeResponse(context, nextStream, chunk, encoding, callback) {
       context[IOPA.Events].on(IOPA.EVENTS.Response, _invokeOnParentResponse.bind(this, context));
       nextStream.write(chunk, encoding, callback);
       context.log.info("[IOPA] RESPONSE OUT " + _responseLog(context));
};

/**
 * @method _write
 * @param context IOPA context dictionary
 * @param nextStream Stream The raw stream saved that is next in chain for writing
 * @param chunk     String | Buffer The data to write
 * @param encoding String The encoding, if chunk is a String
 * @param callback Function Callback for when this chunk of data is flushed
 * @private
*/
function _writeRequest(context, nextStream, chunk, encoding, callback) {

    nextStream.write(chunk, encoding, callback);
      context.log.info("[IOPA] REQUEST OUT " + _requestLog(context));
};

function _url(context) {
    return context[IOPA.Scheme]
        + "//" + context[SERVER.RemoteAddress]
        + ":" + context[SERVER.RemotePort]
        + context[IOPA.Path]
        + (context[IOPA.QueryString] ? + context[IOPA.QueryString] : "");
}

function _requestLog(context) {
    return context[IOPA.Method] + " " + context[IOPA.MessageId] + ":" + context[IOPA.Seq] + " "
        + _url(context)
       + "  " + ((context[IOPA.Body] !== null) ? context[IOPA.Body].toString() : "");
}

function _responseLog(response, chunk) {

    return response[IOPA.Method] + " " + response[IOPA.MessageId] + ":" + response[IOPA.Seq] + " "
        + response[IOPA.StatusCode] + "/"
        + response[IOPA.ReasonPhrase]
        + " [" + response[SERVER.RemoteAddress]
        + ":" + response[SERVER.RemotePort] + "]" + "  "
        + ((response[IOPA.Body] !== null) ? response[IOPA.Body].toString() : "");
}

module.exports = MessageLogger;
<|endoftext|>