<commit_before>exports.recommendMusicFor = function (userId, callback) {
    var result = ["abc", "xyz", "abc", "xyz", "abc"];
    result = rec(userId).results;
    callback(result);
}
var musics = {    "m1": ["jazz", "old school", "instrumental"], "m2": ["samba", "60s"], "m3": ["rock", "alternative"], "m4": ["rock", "alternative"], "m5": ["folk", "instrumental"], "m6": ["60s", "rock", "old school"], "m7": ["alternative", "dance"], "m8": ["electronic", "pop"], "m9": ["60s", "rock"], "m10": ["60s", "jazz"], "m11": ["samba"], "m12": ["jazz", "instrumental"]};
var users = {    "a": {        "userId": "a", "followedUsers": {"b": true, "c": true}, "musics": {"m2": 1, "m6": 1}    }, "b": {        "userId": "b", "followedUsers": {"c": true, "d": true, "e": true}, "musics": {"m4": 1, "m9": 1}    }, "c": {        "userId": "c", "followedUsers": {"a": true}, "musics": {"m8": 1, "m7": 1}    }, "d": {        "userId": "d", "followedUsers": {}, "musics": {"m2": 1, "m6": 1, "m7": 1}    }, "e": {        "userId": "e", "followedUsers": {}, "musics": {"m11": 1}    }};

var buildGenreNumListenedByUserIndex = function (users) {
    var genreNumListened = {};
    for (var userId in users) {
        genreNumListened[userId] = {};
        var user = users[userId];
        var listenedMusics = user.musics;
        for (var musicId in listenedMusics) {
            var musicIdNumListened = listenedMusics[musicId];
            var genres = musics[musicId];
            var lenGenres = genres.length;
            for (var j = 0; j < lenGenres; ++j) {
                var genre = genres[j];
                if (genreNumListened[userId][genre]) genreNumListened[userId][genre] += musicIdNumListened; else genreNumListened[userId][genre] = musicIdNumListened;
            }
        }
    }
    return genreNumListened;
};
var calculateSimilarity = function (genreNumListenedByUser, userId1, userId2) {
    var sim = 0;
    for (var genre in genreNumListenedByUser[userId1]) {
        if (genreNumListenedByUser[userId2][genre]) {
            var numListened = genreNumListenedByUser[userId1][genre];
            if (numListened) {
                sim += numListened > 1 ? 1 + 1 / numListened : numListened;
            }
        }
    }
    return sim;
};
var addAllKeysInDictionaryToList = function (dict, list) {
    for (var key in dict) list.push(key);
};
var findFollowerRelationshipDegree = function (userId, otherUserId) {
    var followedUsers = users[userId].followedUsers;
    if (followedUsers[otherUserId]) return 1;
    var visited = {};
    var queue = [];
    addAllKeysInDictionaryToList(followedUsers, queue);
    var levelDelimeter = function (level) {
        return {"levelDelimeter": {"level": level}};
    };
    queue.push(levelDelimeter(2));
    var lastLevelEncountered = 2;
    while (queue.length > 0) {
        var head = queue.shift();
        if (head["levelDelimeter"]) {
            lastLevelEncountered = head["levelDelimeter"]["level"] + 1;
            continue;
        }
        if (visited[head]) continue;
        visited[head] = true;
        followedUsers = users[head].followedUsers;
        if (followedUsers[otherUserId]) return lastLevelEncountered; // found
        addAllKeysInDictionaryToList(followedUsers, queue);
    }
    return 0;
};
var calculateScoreForGenre = function (userSimilarity, followerDegree, alreadyListened) {
    var followerBiasCoefficient = 2, alreadyListenedCoefficient = 1 / 100;
    var followerBias = followerDegree && followerDegree != 0 && followerDegree < 4 ? followerBiasCoefficient * (1 / followerDegree) : 1 / 10;
    var alreadyListenedBias = alreadyListened ? alreadyListenedCoefficient : 1;
    return (userSimilarity != 0 ? userSimilarity : 1) * followerBias * alreadyListenedBias;
};
var musicStoreReverseIndex = function (musics) {
    var results = {};
    for (musicId in musics) {
        var genreList = musics[musicId];
        var lenGenreList = genreList.length;
        for (var i = 0; i < lenGenreList; ++i) {
            var genre = genreList[i];
            if (results[genre]) {
                results[genre].push(musicId);
            } else {
                results[genre] = [musicId];
            }
        }
    }
    return results;
}(musics);
var dictionaryKeySetDifferenceAMinusB = function (a, b) {
    var diff = [];
    console.log(JSON.stringify(a));
    console.log(JSON.stringify(b));
    for (var key in a) if (!b[key]) diff.push(key);
    console.log(JSON.stringify(diff));
    return diff;
};
var rec = function (userId) {
    var genreNumListenedByUser = buildGenreNumListenedByUserIndex(users);
    var genreRatings = {};
    for (var otherUserId in genreNumListenedByUser) {
        if (otherUserId == userId) continue;
        var similarity = calculateSimilarity(genreNumListenedByUser, userId, otherUserId);
        var followerDegree = findFollowerRelationshipDegree(userId, otherUserId);
        for (var genre in genreNumListenedByUser[otherUserId]) {
            var alreadyListened = genreNumListenedByUser[userId][genre] ? true : false;
            var score = calculateScoreForGenre(similarity, followerDegree, alreadyListened);
            if (genreRatings[genre]) genreRatings[genre] += score; else genreRatings[genre] = score;
        }
    }
    var musicRatings = {};
    for (var genre in genreRatings) {
        var genreRating = genreRatings[genre];
        var musicsReverseIndex = musicStoreReverseIndex[genre];
        var lenMusics = musicsReverseIndex.length;
        for (var i = 0; i < lenMusics; ++i) {
            var musicId = musicsReverseIndex[i];
            if (musicRatings[musicId]) musicRatings[musicId] += genreRating; else musicRatings[musicId] = genreRating;
        }
    }
    var nonRatedMusics = dictionaryKeySetDifferenceAMinusB(musics, musicRatings);
    var lenNonRatedMusics = nonRatedMusics.length;
    for (i = 0; i < lenNonRatedMusics; ++i) {
        musicRatings[nonRatedMusics[i]] = -1;
    }
    var results = [];
    for (var musicId in musicRatings) {
        var musicIdRating = musicRatings[musicId];
        results.push({"ranking": musicIdRating, "musicId": musicId});
    }
    results.sort(function (a, b) {
        return -(a.ranking - b.ranking);
    });
    return {"genreNumListened": genreNumListenedByUser, "genreRatings": genreRatings, "musicRatings": musicRatings, "results": results};
};
<commit_msg>Fixed wrong argument placement in callback<commit_after>exports.recommendMusicFor = function (userId, callback) {
    try {
        var result = rec(userId).results;
        var ret = result.slice(0, 5).map(function(entry) {
            return entry.musicId;
        });
        callback(undefined, ret);
    } catch (err) {
        console.error(err);
        callback(err, undefined);
    }
}
var musics = {    "m1": ["jazz", "old school", "instrumental"], "m2": ["samba", "60s"], "m3": ["rock", "alternative"], "m4": ["rock", "alternative"], "m5": ["folk", "instrumental"], "m6": ["60s", "rock", "old school"], "m7": ["alternative", "dance"], "m8": ["electronic", "pop"], "m9": ["60s", "rock"], "m10": ["60s", "jazz"], "m11": ["samba"], "m12": ["jazz", "instrumental"]};
var users = {    "a": {        "userId": "a", "followedUsers": {"b": true, "c": true}, "musics": {"m2": 1, "m6": 1}    }, "b": {        "userId": "b", "followedUsers": {"c": true, "d": true, "e": true}, "musics": {"m4": 1, "m9": 1}    }, "c": {        "userId": "c", "followedUsers": {"a": true}, "musics": {"m8": 1, "m7": 1}    }, "d": {        "userId": "d", "followedUsers": {}, "musics": {"m2": 1, "m6": 1, "m7": 1}    }, "e": {        "userId": "e", "followedUsers": {}, "musics": {"m11": 1}    }};

var buildGenreNumListenedByUserIndex = function (users) {
    var genreNumListened = {};
    for (var userId in users) {
        genreNumListened[userId] = {};
        var user = users[userId];
        var listenedMusics = user.musics;
        for (var musicId in listenedMusics) {
            var musicIdNumListened = listenedMusics[musicId];
            var genres = musics[musicId];
            var lenGenres = genres.length;
            for (var j = 0; j < lenGenres; ++j) {
                var genre = genres[j];
                if (genreNumListened[userId][genre]) genreNumListened[userId][genre] += musicIdNumListened; else genreNumListened[userId][genre] = musicIdNumListened;
            }
        }
    }
    return genreNumListened;
};
var calculateSimilarity = function (genreNumListenedByUser, userId1, userId2) {
    var sim = 0;
    for (var genre in genreNumListenedByUser[userId1]) {
        if (genreNumListenedByUser[userId2][genre]) {
            var numListened = genreNumListenedByUser[userId1][genre];
            if (numListened) {
                sim += numListened > 1 ? 1 + 1 / numListened : numListened;
            }
        }
    }
    return sim;
};
var addAllKeysInDictionaryToList = function (dict, list) {
    for (var key in dict) list.push(key);
};
var findFollowerRelationshipDegree = function (userId, otherUserId) {
    var followedUsers = users[userId].followedUsers;
    if (followedUsers[otherUserId]) return 1;
    var visited = {};
    var queue = [];
    addAllKeysInDictionaryToList(followedUsers, queue);
    var levelDelimeter = function (level) {
        return {"levelDelimeter": {"level": level}};
    };
    queue.push(levelDelimeter(2));
    var lastLevelEncountered = 2;
    while (queue.length > 0) {
        var head = queue.shift();
        if (head["levelDelimeter"]) {
            lastLevelEncountered = head["levelDelimeter"]["level"] + 1;
            continue;
        }
        if (visited[head]) continue;
        visited[head] = true;
        followedUsers = users[head].followedUsers;
        if (followedUsers[otherUserId]) return lastLevelEncountered; // found
        addAllKeysInDictionaryToList(followedUsers, queue);
    }
    return 0;
};
var calculateScoreForGenre = function (userSimilarity, followerDegree, alreadyListened) {
    var followerBiasCoefficient = 2, alreadyListenedCoefficient = 1 / 100;
    var followerBias = followerDegree && followerDegree != 0 && followerDegree < 4 ? followerBiasCoefficient * (1 / followerDegree) : 1 / 10;
    var alreadyListenedBias = alreadyListened ? alreadyListenedCoefficient : 1;
    return (userSimilarity != 0 ? userSimilarity : 1) * followerBias * alreadyListenedBias;
};
var musicStoreReverseIndex = function (musics) {
    var results = {};
    for (musicId in musics) {
        var genreList = musics[musicId];
        var lenGenreList = genreList.length;
        for (var i = 0; i < lenGenreList; ++i) {
            var genre = genreList[i];
            if (results[genre]) {
                results[genre].push(musicId);
            } else {
                results[genre] = [musicId];
            }
        }
    }
    return results;
}(musics);
var dictionaryKeySetDifferenceAMinusB = function (a, b) {
    var diff = [];
    console.log(JSON.stringify(a));
    console.log(JSON.stringify(b));
    for (var key in a) if (!b[key]) diff.push(key);
    console.log(JSON.stringify(diff));
    return diff;
};
var rec = function (userId) {
    var genreNumListenedByUser = buildGenreNumListenedByUserIndex(users);
    var genreRatings = {};
    for (var otherUserId in genreNumListenedByUser) {
        if (otherUserId == userId) continue;
        var similarity = calculateSimilarity(genreNumListenedByUser, userId, otherUserId);
        var followerDegree = findFollowerRelationshipDegree(userId, otherUserId);
        for (var genre in genreNumListenedByUser[otherUserId]) {
            var alreadyListened = genreNumListenedByUser[userId][genre] ? true : false;
            var score = calculateScoreForGenre(similarity, followerDegree, alreadyListened);
            if (genreRatings[genre]) genreRatings[genre] += score; else genreRatings[genre] = score;
        }
    }
    var musicRatings = {};
    for (var genre in genreRatings) {
        var genreRating = genreRatings[genre];
        var musicsReverseIndex = musicStoreReverseIndex[genre];
        var lenMusics = musicsReverseIndex.length;
        for (var i = 0; i < lenMusics; ++i) {
            var musicId = musicsReverseIndex[i];
            if (musicRatings[musicId]) musicRatings[musicId] += genreRating; else musicRatings[musicId] = genreRating;
        }
    }
    var nonRatedMusics = dictionaryKeySetDifferenceAMinusB(musics, musicRatings);
    var lenNonRatedMusics = nonRatedMusics.length;
    for (i = 0; i < lenNonRatedMusics; ++i) {
        musicRatings[nonRatedMusics[i]] = -1;
    }
    var results = [];
    for (var musicId in musicRatings) {
        var musicIdRating = musicRatings[musicId];
        results.push({"ranking": musicIdRating, "musicId": musicId});
    }
    results.sort(function (a, b) {
        return -(a.ranking - b.ranking);
    });
    return {"genreNumListened": genreNumListenedByUser, "genreRatings": genreRatings, "musicRatings": musicRatings, "results": results};
};
<|endoftext|>


<commit_before>/**
 * @ngdoc service
 * @name com.2fdevs.videogular.service:vgFullscreen
 *
 * @description
 * Native fullscreen polyfill service.
 *
 *    * vgFullscreen.onchange: String with the onchange event name.
 *    * vgFullscreen.onerror: String with the onerror event name.
 *    * vgFullscreen.isAvailable: Boolean with fullscreen availability.
 *    * vgFullscreen.isFullScreen: Boolean with current view mode.
 *    * vgFullscreen.exit: Exit fullscreen function.
 *    * vgFullscreen.request: Request for fullscreen access function.
 **/
"use strict";
angular.module("com.2fdevs.videogular")
    .service("vgFullscreen", ["VG_UTILS", function (VG_UTILS) {
        // Native fullscreen polyfill
        var fsAPI;
        var polyfill = null;
        var APIs = {
            w3: {
                enabled: "fullscreenEnabled",
                element: "fullscreenElement",
                request: "requestFullscreen",
                exit: "exitFullscreen",
                onchange: "fullscreenchange",
                onerror: "fullscreenerror"
            },
            newWebkit: {
                enabled: "webkitFullscreenEnabled",
                element: "webkitFullscreenElement",
                request: "webkitRequestFullscreen",
                exit: "webkitExitFullscreen",
                onchange: "webkitfullscreenchange",
                onerror: "webkitfullscreenerror"
            },
            oldWebkit: {
                enabled: "webkitIsFullScreen",
                element: "webkitCurrentFullScreenElement",
                request: "webkitRequestFullScreen",
                exit: "webkitCancelFullScreen",
                onchange: "webkitfullscreenchange",
                onerror: "webkitfullscreenerror"
            },
            moz: {
                enabled: "mozFullScreen",
                element: "mozFullScreenElement",
                request: "mozRequestFullScreen",
                exit: "mozCancelFullScreen",
                onchange: "mozfullscreenchange",
                onerror: "mozfullscreenerror"
            },
            ios: {
                enabled: "webkitFullscreenEnabled",
                element: "webkitFullscreenElement",
                request: "webkitEnterFullscreen",
                exit: "webkitExitFullscreen",
                onchange: "webkitfullscreenchange",
                onerror: "webkitfullscreenerror"
            },
            ms: {
                enabled: "msFullscreenEnabled",
                element: "msFullscreenElement",
                request: "msRequestFullscreen",
                exit: "msExitFullscreen",
                onchange: "MSFullscreenChange",
                onerror: "MSFullscreenError"
            }
        };

        for (var browser in APIs) {
            if (APIs[browser].enabled in document) {
                polyfill = APIs[browser];
                break;
            }
        }

        // Override APIs on iOS
        if (VG_UTILS.isiOSDevice()) {
            polyfill = APIs.ios;
        }

        function isFullScreen() {
            return (document[polyfill.element] != null);
        }

        this.isAvailable = (polyfill != null);

        if (polyfill) {
            this.onchange = polyfill.onchange;
            this.onerror = polyfill.onerror;
            this.isFullScreen = isFullScreen;
            this.exit = function () {
                document[polyfill.exit]();
            };
            this.request = function (elem) {
                elem[polyfill.request]();
            };
        }
    }]);
<commit_msg>Fixed bug in iOS trying to detect fullscreen mode when video player was in fullscreen mode. close #210<commit_after>/**
 * @ngdoc service
 * @name com.2fdevs.videogular.service:vgFullscreen
 *
 * @description
 * Native fullscreen polyfill service.
 *
 *    * vgFullscreen.onchange: String with the onchange event name.
 *    * vgFullscreen.onerror: String with the onerror event name.
 *    * vgFullscreen.isAvailable: Boolean with fullscreen availability.
 *    * vgFullscreen.isFullScreen: Boolean with current view mode.
 *    * vgFullscreen.exit: Exit fullscreen function.
 *    * vgFullscreen.request: Request for fullscreen access function.
 **/
"use strict";
angular.module("com.2fdevs.videogular")
    .service("vgFullscreen", ["VG_UTILS", function (VG_UTILS) {
        // Native fullscreen polyfill
        var element;
        var polyfill = null;
        var APIs = {
            w3: {
                enabled: "fullscreenEnabled",
                element: "fullscreenElement",
                request: "requestFullscreen",
                exit: "exitFullscreen",
                onchange: "fullscreenchange",
                onerror: "fullscreenerror"
            },
            newWebkit: {
                enabled: "webkitFullscreenEnabled",
                element: "webkitFullscreenElement",
                request: "webkitRequestFullscreen",
                exit: "webkitExitFullscreen",
                onchange: "webkitfullscreenchange",
                onerror: "webkitfullscreenerror"
            },
            oldWebkit: {
                enabled: "webkitIsFullScreen",
                element: "webkitCurrentFullScreenElement",
                request: "webkitRequestFullScreen",
                exit: "webkitCancelFullScreen",
                onchange: "webkitfullscreenchange",
                onerror: "webkitfullscreenerror"
            },
            moz: {
                enabled: "mozFullScreen",
                element: "mozFullScreenElement",
                request: "mozRequestFullScreen",
                exit: "mozCancelFullScreen",
                onchange: "mozfullscreenchange",
                onerror: "mozfullscreenerror"
            },
            ios: {
                enabled: "webkitFullscreenEnabled",
                element: "webkitFullscreenElement",
                request: "webkitEnterFullscreen",
                exit: "webkitExitFullscreen",
                onchange: "webkitfullscreenchange",
                onerror: "webkitfullscreenerror"
            },
            ms: {
                enabled: "msFullscreenEnabled",
                element: "msFullscreenElement",
                request: "msRequestFullscreen",
                exit: "msExitFullscreen",
                onchange: "MSFullscreenChange",
                onerror: "MSFullscreenError"
            }
        };

        for (var browser in APIs) {
            if (APIs[browser].enabled in document) {
                polyfill = APIs[browser];
                break;
            }
        }

        // Override APIs on iOS
        if (VG_UTILS.isiOSDevice()) {
            polyfill = APIs.ios;
        }

        function isFullScreen() {
            var result = false;

            if (element) {
                result = (document[polyfill.element] != null || element.webkitDisplayingFullscreen)
            }
            else {
                result = (document[polyfill.element] != null)
            }

            return result;
        }

        this.isAvailable = (polyfill != null);

        if (polyfill) {
            this.onchange = polyfill.onchange;
            this.onerror = polyfill.onerror;
            this.isFullScreen = isFullScreen;
            this.exit = function () {
                document[polyfill.exit]();
            };
            this.request = function (elem) {
                element = elem;
                element[polyfill.request]();
            };
        }
    }]);
<|endoftext|>


<commit_before>'use strict';

// Generate image link
var SERVICE_FLOODGAP = /(((www.floodgap\.com))\/\w\/\w+)/i;

exports.process = function(media, remix) {
  if (!remix.isMatched && media.link.match(SERVICE_FLOODGAP)) {
    var parts = media.link.split('/');
    var shortcode = parts[parts.length - 2];

    remix.isMatched = true;

    remix.result = (media.text != media.url) ? media.text + ' ' : '';
    remix.result += '
' +
      '
' + media.text + '';
  }

  return remix;
};
<commit_msg>Update floodgap.js<commit_after>'use strict';

// Generate image link
var SERVICE_FLOODGAP = /((www.floodgap\.com)\/(iv|gld)\/\d+)/i;

exports.process = function(media, remix) {
  if (!remix.isMatched && SERVICE_FLOODGAP.test(media.link)) {
    var parts = media.link.split('/');
    var fgid = parts[parts.length - 2];
    var shortcode = parts[parts.length - 1];

    remix.isMatched = true;

    remix.result = (media.text != media.url) ? media.text + ' ' : '';
    remix.result += '
' +
      '
' + media.text + '';
  }

  return remix;
};
<|endoftext|>


<commit_before>//@ts-check
// Copyright 2018 Robert Adams
// 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/.
//
// 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.

'use strict';

import { GP } from 'GLOBALS';
import Config from '../config.js';

import { BItem, BItemType } from '../Items/BItem.js';

import { RandomIdentifier } from '../Utilities.js';

// A simple pub/sub system. An event producer registers a topic
//    and later 'fire's event on the topic. An event consumer subscribers
//    to a topic and has a function called when that topic is 'fire'ed.
// The coding pattern:
// Event producer:
//    topicHandle = Eventing.Register(topicName, whoIsRegistering);
//    ...
//    topicHandle.fire(params);
//        // 'params' is a JS object which is usually a map of values
//
// Event consumer:
//    eventHandle = Eventing.Subscribe(topicName, function(params, topicName) {
//         //event processor
//    });
//    ...
//    Eventing.Unsubscribe(eventHandle);
//
// A topic DOES NOT NEED to be registered. In this case, subscribes will create
// the TopicEntry and Eventing.Fire('topicName', params) is used to fire the
// event. In this case, the TopicEntry is removed when all the subscriptions are
// gone. This is an optimization since many topics can be fired but there will
// not be any subscribers so doing it that way doesn't allocate structures if
// there are no subscribers.
//
// NOTE: there is no locking here so beware of using multi-threaded JavaScript.
// NOTE: subEntry.fire() returns a Promise which is resolved when the event has been processed.
//    This also means that event processing can happen async.

// ===========================================
// One subscription
// Subscriptions are created with a unique ID so individual subscriptions can be
//     found for removal (because there can be multiple subsriptions for the same processor).
// Note: currently 'limits' is unused but someday will be used for timing and frequency limiting
export class SubEntry {
    constructor(topic, processor, id, limits) {
        this.topic = topic;
        this.processor = processor;
        this.id = id;
        this.limits = limits;
        this.numSubscriptionFired = 0;
    }
    // Returns a promise for when event has been processed
    fire(params) {
        this.numSubscriptionFired++;
        return new Promise((resolve, reject) => {
            this.processor(params, this.topic)
            // Debug only so I can use GP
            GP.EventingInstance.numEventsFired++;
            resolve();
        });
    }
}

// ===========================================
// One topic that can be subscribed to.
// This is the datastructure for a topic, its subscriptions, and actions.
// This data structure is passed around to the subscribers so 'fire' can be called.
export class TopicEntry {
    constructor(topicName) {
        this.topic = topicName;
        this.subs = [];
        this.numTopicEventsFired = 0;
    }
    hasSubscriptions() {
        return this.subs.length > 0;
    };
    addSubscription(sub) {
        this.subs.push(sub);
    };
    removeSubscription(sub) {
        for (let ii=0; ii<this.subs.length; ii++) {
            if (this.subs[ii].id == sub.id) {
                this.subs.splice(ii, 1);
            }
        }
    };
    fire(params) {
        this.numTopicEventsFired++;
        if (this.subs.length > 0) {
            this.subs.map( sub => { sub.fire(params); } );
            // Could wait for the resolution of the Promises
            // Promise.all(this.subs.map( sub => { sub.fire(params); } ));
        }
    };
}

export class Eventing extends BItem {
    constructor()  {
        GP.DebugLog('Eventing: constructor');
        super('org.basil.b.eventing', undefined, BItemType.SERVICE);
        this.layer = Config.layers ? Config.layers.eventing : 'org.basil.b.layer.eventing';
        this.topics = new Map();
        this.timedEventProcessors = new Map();
        this.numEventsFired = 0;

        // Eventing generates events on subscriptions and registrations
        this.OnRegister = this.Register('Eventing.Register', 'Eventing');
        this.OnUnregister = this.Register('Eventing.Unregister', 'Eventing');
        this.OnSubscribe = this.Register('Eventing.Subscribe', 'Eventing');
        this.OnUnsubscribe = this.Register('Eventing.Unsubscribe', 'Eventing');

        // Start the timed event clock
        this._processTimedEvents();

        // There should be only one and we keep a pointer in GP
        GP.EventingInstance = this;
    }

    static Instance() {
        return GP.EventingInstance;
    }

    // ===========================================
    // Call the event processors that get called twice a second.
    _processTimedEvents() {
        if (this.timedEventProcessors) {
            this.timedEventProcessors.forEach(function(proc, topic) {
                // GP.DebugLog('Eventing.processTimedEvents: calling processor for topic=' + topic);
                proc(topic);
            });
        }
        let interval = 500;
        if (Config.eventing && Config.eventing.eventPollIntervalMS) {
            interval = Number(Config.eventing.eventPollIntervalMS);
        }
        setTimeout(this._processTimedEvents.bind(this), interval);
    };

    // ===========================================
    // Register to receive events for a topic.
    // Returns a handle to control the subscription.
    Subscribe(topic, processor, limits) {
        let sub = new SubEntry(topic, processor, RandomIdentifier(), limits);
        let topicEnt = this.FindTopic(topic);
        if (topicEnt == undefined) {
            // 'creator' is 'subscribe' so we can tell the topic was initially
            //    created because someone subscribed to it. Hopefully someone
            //    will register the topic.
            topicEnt = this.Register(topic, 'subscribe');
        }
        topicEnt.addSubscription(sub);
        GP.DebugLog("Eventing.subscribe: adding subscription to event " + topic);
        this.OnSubscribe.fire({ 'topic': topic.name, 'topicEntry': topicEnt });
        return sub;
    };

    // Release a topic subscription.
    Unsubscribe(subEntry) {
        if (subEntry && subEntry.topic) {
            let topicEnt = this.FindTopic(subEntry.topic);
            if (topicEnt) {
                topicEnt.removeSubscription(subEntry);
                this.OnUnsubscribe.fire({ 'topic': topicEnt.name, 'topicEntry': topicEnt });
                if (! (topicEnt.hasSubscriptions || topicEnt.wasRegistered)) {
                    // Topics that are created from subscriptions can be removed
                    // when there are no more subscriptions
                    this.Unregister(topicEnt);
                }
            }
            GP.DebugLog("Eventing.unsubscribe: removing subscription for event " + subEntry.topic);
        }
    };

    // Register a topic that can be generated.
    // This returns a handle for the topic for later 'event' calls.
    // 'registar' is just a tag added to the topic registration for debugging.
    // The returned object just happens to be the TopicEntry object.
    Register(topic, registar) {
        let topicEnt = this.FindTopic(topic);
        if (topicEnt == undefined) {
            topicEnt = new TopicEntry(topic);
            topicEnt.registar = registar;
            this.topics.set(topic, topicEnt);
            GP.DebugLog("Eventing.register: registering event " + topic);
            if (this.OnRegister) {
                this.OnRegister.fire({ 'topic': topicEnt.name, 'topicEntry': topicEnt });
            }
        }
        // Remember that it was registered so this topic expects an Unregister()
        topicEnt.wasRegistered = true;
        return topicEnt;
    };

    // Unregister a topic.
    // @ts-ignore
    Unregister(topicEntry, topic) {
        // cannot unregister a topic yet
        GP.DebugLog("Eventing.unregister: unregistering event " + topicEntry.topic);
        this.OnUnregister.fire({ 'topic': topicEntry.name, 'topicEntry': topicEntry });
    };

    // An event happened for a topic.
    // Fire the event processors and pass 'params' to all subscribers.
    // This is a convience function if the caller doesn't have a handle to
    //    the topic entry.
    // If given a topic name, this will find the topic entry then do the fire() on it.
    // This also takes a TopicEntry for convenience.
    Fire(topicEntryOrTopic, params) {
        let topicEntry = topicEntryOrTopic;
        if (typeof(topicEntryOrTopic) === 'string') {
            topicEntry = this.FindTopic(topicEntryOrTopic);
        }
        if (topicEntry && topicEntry.topic) {
            topicEntry.fire(params);
        }
    };

    // Return a TopicEntry for the given topic name.
    // Return undefined if not found.
    FindTopic(topic) {
        return this.topics.get(topic);
    };

    // Return the list of topics that are defined
    RegisteredTopics() {
        return this.topics.keys();
    }
    // Return all the TopicEntry's
    RegisteredTopicEntries() {
        return this.topics.values();
    }

    // Calls a processor every 0.5 seconds for polled type events.
    // First parameter can be either a TopicEntry or a topic name
    // Returns handle  that can be used to remove timer.
    CreateTimedEventProcessor (topicEntryOrTopic, processor) {
        let topic = topicEntryOrTopic;
        if ('topic' in topicEntryOrTopic) {
            topic = topicEntryOrTopic.topic;
        }
        this.timedEventProcessors.set(topic, processor);
        GP.DebugLog("Eventing.createTimedEventProcessor: creating for event " + topic);
        return topic;
    };

    // Return 'true' if a processor entry was actually removed
    RemoveTimedEventProcessor(topicEntryOrTopic) {
        let ret = false;
        if (this.timedEventProcessors) {
            let topic = topicEntryOrTopic;
            if ('topic' in topicEntryOrTopic) {
                topic = topicEntryOrTopic.topic;
            }
            ret = this.timedEventProcessors.delete(topic);
            GP.DebugLog("Eventing.removeTimedEventProcessor: removing for event " + topic);
        }
        return ret;
    };

}
<commit_msg>Eventing: make Fire() and fire() async functions that return     a Promise that is resolved when the event is processed.<commit_after>// Copyright 2018 Robert Adams
// 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/.
//
// 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.

'use strict';

import { GP } from 'GLOBALS';
import Config from '../config.js';

import { BItem, BItemType } from '../Items/BItem.js';

import { RandomIdentifier } from '../Utilities.js';

// A simple pub/sub system. An event producer registers a topic
//    and later 'fire's event on the topic. An event consumer subscribers
//    to a topic and has a function called when that topic is 'fire'ed.
// The coding pattern:
// Event producer:
//    topicHandle = Eventing.Register(topicName, whoIsRegistering);
//    ...
//    topicHandle.fire(params);
//        // 'params' is a JS object which is usually a map of values
//
// Event consumer:
//    eventHandle = Eventing.Subscribe(topicName, function(params, topicName) {
//         //event processor
//    });
//    ...
//    Eventing.Unsubscribe(eventHandle);
//
// A topic DOES NOT NEED to be registered. In this case, subscribes will create
// the TopicEntry and Eventing.Fire('topicName', params) is used to fire the
// event. In this case, the TopicEntry is removed when all the subscriptions are
// gone. This is an optimization since many topics can be fired but there will
// not be any subscribers so doing it that way doesn't allocate structures if
// there are no subscribers.
//
// NOTE: there is no locking here so beware of using multi-threaded JavaScript.
// NOTE: subEntry.fire() returns a Promise which is resolved when the event has been processed.
//    This also means that event processing can happen async.

// ===========================================
// One subscription
// Subscriptions are created with a unique ID so individual subscriptions can be
//     found for removal (because there can be multiple subsriptions for the same processor).
// Note: currently 'limits' is unused but someday will be used for timing and frequency limiting
export class SubEntry {
    constructor(topic, processor, id, limits) {
        this.topic = topic;
        this.processor = processor;
        this.id = id;
        this.limits = limits;
        this.numSubscriptionFired = 0;
    }
    // Returns a promise for when event has been processed
    async fire(params) {
        this.numSubscriptionFired++;
        return new Promise(function(resolve, reject) {
            this.processor(params, this.topic)
            // Debug only so I can use GP
            GP.EventingInstance.numEventsFired++;
            resolve(this);
        }.bind(this) );
    }
}

// ===========================================
// One topic that can be subscribed to.
// This is the datastructure for a topic, its subscriptions, and actions.
// This data structure is passed around to the subscribers so 'fire' can be called.
export class TopicEntry {
    constructor(topicName) {
        this.topic = topicName;
        this.subs = [];
        this.numTopicEventsFired = 0;
    }
    hasSubscriptions() {
        return this.subs.length > 0;
    };
    addSubscription(sub) {
        this.subs.push(sub);
    };
    removeSubscription(sub) {
        for (let ii=0; ii<this.subs.length; ii++) {
            if (this.subs[ii].id == sub.id) {
                this.subs.splice(ii, 1);
            }
        }
    };
    async fire(params) {
        this.numTopicEventsFired++;
        if (this.subs.length > 0) {
            // this.subs.map( sub => { sub.fire(params); } );
            // Could wait for the resolution of the Promises
            return Promise.all(this.subs.map( sub => { return sub.fire(params); } ));
        };
        return null;
    };
}

export class Eventing extends BItem {
    constructor()  {
        GP.DebugLog('Eventing: constructor');
        super('org.basil.b.eventing', undefined, BItemType.SERVICE);
        this.layer = Config.layers ? Config.layers.eventing : 'org.basil.b.layer.eventing';
        this.topics = new Map();
        this.timedEventProcessors = new Map();
        this.numEventsFired = 0;

        // Eventing generates events on subscriptions and registrations
        this.OnRegister = this.Register('Eventing.Register', 'Eventing');
        this.OnUnregister = this.Register('Eventing.Unregister', 'Eventing');
        this.OnSubscribe = this.Register('Eventing.Subscribe', 'Eventing');
        this.OnUnsubscribe = this.Register('Eventing.Unsubscribe', 'Eventing');

        // Start the timed event clock
        this._processTimedEvents();

        // There should be only one and we keep a pointer in GP
        GP.EventingInstance = this;
    }

    static Instance() {
        return GP.EventingInstance;
    }

    // ===========================================
    // Call the event processors that get called twice a second.
    _processTimedEvents() {
        if (this.timedEventProcessors) {
            this.timedEventProcessors.forEach(function(proc, topic) {
                // GP.DebugLog('Eventing.processTimedEvents: calling processor for topic=' + topic);
                proc(topic);
            });
        }
        let interval = 500;
        if (Config.eventing && Config.eventing.eventPollIntervalMS) {
            interval = Number(Config.eventing.eventPollIntervalMS);
        }
        setTimeout(this._processTimedEvents.bind(this), interval);
    };

    // ===========================================
    // Register to receive events for a topic.
    // Returns a handle to control the subscription.
    Subscribe(topic, processor, limits) {
        let sub = new SubEntry(topic, processor, RandomIdentifier(), limits);
        let topicEnt = this.FindTopic(topic);
        if (topicEnt == undefined) {
            // 'creator' is 'subscribe' so we can tell the topic was initially
            //    created because someone subscribed to it. Hopefully someone
            //    will register the topic.
            topicEnt = this.Register(topic, 'subscribe');
        }
        topicEnt.addSubscription(sub);
        GP.DebugLog("Eventing.subscribe: adding subscription to event " + topic);
        this.OnSubscribe.fire({ 'topic': topic.name, 'topicEntry': topicEnt });
        return sub;
    };

    // Release a topic subscription.
    Unsubscribe(subEntry) {
        if (subEntry && subEntry.topic) {
            let topicEnt = this.FindTopic(subEntry.topic);
            if (topicEnt) {
                topicEnt.removeSubscription(subEntry);
                this.OnUnsubscribe.fire({ 'topic': topicEnt.name, 'topicEntry': topicEnt });
                if (! (topicEnt.hasSubscriptions || topicEnt.wasRegistered)) {
                    // Topics that are created from subscriptions can be removed
                    // when there are no more subscriptions
                    this.Unregister(topicEnt);
                }
            }
            GP.DebugLog("Eventing.unsubscribe: removing subscription for event " + subEntry.topic);
        }
    };

    // Register a topic that can be generated.
    // This returns a handle for the topic for later 'event' calls.
    // 'registar' is just a tag added to the topic registration for debugging.
    // The returned object just happens to be the TopicEntry object.
    Register(topic, registar) {
        let topicEnt = this.FindTopic(topic);
        if (topicEnt == undefined) {
            topicEnt = new TopicEntry(topic);
            topicEnt.registar = registar;
            this.topics.set(topic, topicEnt);
            GP.DebugLog("Eventing.register: registering event " + topic);
            if (this.OnRegister) {
                this.OnRegister.fire({ 'topic': topicEnt.name, 'topicEntry': topicEnt });
            };
        };
        // Remember that it was registered so this topic expects an Unregister()
        topicEnt.wasRegistered = true;
        return topicEnt;
    };

    // Unregister a topic.
    // @ts-ignore
    Unregister(topicEntry, topic) {
        // cannot unregister a topic yet
        GP.DebugLog("Eventing.unregister: unregistering event " + topicEntry.topic);
        this.OnUnregister.fire({ 'topic': topicEntry.name, 'topicEntry': topicEntry });
    };

    // An event happened for a topic.
    // Fire the event processors and pass 'params' to all subscribers.
    // This is a convience function if the caller doesn't have a handle to
    //    the topic entry.
    // If given a topic name, this will find the topic entry then do the fire() on it.
    // This also takes a TopicEntry for convenience.
    // Note: this returns a Promise which is completed when the fired event is done processing.
    async Fire(topicEntryOrTopic, params) {
        let topicEntry = topicEntryOrTopic;
        if (typeof(topicEntryOrTopic) === 'string') {
            topicEntry = this.FindTopic(topicEntryOrTopic);
        };
        if (topicEntry && topicEntry.topic) {
            return topicEntry.fire(params);
        };
        return null;
    };

    // Return a TopicEntry for the given topic name.
    // Return undefined if not found.
    FindTopic(topic) {
        return this.topics.get(topic);
    };

    // Return the list of topics that are defined
    RegisteredTopics() {
        return this.topics.keys();
    }
    // Return all the TopicEntry's
    RegisteredTopicEntries() {
        return this.topics.values();
    }

    // Calls a processor every 0.5 seconds for polled type events.
    // First parameter can be either a TopicEntry or a topic name
    // Returns handle  that can be used to remove timer.
    CreateTimedEventProcessor (topicEntryOrTopic, processor) {
        let topic = topicEntryOrTopic;
        if ('topic' in topicEntryOrTopic) {
            topic = topicEntryOrTopic.topic;
        }
        this.timedEventProcessors.set(topic, processor);
        GP.DebugLog("Eventing.createTimedEventProcessor: creating for event " + topic);
        return topic;
    };

    // Return 'true' if a processor entry was actually removed
    RemoveTimedEventProcessor(topicEntryOrTopic) {
        let ret = false;
        if (this.timedEventProcessors) {
            let topic = topicEntryOrTopic;
            if ('topic' in topicEntryOrTopic) {
                topic = topicEntryOrTopic.topic;
            }
            ret = this.timedEventProcessors.delete(topic);
            GP.DebugLog("Eventing.removeTimedEventProcessor: removing for event " + topic);
        }
        return ret;
    };

}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */

var path = require('path'),
    Report = require('./index'),
    Writer = require('../util/file-writer'),
    TreeSummarizer = require('../util/tree-summarizer'),
    utils = require('../object-utils');

/**
 * a `Report` implementation that produces a cobertura-style XML file that conforms to the
 * http://cobertura.sourceforge.net/xml/coverage-04.dtd DTD.
 *
 * Usage
 * -----
 *
 *      var report = require('istanbul').Report.create('text');
 *
 * @class CoberturaReport
 * @extends Report
 * @constructor
 * @param {Object} opts optional
 * @param {String} [opts.dir] the directory in which to the cobertura-coverage.xml will be written
 */
function CoberturaReport(opts) {
    Report.call(this);
    opts = opts || {};
    this.dir = opts.dir || process.cwd();
    this.file = opts.file || 'cobertura-coverage.xml';
}

CoberturaReport.TYPE = 'cobertura';

function asJavaPackage(node) {
    return node.displayShortName().
        replace(/\//g, '.').
        replace(/\\/g, '.').
        replace(/\.$/, '');
}

function asClassName(node) {
    /*jslint regexp: true */
    return node.fullPath().replace(/.*[\\\/]/, '');
}

function quote(thing) {
    return '"' + thing + '"';
}

function attr(n, v) {
    return ' ' + n + '=' + quote(v) + ' ';
}

function branchCoverageByLine(fileCoverage) {
    var branchMap = fileCoverage.branchMap,
        branches = fileCoverage.b,
        ret = {};
    Object.keys(branchMap).forEach(function (k) {
        var line = branchMap[k].line,
            branchData = branches[k];
        ret[line] = ret[line] || [];
        ret[line].push.apply(ret[line], branchData);
    });
    Object.keys(ret).forEach(function (k) {
        var dataArray = ret[k],
            covered = dataArray.filter(function (item) { return item > 0; }),
            coverage = covered.length / dataArray.length * 100;
        ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
    });
    return ret;
}

function addClassStats(node, fileCoverage, writer) {
    var metrics = node.metrics,
        branchByLine = branchCoverageByLine(fileCoverage),
        fnMap,
        lines;

    writer.println('\t\t<class' +
        attr('name', asClassName(node)) +
        attr('filename', node.fullPath()) +
        attr('line-rate', metrics.lines.pct / 100.0) +
        attr('branch-rate', metrics.branches.pct / 100.0) +
        '>');

    writer.println('\t\t<methods>');
    fnMap = fileCoverage.fnMap;
    Object.keys(fnMap).forEach(function (k) {
        var name = fnMap[k].name,
            hits = fileCoverage.f[k];
        writer.println('\t\t\t<method' +
            attr('name', name) +
            attr('hits', hits) +
            attr('signature', '()V') + //fake out a no-args void return
            ' />');
    });
    writer.println('\t\t</methods>');

    writer.println('\t\t<lines>');
    lines = fileCoverage.l;
    Object.keys(lines).forEach(function (k) {
        var str = '\t\t\t<line' +
            attr('number', k) +
            attr('hits', lines[k]),
            branchDetail = branchByLine[k];

        if (!branchDetail) {
            str += attr('branch', false);
        } else {
            str += attr('branch', true) +
                attr('condition-coverage', branchDetail.coverage +
                    '% (' + branchDetail.covered + '/' + branchDetail.total + ')');
        }
        writer.println(str + '/>');
    });
    writer.println('\t\t</lines>');

    writer.println('\t\t</class>');
}

function walk(node, collector, writer, level) {
    var metrics;
    if (level === 0) {
        metrics = node.metrics;
        writer.println('<?xml version="1.0" ?>');
        writer.println('<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">');
        writer.println('<coverage' +
            attr('lines-valid', metrics.lines.total) +
            attr('lines-covered', metrics.lines.covered) +
            attr('line-rate', metrics.lines.pct / 100.0) +
            attr('branches-valid', metrics.branches.total) +
            attr('branches-covered', metrics.branches.covered) +
            attr('branch-rate', metrics.branches.pct / 100.0) +
            attr('timestamp', Date.now()) +
            'complexity="0" version="0.1">');
        writer.println('<sources />');
        writer.println('<packages>');
    }
    if (node.packageMetrics) {
        metrics = node.packageMetrics;
        writer.println('\t<package' +
            attr('name', asJavaPackage(node)) +
            attr('line-rate', metrics.lines.pct / 100.0) +
            attr('branch-rate', metrics.branches.pct / 100.0) +
            '>');
        writer.println('\t<classes>');
        node.children.filter(function (child) { return child.kind !== 'dir'; }).
            forEach(function (child) {
                addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer);
            });
        writer.println('\t</classes>');
        writer.println('\t</package>');
    }
    node.children.filter(function (child) { return child.kind === 'dir'; }).
        forEach(function (child) {
            walk(child, collector, writer, level + 1);
        });

    if (level === 0) {
        writer.println('</packages>');
        writer.println('</coverage>');
    }
}

Report.mix(CoberturaReport, {
    writeReport: function (collector, sync) {
        var summarizer = new TreeSummarizer(),
            outputFile = path.join(this.dir, this.file),
            writer = new Writer(sync),
            tree,
            root;

        collector.files().forEach(function (key) {
            summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
        });
        tree = summarizer.getTreeSummary();
        root = tree.root;
        writer.writeFile(outputFile, function () {
            walk(root, collector, writer, 0);
        });
    }
});

module.exports = CoberturaReport;
<commit_msg>Fixed API docs for cobertura report<commit_after>/*
 Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */

var path = require('path'),
    Report = require('./index'),
    Writer = require('../util/file-writer'),
    TreeSummarizer = require('../util/tree-summarizer'),
    utils = require('../object-utils');

/**
 * a `Report` implementation that produces a cobertura-style XML file that conforms to the
 * http://cobertura.sourceforge.net/xml/coverage-04.dtd DTD.
 *
 * Usage
 * -----
 *
 *      var report = require('istanbul').Report.create('cobertura');
 *
 * @class CoberturaReport
 * @extends Report
 * @constructor
 * @param {Object} opts optional
 * @param {String} [opts.dir] the directory in which to the cobertura-coverage.xml will be written
 */
function CoberturaReport(opts) {
    Report.call(this);
    opts = opts || {};
    this.dir = opts.dir || process.cwd();
    this.file = opts.file || 'cobertura-coverage.xml';
}

CoberturaReport.TYPE = 'cobertura';

function asJavaPackage(node) {
    return node.displayShortName().
        replace(/\//g, '.').
        replace(/\\/g, '.').
        replace(/\.$/, '');
}

function asClassName(node) {
    /*jslint regexp: true */
    return node.fullPath().replace(/.*[\\\/]/, '');
}

function quote(thing) {
    return '"' + thing + '"';
}

function attr(n, v) {
    return ' ' + n + '=' + quote(v) + ' ';
}

function branchCoverageByLine(fileCoverage) {
    var branchMap = fileCoverage.branchMap,
        branches = fileCoverage.b,
        ret = {};
    Object.keys(branchMap).forEach(function (k) {
        var line = branchMap[k].line,
            branchData = branches[k];
        ret[line] = ret[line] || [];
        ret[line].push.apply(ret[line], branchData);
    });
    Object.keys(ret).forEach(function (k) {
        var dataArray = ret[k],
            covered = dataArray.filter(function (item) { return item > 0; }),
            coverage = covered.length / dataArray.length * 100;
        ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
    });
    return ret;
}

function addClassStats(node, fileCoverage, writer) {
    var metrics = node.metrics,
        branchByLine = branchCoverageByLine(fileCoverage),
        fnMap,
        lines;

    writer.println('\t\t<class' +
        attr('name', asClassName(node)) +
        attr('filename', node.fullPath()) +
        attr('line-rate', metrics.lines.pct / 100.0) +
        attr('branch-rate', metrics.branches.pct / 100.0) +
        '>');

    writer.println('\t\t<methods>');
    fnMap = fileCoverage.fnMap;
    Object.keys(fnMap).forEach(function (k) {
        var name = fnMap[k].name,
            hits = fileCoverage.f[k];
        writer.println('\t\t\t<method' +
            attr('name', name) +
            attr('hits', hits) +
            attr('signature', '()V') + //fake out a no-args void return
            ' />');
    });
    writer.println('\t\t</methods>');

    writer.println('\t\t<lines>');
    lines = fileCoverage.l;
    Object.keys(lines).forEach(function (k) {
        var str = '\t\t\t<line' +
            attr('number', k) +
            attr('hits', lines[k]),
            branchDetail = branchByLine[k];

        if (!branchDetail) {
            str += attr('branch', false);
        } else {
            str += attr('branch', true) +
                attr('condition-coverage', branchDetail.coverage +
                    '% (' + branchDetail.covered + '/' + branchDetail.total + ')');
        }
        writer.println(str + '/>');
    });
    writer.println('\t\t</lines>');

    writer.println('\t\t</class>');
}

function walk(node, collector, writer, level) {
    var metrics;
    if (level === 0) {
        metrics = node.metrics;
        writer.println('<?xml version="1.0" ?>');
        writer.println('<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">');
        writer.println('<coverage' +
            attr('lines-valid', metrics.lines.total) +
            attr('lines-covered', metrics.lines.covered) +
            attr('line-rate', metrics.lines.pct / 100.0) +
            attr('branches-valid', metrics.branches.total) +
            attr('branches-covered', metrics.branches.covered) +
            attr('branch-rate', metrics.branches.pct / 100.0) +
            attr('timestamp', Date.now()) +
            'complexity="0" version="0.1">');
        writer.println('<sources />');
        writer.println('<packages>');
    }
    if (node.packageMetrics) {
        metrics = node.packageMetrics;
        writer.println('\t<package' +
            attr('name', asJavaPackage(node)) +
            attr('line-rate', metrics.lines.pct / 100.0) +
            attr('branch-rate', metrics.branches.pct / 100.0) +
            '>');
        writer.println('\t<classes>');
        node.children.filter(function (child) { return child.kind !== 'dir'; }).
            forEach(function (child) {
                addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer);
            });
        writer.println('\t</classes>');
        writer.println('\t</package>');
    }
    node.children.filter(function (child) { return child.kind === 'dir'; }).
        forEach(function (child) {
            walk(child, collector, writer, level + 1);
        });

    if (level === 0) {
        writer.println('</packages>');
        writer.println('</coverage>');
    }
}

Report.mix(CoberturaReport, {
    writeReport: function (collector, sync) {
        var summarizer = new TreeSummarizer(),
            outputFile = path.join(this.dir, this.file),
            writer = new Writer(sync),
            tree,
            root;

        collector.files().forEach(function (key) {
            summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
        });
        tree = summarizer.getTreeSummary();
        root = tree.root;
        writer.writeFile(outputFile, function () {
            walk(root, collector, writer, 0);
        });
    }
});

module.exports = CoberturaReport;
<|endoftext|>


<commit_before>var async = require('async');
var _ = require('underscore');
module.exports = function(app){
    var callback_uri = app.njax.config.callback.uri;

    app.all(callback_uri, function(req, res, next){
        async.series([
            function(cb){
                app.njax.callbacks.trigger(req.body._event, req.body, function(){
                    return cb();
                });
            },
            function(cb){
                app.njax.cache.refresh(req.body.event, req.body, function(){
                    return cb();
                });
            }
        ],
        function(){
            //end async
            res.send("Finished");
        });

    });
}<commit_msg>Fixing cache refresh<commit_after>var async = require('async');
var _ = require('underscore');
module.exports = function(app){
    var callback_uri = app.njax.config.callback.uri;

    app.all(callback_uri, function(req, res, next){
        async.series([
            function(cb){
                app.njax.callbacks.trigger(req.body._event, req.body, function(){
                    return cb();
                });
            },
            function(cb){
                app.njax.cache.refresh(req.body._event, req.body, function(){
                    return cb();
                });
            }
        ],
        function(){
            //end async
            res.send("Finished");
        });

    });
}<|endoftext|>


<commit_before>'use strict';

var ddb = require('../../config/dynamo_database').ddb;
var _ = require('underscore');
var mysql = require('../../config/mysql_database').connection;

var proposalModel = require('../model/proposalModel');

exports.findAll = function (req, res){
	return ddb.scan('proposals', {}, function (err, proposals) {
		if (!err) {
			return res.send(_.sortBy(proposals.items, function(element){
				return element.tema + element.subtema;
			}) );
		} else {
			return console.log(err);
		}
	});
};

exports.add = function (req, res){
	var proposal = proposalModel.newProposal(req);
	ddb.putItem('proposals', proposal, {}, function (err) {
		if (!err) {
			return console.log("Created Proposal");
		} else {
			return console.log(err);
		}
	});
	return res.send(202,proposal);
};

exports.findById = function (req, res){
	return ddb.getItem('proposals', req.params.id, null, {}, function (err, proposal) {
		if (!err) {
			return res.send(proposal);
		} else {
			return console.log(err);
		}
	});
};

exports.update = function (req, res){
	var proposal = proposalModel.updateProposal(req);

	return ddb.updateItem('proposals', req.params.id, null, proposal, function (err, proposals) {
		if (!err) {
			console.log("Updated Proposal");
		} else {
			console.log(err);
		}
		return res.send(202, proposals);
	});
};

exports.updateMedia = function (id, newMedia, field){
	var options = {};
	options[field] = {value:newMedia};

	return ddb.updateItem('proposals', id, null, options, function (err, proposals) {
		if (err) {
			console.log(err);
		}
	});
};

exports.delete = function (req, res){
	ddb.deleteItem('proposals', req.params.id, null, {},  function (err, proposal) {
		if (!err) {
			console.log("Removed Proposal");
			return res.send('');
		} else {
			console.log(err);
		}
	});
};

exports.findRandom = function (req, res){
	return ddb.getItem('accounts', req._passport.session.user, null, {}, function (err, user) {

		return ddb.scan('proposals', {}, function (err, proposals) {
			if (!err) {
				var items = [];
				var politicians = [];
				var politicians_ids = [];
				var blindRatedProposals = [];
				var qtdVotes = [];
				var max = 0;
				var probabilityArray = [];

				if(user != undefined && user.blindRatedProposals != undefined){
					user.blindRatedProposals.forEach(function(rated, index){
						blindRatedProposals[rated.substr(0,rated.length - 1)] = true;
					});
				}

				proposals.items.forEach(function(element,index){
					if(element.politicians_id != undefined){
						if(politicians[element.politicians_id] == undefined){
							politicians[element.politicians_id] = [];
							politicians_ids.push(element.politicians_id);
						}

						//Adiciona somente se ainda não foi avaliada
						if(blindRatedProposals[element.id] != true){
							politicians[element.politicians_id].push(index);
						}else{

							//conta a quantidade de cada politico
							qtdVotes[element.politicians_id] = (qtdVotes[element.politicians_id] || 0) + 1;

							//procura a quantidade de avaliações que o mais avaliado possui
							if(qtdVotes[element.politicians_id]>max)
								max = qtdVotes[element.politicians_id];
						}

					}
				});

				//Preenche a lista do random com o maximo - quantidade + 1 itens para cada político selecionável;
				politicians_ids.forEach(function(element, index){
					if(politicians[element].length > 0){
						for(var i = 0; i < max - (qtdVotes[element]||0) + 1; i++){
							probabilityArray.push(element);
						}
					}
				});

				for(var i=0;i<req.params.qtd;i++){
					//console.log(probabilityArray);
					var randPolitician = parseInt(Math.random() * probabilityArray.length);
					var removed = probabilityArray[randPolitician];
					var index = parseInt(Math.random() * politicians[removed].length);

					items.push(proposals.items[politicians[removed][index]]);

					//retira das possibilidades a proposta escolhida
					politicians[removed].splice(index,1);

					//se o politico não possui mais propostas retira ele das opções de políticos e todos ele da lista de possibilidades
					if(politicians[removed].length == 0){

						probabilityArray = _.without(probabilityArray, removed);
					}else{
						//retira das possibilidades somente uma do politico
						probabilityArray.splice(randPolitician,1);

						//se ele chega a zero registros na lista de prioridades readiciona todas os políticos a ela 1x
						if(_.indexOf(probabilityArray, removed) == -1){
							politicians_ids.forEach(function(element, index){
								if(politicians[element].length > 0){
									probabilityArray.push(element);
								}
							});
						}
					}

				}
				return res.send(items);
			} else {
				return console.log(err);
			}
		});
	});
};

exports.findUFC = function(req, res){
	var id = req.params.id || 1000;
	mysql.query('SELECT id, id_politicians as politicians_id, titulo from propostasUFC where id_tema= ? ', id, function(err, rows, fields) {
		if (err) console.log(err);

		var cand1 = [];
		var cand2 = [];
		var candInicial = rows[0].politicians_id;

		rows.forEach(function(row,index){
			if(row.politicians_id == candInicial){
				cand1.push(index);
			}else{
				cand2.push(index);
			}
		});

		var resposta = [];
		var indexCand1 = cand1[parseInt(Math.random() * cand1.length)];
		var indexCand2 = cand2[parseInt(Math.random() * cand2.length)];
		if(parseInt(Math.random() * 2) == 1){
			resposta.push(rows[indexCand1]);
			resposta.push(rows[indexCand2]);
		}else{
			resposta.push(rows[indexCand2]);
			resposta.push(rows[indexCand1]);
		}

		res.send(resposta);
	});
};

exports.saveUFCVote = function(req, res){
	if(req.body.propostas == undefined)
		res.send(500, "Propostas não foram definidas");

	mysql.query('insert into notasUFC (id_user, id_proposta_ganhadora, id_dupla, created_at) value (?,?,(select id from duplasUFC where id_proposta1 in(?,?) and id_proposta2 in(?,?)), ?)',
		[req._passport.session.user, req.body.propostas[0], req.body.propostas[1], req.body.propostas[0], req.body.propostas[0], req.body.propostas[1], Date.now()],
		function(err, rows, fields) {
			if (err) console.log(err);
			res.send(200);
		});
};

//SQL PARA O RANKING GERAL
//select count(id_user) as quantidade, id_politicians, id_tema from notasUFC as notas right join propostasUFC on id_proposta_ganhadora = propostasUFC.id group by id_tema, id_politicians;
exports.rankUFC = function(req, res){
	mysql.query('select count(id_user) as quantidade, id_politicians, id_tema from notasUFC as notas right join propostasUFC on id_proposta_ganhadora = propostasUFC.id group by id_tema, id_politicians',
		function(err, rows, fields) {
		if (err) console.log(err);
		var resposta = {};

		rows.forEach(function(row,index){
			if(resposta[row.id_politicians] == undefined)
				resposta[row.id_politicians] = {};

			resposta[row.id_politicians][row.id_tema] = row.quantidade;
		});

		res.send(resposta);
	});
};

//SQL PARA O RANKING DO USUÁRIO
//select count(id_user) as quantidade, id_politicians, id_tema from (select * from notasUFC where id_user = ?) as notas right join propostasUFC on id_proposta_ganhadora = propostasUFC.id group by id_tema, id_politicians;

exports.profileRankUFC = function(req, res){
	mysql.query('select count(id_user) as quantidade, id_politicians, id_tema from (select * from notasUFC where id_user = ?) as notas right join propostasUFC on id_proposta_ganhadora = propostasUFC.id group by id_tema, id_politicians',
		[req._passport.session.user],function(err, rows, fields) {
		if (err) console.log(err);
		var resposta = {};

		rows.forEach(function(row,index){
			if(resposta[row.id_politicians] == undefined)
				resposta[row.id_politicians] = {};

			resposta[row.id_politicians][row.id_tema] = row.quantidade;
		});

		res.send(resposta);
	});
};

exports.importPropostas = function(){
	res.send(202);
	var temasUFC = {
				"Cultura e Turismo": 1000,
				"Democracia e Reforma Política": 1001,
				"Desenvolvimento Econômico": 1002,
				"Direitos Humanos e Inclusão social": 1003,
				"Educação": 1004,
				"Esporte e lazer": 1005,
				"Gestão Pública": 1006,
				"Infraestrutura": 1007,
				"Liberdades civis": 1008,
				"Segurança Pública": 1009,
				"Meio-ambiente": 1010,
				"Política Econômica": 1011,
				"Política Externa e Defesa Nacional": 1012,
				"Políticas Sociais": 1013,
				"Saúde": 1014,
				"Outros": 1015
			}

	ddb.scan('proposals', {}, function (err, proposals) {
		if (!err) {
			proposals.items.forEach(function(proposal, index){
				if(proposal.politicians_id == "827c9cc0-0d10-11e4-a4de-3d18690f2356" || proposal.politicians_id == "b6bc0250-0d10-11e4-b416-b9cab1b63b1e"){
					mysql.query('INSERT INTO propostasUFC (id, id_politicians, id_tema, titulo) VALUE (?, ?, ?, ?)',
					[proposal.id, proposal.politicians_id, temasUFC[proposal.tema], proposal.titulo],
					function(err, rows, fields) {
						if (err) console.log(err);
					});
				}
			});
		} else {
			return console.log(err);
		}
	});
};<commit_msg>Tratando erros<commit_after>'use strict';

var ddb = require('../../config/dynamo_database').ddb;
var _ = require('underscore');
var mysql = require('../../config/mysql_database').connection;

var proposalModel = require('../model/proposalModel');

exports.findAll = function (req, res){
	return ddb.scan('proposals', {}, function (err, proposals) {
		if (!err) {
			return res.send(_.sortBy(proposals.items, function(element){
				return element.tema + element.subtema;
			}) );
		} else {
			return console.log(err);
		}
	});
};

exports.add = function (req, res){
	var proposal = proposalModel.newProposal(req);
	ddb.putItem('proposals', proposal, {}, function (err) {
		if (!err) {
			return console.log("Created Proposal");
		} else {
			return console.log(err);
		}
	});
	return res.send(202,proposal);
};

exports.findById = function (req, res){
	return ddb.getItem('proposals', req.params.id, null, {}, function (err, proposal) {
		if (!err) {
			return res.send(proposal);
		} else {
			return console.log(err);
		}
	});
};

exports.update = function (req, res){
	var proposal = proposalModel.updateProposal(req);

	return ddb.updateItem('proposals', req.params.id, null, proposal, function (err, proposals) {
		if (!err) {
			console.log("Updated Proposal");
		} else {
			console.log(err);
		}
		return res.send(202, proposals);
	});
};

exports.updateMedia = function (id, newMedia, field){
	var options = {};
	options[field] = {value:newMedia};

	return ddb.updateItem('proposals', id, null, options, function (err, proposals) {
		if (err) {
			console.log(err);
		}
	});
};

exports.delete = function (req, res){
	ddb.deleteItem('proposals', req.params.id, null, {},  function (err, proposal) {
		if (!err) {
			console.log("Removed Proposal");
			return res.send('');
		} else {
			console.log(err);
		}
	});
};

exports.findRandom = function (req, res){
	return ddb.getItem('accounts', req._passport.session.user, null, {}, function (err, user) {

		return ddb.scan('proposals', {}, function (err, proposals) {
			if (!err) {
				var items = [];
				var politicians = [];
				var politicians_ids = [];
				var blindRatedProposals = [];
				var qtdVotes = [];
				var max = 0;
				var probabilityArray = [];

				if(user != undefined && user.blindRatedProposals != undefined){
					user.blindRatedProposals.forEach(function(rated, index){
						blindRatedProposals[rated.substr(0,rated.length - 1)] = true;
					});
				}

				proposals.items.forEach(function(element,index){
					if(element.politicians_id != undefined){
						if(politicians[element.politicians_id] == undefined){
							politicians[element.politicians_id] = [];
							politicians_ids.push(element.politicians_id);
						}

						//Adiciona somente se ainda não foi avaliada
						if(blindRatedProposals[element.id] != true){
							politicians[element.politicians_id].push(index);
						}else{

							//conta a quantidade de cada politico
							qtdVotes[element.politicians_id] = (qtdVotes[element.politicians_id] || 0) + 1;

							//procura a quantidade de avaliações que o mais avaliado possui
							if(qtdVotes[element.politicians_id]>max)
								max = qtdVotes[element.politicians_id];
						}

					}
				});

				//Preenche a lista do random com o maximo - quantidade + 1 itens para cada político selecionável;
				politicians_ids.forEach(function(element, index){
					if(politicians[element].length > 0){
						for(var i = 0; i < max - (qtdVotes[element]||0) + 1; i++){
							probabilityArray.push(element);
						}
					}
				});

				for(var i=0;i<req.params.qtd;i++){
					//console.log(probabilityArray);
					var randPolitician = parseInt(Math.random() * probabilityArray.length);
					var removed = probabilityArray[randPolitician];
					var index = parseInt(Math.random() * politicians[removed].length);

					items.push(proposals.items[politicians[removed][index]]);

					//retira das possibilidades a proposta escolhida
					politicians[removed].splice(index,1);

					//se o politico não possui mais propostas retira ele das opções de políticos e todos ele da lista de possibilidades
					if(politicians[removed].length == 0){

						probabilityArray = _.without(probabilityArray, removed);
					}else{
						//retira das possibilidades somente uma do politico
						probabilityArray.splice(randPolitician,1);

						//se ele chega a zero registros na lista de prioridades readiciona todas os políticos a ela 1x
						if(_.indexOf(probabilityArray, removed) == -1){
							politicians_ids.forEach(function(element, index){
								if(politicians[element].length > 0){
									probabilityArray.push(element);
								}
							});
						}
					}

				}
				return res.send(items);
			} else {
				return console.log(err);
			}
		});
	});
};

exports.findUFC = function(req, res){
	var id = req.params.id || 1000;
	mysql.query('SELECT id, id_politicians as politicians_id, titulo from propostasUFC where id_tema= ? ', id, function(err, rows, fields) {
		if (err){
			console.log(err);
			res.send(500)
		}
		else if(rows[0] == undefined){
			res.send(422, "Não existem propostas para essa categoria.");
		}
		else{

			var cand1 = [];
			var cand2 = [];
			var candInicial = rows[0].politicians_id;

			rows.forEach(function(row,index){
				if(row.politicians_id == candInicial){
					cand1.push(index);
				}else{
					cand2.push(index);
				}
			});

			if(cand1.length == 0 || cand2.length == 0){
				res.send(422, "Somente um dos candidatos possuem propostas nessa categoria.");
			}
			else{
				var resposta = [];
				var indexCand1 = cand1[parseInt(Math.random() * cand1.length)];
				var indexCand2 = cand2[parseInt(Math.random() * cand2.length)];
				if(parseInt(Math.random() * 2) == 1){
					resposta.push(rows[indexCand1]);
					resposta.push(rows[indexCand2]);
				}else{
					resposta.push(rows[indexCand2]);
					resposta.push(rows[indexCand1]);
				}

				res.send(resposta);
			}
		}
	});
};

exports.saveUFCVote = function(req, res){
	if(req.body.propostas == undefined)
		res.send(500, "Propostas não foram definidas");

	mysql.query('INSERT INTO notasUFC (id_user, id_proposta_ganhadora, id_dupla, created_at) VALUE (?,?,(SELECT id FROM duplasUFC WHERE id_proposta1 IN(?,?) AND id_proposta2 IN(?,?)), ?)',
		[req._passport.session.user, req.body.propostas[0], req.body.propostas[1], req.body.propostas[0], req.body.propostas[0], req.body.propostas[1], Date.now()],
		function(err, rows, fields) {
			if (err) console.log(err);
			res.send(200);
		});
};

//SQL PARA O RANKING GERAL
exports.rankUFC = function(req, res){
	mysql.query('SELECT COUNT(id_user) AS quantidade, id_politicians, id_tema FROM notasUFC AS notas RIGHT JOIN propostasUFC ON id_proposta_ganhadora = propostasUFC.id GROUP BY id_tema, id_politicians',
		function(err, rows, fields) {
		if (err) console.log(err);
		var resposta = {};

		rows.forEach(function(row,index){
			if(resposta[row.id_politicians] == undefined)
				resposta[row.id_politicians] = {};

			resposta[row.id_politicians][row.id_tema] = row.quantidade;
		});

		res.send(resposta);
	});
};

//SQL PARA O RANKING DO USUÁRIO
//select count(id_user) as quantidade, id_politicians, id_tema from (select * from notasUFC where id_user = ?) as notas right join propostasUFC on id_proposta_ganhadora = propostasUFC.id group by id_tema, id_politicians;

exports.profileRankUFC = function(req, res){
	mysql.query('SELECT COUNT(id_user) as quantidade, id_politicians, id_tema from (select * from notasUFC where id_user = ?) as notas right join propostasUFC on id_proposta_ganhadora = propostasUFC.id group by id_tema, id_politicians',
		[req._passport.session.user],function(err, rows, fields) {
		if (err) console.log(err);
		var resposta = {};

		rows.forEach(function(row,index){
			if(resposta[row.id_politicians] == undefined)
				resposta[row.id_politicians] = {};

			resposta[row.id_politicians][row.id_tema] = row.quantidade;
		});

		res.send(resposta);
	});
};

exports.importPropostas = function(){
	res.send(202);
	var temasUFC = {
				"Cultura e Turismo": 1000,
				"Democracia e Reforma Política": 1001,
				"Desenvolvimento Econômico": 1002,
				"Direitos Humanos e Inclusão social": 1003,
				"Educação": 1004,
				"Esporte e lazer": 1005,
				"Gestão Pública": 1006,
				"Infraestrutura": 1007,
				"Liberdades civis": 1008,
				"Segurança Pública": 1009,
				"Meio-ambiente": 1010,
				"Política Econômica": 1011,
				"Política Externa e Defesa Nacional": 1012,
				"Políticas Sociais": 1013,
				"Saúde": 1014,
				"Outros": 1015
			}

	ddb.scan('proposals', {}, function (err, proposals) {
		if (!err) {
			proposals.items.forEach(function(proposal, index){
				if(proposal.politicians_id == "827c9cc0-0d10-11e4-a4de-3d18690f2356" || proposal.politicians_id == "b6bc0250-0d10-11e4-b416-b9cab1b63b1e"){
					mysql.query('INSERT INTO propostasUFC (id, id_politicians, id_tema, titulo) VALUE (?, ?, ?, ?)',
					[proposal.id, proposal.politicians_id, temasUFC[proposal.tema], proposal.titulo],
					function(err, rows, fields) {
						if (err) console.log(err);
					});
				}
			});
		} else {
			return console.log(err);
		}
	});
};<|endoftext|>


<commit_before>'use strict';

module.exports = defineRules;

var broWords = [
    'bro', 'bros',
    'crank',
    'crush',
    'skillz'
];

function defineRules (linter) {

    // Bro terminology
    linter.addRule(function (spec, result) {
        var broMentions = spec.containsAnyOfWords(broWords);
        var amount = (broMentions.length > 2 ? 'Lots of' : 'Some');
        if (broMentions.length > 0) {
            result.addWarning(amount + ' "bro culture" terminology is used');
            result.addCultureFailPoints(broMentions.length);
        }
    });

}
<commit_msg>Update bro-culture.js<commit_after>'use strict';

module.exports = defineRules;

var broWords = [
    'bro', 'bros',
    'crank',
    'crush',
    'dude', 'dudes', 'dudebros',
    'skillz'
];

function defineRules (linter) {

    // Bro terminology
    linter.addRule(function (spec, result) {
        var broMentions = spec.containsAnyOfWords(broWords);
        var amount = (broMentions.length > 2 ? 'Lots of' : 'Some');
        if (broMentions.length > 0) {
            result.addWarning(amount + ' "bro culture" terminology is used');
            result.addCultureFailPoints(broMentions.length);
        }
    });

}
<|endoftext|>