<commit_before>var config = {
    'client_id': 'dbe12e2ba0eb420f97f0a9af9ace03af',
    'access_token_link': 'http://localhost:2424/step_two'
};

var app = (function (config) {
    var _app_id = chrome.runtime.id;
    var _redirect_uri = 'https://' + _app_id + '.chromiumapp.org/oauth-redirect.html';
    var _oauth_link = 'https://api.instagram.com/oauth/authorize/?client_id=' + config.client_id + '&redirect_uri=' + encodeURIComponent(_redirect_uri) + '&response_type=code';

    var _user_data = {};

    var _fn_extractParamFromUrl = function (url, id) {
        var exp = new RegExp(id + "=([^&#=]*)"),
            res = exp.exec(url);

        if(res.length > 1) {
            return res[1];
        }

        return null;
    };

    var _fn_authenticate = function (callback) {
        console.log('authenticate starts here');

        chrome.identity.launchWebAuthFlow({
            'url': _oauth_link,
            'interactive': true
        }, function (redirect_url) {

            if(redirect_url === undefined) {
                // TODO: Handle this later
                console.log('redirect_url is undefined');
                return;
            }

            var code = _fn_extractParamFromUrl(redirect_url, 'code');
            if(code === null) {
                console.log('code param not received');
                return;
            }

            _fn_handle_oauth_second_step(code, callback);
        });
    };

    var _fn_handle_oauth_second_step = function (code, callback) {
        var url = config.access_token_link + '?code=' + code;
        var onsuccess = function (access_data) {
            var accessJSON = JSON.parse(access_data);

            _user_data.access_token = accessJSON.access_token;
            _user_data.username = accessJSON.user.username;
            _user_data.full_name = accessJSON.user.full_name;
            _user_data.profile_picture = accessJSON.user.profile_picture;

            chrome.storage.local.set({'user_data': _user_data}, function () {
                console.log('stored user data in storage');

                if(callback) {
                    callback({'result': 'OK'});
                }

            });

        };
        var onfail = function (status, msg) {
            console.log('error: status ' + status);
            console.log('error: msg' + msg);

            if(callback) {
                callback({'result': 'ERR', 'status': status, 'msg': msg});
            }

        };

        _fn_sendHttpRequest('GET', url, null, onsuccess, onfail);
    };

    var _fn_sendHttpRequest = function(method, url, params, onsuccess, onfail) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, url, true);

        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

        xhr.onreadystatechange = function () {

            if(xhr.readyState === 4) {
                if(xhr.status === 200) {
                    onsuccess(xhr.responseText);
                } else {
                    onfail(xhr.status, xhr.responseText);
                }
            }

        };

        xhr.send(params);
    };

    var _fn_api = function (path, callback) {
        var method = 'GET';
        var url = 'https://api.instagram.com/v1' + path;
        params = 'access_token=' + encodeURIComponent(_user_data.access_token);
        url += '?' + params;

        var onsuccess = function(response) {
            var responseJSON = JSON.parse(response);
            callback(responseJSON);
        };
        var onfail = function (status, response) {
            console.log(status, response);
        };

        _fn_sendHttpRequest(method, url, null, onsuccess, onfail);
    };

    var _fn_setup = function () {
        chrome.storage.local.get('user_data', function (res) {
            _user_data = res.user_data;
        });
    };

    return {
        authenticate: _fn_authenticate,
        api: _fn_api,
        setup: _fn_setup
    };
})(config);

chrome.extension.onRequest.addListener(function (request, tab, sendRequest) {
    if(request.method === 'auth') {
        app.authenticate(function (result) {
            sendRequest(result);
        });
    }

    if(request.method === 'api') {
        var path = request.path;
        app.api(path, function (result) {
            sendRequest(result);
        });
    }
});

app.setup();<commit_msg>fixed: _user_data is set to undefined on setup<commit_after>var config = {
    'client_id': 'dbe12e2ba0eb420f97f0a9af9ace03af',
    'access_token_link': 'http://localhost:2424/step_two'
};

var app = (function (config) {
    var _app_id = chrome.runtime.id;
    var _redirect_uri = 'https://' + _app_id + '.chromiumapp.org/oauth-redirect.html';
    var _oauth_link = 'https://api.instagram.com/oauth/authorize/?client_id=' + config.client_id + '&redirect_uri=' + encodeURIComponent(_redirect_uri) + '&response_type=code';

    var _user_data = {};

    var _fn_extractParamFromUrl = function (url, id) {
        var exp = new RegExp(id + "=([^&#=]*)"),
            res = exp.exec(url);

        if(res.length > 1) {
            return res[1];
        }

        return null;
    };

    var _fn_authenticate = function (callback) {
        console.log('authenticate starts here');

        chrome.identity.launchWebAuthFlow({
            'url': _oauth_link,
            'interactive': true
        }, function (redirect_url) {

            if(redirect_url === undefined) {
                // TODO: Handle this later
                console.log('redirect_url is undefined');
                return;
            }

            var code = _fn_extractParamFromUrl(redirect_url, 'code');
            if(code === null) {
                console.log('code param not received');
                return;
            }

            _fn_handle_oauth_second_step(code, callback);
        });
    };

    var _fn_handle_oauth_second_step = function (code, callback) {
        var url = config.access_token_link + '?code=' + code;
        var onsuccess = function (access_data) {
            var accessJSON = JSON.parse(access_data);

            _user_data.access_token = accessJSON.access_token;
            _user_data.username = accessJSON.user.username;
            _user_data.full_name = accessJSON.user.full_name;
            _user_data.profile_picture = accessJSON.user.profile_picture;

            chrome.storage.local.set({'user_data': _user_data}, function () {
                console.log('stored user data in storage');

                if(callback) {
                    callback({'result': 'OK'});
                }

            });

        };
        var onfail = function (status, msg) {
            console.log('error: status ' + status);
            console.log('error: msg' + msg);

            if(callback) {
                callback({'result': 'ERR', 'status': status, 'msg': msg});
            }

        };

        _fn_sendHttpRequest('GET', url, null, onsuccess, onfail);
    };

    var _fn_sendHttpRequest = function(method, url, params, onsuccess, onfail) {
        var xhr = new XMLHttpRequest();
        xhr.open(method, url, true);

        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

        xhr.onreadystatechange = function () {

            if(xhr.readyState === 4) {
                if(xhr.status === 200) {
                    onsuccess(xhr.responseText);
                } else {
                    onfail(xhr.status, xhr.responseText);
                }
            }

        };

        xhr.send(params);
    };

    var _fn_api = function (path, callback) {
        var method = 'GET';
        var url = 'https://api.instagram.com/v1' + path;
        params = 'access_token=' + encodeURIComponent(_user_data.access_token);
        url += '?' + params;

        var onsuccess = function(response) {
            var responseJSON = JSON.parse(response);
            callback(responseJSON);
        };
        var onfail = function (status, response) {
            console.log(status, response);
        };

        _fn_sendHttpRequest(method, url, null, onsuccess, onfail);
    };

    var _fn_setup = function () {
        chrome.storage.local.get('user_data', function (res) {
            if(res.user_data) {
                _user_data = res.user_data;
            }
        });
    };

    return {
        authenticate: _fn_authenticate,
        api: _fn_api,
        setup: _fn_setup
    };
})(config);

chrome.extension.onRequest.addListener(function (request, tab, sendRequest) {
    if(request.method === 'auth') {
        app.authenticate(function (result) {
            sendRequest(result);
        });
    }

    if(request.method === 'api') {
        var path = request.path;
        app.api(path, function (result) {
            sendRequest(result);
        });
    }
});

app.setup();<|endoftext|>


<commit_before>(function () {
    'use strict';

    const ds = require('data-service');
    const { isEmpty } = require('ts-utils');

    /**
     * @param Base
     * @param {ng.IAugmentedJQuery} $element
     * @param {app.utils} utils
     * @param {Waves} waves
     * @return {AssetLogo}
     */
    const controller = function (Base, $element, utils, waves) {

        class AssetLogo extends Base {

            /**
             * @type {string}
             */
            assetId;
            /**
             * @type {string}
             */
            assetName;
            /**
             * @type {number}
             */
            size;
            /**
             * @type {boolean}
             */
            hasScript;
            /**
             * @type {boolean}
             */
            isNew = false;
            /**
             * @type {boolean}
             * @private
             */
            _canPayFee = false;
            /**
             * @type {boolean}
             * @private
             */
            _isSmart = false;

            constructor() {
                super();

                this.observe('_canPayFee', this._onChangeCanPayFee);
                this.observe(['_isSmart', 'hasScript'], this._onChangeIsSmart);
            }

            $postLink() {
                if (!this.size) {
                    throw new Error('Wrong params!');
                }

                if (!this.isNew) {
                    if (!(this.assetName || this.assetId)) {
                        throw new Error('Wrong params!');
                    }

                    this._canPayFee = !!ds.utils.getTransferFeeList()
                        .find(money => money.asset.id === this.assetId);

                    if (this.assetId) {
                        waves.node.assets.getAsset(this.assetId).then(asset => {
                            this._isSmart = asset.hasScript;
                        });
                    }
                }

                $element.find('.asset-logo__logo')
                    .css({
                        width: `${this.size}px`,
                        height: `${this.size}px`
                    });

                this._addLogo();
            }

            /**
             * @private
             */
            _addLogo() {
                if (!this.isNew && this.assetId) {
                    const { logo } = utils.getDataFromOracles(this.assetId);

                    if (logo) {
                        $element.find('.asset-logo__logo')
                            .addClass('asset-logo_custom')
                            .css('backgroundImage', `url(${logo})`);
                        return null;
                    }

                    waves.node.assets.getAsset(this.assetId)
                        .then((asset) => {
                            const logo = utils.getAssetLogo(this.assetId);

                            if (logo) {
                                utils.loadImage(logo)
                                    .then(() => {
                                        $element.find('.asset-logo__logo')
                                            .css('backgroundImage', `url(${logo})`);
                                    })
                                    .catch(() => this._addLetter(asset.name));
                            } else {
                                this._addLetter(asset.name);
                            }

                            const vostok = 'AMFteLfPzPhTsFc3NfvHG7fSRUnsp3tJXPH88G1PCisT';
                            if (this.assetId === vostok) {
                                $element.find('.asset-logo__logo')
                                    .addClass('asset-logo__logo_vostok');
                            }
                        });
                } else {
                    $element.addClass('asset-logo_new');
                    this.observe(['assetName', 'assetId'], () => this._addLetter(this.assetName));
                    this._addLetter(this.assetName);
                }
            }

            /**
             * @param {string} name
             * @private
             */
            _addLetter(name) {
                const letter = name.charAt(0).toUpperCase();
                const color = utils.getAssetLogoBackground(this.assetId);
                const fontSize = Math.round((Number(this.size) || 0) * 0.43);

                $element.find('.asset-logo__logo, .asset-logo__marker')
                    .css({
                        'background-color': color
                    });

                $element.find('.asset-logo__letter')
                    .text(letter)
                    .css({
                        'font-size': `${fontSize}px`
                    });
            }

            /**
             * @private
             */
            _onChangeCanPayFee() {
                $element.find('.asset-logo__marker').toggleClass('asset-logo__marker_sponsored', this._canPayFee);
            }

            /**
             * @private
             */
            _onChangeIsSmart() {
                const isSmart = isEmpty(this.hasScript) ? this._isSmart : this.hasScript;
                $element.find('.asset-logo__marker').toggleClass('asset-logo__marker_smart', isSmart);
            }

        }

        return new AssetLogo();
    };

    controller.$inject = ['Base', '$element', 'utils', 'waves'];

    angular.module('app.ui')
        .component('wAssetLogo', {
            templateUrl: 'modules/ui/directives/assetLogo/asset-logo.html',
            controller: controller,
            bindings: {
                assetId: '<',
                hasScript: '<',
                assetName: '<',
                isNew: '<',
                size: '@'
            }
        });
})();
<commit_msg>DEXW-2074: fix id<commit_after>(function () {
    'use strict';

    const ds = require('data-service');
    const { isEmpty } = require('ts-utils');

    /**
     * @param Base
     * @param {ng.IAugmentedJQuery} $element
     * @param {app.utils} utils
     * @param {Waves} waves
     * @return {AssetLogo}
     */
    const controller = function (Base, $element, utils, waves) {

        class AssetLogo extends Base {

            /**
             * @type {string}
             */
            assetId;
            /**
             * @type {string}
             */
            assetName;
            /**
             * @type {number}
             */
            size;
            /**
             * @type {boolean}
             */
            hasScript;
            /**
             * @type {boolean}
             */
            isNew = false;
            /**
             * @type {boolean}
             * @private
             */
            _canPayFee = false;
            /**
             * @type {boolean}
             * @private
             */
            _isSmart = false;

            constructor() {
                super();

                this.observe('_canPayFee', this._onChangeCanPayFee);
                this.observe(['_isSmart', 'hasScript'], this._onChangeIsSmart);
            }

            $postLink() {
                if (!this.size) {
                    throw new Error('Wrong params!');
                }

                if (!this.isNew) {
                    if (!(this.assetName || this.assetId)) {
                        throw new Error('Wrong params!');
                    }

                    this._canPayFee = !!ds.utils.getTransferFeeList()
                        .find(money => money.asset.id === this.assetId);

                    if (this.assetId) {
                        waves.node.assets.getAsset(this.assetId).then(asset => {
                            this._isSmart = asset.hasScript;
                        });
                    }
                }

                $element.find('.asset-logo__logo')
                    .css({
                        width: `${this.size}px`,
                        height: `${this.size}px`
                    });

                this._addLogo();
            }

            /**
             * @private
             */
            _addLogo() {
                if (!this.isNew && this.assetId) {

                    if (this.assetId === WavesApp.defaultAssets.VST) {
                        $element.find('.asset-logo__logo')
                            .addClass('asset-logo__logo_vostok');
                    }

                    const { logo } = utils.getDataFromOracles(this.assetId);

                    if (logo) {
                        $element.find('.asset-logo__logo')
                            .addClass('asset-logo_custom')
                            .css('backgroundImage', `url(${logo})`);
                        return null;
                    }

                    waves.node.assets.getAsset(this.assetId)
                        .then((asset) => {
                            const logo = utils.getAssetLogo(this.assetId);

                            if (logo) {
                                utils.loadImage(logo)
                                    .then(() => {
                                        $element.find('.asset-logo__logo')
                                            .css('backgroundImage', `url(${logo})`);
                                    })
                                    .catch(() => this._addLetter(asset.name));
                            } else {
                                this._addLetter(asset.name);
                            }
                        });
                } else {
                    $element.addClass('asset-logo_new');
                    this.observe(['assetName', 'assetId'], () => this._addLetter(this.assetName));
                    this._addLetter(this.assetName);
                }
            }

            /**
             * @param {string} name
             * @private
             */
            _addLetter(name) {
                const letter = name.charAt(0).toUpperCase();
                const color = utils.getAssetLogoBackground(this.assetId);
                const fontSize = Math.round((Number(this.size) || 0) * 0.43);

                $element.find('.asset-logo__logo, .asset-logo__marker')
                    .css({
                        'background-color': color
                    });

                $element.find('.asset-logo__letter')
                    .text(letter)
                    .css({
                        'font-size': `${fontSize}px`
                    });
            }

            /**
             * @private
             */
            _onChangeCanPayFee() {
                $element.find('.asset-logo__marker').toggleClass('asset-logo__marker_sponsored', this._canPayFee);
            }

            /**
             * @private
             */
            _onChangeIsSmart() {
                const isSmart = isEmpty(this.hasScript) ? this._isSmart : this.hasScript;
                $element.find('.asset-logo__marker').toggleClass('asset-logo__marker_smart', isSmart);
            }

        }

        return new AssetLogo();
    };

    controller.$inject = ['Base', '$element', 'utils', 'waves'];

    angular.module('app.ui')
        .component('wAssetLogo', {
            templateUrl: 'modules/ui/directives/assetLogo/asset-logo.html',
            controller: controller,
            bindings: {
                assetId: '<',
                hasScript: '<',
                assetName: '<',
                isNew: '<',
                size: '@'
            }
        });
})();
<|endoftext|>


<commit_before>function Settings () {
    this.blocklist = [];
    this.time_limits = {};
    this.elapsed_times = {};
    this.timerbutton_elapsed_times = {};
    this.default_time_limit = 600;
    this.restore_url = "";
    this.show_badge = true;
    this.loaded = false;
}

Settings.prototype.load_settings = function () {

    function get_JSON_settings (name) {
        if (localStorage.getItem(name) === "undefined") {
            return {};
        }
        if (name in localStorage === false)
            return {};
        var stringified_settings = localStorage[name];
        var setting = JSON.parse(stringified_settings);
        return setting;
        }

    //console.log("load_settings()");

    this.time_limits = get_JSON_settings('timesink_urls');
    for (url in this.time_limits) {
        this.blocklist.push(url);
        if (this.loaded === false || url in this.elapsed_times === false)
            this.elapsed_times[url] = 0;
    }

    var extra_settings = get_JSON_settings('extra_settings');
    if ('show_badge' in extra_settings && 'default_time' in extra_settings) {
        this.default_time_limit = extra_settings['default_time'] * 60;
        this.show_badge = extra_settings['show_badge'];
    }

    this.loaded = true;
}

Settings.prototype.save_settings = function (name, setting) {
        var stringified_settings = JSON.stringify(setting);
        localStorage.setItem(name, stringified_settings);
}

function get_location(url) {
    var link = document.createElement("a");
    link.href = url;
    return link
}

settings = new Settings();
settings.load_settings();

// Listen for messages from other pages.
chrome.extension.onRequest.addListener(
    function(request, sender, sendResponse) {

        // Messages from popup
        if (request.show_settings === true) {
            var options_url = chrome.extension.getURL('options.html')
            chrome.tabs.create({url: options_url });
        }

        if (request.add_page === true) {
            //console.log("Adding page to timesinks!");
            chrome.tabs.query({active: true}, function (tab_array) {
                var tab = tab_array[0];
                var active_hostname = get_location(tab.url).hostname;
                //console.log(active_hostname);
                settings.time_limits[active_hostname] = settings.default_time_limit;
                settings.save_settings('timesink_urls', settings.time_limits);
                settings.load_settings();
                chrome.tabs.reload(tab.id);
            });
        }

        if (request.start_timer === true) {
            //console.log("Timer started!");
            chrome.tabs.query({active: true}, function (tab_array) {
                var tab = tab_array[0];
                var active_hostname = get_location(tab.url).hostname;
                settings.timerbutton_elapsed_times[active_hostname] = 0;
            });
        }

        // Messages from options page
        if (request.update_settings === true) {
            settings.load_settings();
        }
});

// Listen for connections from other scripts
chrome.extension.onConnect.addListener(function(port) {
    port.onMessage.addListener(function(msg) {

        // Connections from timeup page:
        if (msg.close_tabs === true) {
            chrome.tabs.remove(port.sender.tab.id);
        }

        if (msg.restart_timer === true) {
            //console.log("restarting timer");
            //console.log(settings.restore_url);
            var reset_hostname = get_location(settings.restore_url).hostname;
            //console.log(reset_hostname);
            settings.elapsed_times[reset_hostname] = 0;
        }
    });
});

chrome.tabs.onUpdated.addListener(function(tab, changeInfo) {
    if(changeInfo.status === 'complete') {
        chrome.tabs.query({active: true}, function(tab_array) {
            var tab = tab_array[0];
            var parsed_url = get_location(tab.url);
            if (settings.blocklist.indexOf(parsed_url.hostname) !== -1) {
                update_icon("on");
            } else {
                update_icon("off");
            }
        });
    }
 });

var check_interval = 10000;
var check_interval_set = false;
set_check_interval();

function set_check_interval () {
    // Check for current tab time every check_interval milliseconds.
    //console.log("set_check_interval()");
    if (check_interval_set === false) {
        check_interval_set = true;
        var checktime_interval_id = setInterval(check_times, check_interval);
    }
}

function check_times () {
    //console.log("check_times()");
    chrome.tabs.query({active: true}, function(tab_array) {

        var tab = tab_array[0];
        var parsed_url = get_location(tab.url);

        if(tab.status === 'complete'
            && /^(https?):/.test(tab.url)
            && (settings.blocklist.indexOf(parsed_url.hostname) !== -1)) {
            update_icon("on");
            update_times(tab, parsed_url.hostname);
        } else {
            update_icon("off");
        }

    });
}

function update_icon(icon_status) {
    //console.log("update_icon()");
    chrome.browserAction.setIcon({path: "img/" + icon_status + ".png"});
    if (icon_status === "off") {
        chrome.browserAction.setBadgeText({text: ""}); }
}

function update_times (tab, hostname) {
    //console.log("update_times()");

    var time_limit = settings.time_limits[hostname];
    var time_elapsed = settings.elapsed_times[hostname];
    settings.elapsed_times[hostname] = time_elapsed + (check_interval / 1000)
    console.log("Time left: ", time_limit - time_elapsed);
    //console.log(settings.elapsed_times[response.url], settings.time_limits[response.url]);

    var badge_string = '';
    if (settings.show_badge === true) {
        var badge_string = Math.round((time_limit - time_elapsed)/60).toString();
        //console.log(badge_string, time_limit, time_elapsed);
    }

    chrome.browserAction.setBadgeBackgroundColor({color: [99,99,99,255]});
    chrome.browserAction.setBadgeText({text: badge_string, tabId: tab.id});

    if (time_elapsed > time_limit) {
        //console.log("Time's up!");
        settings.restore_url = tab.url;
        var timeup_url = chrome.extension.getURL('timeup.html');
        update_icon("off");
        chrome.tabs.update({url: timeup_url});
    }
}
<commit_msg>Added tabs.onClose listener to turn icon off.<commit_after>function Settings () {
    this.blocklist = [];
    this.time_limits = {};
    this.elapsed_times = {};
    this.timerbutton_elapsed_times = {};
    this.default_time_limit = 600;
    this.restore_url = "";
    this.show_badge = true;
    this.loaded = false;
}

Settings.prototype.load_settings = function () {

    function get_JSON_settings (name) {
        if (localStorage.getItem(name) === "undefined") {
            return {};
        }
        if (name in localStorage === false)
            return {};
        var stringified_settings = localStorage[name];
        var setting = JSON.parse(stringified_settings);
        return setting;
        }

    //console.log("load_settings()");

    this.time_limits = get_JSON_settings('timesink_urls');
    for (url in this.time_limits) {
        this.blocklist.push(url);
        if (this.loaded === false || url in this.elapsed_times === false)
            this.elapsed_times[url] = 0;
    }

    var extra_settings = get_JSON_settings('extra_settings');
    if ('show_badge' in extra_settings && 'default_time' in extra_settings) {
        this.default_time_limit = extra_settings['default_time'] * 60;
        this.show_badge = extra_settings['show_badge'];
    }

    this.loaded = true;
}

Settings.prototype.save_settings = function (name, setting) {
        var stringified_settings = JSON.stringify(setting);
        localStorage.setItem(name, stringified_settings);
}

function get_location(url) {
    var link = document.createElement("a");
    link.href = url;
    return link
}

settings = new Settings();
settings.load_settings();

// Listen for messages from other pages.
chrome.extension.onRequest.addListener(
    function(request, sender, sendResponse) {

        // Messages from popup
        if (request.show_settings === true) {
            var options_url = chrome.extension.getURL('options.html')
            chrome.tabs.create({url: options_url });
        }

        if (request.add_page === true) {
            //console.log("Adding page to timesinks!");
            chrome.tabs.query({active: true}, function (tab_array) {
                var tab = tab_array[0];
                var active_hostname = get_location(tab.url).hostname;
                //console.log(active_hostname);
                settings.time_limits[active_hostname] = settings.default_time_limit;
                settings.save_settings('timesink_urls', settings.time_limits);
                settings.load_settings();
                chrome.tabs.reload(tab.id);
            });
        }

        if (request.start_timer === true) {
            //console.log("Timer started!");
            chrome.tabs.query({active: true}, function (tab_array) {
                var tab = tab_array[0];
                var active_hostname = get_location(tab.url).hostname;
                settings.timerbutton_elapsed_times[active_hostname] = 0;
            });
        }

        // Messages from options page
        if (request.update_settings === true) {
            settings.load_settings();
        }
});

// Listen for connections from other scripts
chrome.extension.onConnect.addListener(function(port) {
    port.onMessage.addListener(function(msg) {

        // Connections from timeup page:
        if (msg.close_tabs === true) {
            chrome.tabs.remove(port.sender.tab.id);
        }

        if (msg.restart_timer === true) {
            //console.log("restarting timer");
            //console.log(settings.restore_url);
            var reset_hostname = get_location(settings.restore_url).hostname;
            //console.log(reset_hostname);
            settings.elapsed_times[reset_hostname] = 0;
        }
    });
});

// Listen for closed tabs
chrome.tabs.onRemoved.addListener(function(tab) {
    update_icon("off");
});

// Listen for updated tabs
chrome.tabs.onUpdated.addListener(function(tab, changeInfo) {
    if(changeInfo.status === 'complete') {
        chrome.tabs.query({active: true}, function(tab_array) {
            var tab = tab_array[0];
            var parsed_url = get_location(tab.url);
            if (settings.blocklist.indexOf(parsed_url.hostname) !== -1) {
                update_icon("on");
            } else {
                update_icon("off");
            }
        });
    }
 });

var check_interval = 10000;
var check_interval_set = false;
set_check_interval();

function set_check_interval () {
    // Check for current tab time every check_interval milliseconds.
    //console.log("set_check_interval()");
    if (check_interval_set === false) {
        check_interval_set = true;
        var checktime_interval_id = setInterval(check_times, check_interval);
    }
}

function check_times () {
    //console.log("check_times()");
    chrome.tabs.query({active: true}, function(tab_array) {

        var tab = tab_array[0];
        var parsed_url = get_location(tab.url);

        if(tab.status === 'complete'
            && /^(https?):/.test(tab.url)
            && (settings.blocklist.indexOf(parsed_url.hostname) !== -1)) {
            update_icon("on");
            update_times(tab, parsed_url.hostname);
        } else {
            update_icon("off");
        }

    });
}

function update_icon(icon_status) {
    //console.log("update_icon()");
    chrome.browserAction.setIcon({path: "img/" + icon_status + ".png"});
    if (icon_status === "off") {
        chrome.browserAction.setBadgeText({text: ""}); }
}

function update_times (tab, hostname) {
    //console.log("update_times()");

    var time_limit = settings.time_limits[hostname];
    var time_elapsed = settings.elapsed_times[hostname];
    settings.elapsed_times[hostname] = time_elapsed + (check_interval / 1000)
    console.log("Time left: ", time_limit - time_elapsed);
    //console.log(settings.elapsed_times[response.url], settings.time_limits[response.url]);

    var badge_string = '';
    if (settings.show_badge === true) {
        var badge_string = Math.round((time_limit - time_elapsed)/60).toString();
        //console.log(badge_string, time_limit, time_elapsed);
    }

    chrome.browserAction.setBadgeBackgroundColor({color: [99,99,99,255]});
    chrome.browserAction.setBadgeText({text: badge_string, tabId: tab.id});

    if (time_elapsed > time_limit) {
        //console.log("Time's up!");
        settings.restore_url = tab.url;
        var timeup_url = chrome.extension.getURL('timeup.html');
        update_icon("off");
        chrome.tabs.update({url: timeup_url});
    }
}
<|endoftext|>


<commit_before>import React from 'react';
import markdown from './README.mdx';
import AnchorNavigation from '.';
import Link from '../Link';

export default {
  title: 'Components/Navigations/AnchorNavigation',
  component: AnchorNavigation,
  parameters: {
    componentSubtitle: 'Component',
    status: 'experimental',
    mdx: markdown,
  },
};

export const RegularAnchorNavigation = (args) => (
    <AnchorNavigation {...args}>
        <Link href="#">
            Item one
        </Link>
        <Link href="#">
            Item two
        </Link>
        <Link href="#">
            Item three
        </Link>
        <Link href="#">
            Item four
        </Link>
    </AnchorNavigation>
  );

RegularAnchorNavigation.args = {
    title:'Title'
}<commit_msg>docs(AnchorNavigation): review anchornavigation code snippet with minor details<commit_after>import React from 'react';
import markdown from './README.mdx';
import AnchorNavigation from '.';
import Link from '../Link';

export default {
  title: 'Components/Navigations/AnchorNavigation',
  component: AnchorNavigation,
  parameters: {
    componentSubtitle: 'Component',
    status: 'experimental',
    mdx: markdown,
  },
};

export const RegularAnchorNavigation = (args) => (
    <AnchorNavigation {...args}>
        <Link href="#">
            Item one
        </Link>
        <Link href="#">
            Item two
        </Link>
        <Link href="#">
            Item three
        </Link>
        <Link href="#">
            Item four
        </Link>
    </AnchorNavigation>
  );

RegularAnchorNavigation.args = {
    title:'Title'
}

const anchorsourcecode = `
import { AnchorNavigation, Link } from "@wfp/ui";

<AnchorNavigation title="Title">
  <Link href="#">
    Item one
  </Link>
  <Link href="#">
    Item two
  </Link>
  <Link href="#">
    Item three
  </Link>
  <Link href="#">
    Item four
  </Link>
</AnchorNavigation>
`

RegularAnchorNavigation.story={
    parameters:{
        docs:{
            source:{
                code: anchorsourcecode
            }
        }
    }
}<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import { push } from 'react-router-redux';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Grid, Row, Col } from 'react-flexbox-grid/lib';
import { updateCollection, fetchCollectionDetails, fetchCollectionSourceList } from '../../../actions/sourceActions';
import withAsyncFetch from '../../common/hocs/AsyncContainer';
import { updateFeedback } from '../../../actions/appActions';
import CollectionForm from './form/CollectionForm';
import { PERMISSION_MEDIA_EDIT } from '../../../lib/auth';
import Permissioned from '../../common/Permissioned';
import { nullOrUndefined } from '../../../lib/formValidators';
import FETCH_SUCCEEDED from '../../../lib/fetchConstants';
import PageTitle from '../../common/PageTitle';
import messages from '../../../resources/messages';

const localMessages = {
  mainTitle: { id: 'collection.mainTitle', defaultMessage: 'Modify this Collection' },
  addButton: { id: 'collection.add.saveAll', defaultMessage: 'Save Changes' },
  feedback: { id: 'collection.add.feedback', defaultMessage: 'We saved your changes to this collection' },
};

const EditCollectionContainer = (props) => {
  const { handleSave, collection, collectionId, sources } = props;
  const { formatMessage } = props.intl;
  const intialValues = {
    ...collection,
    name: collection.label,
    sources,
    static: collection.is_static === true,
    showOnMedia: collection.show_on_media === true,
    showOnStories: collection.show_on_stories === true,
    disabled: collection.is_static === true,
  };
  return (

      <PageTitle value={[messages.edit, collection.label]} />
      <Permissioned onlyRole={PERMISSION_MEDIA_EDIT}>
        <Grid>
          <Row>
            <Col lg={12}>

<FormattedMessage {...localMessages.mainTitle} />

            </Col>
          </Row>
          <CollectionForm
            initialValues={intialValues}
            onSave={handleSave}
            buttonLabel={formatMessage(localMessages.addButton)}
            collectionId={collectionId}
          />
        </Grid>
      </Permissioned>

  );
};

EditCollectionContainer.propTypes = {
  // from context
  intl: PropTypes.object.isRequired,
  // from dispatch
  handleSave: PropTypes.func.isRequired,
  // form state
  collectionId: PropTypes.number.isRequired,
  collection: PropTypes.object,
  sources: PropTypes.array,
};

const mapStateToProps = (state, ownProps) => ({
  collectionId: parseInt(ownProps.params.collectionId, 10),
  collection: state.sources.collections.selected.collectionDetails.object,
  sources: state.sources.collections.selected.collectionSourceList.sources,
  fetchStatus: state.sources.collections.selected.collectionSourceList.fetchStatus,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  handleSave: (values) => {
    const infoToSave = {
      id: ownProps.params.collectionId,
      name: values.name,
      description: nullOrUndefined(values.description) ? '' : values.description,
      static: values.static,
      showOnMedia: values.showOnMedia,
      showOnStories: values.showOnStories,
    };
    if ('sources' in values) {
      infoToSave['sources[]'] = values.sources.map(s => (s.id ? s.id : s.media_id));
    } else {
      infoToSave['sources[]'] = [];
    }
    // try to save it
    return dispatch(updateCollection(infoToSave))
      .then(() => {
        dispatch(fetchCollectionDetails(ownProps.params.collectionId))
          .then(() => {
            dispatch(updateFeedback({ classes: 'info-notice', open: true, message: ownProps.intl.formatMessage(localMessages.feedback) }));
            return dispatch(push(`/collections/${ownProps.params.collectionId}`));
          });
      });
  },
  fetchData: (collectionId) => {
    dispatch(fetchCollectionSourceList(collectionId));
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      if (stateProps.fetchStatus !== FETCH_SUCCEEDED) {
        dispatchProps.fetchData(stateProps.collectionId);
      }
    },
  });
}

export default
injectIntl(
  connect(mapStateToProps, mapDispatchToProps, mergeProps)(
    withAsyncFetch(
      EditCollectionContainer
    )
  ),
);
<commit_msg>Migrate collection edit container to asyncData #1436 (fixes reload bug)<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import { push } from 'react-router-redux';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Grid, Row, Col } from 'react-flexbox-grid/lib';
import { updateCollection, fetchCollectionDetails, fetchCollectionSourceList } from '../../../actions/sourceActions';
import withAsyncData from '../../common/hocs/AsyncDataContainer';
import { updateFeedback } from '../../../actions/appActions';
import CollectionForm from './form/CollectionForm';
import { PERMISSION_MEDIA_EDIT } from '../../../lib/auth';
import Permissioned from '../../common/Permissioned';
import { nullOrUndefined } from '../../../lib/formValidators';
import PageTitle from '../../common/PageTitle';
import messages from '../../../resources/messages';

const localMessages = {
  mainTitle: { id: 'collection.mainTitle', defaultMessage: 'Modify this Collection' },
  addButton: { id: 'collection.add.saveAll', defaultMessage: 'Save Changes' },
  feedback: { id: 'collection.add.feedback', defaultMessage: 'We saved your changes to this collection' },
};

const EditCollectionContainer = (props) => {
  const { handleSave, collection, collectionId, sources } = props;
  const { formatMessage } = props.intl;
  const intialValues = {
    ...collection,
    name: collection.label,
    sources,
    static: collection.is_static === true,
    showOnMedia: collection.show_on_media === true,
    showOnStories: collection.show_on_stories === true,
    disabled: collection.is_static === true,
  };
  return (

      <PageTitle value={[messages.edit, collection.label]} />
      <Permissioned onlyRole={PERMISSION_MEDIA_EDIT}>
        <Grid>
          <Row>
            <Col lg={12}>

<FormattedMessage {...localMessages.mainTitle} />

            </Col>
          </Row>
          <CollectionForm
            initialValues={intialValues}
            onSave={handleSave}
            buttonLabel={formatMessage(localMessages.addButton)}
            collectionId={collectionId}
          />
        </Grid>
      </Permissioned>

  );
};

EditCollectionContainer.propTypes = {
  // from context
  intl: PropTypes.object.isRequired,
  // from dispatch
  handleSave: PropTypes.func.isRequired,
  // form state
  collectionId: PropTypes.number.isRequired,
  collection: PropTypes.object,
  sources: PropTypes.array,
};

const mapStateToProps = state => ({
  collectionId: state.sources.collections.selected.id,
  collection: state.sources.collections.selected.collectionDetails.object,
  sources: state.sources.collections.selected.collectionSourceList.sources,
  fetchStatus: state.sources.collections.selected.collectionSourceList.fetchStatus,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  handleSave: (values) => {
    const infoToSave = {
      id: ownProps.params.collectionId,
      name: values.name,
      description: nullOrUndefined(values.description) ? '' : values.description,
      static: values.static,
      showOnMedia: values.showOnMedia,
      showOnStories: values.showOnStories,
    };
    if ('sources' in values) {
      infoToSave['sources[]'] = values.sources.map(s => (s.id ? s.id : s.media_id));
    } else {
      infoToSave['sources[]'] = [];
    }
    // try to save it
    return dispatch(updateCollection(infoToSave))
      .then(() => {
        dispatch(fetchCollectionDetails(ownProps.params.collectionId))
          .then(() => {
            dispatch(updateFeedback({ classes: 'info-notice', open: true, message: ownProps.intl.formatMessage(localMessages.feedback) }));
            return dispatch(push(`/collections/${ownProps.params.collectionId}`));
          });
      });
  },
});

const fetchAsyncData = (dispatch, { collectionId }) => dispatch(fetchCollectionSourceList(collectionId));

export default
injectIntl(
  connect(mapStateToProps, mapDispatchToProps)(
    withAsyncData(fetchAsyncData)(
      EditCollectionContainer
    )
  ),
);
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedMessage, FormattedHTMLMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import Link from 'react-router/lib/Link';
import RaisedButton from 'material-ui/RaisedButton';
import RemoveRedEye from 'material-ui/svg-icons/image/remove-red-eye';
import { Grid, Row, Col } from 'react-flexbox-grid/lib';
import Title from 'react-title-component';
import DataCard from '../../common/DataCard';
// import { EyeButton } from '../../common/AppButton';
import MediaSourceIcon from '../../common/icons/MediaSourceIcon';
import CollectionList from '../../common/CollectionList';
import SourceSentenceCountContainer from './SourceSentenceCountContainer';
import SourceTopWordsContainer from './SourceTopWordsContainer';
import SourceGeographyContainer from './SourceGeographyContainer';
import messages from '../../../resources/messages';
import HealthBadge from '../HealthBadge';
import FavoriteToggler from '../../common/FavoriteToggler';
import { favoriteSource, updateFeedback } from '../../../actions/sourceActions';
import { isMetaDataTagSet } from '../../../lib/tagUtil';
import { getBrandDarkColor } from '../../../styles/colors';

const localMessages = {
  searchNow: { id: 'source.basicInfo.searchNow', defaultMessage: 'Search on the Dashboard' },
  sourceDetailsTitle: { id: 'source.details.title', defaultMessage: 'Media Source: {name}' },
  sourceDetailsCollectionsTitle: { id: 'source.details.collections.title', defaultMessage: 'Collections' },
  sourceDetailsCollectionsIntro: { id: 'source.details.collections.intro',
    defaultMessage: 'The {name} media source is in {count, plural,\n =0 {no collections}\n =1 {one collection}\n other {# collections}\n}.',
  },

  favoritedCollectionsTitle: { id: 'source.details.collections.favorited.title', defaultMessage: 'Favorited Collections' },
  favoritedCollectionsIntro: { id: 'source.details.collections.favorited.intro',
    defaultMessage: 'You have favorited {count, plural,\n =0 {no collections}\n =1 {one collection}\n other {# collections}\n}.',
  },

  feedInfo: { id: 'source.basicInfo.feeds',
    defaultMessage: 'Content from {feedCount, plural,\n =0 {no RSS feeds}\n =1 {one RSS feed}\n =100 {over 100 RSS feeds}\n other {# RSS feeds}}. {feedListLink}.' },
  feedLink: { id: 'source.basicInfo.feedLink', defaultMessage: 'See all feeds' },
  dateInfo: { id: 'source.basicInfo.dates', defaultMessage: 'We have collected sentences between {startDate} and {endDate}.' },
  contentInfo: { id: 'source.basicInfo.content', defaultMessage: 'Averaging {storyCount} stories per day and {sentenceCount} sentences in the last week.' },
  gapInfo: { id: 'source.basicInfo.gaps', defaultMessage: 'We\'d guess there are {gapCount} "gaps" in our coverage (highlighted in in red on the chart).  Gaps are when we were unable to collect as much content as we expected too, which means we might be missing some content for those dates.' },
  metadataLabel: { id: 'source.basicInfo.metadata', defaultMessage: 'Metadata' },
  metadataDescription: { id: 'source.basicInfo.metadataDescription', defaultMessage: '{label}' },
  metadataEmpty: { id: 'source.basicInfo.metadata.empty', defaultMessage: 'No metadata available at this time' },
  unknown: { id: 'source.basicInfo.health.unknown', defaultMessage: '(unknown)' },
  isMonitored: { id: 'source.basicInfo.isMonitored', defaultMessage: 'monitored to ensure health' },
};

class SourceDetailsContainer extends React.Component {

  searchOnDashboard = () => {
    const { source } = this.props;
    let dashboardUrl = `https://dashboard.mediacloud.org/#query/["*"]/[{"sources":[${source.media_id}]}]/`;
    if (source.health) {
      dashboardUrl += `["${source.health.start_date.substring(0, 10)}"]/["${source.health.end_date.substring(0, 10)}"]/`;
    } else {
      dashboardUrl += '[""]/[""]/';
    }
    dashboardUrl += `[{"uid":3,"name":"${source.name}","color":"55868A"}]`;
    window.open(dashboardUrl, '_blank');
  }

  render() {
    const { source, onChangeFavorited } = this.props;
    const { formatMessage, formatNumber } = this.props.intl;
    const collections = source.media_source_tags.filter(c => c.show_on_media === 1);
    const metadata = source.media_source_tags.filter(c => (isMetaDataTagSet(c.tag_sets_id)));
    const filename = `SentencesOverTime-Source-${source.media_id}`;
    const titleHandler = parentTitle => `${source.name} | ${parentTitle}`;
    const publicMessage = ` • ${formatMessage(messages.public)} `; // for now, every media source is public
    const editMessage = ( // TODO: permissions around this

        •
        <Link to={`/sources/${source.media_id}/edit`} >
          <FormattedMessage {...messages.edit} />
        </Link>

    );
    const favButton = (<FavoriteToggler
      isFavorited={source.isFavorite}
      onChangeFavorited={isFavNow => onChangeFavorited(source.media_id, isFavNow)}
    />);
    let monitoredIcon = null;
    if (source.is_monitored) {
      monitoredIcon = (
        <RemoveRedEye
          color={getBrandDarkColor()}
          style={{ height: 20, width: 25, marginLeft: 10 }}
        />
      );
    }
    let metadataContent = <FormattedMessage {...localMessages.metadataEmpty} />;
    if (metadata.length > 0) {
      metadataContent = (

{ metadata.map(item =>
          <li key={`metadata-${item.label}`}>
            <FormattedMessage {...localMessages.metadataDescription} values={{ label: item.description ? item.description : item.label }} />

          )}

      );
    }
    const publicNotes = (source.public_notes) ?
{source.public_notes}
 : null;
    return (
      <Grid className="details source-details">
        <Title render={titleHandler} />
        <Row>
          <Col lg={10} xs={12}>

              <MediaSourceIcon height={32} />
              <FormattedMessage {...localMessages.sourceDetailsTitle} values={{ name: source.name }} />

                ID #{source.media_id}
                {publicMessage}
                {editMessage}
                {monitoredIcon}
                {favButton}

            {publicNotes}

              <FormattedMessage
                {...localMessages.feedInfo}
                values={{
                  feedCount: source.feedCount,
                  feedListLink: (
                    <Link to={`/sources/${source.media_id}/feeds`} >
                      <FormattedMessage {...localMessages.feedLink} />
                    </Link>
                  ),
                }}
              />

              <FormattedMessage
                {...localMessages.dateInfo}
                values={{
                  startDate: (source.health) ? source.health.start_date.substring(0, 10) : formatMessage(localMessages.unknown),
                  endDate: (source.health) ? source.health.end_date.substring(0, 10) : formatMessage(localMessages.unknown),
                }}
              />

              <FormattedMessage
                {...localMessages.contentInfo}
                values={{
                  storyCount: (source.health) ? formatNumber(source.health.num_stories_w) : formatMessage(localMessages.unknown),
                  sentenceCount: (source.health) ? formatNumber(source.health.num_sentences_w) : formatMessage(localMessages.unknown),
                }}
              />

              <FormattedHTMLMessage
                {...localMessages.gapInfo}
                values={{ gapCount: (source.health) ? formatNumber(source.health.coverage_gaps) : formatMessage(localMessages.unknown) }}
              />

               {source.url}

            <RaisedButton label={formatMessage(localMessages.searchNow)} primary onClick={this.searchOnDashboard} />
          </Col>
          <Col lg={2} xs={12}>
            <HealthBadge isHealthy={(source.health) ? source.health.is_healthy === 1 : true} />
          </Col>
        </Row>
        <Row>
          <Col lg={6} xs={12}>
            <SourceTopWordsContainer source={source} />
          </Col>
          <Col lg={6} xs={12}>
            <SourceSentenceCountContainer sourceId={source.media_id} filename={filename} />
          </Col>
        </Row>
        <Row>
          <Col lg={12} md={12} sm={12}>
            <SourceGeographyContainer source={source} />
          </Col>
        </Row>
        <Row>
          <Col lg={6} md={6} sm={12}>
            <CollectionList
              title={formatMessage(localMessages.sourceDetailsCollectionsTitle)}
              intro={formatMessage(localMessages.sourceDetailsCollectionsIntro, {
                name: source.name,
                count: collections.length,
              })}
              collections={collections}
            />
          </Col>
          <Col lg={6} md={6} sm={12} >
            <DataCard>

                <FormattedMessage {...localMessages.metadataLabel} />

              {metadataContent}
            </DataCard>
          </Col>
        </Row>
      </Grid>
    );
  }

}

SourceDetailsContainer.propTypes = {
  intl: React.PropTypes.object.isRequired,
  // from context
  params: React.PropTypes.object.isRequired,       // params from router
  sourceId: React.PropTypes.number.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
  source: React.PropTypes.object,
  onChangeFavorited: React.PropTypes.func.isRequired,
};

const mapStateToProps = (state, ownProps) => ({
  sourceId: parseInt(ownProps.params.sourceId, 10),
  fetchStatus: state.sources.sources.selected.sourceDetails.fetchStatus,
  source: state.sources.sources.selected.sourceDetails.object,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  onChangeFavorited: (mediaId, isFavorite) => {
    dispatch(favoriteSource(mediaId, isFavorite))
      .then(() => {
        const msg = (isFavorite) ? messages.favorited : messages.unfavorited;
        dispatch(updateFeedback({ open: true, message: ownProps.intl.formatMessage(msg) }));
        // dispatch(fetchFavoriteSources());  // to update the list of favorites
      });
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      SourceDetailsContainer
    )
  );
<commit_msg>permissions on seeing editor notes (#189)<commit_after>import React from 'react';
import { FormattedMessage, FormattedHTMLMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import Link from 'react-router/lib/Link';
import RaisedButton from 'material-ui/RaisedButton';
import RemoveRedEye from 'material-ui/svg-icons/image/remove-red-eye';
import { Grid, Row, Col } from 'react-flexbox-grid/lib';
import Title from 'react-title-component';
import DataCard from '../../common/DataCard';
import Permissioned from '../../common/Permissioned';
import { PERMISSION_MEDIA_EDIT } from '../../../lib/auth';
// import { EyeButton } from '../../common/AppButton';
import MediaSourceIcon from '../../common/icons/MediaSourceIcon';
import CollectionList from '../../common/CollectionList';
import SourceSentenceCountContainer from './SourceSentenceCountContainer';
import SourceTopWordsContainer from './SourceTopWordsContainer';
import SourceGeographyContainer from './SourceGeographyContainer';
import messages from '../../../resources/messages';
import HealthBadge from '../HealthBadge';
import FavoriteToggler from '../../common/FavoriteToggler';
import { favoriteSource, updateFeedback } from '../../../actions/sourceActions';
import { isMetaDataTagSet } from '../../../lib/tagUtil';
import { getBrandDarkColor } from '../../../styles/colors';

const localMessages = {
  searchNow: { id: 'source.basicInfo.searchNow', defaultMessage: 'Search on the Dashboard' },
  sourceDetailsTitle: { id: 'source.details.title', defaultMessage: 'Media Source: {name}' },
  sourceDetailsCollectionsTitle: { id: 'source.details.collections.title', defaultMessage: 'Collections' },
  sourceDetailsCollectionsIntro: { id: 'source.details.collections.intro',
    defaultMessage: 'The {name} media source is in {count, plural,\n =0 {no collections}\n =1 {one collection}\n other {# collections}\n}.',
  },

  favoritedCollectionsTitle: { id: 'source.details.collections.favorited.title', defaultMessage: 'Favorited Collections' },
  favoritedCollectionsIntro: { id: 'source.details.collections.favorited.intro',
    defaultMessage: 'You have favorited {count, plural,\n =0 {no collections}\n =1 {one collection}\n other {# collections}\n}.',
  },

  feedInfo: { id: 'source.basicInfo.feeds',
    defaultMessage: 'Content from {feedCount, plural,\n =0 {no RSS feeds}\n =1 {one RSS feed}\n =100 {over 100 RSS feeds}\n other {# RSS feeds}}. {feedListLink}.' },
  feedLink: { id: 'source.basicInfo.feedLink', defaultMessage: 'See all feeds' },
  dateInfo: { id: 'source.basicInfo.dates', defaultMessage: 'We have collected sentences between {startDate} and {endDate}.' },
  contentInfo: { id: 'source.basicInfo.content', defaultMessage: 'Averaging {storyCount} stories per day and {sentenceCount} sentences in the last week.' },
  gapInfo: { id: 'source.basicInfo.gaps', defaultMessage: 'We\'d guess there are {gapCount} "gaps" in our coverage (highlighted in in red on the chart).  Gaps are when we were unable to collect as much content as we expected too, which means we might be missing some content for those dates.' },
  metadataLabel: { id: 'source.basicInfo.metadata', defaultMessage: 'Metadata' },
  metadataDescription: { id: 'source.basicInfo.metadataDescription', defaultMessage: '{label}' },
  metadataEmpty: { id: 'source.basicInfo.metadata.empty', defaultMessage: 'No metadata available at this time' },
  unknown: { id: 'source.basicInfo.health.unknown', defaultMessage: '(unknown)' },
  isMonitored: { id: 'source.basicInfo.isMonitored', defaultMessage: 'monitored to ensure health' },
  publicNotes: { id: 'source.basicInfo.publicNotes', defaultMessage: '
Notes: {notes}
' },
  editorNotes: { id: 'source.basicInfo.editorNotes', defaultMessage: '
Editor\'s Notes: {notes}
' },
};

class SourceDetailsContainer extends React.Component {

  searchOnDashboard = () => {
    const { source } = this.props;
    let dashboardUrl = `https://dashboard.mediacloud.org/#query/["*"]/[{"sources":[${source.media_id}]}]/`;
    if (source.health) {
      dashboardUrl += `["${source.health.start_date.substring(0, 10)}"]/["${source.health.end_date.substring(0, 10)}"]/`;
    } else {
      dashboardUrl += '[""]/[""]/';
    }
    dashboardUrl += `[{"uid":3,"name":"${source.name}","color":"55868A"}]`;
    window.open(dashboardUrl, '_blank');
  }

  render() {
    const { source, onChangeFavorited } = this.props;
    const { formatMessage, formatNumber } = this.props.intl;
    const collections = source.media_source_tags.filter(c => c.show_on_media === 1);
    const metadata = source.media_source_tags.filter(c => (isMetaDataTagSet(c.tag_sets_id)));
    const filename = `SentencesOverTime-Source-${source.media_id}`;
    const titleHandler = parentTitle => `${source.name} | ${parentTitle}`;
    const publicMessage = ` • ${formatMessage(messages.public)} `; // for now, every media source is public
    const editMessage = ( // TODO: permissions around this

        •
        <Link to={`/sources/${source.media_id}/edit`} >
          <FormattedMessage {...messages.edit} />
        </Link>

    );
    const favButton = (<FavoriteToggler
      isFavorited={source.isFavorite}
      onChangeFavorited={isFavNow => onChangeFavorited(source.media_id, isFavNow)}
    />);
    let monitoredIcon = null;
    if (source.is_monitored) {
      monitoredIcon = (
        <RemoveRedEye
          color={getBrandDarkColor()}
          style={{ height: 20, width: 25, marginLeft: 10 }}
        />
      );
    }
    let metadataContent = <FormattedMessage {...localMessages.metadataEmpty} />;
    if (metadata.length > 0) {
      metadataContent = (

{ metadata.map(item =>
          <li key={`metadata-${item.label}`}>
            <FormattedMessage {...localMessages.metadataDescription} values={{ label: item.description ? item.description : item.label }} />

          )}

      );
    }
    const publicNotes = (source.public_notes) ? <FormattedHTMLMessage {...localMessages.publicNotes} values={{ notes: source.public_notes }} /> : null;
    const editorNotes = (source.editor_notes) ? <FormattedHTMLMessage {...localMessages.editorNotes} values={{ notes: source.editor_notes }} /> : null;
    return (
      <Grid className="details source-details">
        <Title render={titleHandler} />
        <Row>
          <Col lg={10} xs={12}>

              <MediaSourceIcon height={32} />
              <FormattedMessage {...localMessages.sourceDetailsTitle} values={{ name: source.name }} />

                ID #{source.media_id}
                {publicMessage}
                {editMessage}
                {monitoredIcon}
                {favButton}

            {publicNotes}
            <Permissioned onlyRole={PERMISSION_MEDIA_EDIT}>
              {editorNotes}
            </Permissioned>

              <FormattedMessage
                {...localMessages.feedInfo}
                values={{
                  feedCount: source.feedCount,
                  feedListLink: (
                    <Link to={`/sources/${source.media_id}/feeds`} >
                      <FormattedMessage {...localMessages.feedLink} />
                    </Link>
                  ),
                }}
              />

              <FormattedMessage
                {...localMessages.dateInfo}
                values={{
                  startDate: (source.health) ? source.health.start_date.substring(0, 10) : formatMessage(localMessages.unknown),
                  endDate: (source.health) ? source.health.end_date.substring(0, 10) : formatMessage(localMessages.unknown),
                }}
              />

              <FormattedMessage
                {...localMessages.contentInfo}
                values={{
                  storyCount: (source.health) ? formatNumber(source.health.num_stories_w) : formatMessage(localMessages.unknown),
                  sentenceCount: (source.health) ? formatNumber(source.health.num_sentences_w) : formatMessage(localMessages.unknown),
                }}
              />

              <FormattedHTMLMessage
                {...localMessages.gapInfo}
                values={{ gapCount: (source.health) ? formatNumber(source.health.coverage_gaps) : formatMessage(localMessages.unknown) }}
              />

               {source.url}

            <RaisedButton label={formatMessage(localMessages.searchNow)} primary onClick={this.searchOnDashboard} />
          </Col>
          <Col lg={2} xs={12}>
            <HealthBadge isHealthy={(source.health) ? source.health.is_healthy === 1 : true} />
          </Col>
        </Row>
        <Row>
          <Col lg={6} xs={12}>
            <SourceTopWordsContainer source={source} />
          </Col>
          <Col lg={6} xs={12}>
            <SourceSentenceCountContainer sourceId={source.media_id} filename={filename} />
          </Col>
        </Row>
        <Row>
          <Col lg={12} md={12} sm={12}>
            <SourceGeographyContainer source={source} />
          </Col>
        </Row>
        <Row>
          <Col lg={6} md={6} sm={12}>
            <CollectionList
              title={formatMessage(localMessages.sourceDetailsCollectionsTitle)}
              intro={formatMessage(localMessages.sourceDetailsCollectionsIntro, {
                name: source.name,
                count: collections.length,
              })}
              collections={collections}
            />
          </Col>
          <Col lg={6} md={6} sm={12} >
            <DataCard>

                <FormattedMessage {...localMessages.metadataLabel} />

              {metadataContent}
            </DataCard>
          </Col>
        </Row>
      </Grid>
    );
  }

}

SourceDetailsContainer.propTypes = {
  intl: React.PropTypes.object.isRequired,
  // from context
  params: React.PropTypes.object.isRequired,       // params from router
  sourceId: React.PropTypes.number.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
  source: React.PropTypes.object,
  onChangeFavorited: React.PropTypes.func.isRequired,
};

const mapStateToProps = (state, ownProps) => ({
  sourceId: parseInt(ownProps.params.sourceId, 10),
  fetchStatus: state.sources.sources.selected.sourceDetails.fetchStatus,
  source: state.sources.sources.selected.sourceDetails.object,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  onChangeFavorited: (mediaId, isFavorite) => {
    dispatch(favoriteSource(mediaId, isFavorite))
      .then(() => {
        const msg = (isFavorite) ? messages.favorited : messages.unfavorited;
        dispatch(updateFeedback({ open: true, message: ownProps.intl.formatMessage(msg) }));
        // dispatch(fetchFavoriteSources());  // to update the list of favorites
      });
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      SourceDetailsContainer
    )
  );
<|endoftext|>


<commit_before>import { Observable } from 'rxjs/Observable';
import { combineEpics } from 'redux-observable';
import { PermissionsApi, PrincipalsApi, EntityDataModelApi, OrganizationsApi } from 'lattice';

import { ROLE, AUTHENTICATED_USER } from '../../utils/Consts/UserRoleConsts';
import * as actionTypes from './PermissionsSummaryActionTypes';
import * as actionFactory from './PermissionsSummaryActionFactory';
import * as orgsActionTypes from '../organizations/actions/OrganizationsActionTypes';
import { PERMISSIONS } from '../permissions/PermissionsStorage';

/* HELPER FUNCTIONS */
function toCamelCase(s) {
  return s.charAt(0) + s.slice(1).toLowerCase();
}

function getPermission(permissions) {
  const newPermissions = [];
  if (permissions.includes(PERMISSIONS.OWNER)) return [toCamelCase(PERMISSIONS.OWNER)];
  if (permissions.includes(PERMISSIONS.WRITE)) newPermissions.push(toCamelCase(PERMISSIONS.WRITE));
  if (permissions.includes(PERMISSIONS.READ)) newPermissions.push(toCamelCase(PERMISSIONS.READ));
  if (permissions.includes(PERMISSIONS.LINK)) newPermissions.push(toCamelCase(PERMISSIONS.LINK));
  if (permissions.includes(PERMISSIONS.DISCOVER) || permissions.includes('Discover')) newPermissions.push(toCamelCase(PERMISSIONS.DISCOVER));
  return newPermissions;
}

function configureUserPermissions(aces, rolePermissions, allUsersById, orgsMembers) {
  const userPermissions = [];

  try {
    allUsersById.valueSeq().forEach((user) => {
      const userObj = {};

      if (user) {
        userObj.id = user.user_id;
        userObj.nickname = user.nickname;
        userObj.email = user.email;
        userObj.roles = [];
        userObj.individualPermissions = [];
        userObj.permissions = [];

        // Get individual permissions
        aces.forEach((ace) => {
          if (ace.principal.id === user.user_id) {
            userObj.individualPermissions = getPermission(ace.permissions);
            userObj.permissions = getPermission(ace.permissions);
          }
        });

        // Get permissions based on roles
        const members = Object.values(orgsMembers.toJS()).reduce((a, c) => a.concat(c), []);
        members.forEach((member) => {
          let match = false;
          let i = 0;
          while (!match && i < aces.length) {
            if (member.profile.user_id === user.user_id) {
              match = true;

              member.roles.forEach((role) => {
                userObj.roles.push(role.title);

                const permissions = rolePermissions[role.principal.id];
                if (permissions) {
                  permissions.forEach((permission) => {
                    if (userObj.permissions.indexOf(permission) === -1) {
                      userObj.permissions.push(permission);
                    }
                  });
                }
              });
            }

            i += 1;
          }
        });

        userPermissions.push(userObj);
      }
    });
  }
  catch (e) {
    console.error(e);
  }

  return userPermissions;
}

function configurePermissions(aces, allUsersById, orgsMembers) {
  const rolePermissions = {
    [AUTHENTICATED_USER]: []
  };

  aces.forEach((ace) => {
    if (ace.permissions.length > 0 && ace.principal.type === ROLE) {
      if (ace.principal.id === AUTHENTICATED_USER) {
        rolePermissions[AUTHENTICATED_USER] = getPermission(ace.permissions);
      }

      rolePermissions[ace.principal.id] = getPermission(ace.permissions);
    }
  });

  const userPermissions = configureUserPermissions(aces, rolePermissions, allUsersById, orgsMembers);

  return {
    rolePermissions,
    userPermissions
  };
}

function getAclKey(action) {
  const property = action.property;
  const aclKey = [action.entitySetId];
  if (property && property.id) aclKey.push(action.property.id);
  return aclKey;
}

function getUsersAndRoles(users) {
  const allUsersById = users;
  const allRolesList = new Set();
  Object.keys(users).forEach((userId) => {
    users[userId].roles.forEach((role) => {
      if (role !== AUTHENTICATED_USER) allRolesList.add(role);
    });
  });
  return {
    allUsersById,
    allRolesList
  };
}

function createAclsObservables(entitySetId, properties) {
  const loadAclsObservables = properties.map(property =>
    Observable.of(actionFactory.getUserRolePermissionsRequest(entitySetId, property))
  );
  loadAclsObservables.unshift(Observable.of(actionFactory.getUserRolePermissionsRequest(entitySetId)));
  return loadAclsObservables;
}

/* EPICS */
function getOrgsMembersEpic(action$, store) {
  return action$
    .ofType(orgsActionTypes.FETCH_ORGS_SUCCESS)
    .map(() => {
      const orgs = store.getState().getIn(['organizations', 'organizations']);
      const orgIds = orgs.keySeq().toJS();
      return orgIds;
    })
    .mergeMap(val => val)
    .mergeMap(orgId =>
      Observable.from(
          OrganizationsApi.getAllMembers(orgId)
        )
        .map(members => Object.assign({}, { orgId, members }))
    )
    .mergeMap(membersObj =>
      Observable.of(
        actionFactory.setOrgsMembers(membersObj)
      )
    )
    .catch(() =>
      Observable.of(actionFactory.setOrgsMembersFailure())
    );
}

function getOrgsRolesEpic(action$, store) {
  return action$
    .ofType(orgsActionTypes.FETCH_ORGS_SUCCESS)
    .map(() => {
      const orgs = store.getState().getIn(['organizations', 'organizations']);
      const orgIds = orgs.keySeq().toJS();
      return orgIds;
    })
    .mergeMap(val => val)
    .mergeMap(orgId =>
      Observable.from(
          OrganizationsApi.getAllRoles(orgId)
        )
    )
    .mergeMap(roles =>
      Observable.of(
        actionFactory.setOrgsRoles(roles)
      )
    )
    .catch(() =>
      Observable.of(actionFactory.setOrgsRolesFailure())
    );
}

// TODO: Use spinner when loading, based on status ^
function getAllUsersAndRolesEpic(action$) {
  let entitySet;
  return action$
    .ofType(actionTypes.GET_ALL_USERS_AND_ROLES_REQUEST)
    .mergeMap((action) => {
      entitySet = action.entitySet;
      return Observable
        .from(
          PrincipalsApi.getAllUsers()
        )
        .mergeMap((users) => {
          const { allUsersById, allRolesList } = getUsersAndRoles(users);
          return Observable
            .of(
              actionFactory.getAllUsersAndRolesSuccess(allUsersById, allRolesList),
              actionFactory.getAcls(entitySet)
            );
        })
        .catch(() =>
          Observable.of(
            actionFactory.getAllUsersAndRolesFailure()
          )
        );
    });
}

function getAclsEpic(action$) {
  return action$
  .ofType(actionTypes.GET_ACLS)
  .mergeMap((action) => {
    const edmQuery = [{
      type: 'EntitySet',
      id: action.entitySet.id,
      include: ['PropertyTypeInEntitySet']
    }];
    return Observable
      .from(
        EntityDataModelApi.getEntityDataModelProjection(edmQuery)
      )
      .mergeMap(edmDetails =>
        createAclsObservables(action.entitySet.id, Object.values(edmDetails.propertyTypes))
      )
      .mergeMap(observables =>
        observables
      )
      .catch(() =>
        // TODO: add real error handling
        ({ type: 'noop' })
      );
  })
  .catch(() =>
    // TODO: add real error handling
    ({ type: 'noop' })
  );
}

function getUserRolePermissionsEpic(action$, store) {
  return action$
    .ofType(actionTypes.GET_USER_ROLE_PERMISSIONS_REQUEST)
    .mergeMap((action) => {
      const aclKey = getAclKey(action);
      return Observable
        .from(
          PermissionsApi.getAcl(aclKey)
        )
        .mergeMap((acl) => {
          // TODO: Figure out if orgsRoles is necessary; Currently not used
          const orgsRoles = store.getState().getIn(['permissionsSummary', 'orgsRoles']);
          const orgsMembers = store.getState().getIn(['permissionsSummary', 'orgsMembers']);
          const allUsersById = store.getState().getIn(['permissionsSummary', 'allUsersById']);
          const permissions = configurePermissions(acl.aces, allUsersById, orgsMembers);
          return Observable.of(
            actionFactory.getUserRolePermissionsSuccess(),
            actionFactory.setRolePermissions(action.property, permissions.rolePermissions),
            actionFactory.setUserPermissions(action.property, permissions.userPermissions)
          );
        })
        .catch(() =>
          Observable.of(
            actionFactory.getUserRolePermissionsFailure()
          )
        );
    });
}

export default combineEpics(
  getOrgsMembersEpic,
  getOrgsRolesEpic,
  getAclsEpic,
  getUserRolePermissionsEpic,
  getAllUsersAndRolesEpic
);
<commit_msg>Use role.principal.id instead of title to render RoleRow permissions<commit_after>import { Observable } from 'rxjs/Observable';
import { combineEpics } from 'redux-observable';
import { PermissionsApi, PrincipalsApi, EntityDataModelApi, OrganizationsApi } from 'lattice';

import { ROLE, AUTHENTICATED_USER } from '../../utils/Consts/UserRoleConsts';
import * as actionTypes from './PermissionsSummaryActionTypes';
import * as actionFactory from './PermissionsSummaryActionFactory';
import * as orgsActionTypes from '../organizations/actions/OrganizationsActionTypes';
import { PERMISSIONS } from '../permissions/PermissionsStorage';

/* HELPER FUNCTIONS */
function toCamelCase(s) {
  return s.charAt(0) + s.slice(1).toLowerCase();
}

function getPermission(permissions) {
  const newPermissions = [];
  if (permissions.includes(PERMISSIONS.OWNER)) return [toCamelCase(PERMISSIONS.OWNER)];
  if (permissions.includes(PERMISSIONS.WRITE)) newPermissions.push(toCamelCase(PERMISSIONS.WRITE));
  if (permissions.includes(PERMISSIONS.READ)) newPermissions.push(toCamelCase(PERMISSIONS.READ));
  if (permissions.includes(PERMISSIONS.LINK)) newPermissions.push(toCamelCase(PERMISSIONS.LINK));
  if (permissions.includes(PERMISSIONS.DISCOVER) || permissions.includes('Discover')) newPermissions.push(toCamelCase(PERMISSIONS.DISCOVER));
  return newPermissions;
}

function configureUserPermissions(aces, rolePermissions, allUsersById, orgsMembers) {
  const userPermissions = [];

  try {
    allUsersById.valueSeq().forEach((user) => {
      const userObj = {};

      if (user) {
        userObj.id = user.user_id;
        userObj.nickname = user.nickname;
        userObj.email = user.email;
        userObj.roles = [];
        userObj.individualPermissions = [];
        userObj.permissions = [];

        // Get individual permissions
        aces.forEach((ace) => {
          if (ace.principal.id === user.user_id) {
            userObj.individualPermissions = getPermission(ace.permissions);
            userObj.permissions = getPermission(ace.permissions);
          }
        });

        // Get permissions based on roles
        const members = Object.values(orgsMembers.toJS()).reduce((a, c) => a.concat(c), []);
        members.forEach((member) => {
          let match = false;
          let i = 0;
          while (!match && i < aces.length) {
            if (member.profile.user_id === user.user_id) {
              match = true;

              member.roles.forEach((role) => {
                userObj.roles.push(role.principal.id);

                const permissions = rolePermissions[role.principal.id];
                if (permissions) {
                  permissions.forEach((permission) => {
                    if (userObj.permissions.indexOf(permission) === -1) {
                      userObj.permissions.push(permission);
                    }
                  });
                }
              });
            }

            i += 1;
          }
        });

        userPermissions.push(userObj);
      }
    });
  }
  catch (e) {
    console.error(e);
  }

  return userPermissions;
}

function configurePermissions(aces, allUsersById, orgsMembers) {
  const rolePermissions = {
    [AUTHENTICATED_USER]: []
  };

  aces.forEach((ace) => {
    if (ace.permissions.length > 0 && ace.principal.type === ROLE) {
      if (ace.principal.id === AUTHENTICATED_USER) {
        rolePermissions[AUTHENTICATED_USER] = getPermission(ace.permissions);
      }

      rolePermissions[ace.principal.id] = getPermission(ace.permissions);
    }
  });

  const userPermissions = configureUserPermissions(aces, rolePermissions, allUsersById, orgsMembers);

  return {
    rolePermissions,
    userPermissions
  };
}

function getAclKey(action) {
  const property = action.property;
  const aclKey = [action.entitySetId];
  if (property && property.id) aclKey.push(action.property.id);
  return aclKey;
}

function getUsersAndRoles(users) {
  const allUsersById = users;
  const allRolesList = new Set();
  Object.keys(users).forEach((userId) => {
    users[userId].roles.forEach((role) => {
      if (role !== AUTHENTICATED_USER) allRolesList.add(role);
    });
  });
  return {
    allUsersById,
    allRolesList
  };
}

function createAclsObservables(entitySetId, properties) {
  const loadAclsObservables = properties.map(property =>
    Observable.of(actionFactory.getUserRolePermissionsRequest(entitySetId, property))
  );
  loadAclsObservables.unshift(Observable.of(actionFactory.getUserRolePermissionsRequest(entitySetId)));
  return loadAclsObservables;
}

/* EPICS */
function getOrgsMembersEpic(action$, store) {
  return action$
    .ofType(orgsActionTypes.FETCH_ORGS_SUCCESS)
    .map(() => {
      const orgs = store.getState().getIn(['organizations', 'organizations']);
      const orgIds = orgs.keySeq().toJS();
      return orgIds;
    })
    .mergeMap(val => val)
    .mergeMap(orgId =>
      Observable.from(
          OrganizationsApi.getAllMembers(orgId)
        )
        .map(members => Object.assign({}, { orgId, members }))
    )
    .mergeMap(membersObj =>
      Observable.of(
        actionFactory.setOrgsMembers(membersObj)
      )
    )
    .catch(() =>
      Observable.of(actionFactory.setOrgsMembersFailure())
    );
}

function getOrgsRolesEpic(action$, store) {
  return action$
    .ofType(orgsActionTypes.FETCH_ORGS_SUCCESS)
    .map(() => {
      const orgs = store.getState().getIn(['organizations', 'organizations']);
      const orgIds = orgs.keySeq().toJS();
      return orgIds;
    })
    .mergeMap(val => val)
    .mergeMap(orgId =>
      Observable.from(
          OrganizationsApi.getAllRoles(orgId)
        )
    )
    .mergeMap(roles =>
      Observable.of(
        actionFactory.setOrgsRoles(roles)
      )
    )
    .catch(() =>
      Observable.of(actionFactory.setOrgsRolesFailure())
    );
}

// TODO: Use spinner when loading, based on status ^
function getAllUsersAndRolesEpic(action$) {
  let entitySet;
  return action$
    .ofType(actionTypes.GET_ALL_USERS_AND_ROLES_REQUEST)
    .mergeMap((action) => {
      entitySet = action.entitySet;
      return Observable
        .from(
          PrincipalsApi.getAllUsers()
        )
        .mergeMap((users) => {
          const { allUsersById, allRolesList } = getUsersAndRoles(users);
          return Observable
            .of(
              actionFactory.getAllUsersAndRolesSuccess(allUsersById, allRolesList),
              actionFactory.getAcls(entitySet)
            );
        })
        .catch(() =>
          Observable.of(
            actionFactory.getAllUsersAndRolesFailure()
          )
        );
    });
}

function getAclsEpic(action$) {
  return action$
  .ofType(actionTypes.GET_ACLS)
  .mergeMap((action) => {
    const edmQuery = [{
      type: 'EntitySet',
      id: action.entitySet.id,
      include: ['PropertyTypeInEntitySet']
    }];
    return Observable
      .from(
        EntityDataModelApi.getEntityDataModelProjection(edmQuery)
      )
      .mergeMap(edmDetails =>
        createAclsObservables(action.entitySet.id, Object.values(edmDetails.propertyTypes))
      )
      .mergeMap(observables =>
        observables
      )
      .catch(() =>
        // TODO: add real error handling
        ({ type: 'noop' })
      );
  })
  .catch(() =>
    // TODO: add real error handling
    ({ type: 'noop' })
  );
}

function getUserRolePermissionsEpic(action$, store) {
  return action$
    .ofType(actionTypes.GET_USER_ROLE_PERMISSIONS_REQUEST)
    .mergeMap((action) => {
      const aclKey = getAclKey(action);
      return Observable
        .from(
          PermissionsApi.getAcl(aclKey)
        )
        .mergeMap((acl) => {
          // TODO: Figure out if orgsRoles is necessary; Currently not used
          const orgsRoles = store.getState().getIn(['permissionsSummary', 'orgsRoles']);
          const orgsMembers = store.getState().getIn(['permissionsSummary', 'orgsMembers']);
          const allUsersById = store.getState().getIn(['permissionsSummary', 'allUsersById']);
          const permissions = configurePermissions(acl.aces, allUsersById, orgsMembers);
          return Observable.of(
            actionFactory.getUserRolePermissionsSuccess(),
            actionFactory.setRolePermissions(action.property, permissions.rolePermissions),
            actionFactory.setUserPermissions(action.property, permissions.userPermissions)
          );
        })
        .catch(() =>
          Observable.of(
            actionFactory.getUserRolePermissionsFailure()
          )
        );
    });
}

export default combineEpics(
  getOrgsMembersEpic,
  getOrgsRolesEpic,
  getAclsEpic,
  getUserRolePermissionsEpic,
  getAllUsersAndRolesEpic
);
<|endoftext|>


<commit_before>describe("MarkdownTableFormatter", function() {

  // Initialize variables
  var mtf;

  beforeEach(function() {
    mtf = new MarkdownTableFormatter();

  });

  ////////////////////////////////////////////////////////////////////////////////

  describe("add_missing_cell_columns", function() {

    it("should added empty cells where necessary", function() {

      // GIVEN
      mtf.cells = [
        ['h1', 'h2', 'h3', "h4"],
        ['-','-', '-', '-'],
        ['d1','d2','d3']
      ];

      mtf.column_widths = [2,2,2,2];

      // WHEN
      mtf.add_missing_cell_columns();

      // THEN
      expect(mtf.cells).toEqual([
        ['h1', 'h2', 'h3', "h4"],
        ['-','-', '-', '-'],
        ['d1','d2','d3', '']
      ]);

    });
  });

  ////////////////////////////////////////////////////////////////////////////////
  describe("get_column_widths", function() {

    it("should load the proper column widths", function() {

      // GIVEN
      mtf.cells = [
        ['h1', 'h2', 'header_number_3'],
        ['-','-','-'],
        ['data_1', 'd2', 'data_3']
      ];

      // WHEN
      mtf.get_column_widths();

      // THEN
      expect(mtf.column_widths).toEqual([6,2,15]);

    });

  });

  ////////////////////////////////////////////////////////////////////////////////
  describe("import_table", function() {

    it("should import a basic table properly", function() {

      // WHEN
      mtf.import_table('|h1|h2|\n|-|-|\n|d1|d2|');

      // THEN
      expect(mtf.cells).toEqual([ ['h1', 'h2'], ['-','-'], ['d1','d2'] ]);

    });

    it("should strip external white space when importing table", function() {

      // WHEN
      mtf.import_table('| h1 |h2 |  h3|\n|-|-|-|\n|   d1|d2   |       d3     |');

      // THEN
      expect(mtf.cells).toEqual([ ['h1', 'h2', 'h3'], ['-','-', '-'], ['d1','d2','d3'] ]);

    });

  });

  ////////////////////////////////////////////////////////////////////////////////

  describe("initialization", function() {

    it("should initialize instance variables", function() {

      // GIVEN
      // Initialization happened in before.

      // THEN
      expect(mtf.cells.length).toEqual(0);
      expect(mtf.output_table).toEqual("");
      expect(mtf.column_widths).toEqual([]);

    });
  });

  ////////////////////////////////////////////////////////////////////////////////

  describe("Integration Tests", function() {

    it("should build a basic table", function() {

      // WHEN
      mtf.format_table("|h1|h2|\n|-|-|\n|d1|d2|");

      // THEN
      expect(mtf.output_table).toEqual("| h1 | h2 |\n|----|----|\n| d1 | d2 |\n");

    });

  });

});<commit_msg>Refactored tests to add a 'table' variable to the test suite. This makes it easier to show GIVEN states.<commit_after>describe("MarkdownTableFormatter", function() {

  // Initialize variables
  var mtf;
  var table = "";

  beforeEach(function() {
    mtf = new MarkdownTableFormatter();
    table = "";

  });

  ////////////////////////////////////////////////////////////////////////////////

  describe("add_missing_cell_columns", function() {

    it("should added empty cells where necessary", function() {

      // GIVEN
      mtf.cells = [
        ['h1', 'h2', 'h3', "h4"],
        ['-','-', '-', '-'],
        ['d1','d2','d3']
      ];

      mtf.column_widths = [2,2,2,2];

      // WHEN
      mtf.add_missing_cell_columns();

      // THEN
      expect(mtf.cells).toEqual([
        ['h1', 'h2', 'h3', "h4"],
        ['-','-', '-', '-'],
        ['d1','d2','d3', '']
      ]);

    });
  });

  ////////////////////////////////////////////////////////////////////////////////
  describe("get_column_widths", function() {

    it("should load the proper column widths", function() {

      // GIVEN
      mtf.cells = [
        ['h1', 'h2', 'header_number_3'],
        ['-','-','-'],
        ['data_1', 'd2', 'data_3']
      ];

      // WHEN
      mtf.get_column_widths();

      // THEN
      expect(mtf.column_widths).toEqual([6,2,15]);

    });

  });

  ////////////////////////////////////////////////////////////////////////////////
  describe("import_table", function() {

    it("should import a basic table properly", function() {

      // WHEN
      mtf.import_table('|h1|h2|\n|-|-|\n|d1|d2|');

      // THEN
      expect(mtf.cells).toEqual([ ['h1', 'h2'], ['-','-'], ['d1','d2'] ]);

    });

    it("should strip external white space when importing table", function() {

      // WHEN
      mtf.import_table('| h1 |h2 |  h3|\n|-|-|-|\n|   d1|d2   |       d3     |');

      // THEN
      expect(mtf.cells).toEqual([ ['h1', 'h2', 'h3'], ['-','-', '-'], ['d1','d2','d3'] ]);

    });

  });

  ////////////////////////////////////////////////////////////////////////////////

  describe("initialization", function() {

    it("should initialize instance variables", function() {

      // GIVEN
      // Initialization happened in before.

      // THEN
      expect(mtf.cells.length).toEqual(0);
      expect(mtf.output_table).toEqual("");
      expect(mtf.column_widths).toEqual([]);

    });
  });

  ////////////////////////////////////////////////////////////////////////////////

  describe("Integration Tests", function() {

    it("should build a basic table", function() {

      // GIVEN
      table = "|h1|h2|\n|-|-|\n|d1|d2|";

      // WHEN
      mtf.format_table(table);

      // THEN
      expect(mtf.output_table).toEqual("| h1 | h2 |\n|----|----|\n| d1 | d2 |\n");

    });

    // it("should build a table where cells are added", function() {

    //   //

    //   // WHEN
    //   mtf.format_table("|h1|h2|\n|-|-|\n|d1|d2|");

    // });

  });

});<|endoftext|>


<commit_before>// JSON-P Twitter fetcher for Octopress
// (c) Brandon Mathis // MIT Lisence

/* Sky Slavin, Ludopoli. MIT license.  * based on JavaScript Pretty Date * Copyright (c) 2008 John Resig (jquery.com) * Licensed under the MIT license.  */
function prettyDate(time) {
  if (navigator.appName === 'Microsoft Internet Explorer') {
    return "∞"; // because IE date parsing isn't fun.
  }

  var say = {
    just_now:    " now",
    minute_ago:  "1m",
    minutes_ago: "m",
    hour_ago:    "1h",
    hours_ago:   "h",
    yesterday:   "1d",
    days_ago:    "d",
    weeks_ago:   "w"
  };

  var current_date = new Date(),
      current_date_time = current_date.getTime(),
      current_date_full = current_date_time + (1 * 60000),
      date = new Date(time),
      diff = ((current_date_full - date.getTime()) / 1000),
      day_diff = Math.floor(diff / 86400);

  if (isNaN(day_diff) || day_diff < 0) { return "∞"; }

  return day_diff === 0 && (
    diff < 60 && say.just_now ||
    diff < 120 && say.minute_ago ||
    diff < 3600 && Math.floor(diff / 60) + say.minutes_ago ||
    diff < 7200 && say.hour_ago ||
    diff < 86400 && Math.floor(diff / 3600) + say.hours_ago) ||
    day_diff === 1 && say.yesterday ||
    day_diff < 7 && day_diff + say.days_ago ||
    day_diff > 7 && Math.ceil(day_diff / 7) + say.weeks_ago;
}

function linkifyTweet(text, url) {
  // Linkify urls, usernames, hashtags
  text = text.replace(/(https?:\/\/)([\w\-:;?&=+.%#\/]+)/gi, '$2')
    .replace(/(^|\W)@(\w+)/g, '$1@$2')
    .replace(/(^|\W)#(\w+)/g, '$1#$2');

  // Use twitter's api to replace t.co shortened urls with expanded ones.
  for (var u in url) {
    if(url[u].expanded_url != null){
      var shortUrl = new RegExp( url[u].url.replace(/https?:\/\//, ''), 'g');
      text = text.replace(shortUrl, url[u].display_url);
      console.log(text);
    }
  }
  return text
}

function showTwitterFeed(tweets, twitter_user) {
  var timeline = document.getElementById('tweets'),
      content = '';

  for (var t in tweets) {
    content += '
'+'
'+''+prettyDate(tweets[t].created_at)+''+linkifyTweet(tweets[t].text.replace(/\n/g, '
'), tweets[t].entities.urls)+'
'+'
';
  }
  timeline.innerHTML = content;
}

function getTwitterFeed(user, count, replies) {
  $.ajax({
      url: "http://api.twitter.com/1/statuses/user_timeline/" + user + ".json?trim_user=true&count=" + (parseInt(count, 10)) + "&include_entities=1&exclude_replies=" + (replies ? "0" : "1") + "&callback=?"
    , type: 'jsonp'
    , error: function (err) { $('#tweets li.loading').addClass('error').text("Twitter's busted"); }
    , success: function(data) { showTwitterFeed(data, user); }
  })
}

<commit_msg>added buffer for mentions in twitter stream<commit_after>// JSON-P Twitter fetcher for Octopress
// (c) Brandon Mathis // MIT Lisence

/* Sky Slavin, Ludopoli. MIT license.  * based on JavaScript Pretty Date * Copyright (c) 2008 John Resig (jquery.com) * Licensed under the MIT license.  */
function prettyDate(time) {
  if (navigator.appName === 'Microsoft Internet Explorer') {
    return "∞"; // because IE date parsing isn't fun.
  }

  var say = {
    just_now:    " now",
    minute_ago:  "1m",
    minutes_ago: "m",
    hour_ago:    "1h",
    hours_ago:   "h",
    yesterday:   "1d",
    days_ago:    "d",
    weeks_ago:   "w"
  };

  var current_date = new Date(),
      current_date_time = current_date.getTime(),
      current_date_full = current_date_time + (1 * 60000),
      date = new Date(time),
      diff = ((current_date_full - date.getTime()) / 1000),
      day_diff = Math.floor(diff / 86400);

  if (isNaN(day_diff) || day_diff < 0) { return "∞"; }

  return day_diff === 0 && (
    diff < 60 && say.just_now ||
    diff < 120 && say.minute_ago ||
    diff < 3600 && Math.floor(diff / 60) + say.minutes_ago ||
    diff < 7200 && say.hour_ago ||
    diff < 86400 && Math.floor(diff / 3600) + say.hours_ago) ||
    day_diff === 1 && say.yesterday ||
    day_diff < 7 && day_diff + say.days_ago ||
    day_diff > 7 && Math.ceil(day_diff / 7) + say.weeks_ago;
}

function linkifyTweet(text, url) {
  // Linkify urls, usernames, hashtags
  text = text.replace(/(https?:\/\/)([\w\-:;?&=+.%#\/]+)/gi, '$2')
    .replace(/(^|\W)@(\w+)/g, '$1@$2')
    .replace(/(^|\W)#(\w+)/g, '$1#$2');

  // Use twitter's api to replace t.co shortened urls with expanded ones.
  for (var u in url) {
    if(url[u].expanded_url != null){
      var shortUrl = new RegExp( url[u].url.replace(/https?:\/\//, ''), 'g');
      text = text.replace(shortUrl, url[u].display_url);
    }
  }
  return text
}

function showTwitterFeed(tweets, twitter_user) {
  var timeline = document.getElementById('tweets'),
      content = '';

  for (var t in tweets) {
    content += '
'+'
'+''+prettyDate(tweets[t].created_at)+''+linkifyTweet(tweets[t].text.replace(/\n/g, '
'), tweets[t].entities.urls)+'
'+'
';
  }
  timeline.innerHTML = content;
}

function getTwitterFeed(user, count, replies) {
  count = parseInt(count, 10);
  $.ajax({
      url: "http://api.twitter.com/1/statuses/user_timeline/" + user + ".json?trim_user=true&count=" + (count + 20) + "&include_entities=1&exclude_replies=" + (replies ? "0" : "1") + "&callback=?"
    , type: 'jsonp'
    , error: function (err) { $('#tweets li.loading').addClass('error').text("Twitter's busted"); }
    , success: function(data) { showTwitterFeed(data.slice(0, count), user); }
  })
}
<|endoftext|>


<commit_before>var crypto = require('crypto');

// just return true/false if it's at least the format of a sha1
exports.isSHA1 = function(str)
{
  if(typeof str !== "string") return false;
  if(str.length !== 40) return false;
  if(str.replace(/[a-f0-9]+/i, "").length !== 0) return false;
  return true;
}

// convenience
exports.quick = function(str)
{
  return crypto.createHash("sha1").update(str).digest('hex');
}

/**
 * Hash objects represent the sha1 of string content,
 * with methods useful to DHT calculations.
 * @constructor
 */
function Hash(value, hex) {
    if(value == undefined) value = "";
    if(hex) this.digest = hex2buf(hex);
    // if failed still, just treat as a string
    if (!this.digest) {
        var hashAlgorithm = crypto.createHash("SHA1");
        hashAlgorithm.update(value);
        this.digest = new Buffer(hashAlgorithm.digest("base64"), "base64");
    }
    this.nibbles = [];
    for (var i = 0; i < this.digest.length; i++) {
        this.nibbles[this.nibbles.length] = this.digest[i] >> 4;
        this.nibbles[this.nibbles.length] = this.digest[i] & 0xf;
    }
}

function hex2buf(str)
{
    var buf = new Buffer(20);
    for (var i = 0; i < str.length / 2; i ++) {
        var byte = parseInt(str.substr(i * 2, 2), 16);
        if (isNaN(byte)) return null;
        buf[i] = byte;
    }
    return buf;
}

/**
 * Format a byte as a two digit hex string.
 */
function byte2hex(d) {
    return d < 16 ? "0" + d.toString(16) : d.toString(16);
}

exports.Hash = Hash

/**
 * Get the string hash as geometrically "far" as possible from this one.
 * That would be the logical inverse, every bit flipped.
 */
Hash.prototype.far = function() {
    var result = [];
    for (var i = 0; i < this.digest.length; i++) {
        result[i] = byte2hex(this.digest[i] ^ 0xff);
    }
    return result.join("");
}

/**
 * Logical bitwise 'or' this hash with another.
 */
Hash.prototype.or = function(h) {
    if (typeof h == 'string') { h = new Hash(h); }

    var result = new Hash();
    result.digest = new Buffer(this.digest.length);
    for (var i = 0; i < this.digest.length; i++) {
        result.digest[i] = this.digest[i] ^ h.digest[i];
    }
    return result;
}

/**
 * Comparator for hash objects.
 */
Hash.prototype.cmp = function(h) {
    for (var i = 0; i < this.digest.length; i++) {
        var d = this.digest[i] - h.digest[i];
        if (d != 0) {
            return d;
        }
    }
    return 0;
}

/**
 * XOR distance between two sha1 hex hashes, 159 is furthest bit, 0 is closest bit, -1 is same hash
 */
Hash.prototype.distanceTo = function(h) {
    var sbtab = [-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3];
    var ret = 156;
    for (var i = 0; i < this.nibbles.length; i++) {
        var diff = this.nibbles[i] ^ h.nibbles[i];
        if (diff) {
            return ret + sbtab[diff];
        }
        ret -= 4;
    }
    return -1; // samehash ?!
}

/**
 * Represent the hash as a hexadecimal string.
 */
Hash.prototype.toString = function() {
    var result = [];
    for (var i = this.digest.length - 1; i >= 0; i--) {
        result[i] = byte2hex(this.digest[i]);
    }
    return result.join("");
}

/**
 * Test if two hashes are equal.
 */
Hash.prototype.equals = function(h) {
    return this.toString() == h.toString();
}
<commit_msg>simple util to gen a random hash<commit_after>var crypto = require('crypto');

// just return true/false if it's at least the format of a sha1
exports.isSHA1 = function(str)
{
  if(typeof str !== "string") return false;
  if(str.length !== 40) return false;
  if(str.replace(/[a-f0-9]+/i, "").length !== 0) return false;
  return true;
}

// convenience
exports.quick = function(str)
{
  return crypto.createHash("sha1").update(str).digest('hex');
}
exports.random = function(seed)
{
  return crypto.createHash("sha1").update(seed+(Math.random()*Date.now()).toString()).digest('hex');
}

/**
 * Hash objects represent the sha1 of string content,
 * with methods useful to DHT calculations.
 * @constructor
 */
function Hash(value, hex) {
    if(value == undefined) value = "";
    if(hex) this.digest = hex2buf(hex);
    // if failed still, just treat as a string
    if (!this.digest) {
        var hashAlgorithm = crypto.createHash("SHA1");
        hashAlgorithm.update(value);
        this.digest = new Buffer(hashAlgorithm.digest("base64"), "base64");
    }
    this.nibbles = [];
    for (var i = 0; i < this.digest.length; i++) {
        this.nibbles[this.nibbles.length] = this.digest[i] >> 4;
        this.nibbles[this.nibbles.length] = this.digest[i] & 0xf;
    }
}

function hex2buf(str)
{
    var buf = new Buffer(20);
    for (var i = 0; i < str.length / 2; i ++) {
        var byte = parseInt(str.substr(i * 2, 2), 16);
        if (isNaN(byte)) return null;
        buf[i] = byte;
    }
    return buf;
}

/**
 * Format a byte as a two digit hex string.
 */
function byte2hex(d) {
    return d < 16 ? "0" + d.toString(16) : d.toString(16);
}

exports.Hash = Hash

/**
 * Get the string hash as geometrically "far" as possible from this one.
 * That would be the logical inverse, every bit flipped.
 */
Hash.prototype.far = function() {
    var result = [];
    for (var i = 0; i < this.digest.length; i++) {
        result[i] = byte2hex(this.digest[i] ^ 0xff);
    }
    return result.join("");
}

/**
 * Logical bitwise 'or' this hash with another.
 */
Hash.prototype.or = function(h) {
    if (typeof h == 'string') { h = new Hash(h); }

    var result = new Hash();
    result.digest = new Buffer(this.digest.length);
    for (var i = 0; i < this.digest.length; i++) {
        result.digest[i] = this.digest[i] ^ h.digest[i];
    }
    return result;
}

/**
 * Comparator for hash objects.
 */
Hash.prototype.cmp = function(h) {
    for (var i = 0; i < this.digest.length; i++) {
        var d = this.digest[i] - h.digest[i];
        if (d != 0) {
            return d;
        }
    }
    return 0;
}

/**
 * XOR distance between two sha1 hex hashes, 159 is furthest bit, 0 is closest bit, -1 is same hash
 */
Hash.prototype.distanceTo = function(h) {
    var sbtab = [-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3];
    var ret = 156;
    for (var i = 0; i < this.nibbles.length; i++) {
        var diff = this.nibbles[i] ^ h.nibbles[i];
        if (diff) {
            return ret + sbtab[diff];
        }
        ret -= 4;
    }
    return -1; // samehash ?!
}

/**
 * Represent the hash as a hexadecimal string.
 */
Hash.prototype.toString = function() {
    var result = [];
    for (var i = this.digest.length - 1; i >= 0; i--) {
        result[i] = byte2hex(this.digest[i]);
    }
    return result.join("");
}

/**
 * Test if two hashes are equal.
 */
Hash.prototype.equals = function(h) {
    return this.toString() == h.toString();
}
<|endoftext|>