<commit_before>const express = require('express');
const _ = require('lodash');
const request = require('request');
const requestProm = require('request-promise');
const fs = require('fs');
const base64url = require('base64url');
const URL = require('url').URL;

const google = require('googleapis');
const slackWebClient = require('@slack/client').WebClient;
const graph = require('fbgraph');
const PubSub = require('@google-cloud/pubsub');

const authTokens = require(__base + 'secret/auth-tokens.json');
const authConf = {
    'facebook' : {
        'clientID' : authTokens.fbClientID, // also refer to as app-ID
        'clientSecret' : authTokens.fbClientSecret, // also refer to as app-secret
        'scope' : 'email, public_profile, user_friends, user_posts',
        'redirectUri' : 'https://lynxapp.me/api/auth/facebook_oauth'
    },
    'gmail' : {
        'clientID': authTokens.gmailClientID,
        'clientSecret' : authTokens.gmailClientSecret,
        'scope' : ['https://www.googleapis.com/auth/plus.me',
                   'https://www.googleapis.com/auth/gmail.modify'],
        'redirectUri' : 'https://lynxapp.me/api/auth/gmail_oauth'
    },
    'slack' : {
        'clientID' : authTokens.slackClientID,
        'clientSecret' : authTokens.slackClientSecret,
        'scope': 'users.profile%3Aread+users%3Aread+channels%3Ahistory+channels%3Aread+im%3Aread+im%3Ahistory+mpim%3Aread+mpim%3Ahistory',
        'redirectUri' : 'https://lynxapp.me/api/auth/slack_oauth'
    }
};

module.exports.Router = function (MessageDB, socketIo) {
	const router = express.Router();

    // Parses a string and returns an array of links if there are any.
    // @param {string} text - the text to be processed.
    function regParser(message) {
        // Resource: https://gist.github.com/dperini/729294
        // defensive
        console.log("message in reg parser", message);
        if (message) {
            // Slack puts brackets around their links, so we need to remove them.
            var words = message.replace(/[<>]/g,'').split(' ');

            var urls = [];

            for (i = 0; i < words.length; i++) {
                console.log('The word is: ' + words[i]);

                // if it is a link, we will call the link summary API
                try {
                    const url = new URL(words[i]);
                    urls.push(url);
                } catch (e) {
                    console.log("not a link");
                }
            }
            return urls;
        }
    }

    // Calls Ena's link summary API. We return the result back to the client so it can format it.
    // @param {Object} linkinfo - other additional information about the link, which has the following properties:
    //                              service {string} - name of the platform
    //                              sender {string} - name of the sender
    function generateLinkSummary(url, linkInfo) {
        // defensive
        let linkSummary = {
            url: url.href,
            platformName: linkInfo.platform,
            sender: linkInfo.sender,
            timeSent: linkInfo.timeStamp,
            domainName: url.hostname,
            note: linkInfo.bodyText,
            isRead: false,
            tags: [],
            type: "article",
            title: "",
            description: "",
            imageUrl: ""
        };

        const prefix = 'https://info344api.enamarkovic.com/v1/summary?url=';
        return requestProm(prefix + url.href).then(body => {
            const urlData = JSON.parse(body);
            linkSummary.type = urlData.type && urlData.type.length > 0 ? urlData.type : "article";
            linkSummary.title = urlData.title ? urlData.title : "";
            linkSummary.description = urlData.description ? urlData.description : "";
            linkSummary.imageUrl = urlData.image ? urlData.image : "";
        }).catch(err => {
            console.error(err)
        }).then(() => linkSummary)
    }

    // Facebook oauth: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
    router.get('/facebook_oauth', function(req, res) {
        if (!req.query.code) {
            const oauthUrl = 'https://www.facebook.com/v2.9/dialog/oauth?client_id='
                + authConf.facebook.clientID
                + '&scope=' + authConf.facebook.scope
                + '&redirect_uri=' + authConf.facebook.redirectUri;

            if (!req.query.error) {
                res.redirect(oauthUrl);
            } else {
                res.status(401).send('Access denied to Facebook');
            }
        } else {
            // Exchanging an authorization code for an API access token
            const apiTokenUrl = 'https://graph.facebook.com/v2.9/oauth/access_token?client_id='
                + authConf.facebook.clientID
                + '&redirect_uri=' + authConf.facebook.redirectUri
                + '&client_secret=' + authConf.facebook.clientSecret
                + '&code=' + req.query.code;

            request(apiTokenUrl, function (err, apiTokenRes, body) {
                if (!err && apiTokenRes.statusCode == 200) {
                    const info = JSON.parse(body);
                    graph.setAccessToken(info.access_token);
                    res.redirect('https://lynxapp.me/app');
                } else {
                    console.log('Error: unable to get Facebook API token.');
                }
            });
        }
    });

    // Facebook webhook: https://developers.facebook.com/docs/graph-api/webhooks
    router.post('/facebook_incoming', function(facebookReq, facebookRes) {
        // Facebook challenge. Saving just in case.
        // if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === 'my_voice_is_my_password_verify_me') {
        //     console.log("Validating webhook");
        //     res.status(200).send(req.query['hub.challenge']);
        // } else {
        //     console.error("Failed validation. Make sure the validation tokens match.");
        //     res.sendStatus(403);
        // }
        //
        // changes: [ { field: 'status',
        //              id: '44444444_444444444',
        //              value: 'This is an Example Status.' } ]
        const newStatus = facebookReq.body.entry[0].changes[0];

        const statusValue = newStatus.value;

        const links = regParser(statusValue);

        if (links && links.length) {
            const reqParam = {
                fields: 'type,caption,description,link,updated_time,from,message'
            };

            // Grab information about the status via the API
            graph.get(newStatus.id, reqParam, function(err, res) {
                if (err) {
                    console.log("Error: not authorized with Facebook");
                    facebookRes.redirect('/facebook_oauth');
                } else {
                    var message = res;
                    console.log(message);

                    if (message.type === 'video'|| message.type === 'link') {
                        var linkInfo = {
                            platform: 'facebook',
                            sender: message.from.name,
                            bodyText: facebookReq.body.entry[0].changes[0].value ? facebookReq.body.entry[0].changes[0].value : "",
                            timeStamp: message.updated_time
                        };
                        console.log("link info", linkInfo);
                        console.log("link", message.link);
                        generateLinkSummary(message.link, linkInfo).then(linkSummary => {
                            // add the message to the database
                            console.log("link summary:", linkSummary);
                            return MessageDB.insertMessage(1, linkSummary)
                        }).then((message) => {
                            console.log(message);

                            socketIo.emit("new_message", {message: message});
                            // send the added message back to the user through web socket
                            // this should broadcast to users
                            facebookRes.status(200).send(message);
                        }).catch(console.log);
                    } else {
                        facebookRes.status(200).send("not a link");
                    }
                }
            });
        }
    });

    // Gmail Oauth: https://developers.google.com/identity/protocols/OAuth2WebServer
    router.get('/gmail_oauth', function(req, res) {
        /* const oauthUrl = 'https://accounts.google.com/o/oauth2/v2/auth?client_id='
        + authConf.gmail.clientID
        + '&redirect_uri' + authConf.gmail.redirectUri
        + '&response_type=code&scope=' + auth.gmail.scope;
        res.redirect(oauthUrl); */
        const oauth2 = google.auth.OAuth2;
        const oauth2Client = new oauth2(
            authConf.gmail.clientID,
            authConf.gmail.clientSecret,
            authConf.gmail.redirectUri
        );

        if (!req.query.code) {
            const oauthUrl = oauth2Client.generateAuthUrl({
                access_type: 'offline',
                scope: authConf.gmail.scope
            });
            res.redirect(oauthUrl);
        } else {
            oauth2Client.getToken(req.query.code, function(err, tokens) {
                if (!err) {
                    oauth2Client.setCredentials(tokens);
                    const gmail = google.gmail('v1');

                    gmail.users.messages.list({auth: oauth2Client, userId: 'me', maxResults: 100}, function(listErr, listRes) {
                        if (listErr) {
                            console.log('The list messages API call returned an error: ' + listErr);
                            return;
                        } else {
                            const emailIDs = listRes.messages;
                            for (const i in emailIDs) {
                                gmail.users.messages.get({auth: auth2Client, id: emailIDs[i],userId: 'me'}, function(getErr, getResponse) {
                                    if (getErr) {
                                        console.log('The get messages API call returned an error for email with ID: ' + emailIDs[i]);
                                    } else {
                                        console.log(getResponse);
                                    }
                                });
                            }
                        }
                    });
                } else {
                    console.log('Getting the API token returned an error: ' + err);
                }
            });
        }
    });

    router.get('/gmail', function(gmailReq, gmailRes) {
        console.log('in /api/auth/gmail');

        oauth2Client.getToken(gmailReq.query.code, function(err, tokens) {
            if (!err) {
                oauth2Client.setCredentials(tokens);
                const gmail = google.gmail('v1');

                gmail.users.messages.list({auth: oauth2Client, userId: 'me', maxResults: 100}, function(listErr, listRes) {
                    if (listErr) {
                        console.log('The list messages API call returned an error: ' + listErr);
                        return;
                    } else {
                        const emailIDs = listRes.messages;
                        for (const i in emailIDs) {
                            gmail.users.messages.get({auth: auth2Client, id: emailIDs[i],userId: 'me'}, function(getErr, getResponse) {
                                if (getErr) {
                                    console.log('The get messages API call returned an error for email with ID: ' + emailIDs[i]);
                                } else {
                                    console.log(getResponse);
                                }
                            });
                        }
                    }
                });
            } else {
                console.log('Getting the API token returned an error: ' + err);
            }
        });
        // When we are done, redirects back the main page
        gmailRes.redirect('https://lynxapp.me/app');
    });

    router.post('/gmail_incoming', function() {
        const projectId = 'YOUR_PROJECT_ID';

        // Instantiates a client
        const pubsubClient = PubSub({
            projectId: projectId
        });

        // The name for the new topic
        const topicName = 'my-new-topic';

        // Creates the new topic
        pubsubClient.createTopic(topicName)
            .then((results) => {
                const topic = results[0];
                console.log(`Topic ${topic.name} created.`);
            })
            .catch((err) => {
                console.error('ERROR:', err);
            });
    });

    // Slack Oauth: https://api.slack.com/docs/oauth
    router.get('/slack_oauth', function(req, res) {
        if (!req.query.code) {
            const oauthUrl = 'https://slack.com/oauth/authorize?client_id='
                + authConf.slack.clientID
                + '&scope=' + authConf.slack.scope
                + '&redirect_uri=' + authConf.slack.redirectUri;
            res.redirect(oauthUrl);
        } else {
            // Exchanging an authorization code for an API access token
            const apiTokenUrl = 'https://slack.com/api/oauth.access?client_id='
                + authConf.slack.clientID
                + '&client_secret=' + authConf.slack.clientSecret
                + '&code=' + req.query.code
                + '&redirect_uri=' + authConf.slack.redirectUri;

            request(apiTokenUrl, function (err, apiTokenRes, body) {
                if (!err && apiTokenRes.statusCode == 200) {
                    const info = JSON.parse(body);
                    // There seems to be no way to set the token after the object is initiated.
                    // This is the work around.
                    authConf.slack.accessToken = info.access_token;
                    res.redirect('https://lynxapp.me/app');
                }
            });
        }
    });

    router.post('/slack_incoming', function(req, res) {
        // This is used to respond to slack challenges. Saved in case
        // the verification expires in the future.
        // res.type('html');
        // res.status(200).send(req.body.challenge);
        // event:
        //        { type: 'message',
        //          user: 'U50SS1PLJ',
        //          text: 'denver',
        //          ts: '1495684015.632873',
        //          channel: 'D51MCEQ1M',
        //          event_ts: '1495684015.632873' },
        if (req.body.event.text) {

            var info =  req.body.event;
            const slackWeb = new slackWebClient(authConf.slack.accessToken);

            var links = regParser(info.text);

            // parsed data will be the urls
            // let links = regParser(req.body.event.text);
            if (links.length > 0) {

                var linkInfo = {
                    platform : 'slack',
                    timeStamp: info.event_ts,
                    bodyText: (info.text).replace(/[<>]/g,'')
                };

                // identify the user through the Slack API
                slackWeb.users.info(info.user, function(usersInfoErr, usersInfo) {
                    if (usersInfoErr || !usersInfo.ok) {
                        console.log('Error: Unable to identify user.');
                        linkInfo.sender = '';
                    } else {
                        linkInfo.sender = usersInfo.user.profile.real_name;
                    }

                    // send the urls through 344 handler
                    // generate link summary is expecting url object ( new URL() )
                    generateLinkSummary(links[0], linkInfo).then(linkSummary => {
                        // add the message to the database
                        console.log("link summary:", linkSummary);
                        return MessageDB.insertMessage(1, linkSummary)
                    }).then((message) => {
                        console.log(message);

                        socketIo.emit("new_message", {message: message});
                        // send the added message back to the user through web socket
                        // this should broadcast to users
                        res.status(200).send(message);
                    }).catch(console.log)
                });
            } else {
                res.status(200).send("did not have a link");
            }
        } else {
            res.status(200).send("non-text event");
        }
    });

	return router;
};

<commit_msg>gmail code refactor<commit_after>const express = require('express');
const _ = require('lodash');
const request = require('request');
const requestProm = require('request-promise');
const fs = require('fs');
const base64url = require('base64url');
const URL = require('url').URL;

const google = require('googleapis');
const slackWebClient = require('@slack/client').WebClient;
const graph = require('fbgraph');
const PubSub = require('@google-cloud/pubsub');

const authTokens = require(__base + 'secret/auth-tokens.json');
const authConf = {
    'facebook' : {
        'clientID' : authTokens.fbClientID, // also refer to as app-ID
        'clientSecret' : authTokens.fbClientSecret, // also refer to as app-secret
        'scope' : 'email, public_profile, user_friends, user_posts',
        'redirectUri' : 'https://lynxapp.me/api/auth/facebook_oauth'
    },
    'gmail' : {
        'clientID': authTokens.gmailClientID,
        'clientSecret' : authTokens.gmailClientSecret,
        'scope' : ['https://www.googleapis.com/auth/plus.me',
                   'https://www.googleapis.com/auth/gmail.readonly'],
        'redirectUri' : 'https://lynxapp.me/api/auth/gmail_oauth'
    },
    'slack' : {
        'clientID' : authTokens.slackClientID,
        'clientSecret' : authTokens.slackClientSecret,
        'scope': 'users.profile%3Aread+users%3Aread+channels%3Ahistory+channels%3Aread+im%3Aread+im%3Ahistory+mpim%3Aread+mpim%3Ahistory',
        'redirectUri' : 'https://lynxapp.me/api/auth/slack_oauth'
    }
};

module.exports.Router = function (MessageDB, socketIo) {
	const router = express.Router();

    // Parses a string and returns an array of links if there are any.
    // @param {string} text - the text to be processed.
    function regParser(message) {
        // Resource: https://gist.github.com/dperini/729294
        // defensive
        console.log("message in reg parser", message);
        if (message) {
            // Slack puts brackets around their links, so we need to remove them.
            var words = message.replace(/[<>]/g,'').split(' ');

            var urls = [];

            for (i = 0; i < words.length; i++) {
                console.log('The word is: ' + words[i]);

                // if it is a link, we will call the link summary API
                try {
                    const url = new URL(words[i]);
                    urls.push(url);
                } catch (e) {
                    console.log("not a link");
                }
            }
            return urls;
        }
    }

    // Calls Ena's link summary API. We return the result back to the client so it can format it.
    // @param {Object} linkinfo - other additional information about the link, which has the following properties:
    //                              service {string} - name of the platform
    //                              sender {string} - name of the sender
    function generateLinkSummary(url, linkInfo) {
        // defensive
        let linkSummary = {
            url: url.href,
            platformName: linkInfo.platform,
            sender: linkInfo.sender,
            timeSent: linkInfo.timeStamp,
            domainName: url.hostname,
            note: linkInfo.bodyText,
            isRead: false,
            tags: [],
            type: "article",
            title: "",
            description: "",
            imageUrl: ""
        };

        const prefix = 'https://info344api.enamarkovic.com/v1/summary?url=';
        return requestProm(prefix + url.href).then(body => {
            const urlData = JSON.parse(body);
            linkSummary.type = urlData.type && urlData.type.length > 0 ? urlData.type : "article";
            linkSummary.title = urlData.title ? urlData.title : "";
            linkSummary.description = urlData.description ? urlData.description : "";
            linkSummary.imageUrl = urlData.image ? urlData.image : "";
        }).catch(err => {
            console.error(err)
        }).then(() => linkSummary)
    }

    // Facebook oauth: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
    router.get('/facebook_oauth', function(req, res) {
        if (!req.query.code) {
            const oauthUrl = 'https://www.facebook.com/v2.9/dialog/oauth?client_id='
                + authConf.facebook.clientID
                + '&scope=' + authConf.facebook.scope
                + '&redirect_uri=' + authConf.facebook.redirectUri;

            if (!req.query.error) {
                res.redirect(oauthUrl);
            } else {
                res.status(401).send('Access denied to Facebook');
            }
        } else {
            // Exchanging an authorization code for an API access token
            const apiTokenUrl = 'https://graph.facebook.com/v2.9/oauth/access_token?client_id='
                + authConf.facebook.clientID
                + '&redirect_uri=' + authConf.facebook.redirectUri
                + '&client_secret=' + authConf.facebook.clientSecret
                + '&code=' + req.query.code;

            request(apiTokenUrl, function (err, apiTokenRes, body) {
                if (!err && apiTokenRes.statusCode == 200) {
                    const info = JSON.parse(body);
                    graph.setAccessToken(info.access_token);
                    res.redirect('https://lynxapp.me/app');
                } else {
                    console.log('Error: unable to get Facebook API token.');
                }
            });
        }
    });

    // Facebook webhook: https://developers.facebook.com/docs/graph-api/webhooks
    router.post('/facebook_incoming', function(facebookReq, facebookRes) {
        // Facebook challenge. Saving just in case.
        // if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === 'my_voice_is_my_password_verify_me') {
        //     console.log("Validating webhook");
        //     res.status(200).send(req.query['hub.challenge']);
        // } else {
        //     console.error("Failed validation. Make sure the validation tokens match.");
        //     res.sendStatus(403);
        // }
        //
        // changes: [ { field: 'status',
        //              id: '44444444_444444444',
        //              value: 'This is an Example Status.' } ]
        const newStatus = facebookReq.body.entry[0].changes[0];

        const statusValue = newStatus.value;

        const links = regParser(statusValue);

        if (links && links.length) {
            const reqParam = {
                fields: 'type,caption,description,link,updated_time,from,message'
            };

            // Grab information about the status via the API
            graph.get(newStatus.id, reqParam, function(err, res) {
                if (err) {
                    console.log("Error: not authorized with Facebook");
                    facebookRes.redirect('/facebook_oauth');
                } else {
                    var message = res;
                    console.log(message);

                    if (message.type === 'video'|| message.type === 'link') {
                        var linkInfo = {
                            platform: 'facebook',
                            sender: message.from.name,
                            bodyText: facebookReq.body.entry[0].changes[0].value ? facebookReq.body.entry[0].changes[0].value : "",
                            timeStamp: message.updated_time
                        };
                        console.log("link info", linkInfo);
                        console.log("link", message.link);
                        generateLinkSummary(message.link, linkInfo).then(linkSummary => {
                            // add the message to the database
                            console.log("link summary:", linkSummary);
                            return MessageDB.insertMessage(1, linkSummary)
                        }).then((message) => {
                            console.log(message);

                            socketIo.emit("new_message", {message: message});
                            // send the added message back to the user through web socket
                            // this should broadcast to users
                            facebookRes.status(200).send(message);
                        }).catch(console.log);
                    } else {
                        facebookRes.status(200).send("not a link");
                    }
                }
            });
        }
    });

    // Gmail Oauth: https://developers.google.com/identity/protocols/OAuth2WebServer
    router.get('/gmail_oauth', function(req, res) {
        /* const oauthUrl = 'https://accounts.google.com/o/oauth2/v2/auth?client_id='
        + authConf.gmail.clientID
        + '&redirect_uri' + authConf.gmail.redirectUri
        + '&response_type=code&scope=' + auth.gmail.scope;
        res.redirect(oauthUrl); */
        const oauth2 = google.auth.OAuth2;
        const oauth2Client = new oauth2(
            authConf.gmail.clientID,
            authConf.gmail.clientSecret,
            authConf.gmail.redirectUri
        );

        if (!req.query.code) {
            const oauthUrl = oauth2Client.generateAuthUrl({
                access_type: 'offline',
                scope: authConf.gmail.scope
            });
            res.redirect(oauthUrl);
        } else {
            oauth2Client.getToken(req.query.code, function(err, tokens) {
                if (!err) {
                    oauth2Client.setCredentials(tokens);
                    res.redirect('https://lynxapp.me/app');
                }
            });
        }
    });

    router.post('/gmail_incoming', function() {
        const projectId = 'YOUR_PROJECT_ID';

        // Instantiates a client
        const pubsubClient = PubSub({
            projectId: projectId
        });

        // The name for the new topic
        const topicName = 'my-new-topic';

        // Creates the new topic
        pubsubClient.createTopic(topicName)
            .then((results) => {
                const topic = results[0];
                console.log(`Topic ${topic.name} created.`);
            })
            .catch((err) => {
                console.error('ERROR:', err);
            });
    });

    // Slack Oauth: https://api.slack.com/docs/oauth
    router.get('/slack_oauth', function(req, res) {
        if (!req.query.code) {
            const oauthUrl = 'https://slack.com/oauth/authorize?client_id='
                + authConf.slack.clientID
                + '&scope=' + authConf.slack.scope
                + '&redirect_uri=' + authConf.slack.redirectUri;
            res.redirect(oauthUrl);
        } else {
            // Exchanging an authorization code for an API access token
            const apiTokenUrl = 'https://slack.com/api/oauth.access?client_id='
                + authConf.slack.clientID
                + '&client_secret=' + authConf.slack.clientSecret
                + '&code=' + req.query.code
                + '&redirect_uri=' + authConf.slack.redirectUri;

            request(apiTokenUrl, function (err, apiTokenRes, body) {
                if (!err && apiTokenRes.statusCode == 200) {
                    const info = JSON.parse(body);
                    // There seems to be no way to set the token after the object is initiated.
                    // This is the work around.
                    authConf.slack.accessToken = info.access_token;
                    res.redirect('https://lynxapp.me/app');
                }
            });
        }
    });

    router.post('/slack_incoming', function(req, res) {
        // This is used to respond to slack challenges. Saved in case
        // the verification expires in the future.
        // res.type('html');
        // res.status(200).send(req.body.challenge);
        // event:
        //        { type: 'message',
        //          user: 'U50SS1PLJ',
        //          text: 'denver',
        //          ts: '1495684015.632873',
        //          channel: 'D51MCEQ1M',
        //          event_ts: '1495684015.632873' },
        if (req.body.event.text) {

            var info =  req.body.event;
            const slackWeb = new slackWebClient(authConf.slack.accessToken);

            var links = regParser(info.text);

            // parsed data will be the urls
            // let links = regParser(req.body.event.text);
            if (links.length > 0) {

                var linkInfo = {
                    platform : 'slack',
                    timeStamp: info.event_ts,
                    bodyText: (info.text).replace(/[<>]/g,'')
                };

                // identify the user through the Slack API
                slackWeb.users.info(info.user, function(usersInfoErr, usersInfo) {
                    if (usersInfoErr || !usersInfo.ok) {
                        console.log('Error: Unable to identify user.');
                        linkInfo.sender = '';
                    } else {
                        linkInfo.sender = usersInfo.user.profile.real_name;
                    }

                    // send the urls through 344 handler
                    // generate link summary is expecting url object ( new URL() )
                    generateLinkSummary(links[0], linkInfo).then(linkSummary => {
                        // add the message to the database
                        console.log("link summary:", linkSummary);
                        return MessageDB.insertMessage(1, linkSummary)
                    }).then((message) => {
                        console.log(message);

                        socketIo.emit("new_message", {message: message});
                        // send the added message back to the user through web socket
                        // this should broadcast to users
                        res.status(200).send(message);
                    }).catch(console.log)
                });
            } else {
                res.status(200).send("did not have a link");
            }
        } else {
            res.status(200).send("non-text event");
        }
    });

	return router;
};

<|endoftext|>


<commit_before>/*global define */
/*jslint white:true */
define(["jquery", "bluebird"], function ($, Promise) {
"use strict";

function UserProfile(url, auth, auth_cb) {

    if (typeof url !== 'string') {
        throw new Error('Service url was not provided');
    }
    this.url = url;
    var _url = url;
    var deprecationWarningSent = false;

    function deprecationWarning() {
        if (!deprecationWarningSent) {
            deprecationWarningSent = true;
            if (!window.console) return;
            console.log(
                "DEPRECATION WARNING: '*_async' method names will be removed",
                "in a future version. Please use the identical methods without",
                "the'_async' suffix.");
        }
    }

    var _auth = auth ? auth : { 'token' : '', 'user_id' : ''};
    var _auth_cb = auth_cb;

    this.ver = function (_callback, _errorCallback) {
    return json_call_ajax("UserProfile.ver",
        [], 1, _callback, _errorCallback);
};

    this.ver_async = function (_callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.ver", [], 1, _callback, _error_callback);
    };

    this.filter_users = function (p, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.filter_users",
        [p], 1, _callback, _errorCallback);
};

    this.filter_users_async = function (p, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.filter_users", [p], 1, _callback, _error_callback);
    };

    this.get_user_profile = function (usernames, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.get_user_profile",
        [usernames], 1, _callback, _errorCallback);
};

    this.get_user_profile_async = function (usernames, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.get_user_profile", [usernames], 1, _callback, _error_callback);
    };

    this.set_user_profile = function (p, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.set_user_profile",
        [p], 0, _callback, _errorCallback);
};

    this.set_user_profile_async = function (p, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.set_user_profile", [p], 0, _callback, _error_callback);
    };

    this.update_user_profile = function (p, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.update_user_profile",
        [p], 0, _callback, _errorCallback);
};

    this.update_user_profile_async = function (p, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.update_user_profile", [p], 0, _callback, _error_callback);
    };

    this.lookup_globus_user = function (usernames, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.lookup_globus_user",
        [usernames], 1, _callback, _errorCallback);
};

    this.lookup_globus_user_async = function (usernames, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.lookup_globus_user", [usernames], 1, _callback, _error_callback);
    };

    /*
     * JSON call using jQuery method.
     */
    function json_call_ajax(method, params, numRets, callback, errorCallback) {
        var deferred = $.Deferred();

        if (typeof callback === 'function') {
           deferred.done(callback);
        }

        if (typeof errorCallback === 'function') {
           deferred.fail(errorCallback);
        }

        var rpc = {
            params : params,
            method : method,
            version: "1.1",
            id: String(Math.random()).slice(2),
        };

        var beforeSend = null;
        var token = (_auth_cb && typeof _auth_cb === 'function') ? _auth_cb()
            : (_auth.token ? _auth.token : null);
        if (token !== null) {
            beforeSend = function (xhr) {
                xhr.setRequestHeader("Authorization", token);
            }
        }

        var xhr = jQuery.ajax({
            url: _url,
            dataType: "text",
            type: 'POST',
            processData: false,
            // This is what it should be!
            // contentType: "application/json-rpc",
            data: JSON.stringify(rpc),
            beforeSend: beforeSend,
            success: function (data, status, xhr) {
                var result;
                try {
                    var resp = JSON.parse(data);
                    result = (numRets === 1 ? resp.result[0] : resp.result);
                } catch (err) {
                    deferred.reject({
                        status: 503,
                        error: err,
                        url: _url,
                        resp: data
                    });
                    return;
                }
                deferred.resolve(result);
            },
            error: function (xhr, textStatus, errorThrown) {
                var error;
                if (xhr.responseText) {
                    try {
                        var resp = JSON.parse(xhr.responseText);
                        error = resp.error;
                    } catch (err) { // Not JSON
                        error = "Unknown error - " + xhr.responseText;
                    }
                } else {
                    error = "Unknown Error";
                }
                deferred.reject({
                    status: 500,
                    error: error
                });
            }
        });

        var promise = deferred.promise();
        promise.xhr = xhr;
        return Promise.resolve(promise);
    }
}

return UserProfile;
});<commit_msg>removed comment<commit_after>/*global define */
/*jslint white:true */
define(["jquery", "bluebird"], function ($, Promise) {
"use strict";

function UserProfile(url, auth, auth_cb) {

    if (typeof url !== 'string') {
        throw new Error('Service url was not provided');
    }
    this.url = url;
    var _url = url;
    var deprecationWarningSent = false;

    function deprecationWarning() {
        if (!deprecationWarningSent) {
            deprecationWarningSent = true;
            if (!window.console) return;
            console.log(
                "DEPRECATION WARNING: '*_async' method names will be removed",
                "in a future version. Please use the identical methods without",
                "the'_async' suffix.");
        }
    }

    var _auth = auth ? auth : { 'token' : '', 'user_id' : ''};
    var _auth_cb = auth_cb;

    this.ver = function (_callback, _errorCallback) {
    return json_call_ajax("UserProfile.ver",
        [], 1, _callback, _errorCallback);
};

    this.ver_async = function (_callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.ver", [], 1, _callback, _error_callback);
    };

    this.filter_users = function (p, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.filter_users",
        [p], 1, _callback, _errorCallback);
};

    this.filter_users_async = function (p, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.filter_users", [p], 1, _callback, _error_callback);
    };

    this.get_user_profile = function (usernames, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.get_user_profile",
        [usernames], 1, _callback, _errorCallback);
};

    this.get_user_profile_async = function (usernames, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.get_user_profile", [usernames], 1, _callback, _error_callback);
    };

    this.set_user_profile = function (p, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.set_user_profile",
        [p], 0, _callback, _errorCallback);
};

    this.set_user_profile_async = function (p, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.set_user_profile", [p], 0, _callback, _error_callback);
    };

    this.update_user_profile = function (p, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.update_user_profile",
        [p], 0, _callback, _errorCallback);
};

    this.update_user_profile_async = function (p, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.update_user_profile", [p], 0, _callback, _error_callback);
    };

    this.lookup_globus_user = function (usernames, _callback, _errorCallback) {
    return json_call_ajax("UserProfile.lookup_globus_user",
        [usernames], 1, _callback, _errorCallback);
};

    this.lookup_globus_user_async = function (usernames, _callback, _error_callback) {
        deprecationWarning();
        return json_call_ajax("UserProfile.lookup_globus_user", [usernames], 1, _callback, _error_callback);
    };

    /*
     * JSON call using jQuery method.
     */
    function json_call_ajax(method, params, numRets, callback, errorCallback) {
        var deferred = $.Deferred();

        if (typeof callback === 'function') {
           deferred.done(callback);
        }

        if (typeof errorCallback === 'function') {
           deferred.fail(errorCallback);
        }

        var rpc = {
            params : params,
            method : method,
            version: "1.1",
            id: String(Math.random()).slice(2),
        };

        var beforeSend = null;
        var token = (_auth_cb && typeof _auth_cb === 'function') ? _auth_cb()
            : (_auth.token ? _auth.token : null);
        if (token !== null) {
            beforeSend = function (xhr) {
                xhr.setRequestHeader("Authorization", token);
            }
        }

        var xhr = jQuery.ajax({
            url: _url,
            dataType: "text",
            type: 'POST',
            processData: false,
            data: JSON.stringify(rpc),
            beforeSend: beforeSend,
            success: function (data, status, xhr) {
                var result;
                try {
                    var resp = JSON.parse(data);
                    result = (numRets === 1 ? resp.result[0] : resp.result);
                } catch (err) {
                    deferred.reject({
                        status: 503,
                        error: err,
                        url: _url,
                        resp: data
                    });
                    return;
                }
                deferred.resolve(result);
            },
            error: function (xhr, textStatus, errorThrown) {
                var error;
                if (xhr.responseText) {
                    try {
                        var resp = JSON.parse(xhr.responseText);
                        error = resp.error;
                    } catch (err) { // Not JSON
                        error = "Unknown error - " + xhr.responseText;
                    }
                } else {
                    error = "Unknown Error";
                }
                deferred.reject({
                    status: 500,
                    error: error
                });
            }
        });

        var promise = deferred.promise();
        promise.xhr = xhr;
        return Promise.resolve(promise);
    }
}

return UserProfile;
});<|endoftext|>


<commit_before>(function (angular, moment) {
    angular.module('pk-pass-display').component('pkApplePassField', {
        controller: ["$sce", "$element", PKApplePassFieldController],
        templateUrl:"apple-pass-field.html",
        bindings: {
            field: '<',
            lang: '<'
        }
    });

    function PKApplePassFieldController($sce, $element) {
        var ctrl = this;

        ctrl.$onChanges = onChanges;

        function onChanges(changesObj) {
            if (changesObj.hasOwnProperty("field") && ctrl.field != undefined) {
                fieldChange(ctrl.field);
            }
            if (changesObj.hasOwnProperty("lang") && ctrl.lang != undefined) {
                languageChange(ctrl.lang);
            }
        }

        //scope funcs
        ctrl.getValue = getValue;

        var TextAlignmentMap = {
            "PKTextAlignmentLeft":"left",
            "PKTextAlignmentCenter":"center",
            "PKTextAlignmentRight":"right",
            "PKTextAlignmentNatural":"justify"
        };

        var DateStyleMap = {
            "PKDateStyleNone":"none",
            "PKDateStyleShort":"short",
            "PKDateStyleMedium":"medium",
            "PKDateStyleLong":"long",
            "PKDateStyleFull":"full"
        };

        var CurrencyMap = {"AED":"د.إ.","AFN":"؋","ALL":"Lek","AMD":"դր.","ANG":false,"AOA":false,"ARS":"$","AUD":"$","AWG":false,"AZN":"man.","BAM":"KM","BBD":false,"BDT":"৳","BGN":"лв.","BHD":"د.ب.","BIF":false,"BMD":false,"BND":"$","BOB":"Bs","BOV":false,"BRL":"R$","BSD":false,"BTN":"Nu.","BWP":"P","BYR":"","BZD":"$","CAD":"$","CDF":false,"CHE":false,"CHF":"Fr.","CHW":false,"CLF":false,"CLP":"$","CNY":"￥","COP":"$","COU":false,"CRC":"₡","CUC":false,"CUP":false,"CVE":false,"CZK":"Kč","DJF":"Fdj","DKK":"kr","DOP":"RD$","DZD":"د.ج.","EGP":"ج.م.","ERN":"Nfk","ETB":false,"EUR":"€","FJD":false,"FKP":false,"GBP":"£","GEL":"","GHS":"GH₵","GIP":false,"GMD":false,"GNF":false,"GTQ":"Q","GYD":false,"HKD":"$","HNL":"L","HRK":"kn","HTG":false,"HUF":"Ft","IDR":"Rp","ILS":"₪","INR":"रु.","IQD":"د.ع.","IRR":"﷼","ISK":"kr","JMD":"$","JOD":"د.أ.","JPY":"￥","KES":false,"KGS":"сом","KHR":"៛","KMF":false,"KPW":false,"KRW":"₩","KWD":"د.ك.","KYD":false,"KZT":false,"LAK":"₭","LBP":"ل.ل.","LKR":"SL Re","LRD":false,"LSL":false,"LTL":"Lt","LVL":"Ls","LYD":"د.ل.","MAD":"د.م.","MDL":"","MGA":false,"MKD":"","MMK":"K","MNT":"₮","MOP":"MOP$","MRO":false,"MUR":false,"MVR":"ރ.","MWK":"","MXN":"$","MXV":false,"MYR":"RM","MZN":false,"NAD":false,"NGN":false,"NIO":"C$","NOK":"kr","NPR":"नेरू","NZD":"$","OMR":"ر.ع.","PAB":"B/.","PEN":"S/.","PGK":false,"PHP":"₱","PKR":"روپے","PLN":"zł","PYG":false,"QAR":"ر.ق.","RON":"RON","RSD":"дин.","RUB":"руб.","RWF":"RF","SAR":"ر.س.","SBD":false,"SCR":false,"SDG":"","SEK":"kr","SGD":"$","SHP":false,"SLL":false,"SOS":false,"SRD":false,"SSP":false,"STD":false,"SYP":"ل.س.","SZL":false,"THB":"฿","TJS":"сом","TMT":false,"TND":"د.ت.","TOP":"T$","TRY":"TL","TTD":"$","TWD":"NT$","TZS":"TSh","UAH":"₴","UGX":false,"USD":"$","USN":false,"USS":false,"UYI":false,"UYU":"$","UZS":"сўм","VEF":"Bs.F.","VND":false,"VUV":false,"WST":false,"XAF":false,"XAG":false,"XAU":false,"XBA":false,"XBB":false,"XBC":false,"XBD":false,"XCD":false,"XDR":false,"XFU":false,"XOF":"CFA","XPD":false,"XPF":false,"XPT":false,"XTS":false,"XXX":false,"YER":"ر.ي.","ZAR":"R","ZMK":false,"ZWL":false,"EEK":"kr"};

        var NumberStyleMap = {
            "PKNumberStyleDecimal":"decimal",
            "PKNumberStylePercent":"percent",
            "PKNumberStyleScientific":"scientific",
            "PKNumberStyleSpellOut":"spell",
        };

        function fieldChange(val) {
            if(val != null) {
                if(val.hasOwnProperty("textAlignment")) {
                    textAlign($element,TextAlignmentMap[val.textAlignment]);
                }
                if(val.hasOwnProperty("dateStyle")) {
                    /*
                        `json:"dateStyle,omitempty"`
                        `json:"ignoresTimeZone,omitempty"`
                        `json:"isRelative,omitempty"`
                        `json:"timeStyle,omitempty"`
                    */
                    ctrl.field.value = timeFormatValue(val.value, DateStyleMap[val.dateStyle]);
                }
                if(val.hasOwnProperty("currencyCode")) {
                    if(CurrencyMap[val.currencyCode]) {
                        ctrl.field.value = CurrencyMap[val.currencyCode] + val.value;
                    } else {
                        ctrl.field.value = val.currencyCode + val.value;
                    }
                }
                if(val.hasOwnProperty("numberStyle")) {
                    ctrl.field.value = numberFormat(val.value, NumberStyleMap[val.numberStyle]);
                }
            }
        }

        function languageChange(val) {
            if(val != null) {
                if(val.hasOwnProperty('text')) {
                    // sort the key to avoid substring is replaced before

// the whole string

                    var sortedKeys = [];
                    for (var key in val.text) {
                        if (val.text.hasOwnProperty(key)) {
                            sortedKeys.push(key);
                        }
                    }

                    // sort keys by text length to avoid substring is replaced randomly
                    sortedKeys.sort(function(a, b){
                        // ASC  -> a.length - b.length

                        // DESC -> b.length - a.length

                        return b.length - a.length;

                    });

                    sortedKeys.forEach(function(key){
                        if (val.text.hasOwnProperty(key) && typeof ctrl.field !== "undefined") {
                            if(ctrl.field.hasOwnProperty("value")) {
                                ctrl.field.value = replaceAll(ctrl.field.value, key, val.text[key]);
                            }
                            if(ctrl.field.hasOwnProperty("label")) {
                                ctrl.field.label = replaceAll(ctrl.field.label, key, val.text[key]);
                            }
                            if(ctrl.field.hasOwnProperty("attributedValue")) {
                                ctrl.field.attributedValue = replaceAll(ctrl.field.attributedValue, key, val.text[key]);
                            }
                        }
                    })
                }
            }
        }

        function getValue(field) {
            var text = "";
            if(field != null) {
                if(field.hasOwnProperty("attributedValue")) {
                    text = field.attributedValue;
                } else {
                    text = field.value;
                }
            }
            return $sce.trustAsHtml(text.toString().replace(/\n/g, "
"));
        }

        function textAlign(e,alignment) {
            e.css('text-align', alignment);
        }

        //FIXME need to implement number format switch;
        function numberFormat(value, format) {
            switch(format) {
            case 'percent':
                return value+"%";
            default:
                return value;
            }
        }

        function timeFormatValue(date, style) {
            var format;
            switch(style) {
            case 'full': // Wednesday, July 17, 2019
                format = 'LLLL';
                break;
            case 'long': // July 17, 2019
                format = 'LL';
                break;
            case 'medium': // Jul 17, 2019
                format = 'll';
                break;
            case 'short': // 7/17/19 (short)
                format = 'l';
                break;
            default:
                return date;
            }
            return moment(date).format(format);
        }

        function replaceAll(string, find, replaceString) {
            var regescaped = find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
            return new String(string).replace(new RegExp(regescaped, 'g'), replaceString);
        }
    }
})(window.angular, window.moment);<commit_msg>aslant<commit_after>(function (angular, moment) {
    angular.module('pk-pass-display').component('pkApplePassField', {
        controller: ["$sce", "$element", PKApplePassFieldController],
        templateUrl:"apple-pass-field.html",
        bindings: {
            field: '<',
            lang: '<'
        }
    });

    function PKApplePassFieldController($sce, $element) {
        var ctrl = this;

        ctrl.$onChanges = onChanges;

        function onChanges(changesObj) {
            if (changesObj.hasOwnProperty("field") && ctrl.field != undefined) {
                fieldChange(ctrl.field);
            }
            if (changesObj.hasOwnProperty("lang") && ctrl.lang != undefined) {
                languageChange(ctrl.lang);
            }
        }

        //scope funcs
        ctrl.getValue = getValue;

        var TextAlignmentMap = {
            "PKTextAlignmentLeft":"left",
            "PKTextAlignmentCenter":"center",
            "PKTextAlignmentRight":"right",
            "PKTextAlignmentNatural":"justify"
        };

        var DateStyleMap = {
            "PKDateStyleNone":"none",
            "PKDateStyleShort":"short",
            "PKDateStyleMedium":"medium",
            "PKDateStyleLong":"long",
            "PKDateStyleFull":"full"
        };

        var CurrencyMap = {"AED":"د.إ.","AFN":"؋","ALL":"Lek","AMD":"դր.","ANG":false,"AOA":false,"ARS":"$","AUD":"$","AWG":false,"AZN":"man.","BAM":"KM","BBD":false,"BDT":"৳","BGN":"лв.","BHD":"د.ب.","BIF":false,"BMD":false,"BND":"$","BOB":"Bs","BOV":false,"BRL":"R$","BSD":false,"BTN":"Nu.","BWP":"P","BYR":"","BZD":"$","CAD":"$","CDF":false,"CHE":false,"CHF":"Fr.","CHW":false,"CLF":false,"CLP":"$","CNY":"￥","COP":"$","COU":false,"CRC":"₡","CUC":false,"CUP":false,"CVE":false,"CZK":"Kč","DJF":"Fdj","DKK":"kr","DOP":"RD$","DZD":"د.ج.","EGP":"ج.م.","ERN":"Nfk","ETB":false,"EUR":"€","FJD":false,"FKP":false,"GBP":"£","GEL":"","GHS":"GH₵","GIP":false,"GMD":false,"GNF":false,"GTQ":"Q","GYD":false,"HKD":"$","HNL":"L","HRK":"kn","HTG":false,"HUF":"Ft","IDR":"Rp","ILS":"₪","INR":"रु.","IQD":"د.ع.","IRR":"﷼","ISK":"kr","JMD":"$","JOD":"د.أ.","JPY":"￥","KES":false,"KGS":"сом","KHR":"៛","KMF":false,"KPW":false,"KRW":"₩","KWD":"د.ك.","KYD":false,"KZT":false,"LAK":"₭","LBP":"ل.ل.","LKR":"SL Re","LRD":false,"LSL":false,"LTL":"Lt","LVL":"Ls","LYD":"د.ل.","MAD":"د.م.","MDL":"","MGA":false,"MKD":"","MMK":"K","MNT":"₮","MOP":"MOP$","MRO":false,"MUR":false,"MVR":"ރ.","MWK":"","MXN":"$","MXV":false,"MYR":"RM","MZN":false,"NAD":false,"NGN":false,"NIO":"C$","NOK":"kr","NPR":"नेरू","NZD":"$","OMR":"ر.ع.","PAB":"B/.","PEN":"S/.","PGK":false,"PHP":"₱","PKR":"روپے","PLN":"zł","PYG":false,"QAR":"ر.ق.","RON":"RON","RSD":"дин.","RUB":"руб.","RWF":"RF","SAR":"ر.س.","SBD":false,"SCR":false,"SDG":"","SEK":"kr","SGD":"$","SHP":false,"SLL":false,"SOS":false,"SRD":false,"SSP":false,"STD":false,"SYP":"ل.س.","SZL":false,"THB":"฿","TJS":"сом","TMT":false,"TND":"د.ت.","TOP":"T$","TRY":"TL","TTD":"$","TWD":"NT$","TZS":"TSh","UAH":"₴","UGX":false,"USD":"$","USN":false,"USS":false,"UYI":false,"UYU":"$","UZS":"сўм","VEF":"Bs.F.","VND":false,"VUV":false,"WST":false,"XAF":false,"XAG":false,"XAU":false,"XBA":false,"XBB":false,"XBC":false,"XBD":false,"XCD":false,"XDR":false,"XFU":false,"XOF":"CFA","XPD":false,"XPF":false,"XPT":false,"XTS":false,"XXX":false,"YER":"ر.ي.","ZAR":"R","ZMK":false,"ZWL":false,"EEK":"kr"};

        var NumberStyleMap = {
            "PKNumberStyleDecimal":"decimal",
            "PKNumberStylePercent":"percent",
            "PKNumberStyleScientific":"scientific",
            "PKNumberStyleSpellOut":"spell",
        };

        function fieldChange(val) {
            if(val != null) {
                if(val.hasOwnProperty("textAlignment")) {
                    textAlign($element,TextAlignmentMap[val.textAlignment]);
                }
                if(val.hasOwnProperty("dateStyle")) {
                    /*
                        `json:"dateStyle,omitempty"`
                        `json:"ignoresTimeZone,omitempty"`
                        `json:"isRelative,omitempty"`
                        `json:"timeStyle,omitempty"`
                    */
                    ctrl.field.value = timeFormatValue(val.value, DateStyleMap[val.dateStyle]);
                }
                if(val.hasOwnProperty("currencyCode")) {
                    if(CurrencyMap[val.currencyCode]) {
                        ctrl.field.value = CurrencyMap[val.currencyCode] + val.value;
                    } else {
                        ctrl.field.value = val.currencyCode + val.value;
                    }
                }
                if(val.hasOwnProperty("numberStyle")) {
                    ctrl.field.value = numberFormat(val.value, NumberStyleMap[val.numberStyle]);
                }
            }
        }

        function languageChange(val) {
            if(val != null) {
                if(val.hasOwnProperty('text')) {
                    // sort the key to avoid substring is replaced before
                    // the whole string

                    var sortedKeys = [];
                    for (var key in val.text) {
                        if (val.text.hasOwnProperty(key)) {
                            sortedKeys.push(key);
                        }
                    }

                    // sort keys by text length to avoid substring is replaced randomly
                    sortedKeys.sort(function(a, b){
                        // ASC  -> a.length - b.length
                        // DESC -> b.length - a.length
                        return b.length - a.length;
                    });

                    sortedKeys.forEach(function(key){
                        if (val.text.hasOwnProperty(key) && typeof ctrl.field !== "undefined") {
                            if(ctrl.field.hasOwnProperty("value")) {
                                ctrl.field.value = replaceAll(ctrl.field.value, key, val.text[key]);
                            }
                            if(ctrl.field.hasOwnProperty("label")) {
                                ctrl.field.label = replaceAll(ctrl.field.label, key, val.text[key]);
                            }
                            if(ctrl.field.hasOwnProperty("attributedValue")) {
                                ctrl.field.attributedValue = replaceAll(ctrl.field.attributedValue, key, val.text[key]);
                            }
                        }
                    });
                }
            }
        }

        function getValue(field) {
            var text = "";
            if(field != null) {
                if(field.hasOwnProperty("attributedValue")) {
                    text = field.attributedValue;
                } else {
                    text = field.value;
                }
            }
            return $sce.trustAsHtml(text.toString().replace(/\n/g, "
"));
        }

        function textAlign(e,alignment) {
            e.css('text-align', alignment);
        }

        //FIXME need to implement number format switch;
        function numberFormat(value, format) {
            switch(format) {
            case 'percent':
                return value+"%";
            default:
                return value;
            }
        }

        function timeFormatValue(date, style) {
            var format;
            switch(style) {
            case 'full': // Wednesday, July 17, 2019
                format = 'LLLL';
                break;
            case 'long': // July 17, 2019
                format = 'LL';
                break;
            case 'medium': // Jul 17, 2019
                format = 'll';
                break;
            case 'short': // 7/17/19 (short)
                format = 'l';
                break;
            default:
                return date;
            }
            return moment(date).format(format);
        }

        function replaceAll(string, find, replaceString) {
            var regescaped = find.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
            return new String(string).replace(new RegExp(regescaped, 'g'), replaceString);
        }
    }
})(window.angular, window.moment);<|endoftext|>


<commit_before>Charicharts.Pie = CClass.extend({

  modules: [
    p_svg,
    p_pie,
    p_pie_inner_arrow
  ],

  getInstanceProperties: function() {
    return _.pick(this.$scope, 'series', 'moveArrowToId');
  },

  defaults: {
    margin: '0 0 0 0',
    innerRadius: 0.6,
    hoverFade: 1,
    innerArrow: false,
    innerArrowSize: 0.5
  },

  parseOptions: function(options) {
    var o = _.extend({}, this.defaults, options);
    o.margin = _.object(['top', 'right', 'bottom', 'left'],
      o.margin.split(' ').map(Number));
    o.fullWidth = o.target.offsetWidth;
    o.fullHeight = o.target.offsetHeight;
    o.width = o.fullWidth - o.margin.left - o.margin.right;
    o.height = o.fullHeight - o.margin.top - o.margin.bottom;
    o.gmainTranslate = h_getTranslate(o.fullWidth/2, o.fullHeight/2);
    o.radius = Math.min(o.fullWidth, o.fullHeight) / 2;
    return o;
  }

});<commit_msg>Pie delegate exports to its modules (pie_inner_arrow)<commit_after>Charicharts.Pie = CClass.extend({

  modules: [
    p_svg,
    p_pie,
    p_pie_inner_arrow
  ],

  getInstanceProperties: function() {
    return _.pick(this.$scope, 'series', 'pie_inner_arrow');
  },

  defaults: {
    margin: '0 0 0 0',
    innerRadius: 0.6,
    hoverFade: 1,
    innerArrow: false,
    innerArrowSize: 0.5
  },

  parseOptions: function(options) {
    var o = _.extend({}, this.defaults, options);
    o.margin = _.object(['top', 'right', 'bottom', 'left'],
      o.margin.split(' ').map(Number));
    o.fullWidth = o.target.offsetWidth;
    o.fullHeight = o.target.offsetHeight;
    o.width = o.fullWidth - o.margin.left - o.margin.right;
    o.height = o.fullHeight - o.margin.top - o.margin.bottom;
    o.gmainTranslate = h_getTranslate(o.fullWidth/2, o.fullHeight/2);
    o.radius = Math.min(o.fullWidth, o.fullHeight) / 2;
    return o;
  }

});<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

let log = require('./logger.js')(__filename);

let redis = require('redis');
let rclient = redis.createClient();

let Promise = require('bluebird');
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

let async = require('asyncawait/async');
let await = require('asyncawait/await');

let async2 = require('async');

let util = require('util');

let instance = null;

const MAX_FLOW_PER_AGGR = 2000
const MAX_FLOW_PER_SUM = 30000
const MAX_FLOW_PER_HOUR = 7000

function toInt(n){ return Math.floor(Number(n)); };

class FlowAggrTool {
  constructor() {
    if(!instance) {
      instance = this;
    }
    return instance;
  }

  toFloorInt(n) {
    return toInt(n);
  }

  getFlowKey(mac, trafficDirection, interval, ts) {
    return util.format("aggrflow:%s:%s:%s:%s", mac, trafficDirection, interval, ts);
  }

  getSumFlowKey(mac, trafficDirection, begin, end) {
    if(mac) {
      return util.format("sumflow:%s:%s:%s:%s", mac, trafficDirection, begin, end);
    } else {
      return util.format("syssumflow:%s:%s:%s", trafficDirection, begin, end);
    }
  }

  // aggrflow:<device_mac>:download:10m:<ts>
  flowExists(mac, trafficDirection, interval, ts) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.keysAsync(key)
      .then((results) => {
        return results.length === 1;
      })
  }

  // key: aggrflow:<device_mac>:<direction>:<interval>:<ts>
  // content: destination ip address
  // score: traffic size
  addFlow(mac, trafficDirection, interval, ts, destIP, traffic) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.zaddAsync(key, traffic, destIP);
  }

  addAppActivityFlows(mac, interval, ts, traffics, expire) {
    return this.addXActivityFlows(mac, "app", interval, ts, traffics, expire)
  }

  addCategoryActivityFlows(mac, interval, ts, traffics, expire) {
    return this.addXActivityFlows(mac, "category", interval, ts, traffics, expire)
  }

  addXActivityFlows(mac, x, interval, ts, traffics, expire) {
    expire = expire || 24 * 3600; // by default keep 24 hours

    let key = this.getFlowKey(mac, x, interval, ts);
    let args = [key];
    for(let t in traffics) {
      let duration = (traffics[t] && traffics[t]['duration']) || 0;
      args.push(duration)

      let payload = {}
      payload.device = mac
      payload[x] = t
      args.push(JSON.stringify(payload))
    }

    args.push(0);
    args.push("_"); // placeholder to keep key exists

    return rclient.zaddAsync(args)
      .then(() => {
        return rclient.expireAsync(key, expire)
      });
  }

  // this is to make sure flow data is not flooded enough to consume all memory
  trimFlow(mac, trafficDirection, interval, ts) {
    return async(() => {
      const key = this.getFlowKey(mac, trafficDirection, interval, ts);

      let count = await (rclient.zremrangebyrankAsync(key, 0, -1 * MAX_FLOW_PER_AGGR)) // only keep the MAX_FLOW_PER_SUM highest flows
      if(count > 0) {
        log.warn(`${count} flows are removed from ${key} for self protection`)
      }
    })()
  }

  addFlows(mac, trafficDirection, interval, ts, traffics, expire) {
    expire = expire || 24 * 3600; // by default keep 24 hours

    const length = Object.keys(traffics).length // number of dest ips in this aggr flow
    const key = this.getFlowKey(mac, trafficDirection, interval, ts);

    let args = [key];

    if(length > MAX_FLOW_PER_AGGR) { // self protection
      args.push(length)
      args.push(JSON.stringify({
        device: mac,
        destIP: "0.0.0.0"       // special ip address to indicate some flows were skipped due to overflow protection
      }))
    }

    for(let destIP in traffics) {
      let traffic = (traffics[destIP] && traffics[destIP][trafficDirection]) || 0;
      args.push(traffic)
      args.push(JSON.stringify({
        device: mac,
        destIP: destIP
      }))
    }

    args.push(0);
    args.push("_"); // placeholder to keep key exists

    return async(() => {
      await (rclient.zaddAsync(args))
      await (rclient.expireAsync(key, expire))
      await (this.trimFlow(mac, trafficDirection, interval, ts))
    })()
  }

  removeFlow(mac, trafficDirection, interval, ts, destIP) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.zremAsync(key, destIP);
  }

  removeFlowKey(mac, trafficDirection, interval, ts) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.delAsync(key);
  }

  removeAllFlowKeys(mac, trafficDirection, interval) {
    let keyPattern = util.format("aggrflow:%s:%s:%s:*", mac, trafficDirection, interval);

    return async(() => {
      let keys = await (rclient.keysAsync(keyPattern));
      keys.forEach((key) => {
        await (rclient.delAsync(key))
      })
    })();
  }

  // this is to make sure flow data is not flooded enough to consume all memory
  trimSumFlow(trafficDirection, options) {
    return async(() => {
      if(!options.begin || !options.end) {
        return Promise.reject(new Error("Require begin and end"));
      }

      let begin = options.begin;
      let end = options.end;

      let max_flow = MAX_FLOW_PER_SUM

      if(end-begin < 4000) { // hourly sum
        max_flow = MAX_FLOW_PER_HOUR
      }

      let mac = options.mac; // if mac is undefined, by default it will scan over all machines

      let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

      let count = await (rclient.zremrangebyscoreAsync(sumFlowKey, 0, -1 * MAX_FLOW_PER_SUM)) // only keep the MAX_FLOW_PER_SUM highest flows
      if(count > 0) {
        log.warn(`${count} flows are removed from ${sumFlowKey} for self protection`)
      }
    })()
  }

  // sumflow:<device_mac>:download:<begin_ts>:<end_ts>
  // content: destination ip address
  // score: traffic size

  // interval is the interval of each aggr flow (aggrflow:...)
  addSumFlow(trafficDirection, options) {

    if(!options.begin || !options.end) {
      return Promise.reject(new Error("Require begin and end"));
    }

    let begin = options.begin;
    let end = options.end;

    // if working properly, sumflow should be refreshed in every 10 minutes
    let expire = options.expireTime || 24 * 60; // by default expire in 24 minutes
    let interval = options.interval || 600; // by default 10 mins

    let mac = options.mac; // if mac is undefined, by default it will scan over all machines

    let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

    return async(() => {

      if(options.skipIfExists) {
        let exists = await(rclient.keysAsync(sumFlowKey));
        if(exists.length > 0) {
          return;
        }
      }

      let endString = new Date(end * 1000).toLocaleTimeString();
      let beginString = new Date(begin * 1000).toLocaleTimeString();

      if(mac) {
        log.debug(util.format("Summing %s %s flows between %s and %s", mac, trafficDirection, beginString, endString));
      } else {
        log.debug(util.format("Summing all %s flows in the network between %s and %s", trafficDirection, beginString, endString));
      }

      let ticks = this.getTicks(begin, end, interval);
      let tickKeys = null

      if(mac) {
        tickKeys = ticks.map((tick) => this.getFlowKey(mac, trafficDirection, interval, tick));
      } else {
        // * is a hack code here, in redis, it means matching everything during keys command
        tickKeys = ticks.map((tick) => {
          let keyPattern = this.getFlowKey('*', trafficDirection, interval, tick);
          log.debug("Checking key pattern:", keyPattern);
          let keys = await (rclient.keysAsync(keyPattern));
          return keys;
        }).reduce((a,b) => a.concat(b), []); // reduce version of flatMap
      }

      let num = tickKeys.length;

      if(num <= 0) {
        log.warn("Nothing to sum for key", sumFlowKey, {});

        // add a placeholder in redis to avoid duplicated queries
        await (rclient.zaddAsync(sumFlowKey, 0, '_'));
        return Promise.resolve();
      }

      // ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
      let args = [sumFlowKey, num];
      args.push.apply(args, tickKeys);

      log.debug("zunionstore args: ", args, {});

      if(options.skipIfExists) {
        let exists = await(rclient.keysAsync(sumFlowKey));
        if(exists.length > 0) {
          return;
        }
      }

      let result = await (rclient.zunionstoreAsync(args));
      if(result > 0) {
        await(this.setLastSumFlow(mac, trafficDirection, sumFlowKey))
        await(rclient.expireAsync(sumFlowKey, expire))
        await(this.trimSumFlow(trafficDirection, options))
      }

      return Promise.resolve(result);
    })();
  }

  setLastSumFlow(mac, trafficDirection, keyName) {
    let key = util.format("lastsumflow:%s:%s", mac, trafficDirection);
    return rclient.setAsync(key, keyName);
  }

  getLastSumFlow(mac, trafficDirection) {
    let key = util.format("lastsumflow:%s:%s", mac, trafficDirection);
    return rclient.getAsync(key);
  }

  getSumFlow(mac, trafficDirection, begin, end, count) {
    let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

    return rclient.zrangeAsync(sumFlowKey, 0, count, 'withscores');
  }

  getTopSumFlowByKey(key, count) {
    // ZREVRANGEBYSCORE sumflow:B4:0B:44:9F:C1:1A:download:1501075800:1501162200 +inf 0  withscores limit 0 20
    return async(() => {
      let destAndScores = await (rclient.zrevrangebyscoreAsync(key, '+inf', 0, 'withscores', 'limit', 0, count));
      let results = [];
      for(let i = 0; i < destAndScores.length; i++) {
        if(i % 2 === 1) {
          let payload = destAndScores[i-1];
          let count = destAndScores[i];
          if(payload !== '_' && count !== 0) {
            try {
              let json = JSON.parse(payload);
              results.push({ip: json.destIP, device: json.device, count: count});
            } catch(err) {
              log.error("Failed to parse payload: ", payload, {});
            }
          }
        }
      }
      return results;
    })();
  }

  getAppActivitySumFlowByKey(key, count) {
    return this.getXActivitySumFlowByKey(key, 'app', count)
  }

  getCategoryActivitySumFlowByKey(key, count) {
    return this.getXActivitySumFlowByKey(key, 'category', count)
  }

  getXActivitySumFlowByKey(key, x, count) {
    // ZREVRANGEBYSCORE sumflow:B4:0B:44:9F:C1:1A:download:1501075800:1501162200 +inf 0  withscores limit 0 20
    return async(() => {
      let appAndScores = await (rclient.zrevrangebyscoreAsync(key, '+inf', 0, 'withscores', 'limit', 0, count));
      let results = [];
      for(let i = 0; i < appAndScores.length; i++) {
        if(i % 2 === 1) {
          let payload = appAndScores[i-1];
          let count = appAndScores[i];
          if(payload !== '_' && count !== 0) {
            try {
              let json = JSON.parse(payload);
              let result = {}
              result[x] = json[x]
              result.device = json.device
              result.count = count
              results.push(result)
            } catch(err) {
              log.error("Failed to parse payload: ", payload, {});
            }
          }
        }
      }
      return results;
    })();
  }

  getTopSumFlow(mac, trafficDirection, begin, end, count) {
    let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

    return this.getTopSumFlowByKey(sumFlowKey, count);
  }

  removeAllSumFlows(mac, trafficDirection) {
    let keyPattern = util.format("sumflow:%s:%s:*", mac, trafficDirection);

    return async(() => {
      let keys = await (rclient.keysAsync(keyPattern));
      keys.forEach((key) => {
        await (rclient.delAsync(key))
      })
    })();
  }

  getFlowTrafficByDestIP(mac, trafficDirection, interval, ts, destIP) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);

    // MUST device first, destIP second!!
    return rclient.zscoreAsync(key, JSON.stringify({device:mac, destIP: destIP}));
  }

  getActivityFlowTrafficByActivity(mac, interval, ts, app) {
    let key = this.getFlowKey(mac, "app", interval, ts);

    return async(() => {
      let xx = await (rclient.zrangeAsync(key, 0, -1, 'withscores'));
      let score = await (rclient.zscoreAsync(key, JSON.stringify({device:mac, app: app})));
      return score;
    })();
    // MUST device first, destIP second!!
    return
  }

  getIntervalTick(ts, interval) {
    let i = toInt(interval);
    return toInt(ts / i) * i;
  }

  getLargerIntervalTick(ts, interval) {
    let i = toInt(interval);
    return toInt(ts / i) * i + i;
  }

  // Find all the tick points in this range
  getTicks(begin, end, interval) {
    let x = this.getLargerIntervalTick(begin, interval);
    let y = this.getIntervalTick(end, interval);

    let ticks = [];
    for(let i = x; i <= y; i += interval) {
      ticks.push(i);
    }

    return ticks;
  }
}

module.exports = FlowAggrTool;
<commit_msg>bug fix<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

let log = require('./logger.js')(__filename);

let redis = require('redis');
let rclient = redis.createClient();

let Promise = require('bluebird');
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);

let async = require('asyncawait/async');
let await = require('asyncawait/await');

let async2 = require('async');

let util = require('util');

let instance = null;

const MAX_FLOW_PER_AGGR = 2000
const MAX_FLOW_PER_SUM = 30000
const MAX_FLOW_PER_HOUR = 7000

const MIN_AGGR_TRAFFIC = 256
const MIN_SUM_TRAFFIC = 1024

function toInt(n){ return Math.floor(Number(n)); };

class FlowAggrTool {
  constructor() {
    if(!instance) {
      instance = this;
    }
    return instance;
  }

  toFloorInt(n) {
    return toInt(n);
  }

  getFlowKey(mac, trafficDirection, interval, ts) {
    return util.format("aggrflow:%s:%s:%s:%s", mac, trafficDirection, interval, ts);
  }

  getSumFlowKey(mac, trafficDirection, begin, end) {
    if(mac) {
      return util.format("sumflow:%s:%s:%s:%s", mac, trafficDirection, begin, end);
    } else {
      return util.format("syssumflow:%s:%s:%s", trafficDirection, begin, end);
    }
  }

  // aggrflow:<device_mac>:download:10m:<ts>
  flowExists(mac, trafficDirection, interval, ts) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.keysAsync(key)
      .then((results) => {
        return results.length === 1;
      })
  }

  // key: aggrflow:<device_mac>:<direction>:<interval>:<ts>
  // content: destination ip address
  // score: traffic size
  addFlow(mac, trafficDirection, interval, ts, destIP, traffic) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.zaddAsync(key, traffic, destIP);
  }

  addAppActivityFlows(mac, interval, ts, traffics, expire) {
    return this.addXActivityFlows(mac, "app", interval, ts, traffics, expire)
  }

  addCategoryActivityFlows(mac, interval, ts, traffics, expire) {
    return this.addXActivityFlows(mac, "category", interval, ts, traffics, expire)
  }

  addXActivityFlows(mac, x, interval, ts, traffics, expire) {
    expire = expire || 24 * 3600; // by default keep 24 hours

    let key = this.getFlowKey(mac, x, interval, ts);
    let args = [key];
    for(let t in traffics) {
      let duration = (traffics[t] && traffics[t]['duration']) || 0;
      args.push(duration)

      let payload = {}
      payload.device = mac
      payload[x] = t
      args.push(JSON.stringify(payload))
    }

    args.push(0);
    args.push("_"); // placeholder to keep key exists

    return rclient.zaddAsync(args)
      .then(() => {
        return rclient.expireAsync(key, expire)
      });
  }

  // this is to make sure flow data is not flooded enough to consume all memory
  trimFlow(mac, trafficDirection, interval, ts) {
    return async(() => {
      const key = this.getFlowKey(mac, trafficDirection, interval, ts);

      let count = await (rclient.zremrangebyrankAsync(key, 0, -1 * MAX_FLOW_PER_AGGR)) // only keep the MAX_FLOW_PER_SUM highest flows
      if(count > 0) {
        log.warn(`${count} flows are removed from ${key} for self protection`)
      }
    })()
  }

  addFlows(mac, trafficDirection, interval, ts, traffics, expire) {
    expire = expire || 24 * 3600; // by default keep 24 hours

    const length = Object.keys(traffics).length // number of dest ips in this aggr flow
    const key = this.getFlowKey(mac, trafficDirection, interval, ts);

    let args = [key];

    if(length > MAX_FLOW_PER_AGGR) { // self protection
      args.push(length)
      args.push(JSON.stringify({
        device: mac,
        destIP: "0.0.0.0"       // special ip address to indicate some flows were skipped due to overflow protection
      }))
    }

    for(let destIP in traffics) {
      let traffic = (traffics[destIP] && traffics[destIP][trafficDirection]) || 0;

      if(traffic < MIN_AGGR_TRAFFIC) {
        continue                // skip very small traffic
      }

      args.push(traffic)
      args.push(JSON.stringify({
        device: mac,
        destIP: destIP
      }))
    }

    args.push(0);
    args.push("_"); // placeholder to keep key exists

    return async(() => {
      await (rclient.zaddAsync(args))
      await (rclient.expireAsync(key, expire))
      await (this.trimFlow(mac, trafficDirection, interval, ts))
    })()
  }

  removeFlow(mac, trafficDirection, interval, ts, destIP) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.zremAsync(key, destIP);
  }

  removeFlowKey(mac, trafficDirection, interval, ts) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);
    return rclient.delAsync(key);
  }

  removeAllFlowKeys(mac, trafficDirection, interval) {
    let keyPattern = util.format("aggrflow:%s:%s:%s:*", mac, trafficDirection, interval);

    return async(() => {
      let keys = await (rclient.keysAsync(keyPattern));
      keys.forEach((key) => {
        await (rclient.delAsync(key))
      })
    })();
  }

  // this is to make sure flow data is not flooded enough to consume all memory
  trimSumFlow(trafficDirection, options) {
    return async(() => {
      if(!options.begin || !options.end) {
        return Promise.reject(new Error("Require begin and end"));
      }

      let begin = options.begin;
      let end = options.end;

      let max_flow = MAX_FLOW_PER_SUM

      if(end-begin < 4000) { // hourly sum
        max_flow = MAX_FLOW_PER_HOUR
      }

      let mac = options.mac; // if mac is undefined, by default it will scan over all machines

      let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

      let count = await (rclient.zremrangebyrankAsync(sumFlowKey, 0, -1 * max_flow)) // only keep the MAX_FLOW_PER_SUM highest flows
      if(count > 0) {
        log.warn(`${count} flows are removed from ${sumFlowKey} for self protection`)
      }
    })()
  }

  // sumflow:<device_mac>:download:<begin_ts>:<end_ts>
  // content: destination ip address
  // score: traffic size

  // interval is the interval of each aggr flow (aggrflow:...)
  addSumFlow(trafficDirection, options) {

    if(!options.begin || !options.end) {
      return Promise.reject(new Error("Require begin and end"));
    }

    let begin = options.begin;
    let end = options.end;

    // if working properly, sumflow should be refreshed in every 10 minutes
    let expire = options.expireTime || 24 * 60; // by default expire in 24 minutes
    let interval = options.interval || 600; // by default 10 mins

    let mac = options.mac; // if mac is undefined, by default it will scan over all machines

    let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

    return async(() => {

      if(options.skipIfExists) {
        let exists = await(rclient.keysAsync(sumFlowKey));
        if(exists.length > 0) {
          return;
        }
      }

      let endString = new Date(end * 1000).toLocaleTimeString();
      let beginString = new Date(begin * 1000).toLocaleTimeString();

      if(mac) {
        log.debug(util.format("Summing %s %s flows between %s and %s", mac, trafficDirection, beginString, endString));
      } else {
        log.debug(util.format("Summing all %s flows in the network between %s and %s", trafficDirection, beginString, endString));
      }

      let ticks = this.getTicks(begin, end, interval);
      let tickKeys = null

      if(mac) {
        tickKeys = ticks.map((tick) => this.getFlowKey(mac, trafficDirection, interval, tick));
      } else {
        // * is a hack code here, in redis, it means matching everything during keys command
        tickKeys = ticks.map((tick) => {
          let keyPattern = this.getFlowKey('*', trafficDirection, interval, tick);
          log.debug("Checking key pattern:", keyPattern);
          let keys = await (rclient.keysAsync(keyPattern));
          return keys;
        }).reduce((a,b) => a.concat(b), []); // reduce version of flatMap
      }

      let num = tickKeys.length;

      if(num <= 0) {
        log.warn("Nothing to sum for key", sumFlowKey, {});

        // add a placeholder in redis to avoid duplicated queries
        await (rclient.zaddAsync(sumFlowKey, 0, '_'));
        return Promise.resolve();
      }

      // ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
      let args = [sumFlowKey, num];
      args.push.apply(args, tickKeys);

      log.debug("zunionstore args: ", args, {});

      if(options.skipIfExists) {
        let exists = await(rclient.keysAsync(sumFlowKey));
        if(exists.length > 0) {
          return;
        }
      }

      let result = await (rclient.zunionstoreAsync(args));
      if(result > 0) {
        await(this.setLastSumFlow(mac, trafficDirection, sumFlowKey))
        await(rclient.expireAsync(sumFlowKey, expire))
        await(this.trimSumFlow(trafficDirection, options))
      }

      return Promise.resolve(result);
    })();
  }

  setLastSumFlow(mac, trafficDirection, keyName) {
    let key = util.format("lastsumflow:%s:%s", mac, trafficDirection);
    return rclient.setAsync(key, keyName);
  }

  getLastSumFlow(mac, trafficDirection) {
    let key = util.format("lastsumflow:%s:%s", mac, trafficDirection);
    return rclient.getAsync(key);
  }

  getSumFlow(mac, trafficDirection, begin, end, count) {
    let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

    return rclient.zrangeAsync(sumFlowKey, 0, count, 'withscores');
  }

  getTopSumFlowByKey(key, count) {
    // ZREVRANGEBYSCORE sumflow:B4:0B:44:9F:C1:1A:download:1501075800:1501162200 +inf 0  withscores limit 0 20
    return async(() => {
      let destAndScores = await (rclient.zrevrangebyscoreAsync(key, '+inf', 0, 'withscores', 'limit', 0, count));
      let results = [];
      for(let i = 0; i < destAndScores.length; i++) {
        if(i % 2 === 1) {
          let payload = destAndScores[i-1];
          let count = destAndScores[i];
          if(payload !== '_' && count !== 0) {
            try {
              let json = JSON.parse(payload);
              results.push({ip: json.destIP, device: json.device, count: count});
            } catch(err) {
              log.error("Failed to parse payload: ", payload, {});
            }
          }
        }
      }
      return results;
    })();
  }

  getAppActivitySumFlowByKey(key, count) {
    return this.getXActivitySumFlowByKey(key, 'app', count)
  }

  getCategoryActivitySumFlowByKey(key, count) {
    return this.getXActivitySumFlowByKey(key, 'category', count)
  }

  getXActivitySumFlowByKey(key, x, count) {
    // ZREVRANGEBYSCORE sumflow:B4:0B:44:9F:C1:1A:download:1501075800:1501162200 +inf 0  withscores limit 0 20
    return async(() => {
      let appAndScores = await (rclient.zrevrangebyscoreAsync(key, '+inf', 0, 'withscores', 'limit', 0, count));
      let results = [];
      for(let i = 0; i < appAndScores.length; i++) {
        if(i % 2 === 1) {
          let payload = appAndScores[i-1];
          let count = appAndScores[i];
          if(payload !== '_' && count !== 0) {
            try {
              let json = JSON.parse(payload);
              let result = {}
              result[x] = json[x]
              result.device = json.device
              result.count = count
              results.push(result)
            } catch(err) {
              log.error("Failed to parse payload: ", payload, {});
            }
          }
        }
      }
      return results;
    })();
  }

  getTopSumFlow(mac, trafficDirection, begin, end, count) {
    let sumFlowKey = this.getSumFlowKey(mac, trafficDirection, begin, end);

    return this.getTopSumFlowByKey(sumFlowKey, count);
  }

  removeAllSumFlows(mac, trafficDirection) {
    let keyPattern = util.format("sumflow:%s:%s:*", mac, trafficDirection);

    return async(() => {
      let keys = await (rclient.keysAsync(keyPattern));
      keys.forEach((key) => {
        await (rclient.delAsync(key))
      })
    })();
  }

  getFlowTrafficByDestIP(mac, trafficDirection, interval, ts, destIP) {
    let key = this.getFlowKey(mac, trafficDirection, interval, ts);

    // MUST device first, destIP second!!
    return rclient.zscoreAsync(key, JSON.stringify({device:mac, destIP: destIP}));
  }

  getActivityFlowTrafficByActivity(mac, interval, ts, app) {
    let key = this.getFlowKey(mac, "app", interval, ts);

    return async(() => {
      let xx = await (rclient.zrangeAsync(key, 0, -1, 'withscores'));
      let score = await (rclient.zscoreAsync(key, JSON.stringify({device:mac, app: app})));
      return score;
    })();
    // MUST device first, destIP second!!
    return
  }

  getIntervalTick(ts, interval) {
    let i = toInt(interval);
    return toInt(ts / i) * i;
  }

  getLargerIntervalTick(ts, interval) {
    let i = toInt(interval);
    return toInt(ts / i) * i + i;
  }

  // Find all the tick points in this range
  getTicks(begin, end, interval) {
    let x = this.getLargerIntervalTick(begin, interval);
    let y = this.getIntervalTick(end, interval);

    let ticks = [];
    for(let i = x; i <= y; i += interval) {
      ticks.push(i);
    }

    return ticks;
  }
}

module.exports = FlowAggrTool;
<|endoftext|>


<commit_before>var configParser = require('./lib/configXmlParser.js');
var fs = require('fs-extra');
var gm = require('gm');

module.exports = function(ctx) {
    run(ctx);
};

function run(cordovaContext) {
    // Retrieve overlay image from config.xml or provide fallback image with plugin
    var alphaOverlay = configParser.getIconOverlay(cordovaContext, 'alpha');
    var betaOverlay = configParser.getIconOverlay(cordovaContext, 'beta');

    // Retrieve platform icons declared in config.xml
    var platformIcons = configParser.getIcons(cordovaContext);
    if (platformIcons == null) {
        return;
    }

    // Loop through each platform and generate alpha/beta icons
    var installedPlatforms = cordovaContext.opts.platforms;
    installedPlatforms.forEach(function(installedPlatform) {
        // If the android platform is installed and there are icons declared in config.xml
       if (installedPlatform === 'android' && platformIcons.android) {
            generateAndroidIcons(platformIcons.android, 'alpha', alphaOverlay);
            generateAndroidIcons(platformIcons.android, 'beta', betaOverlay);
       }
    });
}

function generateAndroidIcons(androidIcons, variant, iconOverlay) {
    // Loop through each android icon declaration
    androidIcons.forEach(function(icon) {
        var iconSource = icon.src;
        var iconDestination = 'platforms/android/src/' + variant + '/res/drawable-' + icon.density + '/icon.png';

        // Get size of source icon
        gm(iconSource)
        .size(function(size_error, size) {
            if (!size_error) {
                var iconWidth = size.width;
                var iconHeight = size.height;
                // add icon overlay to source icon and write it to the variant destination
                gm(iconSource)
                .draw(['image over 0,0 ' + iconWidth + ','+iconHeight + ' "' + iconOverlay + '"'])
                .write(iconDestination, function(error){
                    if (error) {
                        console.log('Error overlaying ' + variant + ' icon ' + iconSource + ': ' + error);
                    } else {
                        console.log('Generated ' + variant + ' icon: ' + iconDestination);
                    }
                });
            }
        });
    });
}
<commit_msg>fixed non-existent folder<commit_after>var configParser = require('./lib/configXmlParser.js');
var fs = require('fs-extra');
var gm = require('gm');

module.exports = function(ctx) {
    run(ctx);
};

function run(cordovaContext) {
    // Retrieve overlay image from config.xml or provide fallback image with plugin
    var alphaOverlay = configParser.getIconOverlay(cordovaContext, 'alpha');
    var betaOverlay = configParser.getIconOverlay(cordovaContext, 'beta');

    // Retrieve platform icons declared in config.xml
    var platformIcons = configParser.getIcons(cordovaContext);
    if (platformIcons === null) {
        return;
    }

    // Loop through each platform and generate alpha/beta icons
    var installedPlatforms = cordovaContext.opts.platforms;
    installedPlatforms.forEach(function(installedPlatform) {
        // If the android platform is installed and there are icons declared in config.xml
       if (installedPlatform === 'android' && platformIcons.android) {
            generateAndroidIcons(platformIcons.android, 'alpha', alphaOverlay);
            generateAndroidIcons(platformIcons.android, 'beta', betaOverlay);
       }
    });
}

function generateAndroidIcons(androidIcons, variant, iconOverlay) {
    // Loop through each android icon declaration
    androidIcons.forEach(function(icon) {
        var iconSource = icon.src;
        var iconDestinationFolder = 'platforms/android/src/' + variant + '/res/drawable-' + icon.density + '/';

        // Create destination directory
        fs.ensureDir(iconDestinationFolder, function(dir_error) {
            if (!dir_error) {
                // Get size of source icon
                gm(iconSource)
                .size(function(size_error, size) {
                    if (!size_error) {
                        var iconWidth = size.width;
                        var iconHeight = size.height;
                        // add icon overlay to source icon and write it to the variant destination
                        gm(iconSource)
                        .draw(['image over 0,0 ' + iconWidth + ','+iconHeight + ' "' + iconOverlay + '"'])
                        .write(iconDestinationFolder + 'icon.png', function(error){
                            if (error) {
                                console.log('Error overlaying ' + variant + ' icon ' + iconSource + ': ' + error);
                            } else {
                                console.log('Generated ' + variant + ' icon: ' + iconDestinationFolder + 'icon.png');
                            }
                        });
                    } else {
                        console.log('Error getting size of source icons: ' + size_error);
                    }
                });
            } else {
                console.log('Error creating destination directory: ' + dir_error);
            }
        });
    });
}
<|endoftext|>