<commit_before>var debug = require('debug')('json-schema');

var loopback = require('loopback');
var Model = require('loopback').Model;

var logger = require('../support/logger')
var JsonSchema = require('../../lib/models/json-schema');

var CollectionSchema = function(schemaId) {
    this.schemaId = schemaId;
};

CollectionSchema.prototype.data = function(callback) {
    findBySchemaId(this.schemaId, function(err, itemSchema){
        var schema;

        if (itemSchema) {
            schema = {
                $schema: itemSchema.$schema,
                title: itemSchema.collectionTitle,
                type: 'array',
                items: {
                    $ref: "itemSchema"
                }
            };
        }

        callback(err, schema);
    });
};

function findBySchemaId(schemaId, callback) {
    JsonSchema.findById(schemaId, function(err, itemSchema){
        if (err) {
            logger.error("Error fetching ItemSchema for schemaId:", schemaId, "Error:", err);
        } else if (itemSchema === null) {
            logger.info("Item Schema for schemaId", schemaId, "not found.");
        }

        callback(err, itemSchema);
    });
};

module.exports = CollectionSchema;<commit_msg>Whitespace.<commit_after>var debug = require('debug')('json-schema');

var loopback = require('loopback');
var Model = require('loopback').Model;

var logger = require('../support/logger')
var JsonSchema = require('../../lib/models/json-schema');

var CollectionSchema = function(schemaId) {
    this.schemaId = schemaId;
};

CollectionSchema.prototype.data = function(callback) {
    findBySchemaId(this.schemaId, function(err, itemSchema){
        var schema;

        if (itemSchema) {
            schema = {
                $schema: itemSchema.$schema,
                title: itemSchema.collectionTitle,
                type: 'array',
                items: {
                    $ref: "itemSchema"
                }
            };
        }

        callback(err, schema);
    });
};

function findBySchemaId(schemaId, callback) {
    JsonSchema.findById(schemaId, function(err, itemSchema){
        if (err) {
            logger.error("Error fetching ItemSchema for schemaId:", schemaId, "Error:", err);
        } else if (itemSchema === null) {
            logger.info("Item Schema for schemaId", schemaId, "not found.");
        }
        callback(err, itemSchema);
    });
};

module.exports = CollectionSchema;<|endoftext|>


<commit_before>const Cookies          = require('js-cookie');
const Client           = require('./client');
const Contents         = require('./contents');
const Header           = require('./header');
const Login            = require('./login');
const Menu             = require('./menu');
const BinarySocket     = require('./socket');
const checkLanguage    = require('../common/country_base').checkLanguage;
const TrafficSource    = require('../common/traffic_source');
const RealityCheck     = require('../pages/user/reality_check/reality_check');
const elementInnerHtml = require('../../_common/common_functions').elementInnerHtml;
const getElementById   = require('../../_common/common_functions').getElementById;
const Crowdin          = require('../../_common/crowdin');
const Language         = require('../../_common/language');
const PushNotification = require('../../_common/lib/push_notification');
const Localize         = require('../../_common/localize');
const localize         = require('../../_common/localize').localize;
const State            = require('../../_common/storage').State;
const scrollToTop      = require('../../_common/scroll').scrollToTop;
const ScrollToAnchor   = require('../../_common/scroll_to_anchor');
const Url              = require('../../_common/url');
const createElement    = require('../../_common/utility').createElement;
const AffiliatePopup   = require('../../static/japan/affiliate_popup');
require('../../_common/lib/polyfills/array.includes');
require('../../_common/lib/polyfills/string.includes');

const Page = (() => {
    const init = () => {
        State.set('is_loaded_by_pjax', false);
        Url.init();
        PushNotification.init();
        onDocumentReady();
        Crowdin.init();
    };

    const onDocumentReady = () => {
        // LocalStorage can be used as a means of communication among
        // different windows. The problem that is solved here is what
        // happens if the user logs out or switches loginid in one
        // window while keeping another window or tab open. This can
        // lead to unintended trades. The solution is to reload the
        // page in all windows after switching loginid or after logout.

        // onLoad.queue does not work on the home page.
        // jQuery's ready function works always.
        $(document).ready(() => {
            // Cookies is not always available.
            // So, fall back to a more basic solution.
            window.addEventListener('storage', (evt) => {
                switch (evt.key) {
                    case 'active_loginid':
                        // not the active tab and logged out or loginid switch
                        if (document.hidden && (evt.newValue === '' || !window.is_logging_in)) {
                            reload();
                        }
                        break;
                    case 'new_release_reload_time':
                        if (evt.newValue !== evt.oldValue) {
                            reload(true);
                        }
                        break;
                    // no default
                }
            });
            scrollToTop();
            ScrollToAnchor.init();
        });
    };

    const onLoad = () => {
        if (State.get('is_loaded_by_pjax')) {
            Url.reset();
        } else {
            init();
            if (!Login.isLoginPages()) {
                Language.setCookie(Language.urlLang());
            }
            Localize.forLang(Language.urlLang());
            Header.onLoad();
            Language.setCookie();
            Menu.makeMobileMenu();
            recordAffiliateExposure();
            endpointNotification();
        }
        Contents.onLoad();

        if (sessionStorage.getItem('showLoginPage')) {
            sessionStorage.removeItem('showLoginPage');
            Login.redirectToLogin();
        }
        if (Client.isLoggedIn()) {
            BinarySocket.wait('authorize', 'website_status', 'get_account_status').then(() => {
                checkLanguage();
                RealityCheck.onLoad();
                Menu.init();
            });
        } else {
            checkLanguage();
            Menu.init();
        }
        TrafficSource.setData();
    };

    const recordAffiliateExposure = () => {
        const token = Url.param('t');
        if (!token || token.length !== 32) {
            return false;
        }

        AffiliatePopup.show();

        const token_length  = token.length;
        const is_subsidiary = /\w{1}/.test(Url.param('s'));

        const cookie_token = Cookies.getJSON('affiliate_tracking');
        if (cookie_token) {
            // Already exposed to some other affiliate.
            if (is_subsidiary && cookie_token && cookie_token.t) {
                return false;
            }
        }

        // Record the affiliate exposure. Overwrite existing cookie, if any.
        const cookie_hash = {};
        if (token_length === 32) {
            cookie_hash.t = token.toString();
        }
        if (is_subsidiary) {
            cookie_hash.s = '1';
        }

        Cookies.set('affiliate_tracking', cookie_hash, {
            expires: 365, // expires in 365 days
            path   : '/',
            domain : `.${location.hostname.split('.').slice(-2).join('.')}`,
        });
        return true;
    };

    const reload = (forced_reload) => { window.location.reload(!!forced_reload); };

    const endpointNotification = () => {
        const server = localStorage.getItem('config.server_url');
        if (server && server.length > 0) {
            const message = `${(/www\.binary\.com/i.test(window.location.hostname) ? '' :
                `${localize('This is a staging server - For testing purposes only')} - `)}
                ${localize('The server endpoint is: [_2]', [Url.urlFor('endpoint'), server])}`;

            const end_note = getElementById('end-note');
            elementInnerHtml(end_note, message);
            end_note.setVisibility(1);

            getElementById('footer').style['padding-bottom'] = end_note.offsetHeight;
        }
    };

    const showNotificationOutdatedBrowser = () => {
        const src = '//browser-update.org/update.min.js';
        if (document.querySelector(`script[src*="${src}"]`)) return;
        window.$buoop = {
            vs     : { i: 11, f: -4, o: -4, s: 9, c: -4 },
            api    : 4,
            l      : Language.get().toLowerCase(),
            url    : 'https://whatbrowser.org/',
            noclose: true, // Do not show the 'ignore' button to close the notification
            text   : localize('Your web browser ([_1]) is out of date and may affect your trading experience. Proceed at your own risk. [_2]Update browser[_3]',
                ['{brow_name}', '', '']),
            reminder: 0, // show all the time
        };
        if (document.body) {
            document.body.appendChild(createElement('script', { src }));
        }
    };

    return {
        onLoad,
        showNotificationOutdatedBrowser,
    };
})();

module.exports = Page;
<commit_msg>move anchor init to onload<commit_after>const Cookies          = require('js-cookie');
const Client           = require('./client');
const Contents         = require('./contents');
const Header           = require('./header');
const Login            = require('./login');
const Menu             = require('./menu');
const BinarySocket     = require('./socket');
const checkLanguage    = require('../common/country_base').checkLanguage;
const TrafficSource    = require('../common/traffic_source');
const RealityCheck     = require('../pages/user/reality_check/reality_check');
const elementInnerHtml = require('../../_common/common_functions').elementInnerHtml;
const getElementById   = require('../../_common/common_functions').getElementById;
const Crowdin          = require('../../_common/crowdin');
const Language         = require('../../_common/language');
const PushNotification = require('../../_common/lib/push_notification');
const Localize         = require('../../_common/localize');
const localize         = require('../../_common/localize').localize;
const State            = require('../../_common/storage').State;
const scrollToTop      = require('../../_common/scroll').scrollToTop;
const ScrollToAnchor   = require('../../_common/scroll_to_anchor');
const Url              = require('../../_common/url');
const createElement    = require('../../_common/utility').createElement;
const AffiliatePopup   = require('../../static/japan/affiliate_popup');
require('../../_common/lib/polyfills/array.includes');
require('../../_common/lib/polyfills/string.includes');

const Page = (() => {
    const init = () => {
        State.set('is_loaded_by_pjax', false);
        Url.init();
        PushNotification.init();
        onDocumentReady();
        Crowdin.init();
    };

    const onDocumentReady = () => {
        // LocalStorage can be used as a means of communication among
        // different windows. The problem that is solved here is what
        // happens if the user logs out or switches loginid in one
        // window while keeping another window or tab open. This can
        // lead to unintended trades. The solution is to reload the
        // page in all windows after switching loginid or after logout.

        // onLoad.queue does not work on the home page.
        // jQuery's ready function works always.
        $(document).ready(() => {
            // Cookies is not always available.
            // So, fall back to a more basic solution.
            window.addEventListener('storage', (evt) => {
                switch (evt.key) {
                    case 'active_loginid':
                        // not the active tab and logged out or loginid switch
                        if (document.hidden && (evt.newValue === '' || !window.is_logging_in)) {
                            reload();
                        }
                        break;
                    case 'new_release_reload_time':
                        if (evt.newValue !== evt.oldValue) {
                            reload(true);
                        }
                        break;
                    // no default
                }
            });
            scrollToTop();
        });
    };

    const onLoad = () => {
        if (State.get('is_loaded_by_pjax')) {
            Url.reset();
        } else {
            init();
            if (!Login.isLoginPages()) {
                Language.setCookie(Language.urlLang());
            }
            Localize.forLang(Language.urlLang());
            Header.onLoad();
            Language.setCookie();
            Menu.makeMobileMenu();
            recordAffiliateExposure();
            endpointNotification();
        }
        Contents.onLoad();

        if (sessionStorage.getItem('showLoginPage')) {
            sessionStorage.removeItem('showLoginPage');
            Login.redirectToLogin();
        }
        if (Client.isLoggedIn()) {
            BinarySocket.wait('authorize', 'website_status', 'get_account_status').then(() => {
                checkLanguage();
                RealityCheck.onLoad();
                Menu.init();
            });
        } else {
            checkLanguage();
            Menu.init();
        }
        TrafficSource.setData();
        ScrollToAnchor.init();
    };

    const recordAffiliateExposure = () => {
        const token = Url.param('t');
        if (!token || token.length !== 32) {
            return false;
        }

        AffiliatePopup.show();

        const token_length  = token.length;
        const is_subsidiary = /\w{1}/.test(Url.param('s'));

        const cookie_token = Cookies.getJSON('affiliate_tracking');
        if (cookie_token) {
            // Already exposed to some other affiliate.
            if (is_subsidiary && cookie_token && cookie_token.t) {
                return false;
            }
        }

        // Record the affiliate exposure. Overwrite existing cookie, if any.
        const cookie_hash = {};
        if (token_length === 32) {
            cookie_hash.t = token.toString();
        }
        if (is_subsidiary) {
            cookie_hash.s = '1';
        }

        Cookies.set('affiliate_tracking', cookie_hash, {
            expires: 365, // expires in 365 days
            path   : '/',
            domain : `.${location.hostname.split('.').slice(-2).join('.')}`,
        });
        return true;
    };

    const reload = (forced_reload) => { window.location.reload(!!forced_reload); };

    const endpointNotification = () => {
        const server = localStorage.getItem('config.server_url');
        if (server && server.length > 0) {
            const message = `${(/www\.binary\.com/i.test(window.location.hostname) ? '' :
                `${localize('This is a staging server - For testing purposes only')} - `)}
                ${localize('The server endpoint is: [_2]', [Url.urlFor('endpoint'), server])}`;

            const end_note = getElementById('end-note');
            elementInnerHtml(end_note, message);
            end_note.setVisibility(1);

            getElementById('footer').style['padding-bottom'] = end_note.offsetHeight;
        }
    };

    const showNotificationOutdatedBrowser = () => {
        const src = '//browser-update.org/update.min.js';
        if (document.querySelector(`script[src*="${src}"]`)) return;
        window.$buoop = {
            vs     : { i: 11, f: -4, o: -4, s: 9, c: -4 },
            api    : 4,
            l      : Language.get().toLowerCase(),
            url    : 'https://whatbrowser.org/',
            noclose: true, // Do not show the 'ignore' button to close the notification
            text   : localize('Your web browser ([_1]) is out of date and may affect your trading experience. Proceed at your own risk. [_2]Update browser[_3]',
                ['{brow_name}', '', '']),
            reminder: 0, // show all the time
        };
        if (document.body) {
            document.body.appendChild(createElement('script', { src }));
        }
    };

    return {
        onLoad,
        showNotificationOutdatedBrowser,
    };
})();

module.exports = Page;
<|endoftext|>


<commit_before>/*jslint browser:true, plusplus:true, vars:true, regexp:false */
/*jshint browser:true, jquery:true */

(function ($, window, document, undefined) {
    'use strict';

    var options = $.lazyLoadXT,
        srcsetSupport = (function () {
            return 'srcset' in (new Image());
        })(),
        reUrl = /^\s*(\S*)/,
        reWidth = /\S\s+(\d+)w/,
        reHeight = /\S\s+(\d+)h/,
        reDpr = /\S\s+([\d\.]+)x/,
        infty = [0, Infinity],
        one = [0, 1],
        srcsetOptions = {
            srcsetAttr: 'data-srcset',
            srcsetExtended: true,
            srcsetBaseAttr: 'data-srcset-base',
            srcsetExtAttr: 'data-srcset-ext'
        },
        viewport = {
            w: 0,
            h: 0,
            x: 0
        },
        property,
        limit;

    for (property in srcsetOptions) {
        if (options[property] === undefined) {
            options[property] = srcsetOptions[property];
        }
    }
    options.selector += ',img[' + options.srcsetAttr + ']';

    function mathFilter(array, action) {
        return Math[action].apply(null, $.map(array, function (item) {
            return item[property];
        }));
    }

    function compareMax(item) {
        return item[property] >= viewport[property] || item[property] === limit;
    }

    function compareMin(item) {
        return item[property] === limit;
    }

    $(document).on('lazyshow', 'img', function (e, $el) {
        var srcset = $el.attr(options.srcsetAttr);

        if (srcset) {
            if (!options.srcsetExtended && srcsetSupport) {
                $el.attr('srcset', srcset);
            } else {
                var list = srcset.split(',').map(function (item) {
                    return {
                        url: reUrl.exec(item)[1],
                        w: parseFloat((reWidth.exec(item) || infty)[1]),
                        h: parseFloat((reHeight.exec(item) || infty)[1]),
                        x: parseFloat((reDpr.exec(item) || one)[1])
                    };
                });

                if (list.length) {
                    var documentElement = document.documentElement,
                        whx,
                        src;

                    viewport = {
                        w: window.innerWidth || documentElement.clientWidth,
                        h: window.innerHeight || documentElement.clientHeight,
                        x: window.devicePixelRatio || 1
                    };

                    for (whx in viewport) {
                        property = whx;
                        limit = mathFilter(list, 'max');
                        list = $.grep(list, compareMax);
                    }

                    for (whx in viewport) {
                        property = whx;
                        limit = mathFilter(list, 'min');
                        list = $.grep(list, compareMin);
                    }

                    src = list[0].url;

                    if (options.srcsetExtended) {
                        src = ($el.attr(options.srcsetBaseAttr) || '') + src + ($el.attr(options.srcsetExtAttr) || '');
                    }

                    $el.attr('src', src);
                }
            }
        }
    });

})(window.jQuery || window.Zepto, window, document);
<commit_msg>Fix support of lazyload/lazyerror events in srcset addon (issue #2)<commit_after>/*jslint browser:true, plusplus:true, vars:true, regexp:false */
/*jshint browser:true, jquery:true */

(function ($, window, document, undefined) {
    'use strict';

    var options = $.lazyLoadXT,
        srcsetSupport = (function () {
            return 'srcset' in (new Image());
        })(),
        reUrl = /^\s*(\S*)/,
        reWidth = /\S\s+(\d+)w/,
        reHeight = /\S\s+(\d+)h/,
        reDpr = /\S\s+([\d\.]+)x/,
        infty = [0, Infinity],
        one = [0, 1],
        srcsetOptions = {
            srcsetAttr: 'data-srcset',
            srcsetExtended: true,
            srcsetBaseAttr: 'data-srcset-base',
            srcsetExtAttr: 'data-srcset-ext'
        },
        viewport = {
            w: 0,
            h: 0,
            x: 0
        },
        property,
        limit;

    for (property in srcsetOptions) {
        if (options[property] === undefined) {
            options[property] = srcsetOptions[property];
        }
    }
    options.selector += ',img[' + options.srcsetAttr + ']';

    function mathFilter(array, action) {
        return Math[action].apply(null, $.map(array, function (item) {
            return item[property];
        }));
    }

    function compareMax(item) {
        return item[property] >= viewport[property] || item[property] === limit;
    }

    function compareMin(item) {
        return item[property] === limit;
    }

    function parseSrcset($el) {
        var srcset = $el.attr(options.srcsetAttr);

        if (!srcset) {
            return false;
        }

        var list = srcset.split(',').map(function (item) {
            return {
                url: reUrl.exec(item)[1],
                w: parseFloat((reWidth.exec(item) || infty)[1]),
                h: parseFloat((reHeight.exec(item) || infty)[1]),
                x: parseFloat((reDpr.exec(item) || one)[1])
            };
        });

        if (!list.length) {
            return false;
        }

        var documentElement = document.documentElement,
            whx,
            src;

        viewport = {
            w: window.innerWidth || documentElement.clientWidth,
            h: window.innerHeight || documentElement.clientHeight,
            x: window.devicePixelRatio || 1
        };

        for (whx in viewport) {
            property = whx;
            limit = mathFilter(list, 'max');
            list = $.grep(list, compareMax);
        }

        for (whx in viewport) {
            property = whx;
            limit = mathFilter(list, 'min');
            list = $.grep(list, compareMin);
        }

        src = list[0].url;

        if (options.srcsetExtended) {
            src = ($el.attr(options.srcsetBaseAttr) || '') + src + ($el.attr(options.srcsetExtAttr) || '');
        }

        return src;
    }

    $(document).on('lazyshow', 'img', function (e, $el) {
        var srcset = $el.attr(options.srcsetAttr);

        if (srcset) {
            if (!options.srcsetExtended && srcsetSupport) {
                $el.attr('srcset', srcset);
            } else {
                $el.lazyLoadXT.srcAttr = parseSrcset;
            }
        }
    });

})(window.jQuery || window.Zepto, window, document);
<|endoftext|>


<commit_before>function DisplayManager(editor) {
    /* Outlets */
    this.convertedDiv = $("#markdown");
    this.conversionDiv = $("#html-conversion");

    /* Variables */
    this.currentGallery;
    this.editor = editor;
    this.galleries = [];
    this.imagesDisplayed = new ImageArray();
    this.imgFormats = ["png", "bmp", "jpeg", "jpg", "gif", "png", "svg", "xbm", "webp"]; // Authorized images' type.
    this.imageFound;
    this.loadedImagePath;
    this.imagePosition = 0;
    this.scrollManager = new ScrollManager(this.convertedDiv, this.conversionDiv);
    this.styleManager = new StyleManager();
    this.tempConversion; // Temporary conversion before it is displayed in the conversionDiv.
    this.urlManager = new UrlManager(this.conversionDiv);

    /* Events */
    this.conversionDiv.on("click", "a", function(e) {
        if (e.currentTarget.href.indexOf("chrome-extension://") != -1) { // Click on an inner link.
            e.preventDefault();
            if (e.currentTarget.hash != "" && $(e.currentTarget.hash).length != 0) {
                $("#html-conversion").scrollTop($(e.currentTarget.hash).position().top);
            }
        }
    });
    chrome.storage.onChanged.addListener($.proxy(function (changes, namespace) {
        for (key in changes) {
            switch (key) {
            case "gfm":
                this.setSyntax();
                break;
            }
        }
    }, this));

    /* Initialization */
    this.setSyntax();
}

DisplayManager.prototype = {
    constructor : DisplayManager,
    displayImages: function() {
        if (this.tempConversion.indexOf(" -1) {
            this.imagePosition = this.tempConversion.indexOf(" -1) {
                if (this.imagesDisplayed.hasPath(this.loadedImagePath)) { // Image is already stored.
                    var image = this.imagesDisplayed.getImage(this.loadedImagePath);
                    this.tempConversion = this.tempConversion.substring(0, this.imagePosition) + image + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length); // Replace the path.
                    this.displayImages(); // Recursivity.
                } else if (this.loadedImagePath.substring(0, 7) == "http://" || this.loadedImagePath.substring(0, 8) == "https://") {
                    if (navigator.onLine) { // Online images
                        this.getOnlineImage();
                    } else {
                        tempConversion = tempConversion.substring(0, this.imagePosition - 10) + " " + chrome.i18n.getMessage("msgNoInternet") + " <img class=\"nofile\" srcset=\"img/nointernet.png 1x, img/nointernet@2x.png 2x" + tempConversion.substring(this.imagePosition + this.loadedImagePath.length);
                    }
                } else {
                    this.getOfflineImage();
                }
            } else if (this.loadedImagePath.substring(0, 5) != "data:" && this.loadedImagePath.substring(0, 5) != "blob:") {
                this.tempConversion = this.tempConversion.substring(0, this.imagePosition - 10) + " " + chrome.i18n.getMessage("msgNotAnImage") + " <img class=\"nofile\" srcset=\"img/notimage.png 1x, img/notimage@2x.png 2x" + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length);;
                this.displayImages();
            }
        } else {
            this.imagesDisplayed.clean();
            this.imagePosition = 0;
            this.finishDisplaying();
        }
    },
    finishDisplaying: function() {
        this.conversionDiv.html(this.tempConversion);

        $("#html-conversion a").each(function() { // Add target="_blank" to make links work.
            if ($(this).attr("href").substring(0,1) != '#' && $(this).attr("href").substring(0,4) != "http") { // External link without correct syntax.
                $(this).attr("href", "http://" + $(this).attr("href"));
            }
            $(this).attr("target", "_blank");
        });

        $("#html-conversion .nofile, #html-conversion .nofile-link, #html-conversion .nofile-visual").on("click", $.proxy(function(e){ this.editor.setGalleries(); }, this)); // If an image isn't loaded, a default image appeared and, if the user clicks, the galleries choice appeared.
    },
    galleryAnalysis: function(index) {
        var t = this;
        if (! this.imageFound) {
            if (index < this.galleries.length) {
                this.currentGallery = index;
                this.galleries.forEach(function(item, indx, arr) { // For each gallery.
                    item.root.createReader().readEntries(function(entries) {
                        t.getImages(entries);
                    });
                });
            } else {
                this.tempConversion = this.tempConversion.substring(0, this.imagePosition - 10) + " " + this.loadedImagePath.replace(/\\/g, "/").substring(this.loadedImagePath.lastIndexOf('/') + 1); + chrome.i18n.getMessage("msgNotFound") + " <img class=\"nofile\" srcset=\"img/nofile.png 1x, img/nofile@2x.png 2x" + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length);
                this.displayImages();
            }
        }
    },
    getImages: function(entries) {
        console.log(entries);
        for (var i = 0; i < entries.length && !this.imageFound; i++) { // All the files in the repository, the correct file is not found yet.
            if (entries[i].isDirectory && this.loadedImagePath.indexOf(entries[i].fullPath) != -1) {// If the file is a directory and the right directory.
                entries[i].createReader().readEntries($.proxy(function(entries) {
                    this.getImages(entries);
                }, this)); // Recursivity.
                break;
            } else if (this.loadedImagePath.indexOf(entries[i].fullPath) != -1) { // It's the correct image!
                var t = this;
                entries[i].file(function(file) {
                    var reader = new FileReader();
                    reader.onloadend = function(e) {
                        t.imagesDisplayed.addImage(t.loadedImagePath, e.target.result); // Add a new line.
                        t.tempConversion = t.tempConversion.substring(0, t.imagePosition) + e.target.result + t.tempConversion.substring(t.imagePosition + t.loadedImagePath.length); // Replace the path.
                        t.displayImages();
                    };
                    reader.readAsDataURL(file);
                });
                this.imageFound = true;
            } else if (i == (entries.length - 1)) { // End of the gallery.
                this.galleryAnalysis(this.currentGallery + 1);
            }
        }
    },
    getOfflineImage: function() {
        this.imageFound = false;
        chrome.mediaGalleries.getMediaFileSystems({ interactive : "no" }, $.proxy(function(galleries) {
            this.galleries = galleries;
            this.galleryAnalysis(0);
        }, this));
    },
    getOnlineImage: function() {
        var xhr = new XMLHttpRequest();
        xhr.responseType = "blob";
        xhr.onload = $.proxy(function() {
            var webImage = window.URL.createObjectURL(xhr.response);
            this.imagesDisplayed.addImage(this.loadedImagePath, webImage); // Add a new line.
            this.tempConversion = this.tempConversion.substring(0, this.imagePosition) + webImage + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length); // Replace the path.
            this.displayImages();
        }, this);
        xhr.open('GET', this.loadedImagePath, true);
        xhr.send(this.loadedImagePath);
    },
    setSyntax: function() {
        chrome.storage.local.get("gfm", $.proxy(function(mado) {
            if (mado["gfm"] != undefined) {
                marked.setOptions({ gfm : mado["gfm"] });
            } else {
                chrome.storage.local.set({ "gfm" : true });
                marked.setOptions({ gfm : true });
            }
            this.update();
        }, this));
    },
    update: function() {
        if (this.editor.getLength() > 0) { // There is Markdown in the textarea.
            this.tempConversion = marked(this.editor.getMarkdown());
            this.scrollManager.checkZonesHeight();
            this.displayImages(); // We will finish displaying it after displaying every images.
        } else { // No Markdown here.
            this.conversionDiv.html(chrome.i18n.getMessage("msgNoTextInEditor"));
        }
    }
}
<commit_msg>Improvements in displayManager<commit_after>function DisplayManager(editor) {
    /* Outlets */
    this.convertedDiv = $("#markdown");
    this.conversionDiv = $("#html-conversion");

    /* Variables */
    this.currentGallery;
    this.editor = editor;
    this.galleries = [];
    this.imagesDisplayed = new ImageArray();
    this.imgFormats = ["png", "bmp", "jpeg", "jpg", "gif", "png", "svg", "xbm", "webp"]; // Authorized images' type.
    this.imageFound;
    this.loadedImagePath;
    this.imagePosition = 0;
    this.scrollManager = new ScrollManager(this.convertedDiv, this.conversionDiv);
    this.styleManager = new StyleManager();
    this.tempConversion; // Temporary conversion before it is displayed in the conversionDiv.
    this.urlManager = new UrlManager(this.conversionDiv);

    /* Events */
    this.conversionDiv.on("click", "a", function(e) {
        if (e.currentTarget.href.indexOf("chrome-extension://") != -1) { // Click on an inner link.
            e.preventDefault();
            if (e.currentTarget.hash != "" && $(e.currentTarget.hash).length != 0) {
                $("#html-conversion").scrollTop($(e.currentTarget.hash).position().top);
            }
        }
    });
    chrome.storage.onChanged.addListener($.proxy(function (changes, namespace) {
        for (key in changes) {
            switch (key) {
            case "gfm":
                this.setSyntax();
                break;
            }
        }
    }, this));

    /* Initialization */
    this.setSyntax();
}

DisplayManager.prototype = {
    constructor : DisplayManager,
    displayImages: function() {
        console.log("Display images");
        if (this.tempConversion.indexOf(" -1) {
            this.imagePosition = this.tempConversion.indexOf(" -1) {
                if (this.imagesDisplayed.hasPath(this.loadedImagePath)) { // Image is already stored.
                    var image = this.imagesDisplayed.getImage(this.loadedImagePath);
                    this.tempConversion = this.tempConversion.substring(0, this.imagePosition) + image + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length); // Replace the path.
                    this.displayImages(); // Recursivity.
                } else if (this.loadedImagePath.substring(0, 7) == "http://" || this.loadedImagePath.substring(0, 8) == "https://") {
                    if (navigator.onLine) { // Online images
                        this.getOnlineImage();
                    } else {
                        tempConversion = tempConversion.substring(0, this.imagePosition - 10) + " " + chrome.i18n.getMessage("msgNoInternet") + " <img class=\"nofile\" srcset=\"img/nointernet.png 1x, img/nointernet@2x.png 2x" + tempConversion.substring(this.imagePosition + this.loadedImagePath.length);
                    }
                } else {
                    this.getOfflineImage();
                }
            } else if (this.loadedImagePath.substring(0, 5) != "data:" && this.loadedImagePath.substring(0, 5) != "blob:") {
                console.loh("Ce n'est pas une image");
                this.tempConversion = this.tempConversion.substring(0, this.imagePosition - 10) + " " + chrome.i18n.getMessage("msgNotAnImage") + " <img class=\"nofile\" srcset=\"img/notimage.png 1x, img/notimage@2x.png 2x" + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length);;
                this.displayImages();
            }
        } else {
            this.imagesDisplayed.clean();
            this.imagePosition = 0;
            this.finishDisplaying();
        }
    },
    finishDisplaying: function() {
        this.conversionDiv.html(this.tempConversion);

        $("#html-conversion a").each(function() { // Add target="_blank" to make links work.
            if ($(this).attr("href").substring(0,1) != '#' && $(this).attr("href").substring(0,4) != "http") { // External link without correct syntax.
                $(this).attr("href", "http://" + $(this).attr("href"));
            }
            $(this).attr("target", "_blank");
        });

        $("#html-conversion .nofile, #html-conversion .nofile-link, #html-conversion .nofile-visual").on("click", $.proxy(function(e){ this.editor.setGalleries(); }, this)); // If an image isn't loaded, a default image appeared and, if the user clicks, the galleries choice appeared.
    },
    galleryAnalysis: function(index) {
        var t = this;
        if (! this.imageFound) {
            if (index < this.galleries.length) {
                this.currentGallery = index;
                this.galleries.forEach(function(item, indx, arr) { // For each gallery.
                    if (indx == index) {
                        item.root.createReader().readEntries(function(entries) {
                            console.log("Nouvelle gallerie");
                            t.getImages(entries, 0);
                        });
                    }
                });
            } else {
                this.tempConversion = this.tempConversion.substring(0, this.imagePosition - 10) + " " + this.loadedImagePath.replace(/\\/g, "/").substring(this.loadedImagePath.lastIndexOf('/') + 1); + chrome.i18n.getMessage("msgNotFound") + " <img class=\"nofile\" srcset=\"img/nofile.png 1x, img/nofile@2x.png 2x" + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length);
                this.displayImages();
            }
        }
    },
    getImages: function(entries, i) {
        if (i < entries.length) {
            console.log(entries[i]);
            if (entries[i].isDirectory && this.loadedImagePath.indexOf(entries[i].fullPath.replace(/\'/g, "'")) != -1) { // If the file is a directory and the right directory.
                console.log("C'est le bon dossier, on y va et on diminue le jeton");
                entries[i].createReader().readEntries($.proxy(function(directory) {
                    this.getImages(directory, 0);
                }, this)); // Recursivity.
            } else if (this.loadedImagePath.indexOf(entries[i].fullPath.replace(/\'/g, "'")) != -1) { // It's the correct image!
                console.log("Bonne image !");
                this.imageFound = true;
                var t = this;
                entries[i].file(function(file) {
                    var reader = new FileReader();
                    reader.onloadend = function(e) {
                        t.imagesDisplayed.addImage(t.loadedImagePath, e.target.result); // Add a new line.
                        t.tempConversion = t.tempConversion.substring(0, t.imagePosition) + e.target.result + t.tempConversion.substring(t.imagePosition + t.loadedImagePath.length); // Replace the path.
                        t.displayImages();
                    };
                    reader.readAsDataURL(file);
                });
            }  else {
                console.log("Mauvaise image ou dossier, on continue sans diminuer le jeton");
                this.getImages(entries, i + 1);
            }
        } else {
            if (this.currentGallery < (this.galleries.length - 1)) { // We still have galleries to search.
                console.log("Changement de gallerie");
                this.galleryAnalysis(this.currentGallery + 1);
            }
        }
    },
    getOfflineImage: function() {
        this.imageFound = false;
        chrome.mediaGalleries.getMediaFileSystems({ interactive : "no" }, $.proxy(function(galleries) {
            this.galleries = galleries;
            this.galleryAnalysis(0);
        }, this));
    },
    getOnlineImage: function() {
        var xhr = new XMLHttpRequest();
        xhr.responseType = "blob";
        xhr.onload = $.proxy(function() {
            var webImage = window.URL.createObjectURL(xhr.response);
            this.imagesDisplayed.addImage(this.loadedImagePath, webImage); // Add a new line.
            this.tempConversion = this.tempConversion.substring(0, this.imagePosition) + webImage + this.tempConversion.substring(this.imagePosition + this.loadedImagePath.length); // Replace the path.
            this.displayImages();
        }, this);
        xhr.open('GET', this.loadedImagePath, true);
        xhr.send(this.loadedImagePath);
    },
    setSyntax: function() {
        chrome.storage.local.get("gfm", $.proxy(function(mado) {
            if (mado["gfm"] != undefined) {
                marked.setOptions({ gfm : mado["gfm"] });
            } else {
                chrome.storage.local.set({ "gfm" : true });
                marked.setOptions({ gfm : true });
            }
            this.update();
        }, this));
    },
    update: function() {
        if (this.editor.getLength() > 0) { // There is Markdown in the textarea.
            this.tempConversion = marked(this.editor.getMarkdown());
            this.scrollManager.checkZonesHeight();
            this.displayImages(); // We will finish displaying it after displaying every images.
        } else { // No Markdown here.
            this.conversionDiv.html(chrome.i18n.getMessage("msgNoTextInEditor"));
        }
    }
}
<|endoftext|>


<commit_before>// Set up a cluster and set up resizing and such.

var cluster = require("cluster")
, quitting = false
, restarting = false
, path = require("path")
, clusterSize = 0
, env
, os = require("os")
, onmessage
, repl = require('repl')
, replAddressPath = process.env.CLUSTER_MASTER_REPL || 'cluster-master-socket'
, net = require('net')
, fs = require('fs')
, util = require('util')
, minRestartAge = 2000
, danger = false

exports = module.exports = clusterMaster
exports.restart = restart
exports.resize = resize
exports.quitHard = quitHard
exports.quit = quit

var debugStreams = {}
function debug () {
  console.error.apply(console, arguments)

  var msg = util.format.apply(util, arguments)
  Object.keys(debugStreams).forEach(function (s) {
    try {
      // if the write fails, just remove it.
      debugStreams[s].write(msg + '\n')
      if (debugStreams[s].repl) debugStreams[s].repl.displayPrompt()
    } catch (e) {
      delete debugStreams[s]
    }
  })
}

function clusterMaster (config) {
  if (typeof config === "string") config = { exec: config }

  if (!config.exec) {
    throw new Error("Must define a 'exec' script")
  }

  if (!cluster.isMaster) {
    throw new Error("ClusterMaster answers to no one!\n"+
                    "(don't run in a cluster worker script)")
  }

  if (cluster._clusterMaster) {
    throw new Error("This cluster has a master already")
  }

  cluster._clusterMaster = module.exports

  if (typeof config.repl !== 'undefined') replAddressPath = config.repl  // allow null and false

  onmessage = config.onMessage || config.onmessage

  clusterSize = config.size || os.cpus().length

  minRestartAge = config.minRestartAge || minRestartAge

  env = config.env

  var masterConf = { exec: path.resolve(config.exec) }
  if (config.silent) masterConf.silent = true
  if (config.env) masterConf.env = config.env
  if (config.args) masterConf.args = config.args

  cluster.setupMaster(masterConf)

  if (config.signals !== false) {
    // sighup/sigint listeners
    setupSignals()
  }

  forkListener()

  // now make it the right size
  debug((replAddressPath) ? 'resize and then setup repl' : 'resize')
  resize(setupRepl)
}

function select (field) {
  return Object.keys(cluster.workers).map(function (k) {
    return [k, cluster.workers[k][field]]
  }).reduce(function (set, kv) {
    set[kv[0]] = kv[1]
    return set
  }, {})
}

function setupRepl () {
  if (!replAddressPath) return  // was disabled

  debug('setup repl')
  var socket = null
  var socketAddress = undefined
  if (typeof replAddressPath === 'string') {
    socket = path.resolve(replAddressPath)
  } else if (typeof replAddressPath === 'number') {
    socket = replAddressPath
    if (!isNaN(socket)) socket = +socket
  } else if (replAddressPath.address && replAddressPath.port) {
    socket = replAddressPath.port
    socketAddress = replAddressPath.address
  }
  var connections = 0

  if (typeof socket === 'string') {
    fs.unlink(socket, function (er) {
      if (er && er.code !== 'ENOENT') throw er
      startRepl()
    })
  } else {
    startRepl()
  }

  function startRepl () {
    console.error('starting repl on '+socket+'=')
    process.on('exit', function() {
      try { fs.unlinkSync(socket) } catch (er) {}
    })
    var sockId = 0
    var replServer = net.createServer(function (sock) {
      connections ++
      replEnded = false

      sock.id = sockId ++
      debugStreams['repl-' + sockId] = sock

      sock.write('Starting repl #' + sock.id)
      var r = repl.start({
        prompt: 'ClusterMaster (`help` for cmds) ' + process.pid + ' ' + sock.id + '> ',
        input: sock,
        output: sock,
        terminal: true,
        useGlobal: false,
        ignoreUndefined: true
      })

      var helpCommands = [
        'help        - display these commands',
        'repl        - access the REPL',
        'resize(n)   - resize the cluster to `n` workers',
        'restart(cb) - gracefully restart workers, cb is optional',
        'stop()      - gracefully stop workers and master',
        'kill()      - forcefully kill workers and master',
        'cluster     - node.js cluster module',
        'size        - current cluster size',
        'connections - number of REPL connections to master',
        'workers     - current workers',
        'select(fld) - map of id to field (from workers)',
        'pids        - map of id to pids',
        'ages        - map of id to worker ages',
        'states      - map of id to worker states',
        'debug(a1)   - output `a1` to stdout and all REPLs',
        'sock        - this REPL socket',
        '.exit       - close this connection to the REPL'
      ]

      var context = {
        help: helpCommands,
        repl: r,
        resize: resize,
        restart: restart,
        stop: quit,
        kill: quitHard,
        cluster: cluster,
        get size () {
          return clusterSize
        },
        get connections () {
          return connections
        },
        get workers () {
          var p = select('pid')
          var s = select('state')
          var a = select('age')
          return Object.keys(cluster.workers).map(function (k) {
            return new Worker({ id: k, pid: p[k], state: s[k], age: a[k] })
          })
        },
        select: select,
        get pids () {
          return select('pid')
        },
        get ages () {
          return select('age')
        },
        get states () {
          return select('state')
        },
        // like 'wall'
        debug: debug,
        sock: sock
      }
      var desc = Object.getOwnPropertyNames(context).map(function (n) {
        return [n, Object.getOwnPropertyDescriptor(context, n)]
      }).reduce(function (set, kv) {
        set[kv[0]] = kv[1]
        return set
      }, {})
      Object.defineProperties(r.context, desc)

      sock.repl = r

      r.on('end', function () {
        connections --
        replEnded = true
        if (!ended) sock.end()
      })

      sock.on('end', end)
      sock.on('close', end)
      sock.on('error', end)

      ended = false
      function end () {
        if (ended) return
        ended = true
        if (!replEnded) r.rli.close()
        delete debugStreams['repl-' + sockId]
      }

    })

    if (socketAddress) {
      replServer.listen(socket, socketAddress, function () {
        debug('ClusterMaster repl listening on '+socketAddress+':'+socket)
      })
    } else {
      replServer.listen(socket,  function () {
        debug('ClusterMaster repl listening on '+socket)
      })
    }
  }
}

function Worker (d) {
  this.id = d.id
  this.pid = d.pid
  this.state = d.state
  this.age = d.age
}

Worker.prototype.disconnect = function () {
  cluster.workers[this.id].disconnect()
}

Worker.prototype.kill = function () {
  process.kill(this.pid)
}

function forkListener () {
  cluster.on("fork", function (worker) {
    worker.birth = Date.now()
    Object.defineProperty(worker, 'age', { get: function () {
      return Date.now() - this.birth
    }, enumerable: true, configurable: true })
    worker.pid = worker.process.pid
    var id = worker.id
    debug("Worker %j setting up", id)
    if (onmessage) worker.on("message", onmessage)
    var disconnectTimer

    worker.on("exit", function () {
      clearTimeout(disconnectTimer)

      if (!worker.suicide) {
        debug("Worker %j exited abnormally", id)
        // don't respawn right away if it's a very fast failure.
        // otherwise server crashes are hard to detect from monitors.
        if (worker.age < minRestartAge) {
          debug("Worker %j died too quickly, danger", id)
          danger = true
          // still try again in a few seconds, though.
          setTimeout(resize, 2000)
          return
        }
      } else {
        debug("Worker %j exited", id)
      }

      if (Object.keys(cluster.workers).length < clusterSize && !resizing) {
        resize()
      }
    })

    worker.on("disconnect", function () {
      debug("Worker %j disconnect", id)
      // give it 1 second to shut down gracefully, or kill
      if(config.killWaitTime !== 0) {
        disconnectTimer = setTimeout(function () {
          debug("Worker %j, forcefully killing", id)
          worker.process.kill("SIGKILL")
        }, config.killWaitTime || 5000)
      }
    })
  })
}

function restart (cb) {
  if (restarting) {
    debug("Already restarting.  Cannot restart yet.")
    return
  }

  restarting = true

  // graceful restart.
  // all the existing workers get killed, and this
  // causes new ones to be spawned.  If there aren't
  // already the intended number, then fork new extras.
  var current = Object.keys(cluster.workers)
  , length = current.length
  , reqs = clusterSize - length

  var i = 0

  // if we're resizing, then just kill off a few.
  if (reqs !== 0) {
    debug('resize %d -> %d, change = %d',
                  current.length, clusterSize, reqs)

    return resize(clusterSize, function () {
      debug('resize cb')
      length = clusterSize
      graceful()
    })
  }

  // all the current workers, kill and then wait for a
  // new one to spawn before moving on.
  graceful()
  function graceful () {
    debug("graceful %d of %d", i, length)
    if (i >= current.length) {
      debug("graceful completion")
      restarting = false
      return cb && cb()
    }

    var first = (i === 0)
    , id = current[i++]
    , worker = cluster.workers[id]

    if (quitting) {
      if (worker && worker.process.connected) {
        worker.disconnect()
      }
      return graceful()
    }

    // start a new one. if it lives for 2 seconds, kill the worker.
    if (first) {
      cluster.once('listening', function (newbie) {
        var timer = setTimeout(function () {
          newbie.removeListener('exit', skeptic)
          if (worker && worker.process.connected) {
            worker.disconnect()
          }
          graceful()
        }, 2000)
        newbie.on('exit', skeptic)
        function skeptic () {
          debug('New worker died quickly. Aborting restart.')
          restarting = false
          clearTimeout(timer)
        }
      })
    } else {
      cluster.once('listening', function (newbie) {
        if (worker && worker.process.connected) {
          worker.disconnect()
        }
      })
      graceful()
    }

    cluster.fork(env)
  }
}

var resizing = false
var resizeCbs = []
function resize (n, cb_) {
  if (typeof n === 'function') cb_ = n, n = clusterSize

  if (cb_)
    resizeCbs.push(cb_)

  if (resizing)
    return

  function cb() {
    console.error('done resizing')

    resizing = false
    var q = resizeCbs.slice(0)
    resizeCbs.length = 0
    q.forEach(function(c) {
      c()
    })
    if (clusterSize !== Object.keys(cluster.workers).length) {
      if (danger && clusterSize === 0) {
        debug('DANGER! something bad has happened')
        process.exit(1)
      } else {
        danger = true
        debug('DANGER! wrong number of workers')
        setTimeout(resize, 1000)
      }
    } else {
      danger = false
    }
  }

  if (n >= 0) clusterSize = n
  var current = Object.keys(cluster.workers)
  , c = current.length
  , req = clusterSize - c

  // avoid angry "listening" listeners
  cluster.setMaxListeners(clusterSize * 2)

  if (c === clusterSize) {
    resizing = false
    return cb()
  }

  var thenCnt = 0
  function then () {
    thenCnt ++
    return then2
  }
  function then2 () {
    if (--thenCnt === 0) {
      resizing = false
      return cb()
    }
  }

  // make us have the right number of them.
  if (req > 0) while (req -- > 0) {
    debug('resizing up', req)
    cluster.once('listening', then())
    cluster.fork(env)
  } else for (var i = clusterSize; i < c; i ++) {
    var worker = cluster.workers[current[i]]
    debug('resizing down', current[i])
    worker.once('exit', then())
    if (worker && worker.process.connected) {
      worker.disconnect()
    }
  }
}

function quitHard () {
  quitting = true
  quit()
}

function quit () {
  if (quitting) {
    debug("Forceful shutdown")
    // last ditch effort to force-kill all workers.
    Object.keys(cluster.workers).forEach(function (id) {
      var w = cluster.workers[id]
      if (w && w.process) w.process.kill("SIGKILL")
    })
    process.exit(1)
  }

  debug("Graceful shutdown...")
  clusterSize = 0
  quitting = true
  restart(function () {
    debug("Graceful shutdown successful")
    process.exit(0)
  })
}

function setupSignals () {
  try {
    process.on("SIGHUP", restart)
    process.on("SIGINT", quit)
  } catch (e) {
    // Must be on Windows, waaa-waaah.
  }

  process.on("exit", function () {
    if (!quitting) quitHard()
  })
}
<commit_msg>Fix config out of scope<commit_after>// Set up a cluster and set up resizing and such.

var cluster = require("cluster")
, quitting = false
, restarting = false
, path = require("path")
, clusterSize = 0
, env
, os = require("os")
, onmessage
, repl = require('repl')
, replAddressPath = process.env.CLUSTER_MASTER_REPL || 'cluster-master-socket'
, net = require('net')
, fs = require('fs')
, util = require('util')
, minRestartAge = 2000
, danger = false

exports = module.exports = clusterMaster
exports.restart = restart
exports.resize = resize
exports.quitHard = quitHard
exports.quit = quit

var debugStreams = {}
function debug () {
  console.error.apply(console, arguments)

  var msg = util.format.apply(util, arguments)
  Object.keys(debugStreams).forEach(function (s) {
    try {
      // if the write fails, just remove it.
      debugStreams[s].write(msg + '\n')
      if (debugStreams[s].repl) debugStreams[s].repl.displayPrompt()
    } catch (e) {
      delete debugStreams[s]
    }
  })
}

var config;

function clusterMaster (aConfig) {
  config = aConfig;
  if (typeof config === "string") config = { exec: config }

  if (!config.exec) {
    throw new Error("Must define a 'exec' script")
  }

  if (!cluster.isMaster) {
    throw new Error("ClusterMaster answers to no one!\n"+
                    "(don't run in a cluster worker script)")
  }

  if (cluster._clusterMaster) {
    throw new Error("This cluster has a master already")
  }

  cluster._clusterMaster = module.exports

  if (typeof config.repl !== 'undefined') replAddressPath = config.repl  // allow null and false

  onmessage = config.onMessage || config.onmessage

  clusterSize = config.size || os.cpus().length

  minRestartAge = config.minRestartAge || minRestartAge

  env = config.env

  var masterConf = { exec: path.resolve(config.exec) }
  if (config.silent) masterConf.silent = true
  if (config.env) masterConf.env = config.env
  if (config.args) masterConf.args = config.args

  cluster.setupMaster(masterConf)

  if (config.signals !== false) {
    // sighup/sigint listeners
    setupSignals()
  }

  forkListener()

  // now make it the right size
  debug((replAddressPath) ? 'resize and then setup repl' : 'resize')
  resize(setupRepl)
}

function select (field) {
  return Object.keys(cluster.workers).map(function (k) {
    return [k, cluster.workers[k][field]]
  }).reduce(function (set, kv) {
    set[kv[0]] = kv[1]
    return set
  }, {})
}

function setupRepl () {
  if (!replAddressPath) return  // was disabled

  debug('setup repl')
  var socket = null
  var socketAddress = undefined
  if (typeof replAddressPath === 'string') {
    socket = path.resolve(replAddressPath)
  } else if (typeof replAddressPath === 'number') {
    socket = replAddressPath
    if (!isNaN(socket)) socket = +socket
  } else if (replAddressPath.address && replAddressPath.port) {
    socket = replAddressPath.port
    socketAddress = replAddressPath.address
  }
  var connections = 0

  if (typeof socket === 'string') {
    fs.unlink(socket, function (er) {
      if (er && er.code !== 'ENOENT') throw er
      startRepl()
    })
  } else {
    startRepl()
  }

  function startRepl () {
    console.error('starting repl on '+socket+'=')
    process.on('exit', function() {
      try { fs.unlinkSync(socket) } catch (er) {}
    })
    var sockId = 0
    var replServer = net.createServer(function (sock) {
      connections ++
      replEnded = false

      sock.id = sockId ++
      debugStreams['repl-' + sockId] = sock

      sock.write('Starting repl #' + sock.id)
      var r = repl.start({
        prompt: 'ClusterMaster (`help` for cmds) ' + process.pid + ' ' + sock.id + '> ',
        input: sock,
        output: sock,
        terminal: true,
        useGlobal: false,
        ignoreUndefined: true
      })

      var helpCommands = [
        'help        - display these commands',
        'repl        - access the REPL',
        'resize(n)   - resize the cluster to `n` workers',
        'restart(cb) - gracefully restart workers, cb is optional',
        'stop()      - gracefully stop workers and master',
        'kill()      - forcefully kill workers and master',
        'cluster     - node.js cluster module',
        'size        - current cluster size',
        'connections - number of REPL connections to master',
        'workers     - current workers',
        'select(fld) - map of id to field (from workers)',
        'pids        - map of id to pids',
        'ages        - map of id to worker ages',
        'states      - map of id to worker states',
        'debug(a1)   - output `a1` to stdout and all REPLs',
        'sock        - this REPL socket',
        '.exit       - close this connection to the REPL'
      ]

      var context = {
        help: helpCommands,
        repl: r,
        resize: resize,
        restart: restart,
        stop: quit,
        kill: quitHard,
        cluster: cluster,
        get size () {
          return clusterSize
        },
        get connections () {
          return connections
        },
        get workers () {
          var p = select('pid')
          var s = select('state')
          var a = select('age')
          return Object.keys(cluster.workers).map(function (k) {
            return new Worker({ id: k, pid: p[k], state: s[k], age: a[k] })
          })
        },
        select: select,
        get pids () {
          return select('pid')
        },
        get ages () {
          return select('age')
        },
        get states () {
          return select('state')
        },
        // like 'wall'
        debug: debug,
        sock: sock
      }
      var desc = Object.getOwnPropertyNames(context).map(function (n) {
        return [n, Object.getOwnPropertyDescriptor(context, n)]
      }).reduce(function (set, kv) {
        set[kv[0]] = kv[1]
        return set
      }, {})
      Object.defineProperties(r.context, desc)

      sock.repl = r

      r.on('end', function () {
        connections --
        replEnded = true
        if (!ended) sock.end()
      })

      sock.on('end', end)
      sock.on('close', end)
      sock.on('error', end)

      ended = false
      function end () {
        if (ended) return
        ended = true
        if (!replEnded) r.rli.close()
        delete debugStreams['repl-' + sockId]
      }

    })

    if (socketAddress) {
      replServer.listen(socket, socketAddress, function () {
        debug('ClusterMaster repl listening on '+socketAddress+':'+socket)
      })
    } else {
      replServer.listen(socket,  function () {
        debug('ClusterMaster repl listening on '+socket)
      })
    }
  }
}

function Worker (d) {
  this.id = d.id
  this.pid = d.pid
  this.state = d.state
  this.age = d.age
}

Worker.prototype.disconnect = function () {
  cluster.workers[this.id].disconnect()
}

Worker.prototype.kill = function () {
  process.kill(this.pid)
}

function forkListener () {
  cluster.on("fork", function (worker) {
    worker.birth = Date.now()
    Object.defineProperty(worker, 'age', { get: function () {
      return Date.now() - this.birth
    }, enumerable: true, configurable: true })
    worker.pid = worker.process.pid
    var id = worker.id
    debug("Worker %j setting up", id)
    if (onmessage) worker.on("message", onmessage)
    var disconnectTimer

    worker.on("exit", function () {
      clearTimeout(disconnectTimer)

      if (!worker.suicide) {
        debug("Worker %j exited abnormally", id)
        // don't respawn right away if it's a very fast failure.
        // otherwise server crashes are hard to detect from monitors.
        if (worker.age < minRestartAge) {
          debug("Worker %j died too quickly, danger", id)
          danger = true
          // still try again in a few seconds, though.
          setTimeout(resize, 2000)
          return
        }
      } else {
        debug("Worker %j exited", id)
      }

      if (Object.keys(cluster.workers).length < clusterSize && !resizing) {
        resize()
      }
    })

    worker.on("disconnect", function () {
      debug("Worker %j disconnect", id)
      // give it 1 second to shut down gracefully, or kill
      if(config.killWaitTime !== 0) {
        disconnectTimer = setTimeout(function () {
          debug("Worker %j, forcefully killing", id)
          worker.process.kill("SIGKILL")
        }, config.killWaitTime || 5000)
      }
    })
  })
}

function restart (cb) {
  if (restarting) {
    debug("Already restarting.  Cannot restart yet.")
    return
  }

  restarting = true

  // graceful restart.
  // all the existing workers get killed, and this
  // causes new ones to be spawned.  If there aren't
  // already the intended number, then fork new extras.
  var current = Object.keys(cluster.workers)
  , length = current.length
  , reqs = clusterSize - length

  var i = 0

  // if we're resizing, then just kill off a few.
  if (reqs !== 0) {
    debug('resize %d -> %d, change = %d',
                  current.length, clusterSize, reqs)

    return resize(clusterSize, function () {
      debug('resize cb')
      length = clusterSize
      graceful()
    })
  }

  // all the current workers, kill and then wait for a
  // new one to spawn before moving on.
  graceful()
  function graceful () {
    debug("graceful %d of %d", i, length)
    if (i >= current.length) {
      debug("graceful completion")
      restarting = false
      return cb && cb()
    }

    var first = (i === 0)
    , id = current[i++]
    , worker = cluster.workers[id]

    if (quitting) {
      if (worker && worker.process.connected) {
        worker.disconnect()
      }
      return graceful()
    }

    // start a new one. if it lives for 2 seconds, kill the worker.
    if (first) {
      cluster.once('listening', function (newbie) {
        var timer = setTimeout(function () {
          newbie.removeListener('exit', skeptic)
          if (worker && worker.process.connected) {
            worker.disconnect()
          }
          graceful()
        }, 2000)
        newbie.on('exit', skeptic)
        function skeptic () {
          debug('New worker died quickly. Aborting restart.')
          restarting = false
          clearTimeout(timer)
        }
      })
    } else {
      cluster.once('listening', function (newbie) {
        if (worker && worker.process.connected) {
          worker.disconnect()
        }
      })
      graceful()
    }

    cluster.fork(env)
  }
}

var resizing = false
var resizeCbs = []
function resize (n, cb_) {
  if (typeof n === 'function') cb_ = n, n = clusterSize

  if (cb_)
    resizeCbs.push(cb_)

  if (resizing)
    return

  function cb() {
    console.error('done resizing')

    resizing = false
    var q = resizeCbs.slice(0)
    resizeCbs.length = 0
    q.forEach(function(c) {
      c()
    })
    if (clusterSize !== Object.keys(cluster.workers).length) {
      if (danger && clusterSize === 0) {
        debug('DANGER! something bad has happened')
        process.exit(1)
      } else {
        danger = true
        debug('DANGER! wrong number of workers')
        setTimeout(resize, 1000)
      }
    } else {
      danger = false
    }
  }

  if (n >= 0) clusterSize = n
  var current = Object.keys(cluster.workers)
  , c = current.length
  , req = clusterSize - c

  // avoid angry "listening" listeners
  cluster.setMaxListeners(clusterSize * 2)

  if (c === clusterSize) {
    resizing = false
    return cb()
  }

  var thenCnt = 0
  function then () {
    thenCnt ++
    return then2
  }
  function then2 () {
    if (--thenCnt === 0) {
      resizing = false
      return cb()
    }
  }

  // make us have the right number of them.
  if (req > 0) while (req -- > 0) {
    debug('resizing up', req)
    cluster.once('listening', then())
    cluster.fork(env)
  } else for (var i = clusterSize; i < c; i ++) {
    var worker = cluster.workers[current[i]]
    debug('resizing down', current[i])
    worker.once('exit', then())
    if (worker && worker.process.connected) {
      worker.disconnect()
    }
  }
}

function quitHard () {
  quitting = true
  quit()
}

function quit () {
  if (quitting) {
    debug("Forceful shutdown")
    // last ditch effort to force-kill all workers.
    Object.keys(cluster.workers).forEach(function (id) {
      var w = cluster.workers[id]
      if (w && w.process) w.process.kill("SIGKILL")
    })
    process.exit(1)
  }

  debug("Graceful shutdown...")
  clusterSize = 0
  quitting = true
  restart(function () {
    debug("Graceful shutdown successful")
    process.exit(0)
  })
}

function setupSignals () {
  try {
    process.on("SIGHUP", restart)
    process.on("SIGINT", quit)
  } catch (e) {
    // Must be on Windows, waaa-waaah.
  }

  process.on("exit", function () {
    if (!quitting) quitHard()
  })
}
<|endoftext|>


<commit_before>

//public group object
var GroupObject = function (group){

	var obj = {
		type:"groupObject", //type of element

	}

	return obj;
}

//group object
var Group = function(parent, id, generator, visible){
	var group = {
		id:id,
		parent:parent,
		type:"group", //type of element
		rows:[],
		generator:generator,
		element:$("

"),
		arrowElement:$("

"),
		height:0,
		outerHeight:0,
		initialized:false,
		visible:visible,

		//////////////// Group Functions /////////////////

		addBindings:function(){
			var self = this;

			self.element.on("click", function(e){
				e.stopPropagation();
				e.stopImmediatePropagation();
				self.toggleVisibility();
			});
		},

		addRow:function(row){
			this.rows.push(row);
		},

		getRows:function(){
			return this.visible ? this.rows : [];
		},

		getRowCount:function(){
			return this.rows.length;
		},

		toggleVisibility:function(){
			if(this.visible){
				this.hide();
			}else{
				this.show();
			}

			this.parent.updateGroupRows(true);
		},

		hide:function(){
			this.visible = false;
		},

		show:function(){
			var self = this;

			self.visible = true;
		},

		////////////// Standard Row Functions //////////////

		getElement:function(){
			this.addBindingsd = false;

			var data = [];

			this.rows.forEach(function(row){
				data.push(row.getData());
			});

			if(this.visible){
				this.element.addClass("tabulator-group-visible");
			}else{
				this.element.removeClass("tabulator-group-visible");
			}

			this.element.empty().html(this.generator(this.id, this.getRowCount(), data)).prepend(this.arrowElement);

			this.addBindings();

			return this.element;
		},

		//normalize the height of elements in the row
		normalizeHeight:function(){
			this.setHeight(this.element.innerHeight())
		},

		initialize:function(force){
			if(!this.initialized || force){
				this.normalizeHeight();
				this.initialized = true;
			}
		},

		reinitialize:function(){
			this.initialized = false;
			this.height = 0;

			if(this.element.is(":visible")){
				this.initialize(true);
			}
		},

		setHeight:function(height){
			if(this.height != height){
				this.height = height;
				this.outerHeight = this.element.outerHeight();
			}
		},

		//return rows outer height
		getHeight:function(){
			return this.outerHeight;
		},

		//////////////// Object Generation /////////////////
		getObject:function(){
			return new GroupObject(group);
		},
	}

	group.addBindings();

	return group;
}

var GroupRows = function(table){

	var extension = {
		table:table, //hold Tabulator object

		findGroupId:false, //enable table grouping and set field to group by
		startOpen:true, //starting state of group
		headerGenerator:function(value, count, data){ //header layout function
			return value + "(" + count + " " + ((count === 1) ? "item" : "items") + ")";
		},

		groupList:[], //ordered list of groups
		groups:{}, //hold row groups

		//initialize group configuration
		initialize:function(){
			var self = this;

			if(typeof self.table.options.groupBy == "function"){
				self.findGroupId = self.table.options.groupBy;
			}else{
				self.findGroupId = function(data){
					return data[self.table.options.groupBy];
				}
			}

			if(self.table.options.groupStartOpen){
				self.startOpen = self.table.options.groupStartOpen;
			}

			if(self.table.options.groupHeader){
				self.headerGenerator = self.table.options.groupHeader
			}

		},

		//return appropriate rows with group headers
		getRows:function(data){
			var self = this;

			var oldGroups = self.groups;
			self.groups = {};
			self.groupList =[];

			if(self.findGroupId){

				data.forEach(function(row){

					var groupID = self.findGroupId(row.getData());

					if(!self.groups[groupID]){
						var group = new Group(self, groupID, self.headerGenerator, oldGroups[groupID] ? oldGroups[groupID].visible : self.startOpen);

						self.groups[groupID] = group;
						self.groupList.push(group);
					}

					self.groups[groupID].addRow(row);
				});

				return self.updateGroupRows();

			}else{
				return data.slice(0);
			}

		},

		updateGroupRows:function(force){
			var self = this,
			output = [],
			oldRowCount;

			self.groupList.forEach(function(group){
				output.push(group);

				group.getRows().forEach(function(row){
					output.push(row);
				})
			});

			//force update of table display
			if(force){
				oldRowCount = self.table.rowManager.displayRowsCount;

				self.table.rowManager.setDisplayRows(output);
				self.table.rowManager._virtualRenderFill(Math.floor((self.table.rowManager.element.scrollTop() / self.table.rowManager.element[0].scrollHeight) * oldRowCount));
			}

			return output;
		}

	}

	return extension;
}

Tabulator.registerExtension("groupRows", GroupRows);<commit_msg>fixed groupStartOpen callback<commit_after>

//public group object
var GroupObject = function (group){

	var obj = {
		type:"groupObject", //type of element

	}

	return obj;
}

//group object
var Group = function(parent, id, generator, visible){
	var group = {
		id:id,
		parent:parent,
		type:"group", //type of element
		rows:[],
		generator:generator,
		element:$("

"),
		arrowElement:$("

"),
		height:0,
		outerHeight:0,
		initialized:false,
		visible:visible,

		//////////////// Group Functions /////////////////

		addBindings:function(){
			var self = this;

			self.element.on("click", function(e){
				e.stopPropagation();
				e.stopImmediatePropagation();
				self.toggleVisibility();
			});
		},

		addRow:function(row){
			this.rows.push(row);
		},

		getRows:function(){
			this._visSet();

			return this.visible ? this.rows : [];
		},

		getRowCount:function(){
			return this.rows.length;
		},

		toggleVisibility:function(){
			if(this.visible){
				this.hide();
			}else{
				this.show();
			}

			this.parent.updateGroupRows(true);
		},

		hide:function(){
			this.visible = false;
		},

		show:function(){
			var self = this;

			self.visible = true;
		},

		_visSet:function(){
			var data = [];

			if(typeof this.visible == "function"){

				this.rows.forEach(function(row){
					data.push(row.getData());
				});

				this.visible = this.visible(this.id, this.getRowCount(), data);
			}
		},

		////////////// Standard Row Functions //////////////

		getElement:function(){
			this.addBindingsd = false;

			this._visSet();

			var data = [];

			this.rows.forEach(function(row){
				data.push(row.getData());
			});

			if(this.visible){
				this.element.addClass("tabulator-group-visible");
			}else{
				this.element.removeClass("tabulator-group-visible");
			}

			this.element.empty().html(this.generator(this.id, this.getRowCount(), data)).prepend(this.arrowElement);

			this.addBindings();

			return this.element;
		},

		//normalize the height of elements in the row
		normalizeHeight:function(){
			this.setHeight(this.element.innerHeight())
		},

		initialize:function(force){
			if(!this.initialized || force){
				this.normalizeHeight();
				this.initialized = true;
			}
		},

		reinitialize:function(){
			this.initialized = false;
			this.height = 0;

			if(this.element.is(":visible")){
				this.initialize(true);
			}
		},

		setHeight:function(height){
			if(this.height != height){
				this.height = height;
				this.outerHeight = this.element.outerHeight();
			}
		},

		//return rows outer height
		getHeight:function(){
			return this.outerHeight;
		},

		//////////////// Object Generation /////////////////
		getObject:function(){
			return new GroupObject(group);
		},
	}

	group.addBindings();

	return group;
}

var GroupRows = function(table){

	var extension = {
		table:table, //hold Tabulator object

		findGroupId:false, //enable table grouping and set field to group by
		startOpen:function(){return false;}, //starting state of group
		headerGenerator:function(value, count, data){ //header layout function
			return value + "(" + count + " " + ((count === 1) ? "item" : "items") + ")";
		},

		groupList:[], //ordered list of groups
		groups:{}, //hold row groups

		//initialize group configuration
		initialize:function(){
			var self = this;

			if(typeof self.table.options.groupBy == "function"){
				self.findGroupId = self.table.options.groupBy;
			}else{
				self.findGroupId = function(data){
					return data[self.table.options.groupBy];
				}
			}

			if(self.table.options.groupStartOpen){
				self.startOpen = typeof self.table.options.groupStartOpen == "function" ? self.table.options.groupStartOpen : function(){return true;};
			}

			if(self.table.options.groupHeader){
				self.headerGenerator = self.table.options.groupHeader
			}

		},

		//return appropriate rows with group headers
		getRows:function(data){
			var self = this;

			var oldGroups = self.groups;
			self.groups = {};
			self.groupList =[];

			if(self.findGroupId){

				data.forEach(function(row){

					var groupID = self.findGroupId(row.getData());

					if(!self.groups[groupID]){
						var group = new Group(self, groupID, self.headerGenerator, oldGroups[groupID] ? oldGroups[groupID].visible : self.startOpen);

						self.groups[groupID] = group;
						self.groupList.push(group);
					}

					self.groups[groupID].addRow(row);
				});

				return self.updateGroupRows();

			}else{
				return data.slice(0);
			}

		},

		updateGroupRows:function(force){
			var self = this,
			output = [],
			oldRowCount;

			self.groupList.forEach(function(group){
				output.push(group);

				group.getRows().forEach(function(row){
					output.push(row);
				})
			});

			//force update of table display
			if(force){
				oldRowCount = self.table.rowManager.displayRowsCount;

				self.table.rowManager.setDisplayRows(output);
				self.table.rowManager._virtualRenderFill(Math.floor((self.table.rowManager.element.scrollTop() / self.table.rowManager.element[0].scrollHeight) * oldRowCount));
			}

			return output;
		}

	}

	return extension;
}

Tabulator.registerExtension("groupRows", GroupRows);<|endoftext|>


<commit_before>import test from 'ava';
import sinon from 'sinon';
import { put } from 'redux-saga/effects';
import DockerService from '../../Service/DockerService';
import actions from '../actions';
import { readStatsSaga } from './';

test('should call DockerService.stats with given id', t => {
  const statsSpy = sinon.stub(DockerService, 'stats').callsFake(() => ({
    close: () => null,
  }));

  const iterator = readStatsSaga({ id: 'Test' });
  const value = iterator.next().value;
  DockerService.stats.restore();

  t.true(statsSpy.called);
  t.truthy(value.TAKE);
  t.truthy(value.TAKE.channel);
});

test('should addLog when content is in channel', t => {
  sinon.stub(DockerService, 'stats');

  const iterator = readStatsSaga({ id: 'Test' });
  iterator.next();
  DockerService.stats.restore();

  t.deepEqual(iterator.next('content').value, put(actions.addStat('content')));
});

test('should close channel and websocket on error/cancel', t => {
  const close = sinon.spy();
  sinon.stub(DockerService, 'stats').callsFake(() => ({
    close,
  }));

  const iterator = readStatsSaga({ id: 'Test' });
  iterator.next();
  DockerService.stats.restore();
  try {
    iterator.throw(new Error('Error test'));
  } catch (e) {} // eslint-disable-line no-empty

  t.true(close.called);
});
<commit_msg>Update readStatsSaga.test.js<commit_after>import test from 'ava';
import sinon from 'sinon';
import { put } from 'redux-saga/effects';
import DockerService from '../../Service/DockerService';
import actions from '../actions';
import { readStatsSaga } from './';

test('should call DockerService.stats with given id', t => {
  const statsSpy = sinon.stub(DockerService, 'stats').callsFake(() => ({
    close: () => null,
  }));

  const iterator = readStatsSaga({ id: 'Test' });
  const value = iterator.next().value;
  DockerService.stats.restore();

  t.true(statsSpy.called);
  t.truthy(value.TAKE);
  t.truthy(value.TAKE.channel);
});

test('should addLog when content is in channel', t => {
  sinon.stub(DockerService, 'stats').callsFake(() => ({
    close: () => null,
  });

  const iterator = readStatsSaga({ id: 'Test' });
  iterator.next();
  DockerService.stats.restore();

  t.deepEqual(iterator.next('content').value, put(actions.addStat('content')));
});

test('should close channel and websocket on error/cancel', t => {
  const close = sinon.spy();
  sinon.stub(DockerService, 'stats').callsFake(() => ({
    close,
  }));

  const iterator = readStatsSaga({ id: 'Test' });
  iterator.next();
  DockerService.stats.restore();
  try {
    iterator.throw(new Error('Error test'));
  } catch (e) {} // eslint-disable-line no-empty

  t.true(close.called);
});
<|endoftext|>


<commit_before>import React from 'react';
import { shallow } from 'enzyme';
import createConfig from '../../curi/src';
import Response from '../../curi/src/response';
import { createMemoryHistory } from 'history';
import Navigator from '../src/Navigator';

describe('<Navigator>', () => {
  it('calls children function when it renders', () => {
    const fakeConfig = { subscribe: () => {} };
    const fn = jest.fn(() => {
      return null;
    });
    const wrapper = shallow(
      <Navigator config={fakeConfig}>
        {fn}
      </Navigator>
    );
    expect(fn.mock.calls.length).toBe(1);
  });

  it('passes the children function a response object', done => {
    const history = createMemoryHistory();
    const routes = [
      { name: 'Home', path: '', pathOptions: { end: true } },
      { name: 'About', path: 'about' }
    ];
    let receivedResponse;
    const fn = jest.fn(response => {
      receivedResponse = response;
      return null;
    });

    const config = createConfig(history, routes);
    config.ready().then(resp => {
      const wrapper = shallow(
        <Navigator config={config}>
          {fn}
        </Navigator>
      );

      const properties = [
        'location',
        'status',
        'name',
        'partials',
        'params',
        'body'
      ];
      expect(Object.keys(receivedResponse).length).toEqual(properties.length);
      properties.forEach(key => {
        expect(receivedResponse.hasOwnProperty(key)).toBe(true);
      });
      done();
    });
  });

  it('passes the children function the config object', done => {
    const history = createMemoryHistory();
    const routes = [
      { name: 'Home', path: '', pathOptions: { end: true } },
      { name: 'About', path: 'about' }
    ];
    const fn = jest.fn(() => {
      return null;
    });

    const config = createConfig(history, routes);
    config.ready().then(resp => {
      const wrapper = shallow(
        <Navigator config={config}>
          {fn}
        </Navigator>
      );

      expect(fn.mock.calls[0][1]).toBe(config);
      done();
    });
  });

  it('updates on location changes', done => {
    const history = createMemoryHistory();
    const routes = [
      { name: 'Home', path: '', pathOptions: { end: true } },
      { name: 'About', path: 'about' }
    ];

    let first = true;
    const fn = jest.fn(resp => {
      if (first) {
        expect(resp.name).toBe('Home');
        first = false;
      } else {
        expect(resp.name).toBe('About');
      }
      return null;
    });

    const config = createConfig(history, routes);
    config.ready().then(() => {
      const wrapper = shallow(
        <Navigator config={config}>
          {fn}
        </Navigator>
      );
      history.push('/about');
      done();
    });
  });

  describe('response', () => {
    it('renders using response if provided', () => {
      const fakeConfig = {
        subscribe: jest.fn()
      };
      const resp = new Response({ pathname: '/testing' });
      const respObj = resp.asObject();
      let received;
      const children = jest.fn(response => {
        received = response;
        return null;
      });
      const wrapper = shallow(
        <Navigator response={respObj} config={fakeConfig} children={children} />
      );
      expect(received).toBe(respObj);
    });

    it('does not call subscribe when passed a response', () => {
      const fakeConfig = {
        subscribe: jest.fn()
      };
      const resp = new Response({ pathname: '/testing' });
      const respObj = resp.asObject();
      const wrapper = shallow(
        <Navigator
          response={respObj}
          config={fakeConfig}
          children={() => null}
        />
      );
      expect(fakeConfig.subscribe.mock.calls.length).toBe(0);
    });
  });

  describe('unmount', () => {
    it('unsubscribes from the config', () => {
      const unsub = jest.fn();
      const sub = jest.fn(fn => {
        return unsub;
      });
      const fakeConfig = {
        subscribe: sub
      };

      expect(unsub.mock.calls.length).toBe(0);
      expect(sub.mock.calls.length).toBe(0);

      const wrapper = shallow(
        <Navigator config={fakeConfig} children={() => null} />
      );
      expect(sub.mock.calls.length).toBe(1);
      expect(unsub.mock.calls.length).toBe(0);
      wrapper.unmount();
      expect(unsub.mock.calls.length).toBe(1);
    });
  });
});
<commit_msg>Fix <Navigator> tests for response.key<commit_after>import React from 'react';
import { shallow } from 'enzyme';
import createConfig from '../../curi/src';
import Response from '../../curi/src/response';
import { createMemoryHistory } from 'history';
import Navigator from '../src/Navigator';

describe('<Navigator>', () => {
  it('calls children function when it renders', () => {
    const fakeConfig = { subscribe: () => {} };
    const fn = jest.fn(() => {
      return null;
    });
    const wrapper = shallow(
      <Navigator config={fakeConfig}>
        {fn}
      </Navigator>
    );
    expect(fn.mock.calls.length).toBe(1);
  });

  it('passes the children function a response object', done => {
    const history = createMemoryHistory();
    const routes = [
      { name: 'Home', path: '', pathOptions: { end: true } },
      { name: 'About', path: 'about' }
    ];
    let receivedResponse;
    const fn = jest.fn(response => {
      receivedResponse = response;
      return null;
    });

    const config = createConfig(history, routes);
    config.ready().then(resp => {
      const wrapper = shallow(
        <Navigator config={config}>
          {fn}
        </Navigator>
      );

      const properties = [
        'key',
        'location',
        'status',
        'name',
        'partials',
        'params',
        'body'
      ];
      expect(Object.keys(receivedResponse).length).toEqual(properties.length);
      properties.forEach(key => {
        expect(receivedResponse.hasOwnProperty(key)).toBe(true);
      });
      done();
    });
  });

  it('passes the children function the config object', done => {
    const history = createMemoryHistory();
    const routes = [
      { name: 'Home', path: '', pathOptions: { end: true } },
      { name: 'About', path: 'about' }
    ];
    const fn = jest.fn(() => {
      return null;
    });

    const config = createConfig(history, routes);
    config.ready().then(resp => {
      const wrapper = shallow(
        <Navigator config={config}>
          {fn}
        </Navigator>
      );

      expect(fn.mock.calls[0][1]).toBe(config);
      done();
    });
  });

  it('updates on location changes', done => {
    const history = createMemoryHistory();
    const routes = [
      { name: 'Home', path: '', pathOptions: { end: true } },
      { name: 'About', path: 'about' }
    ];

    let first = true;
    const fn = jest.fn(resp => {
      if (first) {
        expect(resp.name).toBe('Home');
        first = false;
      } else {
        expect(resp.name).toBe('About');
      }
      return null;
    });

    const config = createConfig(history, routes);
    config.ready().then(() => {
      const wrapper = shallow(
        <Navigator config={config}>
          {fn}
        </Navigator>
      );
      history.push('/about');
      done();
    });
  });

  describe('response', () => {
    it('renders using response if provided', () => {
      const fakeConfig = {
        subscribe: jest.fn()
      };
      const resp = new Response({ pathname: '/testing' });
      const respObj = resp.asObject();
      let received;
      const children = jest.fn(response => {
        received = response;
        return null;
      });
      const wrapper = shallow(
        <Navigator response={respObj} config={fakeConfig} children={children} />
      );
      expect(received).toBe(respObj);
    });

    it('does not call subscribe when passed a response', () => {
      const fakeConfig = {
        subscribe: jest.fn()
      };
      const resp = new Response({ pathname: '/testing' });
      const respObj = resp.asObject();
      const wrapper = shallow(
        <Navigator
          response={respObj}
          config={fakeConfig}
          children={() => null}
        />
      );
      expect(fakeConfig.subscribe.mock.calls.length).toBe(0);
    });
  });

  describe('unmount', () => {
    it('unsubscribes from the config', () => {
      const unsub = jest.fn();
      const sub = jest.fn(fn => {
        return unsub;
      });
      const fakeConfig = {
        subscribe: sub
      };

      expect(unsub.mock.calls.length).toBe(0);
      expect(sub.mock.calls.length).toBe(0);

      const wrapper = shallow(
        <Navigator config={fakeConfig} children={() => null} />
      );
      expect(sub.mock.calls.length).toBe(1);
      expect(unsub.mock.calls.length).toBe(0);
      wrapper.unmount();
      expect(unsub.mock.calls.length).toBe(1);
    });
  });
});
<|endoftext|>