<commit_before>/*
 * Copyright (C) 2012 KTH, Royal Institute of Technology (http://www.kth.se/)
 *
 * This file is part of tinywebtex.
 *
 * WebTex is free software: you can redistribute it and/or modify
 * it under the terms of a MIT style license which is included in
 * the software.
 *
 * Images handled by this plugin have these additional requirements on top
 * of the WebTex API documentation.
 *
 * 1. The D parameter in the URL is assumed to have a 1 digit argument 0-9 only.
 * 2. The tex parameter is assumed to be the last parameter.
 * 3. An existing image to edit is assumed to have an "longdesc" attribute
 *    containing a random string uniquely identifying the image on the page,
 *    but will handle most common use cases when it does not exist.
 */

tinyMCEPopup.requireLangPack();

/*
 * Add trim() to strings for browsers which don't have them.
 */
if (!String.prototype.trim) {
    String.prototype.trim = function() {
        return this.replace(/^\s+|\s+$/g, '');
    }
}

/*
 * A sprintf kind of format() enabling constructions like:
 * "Apples: {0}, Pears: {1}".format("Golden Delicious", "Alexander Lucas");
 */
String.prototype.format = function() {
    var formatted = this;
    for (arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};

/*
 * JavaScript object driving the WebTex dialog.
 */
var TinyWebtexDialog = {
    url : null,
    size : null,
    max : null,
    timer : null,
    xmlhttp : null,

    /*
     * Set up the window and populate data from selection in editor if any.
     */
    init : function() {
        var tw = this,
            f = document.forms[0],
            ed = tinyMCEPopup.editor,
            div = ed.dom.create('div', {}, ed.dom.get("tw_stupid_ie_workaround").innerHTML),
            img;

        tw.url = tinyMCEPopup.getWindowArg('webtex_url');
        tw.size = tinyMCEPopup.getWindowArg('default_size');
        tw.max = tinyMCEPopup.getWindowArg('max_length');
        f.tex.onkeyup = f.size.onchange = f.style.onchange = tw.update;

        if (!div.childNodes.length) {
            f.uuid.value = tw.randomId();
        } else {
            img = div.childNodes.item(0);
            if ((img.nodeName == 'IMG') && img.className.match("webtex")) {
                f.tex.value = tw.getTex(img);
                f.size.value = tw.getSize(img);
                f.uuid.value = img.id;
                f.style.value = tw.isDisplayStyle(f.tex.value) ? "display" : "inline" ;
            } else if (img.nodeType == Node.TEXT_NODE) {
                f.uuid.value = tw.randomId();
                f.tex.value = img.textContent;
            }
        }
        tw.initShortcuts();
        tw.update();
        f.tex.focus();
    },

    randomId: function() {
        var CHARS = "0123456789abcdef",
            id = "uuid-", i;

        for (i = 0; i < 32; i++) {
            id += CHARS[Math.floor(Math.random() * CHARS.length)];
        }
        return id;
    },

    getSize: function(img) {
        var s = img.src.split(/[\?&]D=/g)[1].substr(0,1);
        if (s) {
            return s;
        }
        return TinyWebtexDialog.size;
    },

    getTex: function(img) {
        return img.alt.substr(4);
    },

    getUuid: function(img) {
        if (img.id) {
            return img.id;
        }
        return TinyWebtexDialog.randomId();
    },

    /*
     * Send an asynchronous call to WebTex backend service for image,
     * will update the editor if successful, indicate error states
     * otherwise.
     */
    callWebTex : function(img) {
        var xmlhttp = new XMLHttpRequest(),
            tw = this;

        if (tw.xmlhttp != null) {
            tw.xmlhttp.abort();
        }

        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState < 4) {
                tw.inProgress(true);
            } else if (xmlhttp.readyState == 4) {
                tw.inProgress(false);
                if (xmlhttp.status == 200 || xmlhttp.status == 304) {
                    img.webtex = {
                        log : decodeURIComponent(xmlhttp.getResponseHeader("X-MathImage-log")),
                        tex : decodeURIComponent(xmlhttp.getResponseHeader("X-MathImage-tex")),
                        depth : xmlhttp.getResponseHeader("X-MathImage-depth"),
                        width : xmlhttp.getResponseHeader("X-MathImage-width"),
                        height : xmlhttp.getResponseHeader("X-MathImage-height")
                    };
                    tw.updateCounter(img);
                    if (img.webtex.log == "OK") {
                        tw.isOk(true);
                        tw.updateEditor(img);
                    } else {
                        tw.isOk(false, img.webtex.log);
                    }
                }
            }
        };
        tw.xmlhttp = xmlhttp;
        xmlhttp.open("GET", img.src, true);
        xmlhttp.send();
    },

    /*
     * Updates the URL length counter.
     */
    updateCounter : function(img) {
        var c = document.getElementById("counter"),
            l = this.max;

        if (img) {
            l -= img.src.split(/[\?&]tex=/g)[1].length;
        }
        c.textContent = l;
        if (c.textContent < 0) {
            c.className = "error";
        }
    },

    /*
     * Updates the editor with the given image.
     */
    updateEditor : function(img) {
        var f = document.forms[0],
            ed = tinyMCEPopup.editor,
            old = ed.dom.get(f.uuid.value);

        img.className = "webtex dp" + img.webtex.depth.replace("-", "_");

        if (img.webtex.width && img.webtex.height) {
            img.width = img.webtex.width;
            img.height = img.webtex.height;
        }

        ed.undoManager.add();
        if (ed.dom.get('tw_stupid_ie_workaround')) {
            ed.dom.replace(img, ed.dom.get('tw_stupid_ie_workaround'));
        } else if (old) {
            ed.dom.replace(img, old);
        } else {
            ed.selection.setNode(img);
        }

        ed.execCommand('mceRepaint', false);
    },

    /*
     * Indicates error state from WebTex service in UI.
     */
    isOk : function(isOk, str) {
        var e = document.getElementById("error");

        if (isOk) {
            e.className = "";
            e.textContent = "";
        } else {
            e.className = "alert";
            e.textContent = str.substr(2).split(/.\Wl.[0-9]+\W/g)[0]
        }
        // e.textContent = str[0];
        // if (str.length > 1) {
            // if (str[1].length > 30) {
                // e.innerHTML += ': ...{0}'.format(str[1].slice(-30));
            // } else {
                // e.innerHTML += ': {0}'.format(str[1]);
            // }
        // }
    },

    /*
     * Indicates whether there is ongoing fetch activity in UI or not.
     */
    inProgress : function(inProgress) {
        var e = document.getElementById("error");

        e.textContent = "";
        if (inProgress) {
            e.className = "working";
        } else {
            e.className = "";
        }
    },

    isDisplayStyle : function(tex) {
        return tex.match(/^\s*\\displaystyle.*/g);
    },

    setStyle : function(tex, style) {
        var tw = TinyWebtexDialog;

        if (style == "display" && ! tw.isDisplayStyle(tex)) {
            return "\\displaystyle " + tex;
        } else if (style == "inline" && tw.isDisplayStyle(tex)) {
            return tex.replace(/\s*\\displaystyle\s*/g, "");
        }
        return tex;
    },

    /*
     * Callback for keyup events in tex field of dialog. Will call for
     * a new image from WebTex if we believe that the contents have
     * changed.
     */
    update : function() {
        var tw = TinyWebtexDialog,
            f = document.forms[0],
            ed = tinyMCEPopup.editor,
            size = f.size.value,
            old = ed.dom.get(f.uuid.value),
            tex;

        f.tex.value = tw.setStyle(f.tex.value, f.style.value),
        tex = f.tex.value.trim();

        if (tex == "") {
            // Expression is empty, reset status.
            tw.updateCounter();
            tw.isOk(true);

            if (old) {
                // Remove any old image.
                ed.dom.remove(old);
                ed.execCommand('mceRepaint', false);
            }
            return;
        }

        if (old &&
            tex == tw.getTex(old) &&
            size == tw.getSize(old)) {
            // No changes compared to old image.
            return;
        }

        // New or modified image.
        tw.callWebTex(
            ed.dom.create('img', {
                id : f.uuid.value,
                'src' : "{0}/WebTex?D={1}&tex={2}".format(tw.url, size, encodeURIComponent(tex)),
                'alt' : 'tex:' + tex,
                'class' : 'webtex'
            })
        );
    },

    /*
     * Inserts string at cursor in tex buffer.
     */
    insertAtCursor : function(str) {
        var tex = document.forms[0].tex,
            end,
            range;

        if (typeof tex.selectionStart != "undefined" && typeof tex.selectionEnd != "undefined") {
            end = tex.selectionEnd;
            tex.value = tex.value.slice(0, end) + str + tex.value.slice(end);
            tex.selectionStart = tex.selectionEnd = end + str.length;
        } else if (typeof document.selection != "undefined" && typeof document.selection.createRange != "undefined") {
            // IE <= 8.
            tex.focus();
            range = document.selection.createRange();
            range.collapse(false);
            range.text = str;
            range.select();
        }
    },

    closeMenues: function() {
        var menues = document.getElementsByClassName("twMenuPane"),
            i;

        for (i = 0; i < menues.length; i++) {
            menues.item(i).style.display = "none";
        }
    },

    initShortcuts : function() {
        var tw = this,
            entries = document.getElementsByClassName("twMenuEntry"),
            menues = document.getElementsByClassName("twMenu"),
            panes = document.getElementsByClassName("twMenuPane"),
            i;

        for (i = 0; i < panes.length; i++) {
            panes.item(i).onmouseout = function() {
                tw.timer = setTimeout(tw.closeMenues, 150);
            };
            panes.item(i).onmouseover = function() {
                clearTimeout(tw.timer);
            };
        }
        for (i = 0; i < entries.length; i++) {
            entries.item(i).onclick = function() {
                tw.closeMenues();
                tw.insertAtCursor(this.title);
                tw.update();
                document.forms[0].tex.focus();
                return false;
            };
        }
        for (i = 0; i < menues.length; i++) {
            menues.item(i).onclick = function() {
                var el = document.getElementById(this.getAttribute('href').substr(1));
                if (el.style.display == "block") {
                    tw.closeMenues();
                } else {
                    tw.closeMenues();
                    el.style.display = "block";
                }
                return false;
            };
        }
    },

    /*
     * Callback for the done button in dialog.
     */
    done : function() {
        ed.dom.remove(ed.dom.get("tw_ie_stupid_workaround"));
        tinyMCEPopup.close();
    }
};

tinyMCEPopup.onInit.add(TinyWebtexDialog.init, TinyWebtexDialog);<commit_msg>Make sure we clean up regardless of how the popup is closed.<commit_after>/*
 * Copyright (C) 2012 KTH, Royal Institute of Technology (http://www.kth.se/)
 *
 * This file is part of tinywebtex.
 *
 * WebTex is free software: you can redistribute it and/or modify
 * it under the terms of a MIT style license which is included in
 * the software.
 *
 * Images handled by this plugin have these additional requirements on top
 * of the WebTex API documentation.
 *
 * 1. The D parameter in the URL is assumed to have a 1 digit argument 0-9 only.
 * 2. The tex parameter is assumed to be the last parameter.
 * 3. An existing image to edit is assumed to have an "longdesc" attribute
 *    containing a random string uniquely identifying the image on the page,
 *    but will handle most common use cases when it does not exist.
 */

tinyMCEPopup.requireLangPack();

/*
 * Add trim() to strings for browsers which don't have them.
 */
if (!String.prototype.trim) {
    String.prototype.trim = function() {
        return this.replace(/^\s+|\s+$/g, '');
    }
}

/*
 * A sprintf kind of format() enabling constructions like:
 * "Apples: {0}, Pears: {1}".format("Golden Delicious", "Alexander Lucas");
 */
String.prototype.format = function() {
    var formatted = this;
    for (arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};

/*
 * JavaScript object driving the WebTex dialog.
 */
var TinyWebtexDialog = {
    url : null,
    size : null,
    max : null,
    timer : null,
    xmlhttp : null,

    /*
     * Set up the window and populate data from selection in editor if any.
     */
    init : function() {
        var tw = this,
            f = document.forms[0],
            ed = tinyMCEPopup.editor,
            div = ed.dom.create('div', {}, ed.dom.get("tw_stupid_ie_workaround").innerHTML),
            img;

        // Make sure we clean up on exit.
        ed.windowManager.onClose.add(function () {
            var garbage = ed.dom.get('tw_stupid_ie_workaround');
            if (garbage) {
                ed.dom.setOuterHTML(garbage, garbage.innerHTML);
            }
        });

        tw.url = tinyMCEPopup.getWindowArg('webtex_url');
        tw.size = tinyMCEPopup.getWindowArg('default_size');
        tw.max = tinyMCEPopup.getWindowArg('max_length');
        f.tex.onkeyup = f.size.onchange = f.style.onchange = tw.update;

        if (!div.childNodes.length) {
            f.uuid.value = tw.randomId();
        } else {
            img = div.childNodes.item(0);
            if ((img.nodeName == 'IMG') && img.className.match("webtex")) {
                f.tex.value = tw.getTex(img);
                f.size.value = tw.getSize(img);
                f.uuid.value = img.id;
                f.style.value = tw.isDisplayStyle(f.tex.value) ? "display" : "inline" ;
            } else if (img.nodeType == Node.TEXT_NODE) {
                f.uuid.value = tw.randomId();
                f.tex.value = img.textContent;
            }
        }
        tw.initShortcuts();
        tw.update();
        f.tex.focus();
    },

    randomId: function() {
        var CHARS = "0123456789abcdef",
            id = "uuid-", i;

        for (i = 0; i < 32; i++) {
            id += CHARS[Math.floor(Math.random() * CHARS.length)];
        }
        return id;
    },

    getSize: function(img) {
        var s = img.src.split(/[\?&]D=/g)[1].substr(0,1);
        if (s) {
            return s;
        }
        return TinyWebtexDialog.size;
    },

    getTex: function(img) {
        return img.alt.substr(4);
    },

    getUuid: function(img) {
        if (img.id) {
            return img.id;
        }
        return TinyWebtexDialog.randomId();
    },

    /*
     * Send an asynchronous call to WebTex backend service for image,
     * will update the editor if successful, indicate error states
     * otherwise.
     */
    callWebTex : function(img) {
        var xmlhttp = new XMLHttpRequest(),
            tw = this;

        if (tw.xmlhttp != null) {
            tw.xmlhttp.abort();
        }

        xmlhttp.onreadystatechange = function() {
            if (xmlhttp.readyState < 4) {
                tw.inProgress(true);
            } else if (xmlhttp.readyState == 4) {
                tw.inProgress(false);
                if (xmlhttp.status == 200 || xmlhttp.status == 304) {
                    img.webtex = {
                        log : decodeURIComponent(xmlhttp.getResponseHeader("X-MathImage-log")),
                        tex : decodeURIComponent(xmlhttp.getResponseHeader("X-MathImage-tex")),
                        depth : xmlhttp.getResponseHeader("X-MathImage-depth"),
                        width : xmlhttp.getResponseHeader("X-MathImage-width"),
                        height : xmlhttp.getResponseHeader("X-MathImage-height")
                    };
                    tw.updateCounter(img);
                    if (img.webtex.log == "OK") {
                        tw.isOk(true);
                        tw.updateEditor(img);
                    } else {
                        tw.isOk(false, img.webtex.log);
                    }
                }
            }
        };
        tw.xmlhttp = xmlhttp;
        xmlhttp.open("GET", img.src, true);
        xmlhttp.send();
    },

    /*
     * Updates the URL length counter.
     */
    updateCounter : function(img) {
        var c = document.getElementById("counter"),
            l = this.max;

        if (img) {
            l -= img.src.split(/[\?&]tex=/g)[1].length;
        }
        c.textContent = l;
        if (c.textContent < 0) {
            c.className = "error";
        }
    },

    /*
     * Updates the editor with the given image.
     */
    updateEditor : function(img) {
        var f = document.forms[0],
            ed = tinyMCEPopup.editor,
            old = ed.dom.get(f.uuid.value);

        img.className = "webtex dp" + img.webtex.depth.replace("-", "_");

        if (img.webtex.width && img.webtex.height) {
            img.width = img.webtex.width;
            img.height = img.webtex.height;
        }

        ed.undoManager.add();
        if (ed.dom.get('tw_stupid_ie_workaround')) {
            ed.dom.replace(img, ed.dom.get('tw_stupid_ie_workaround'));
        } else if (old) {
            ed.dom.replace(img, old);
        } else {
            ed.selection.setNode(img);
        }

        ed.execCommand('mceRepaint', false);
    },

    /*
     * Indicates error state from WebTex service in UI.
     */
    isOk : function(isOk, str) {
        var e = document.getElementById("error");

        if (isOk) {
            e.className = "";
            e.textContent = "";
        } else {
            e.className = "alert";
            e.textContent = str.substr(2).split(/.\Wl.[0-9]+\W/g)[0]
        }
        // e.textContent = str[0];
        // if (str.length > 1) {
            // if (str[1].length > 30) {
                // e.innerHTML += ': ...{0}'.format(str[1].slice(-30));
            // } else {
                // e.innerHTML += ': {0}'.format(str[1]);
            // }
        // }
    },

    /*
     * Indicates whether there is ongoing fetch activity in UI or not.
     */
    inProgress : function(inProgress) {
        var e = document.getElementById("error");

        e.textContent = "";
        if (inProgress) {
            e.className = "working";
        } else {
            e.className = "";
        }
    },

    isDisplayStyle : function(tex) {
        return tex.match(/^\s*\\displaystyle.*/g);
    },

    setStyle : function(tex, style) {
        var tw = TinyWebtexDialog;

        if (style == "display" && ! tw.isDisplayStyle(tex)) {
            return "\\displaystyle " + tex;
        } else if (style == "inline" && tw.isDisplayStyle(tex)) {
            return tex.replace(/\s*\\displaystyle\s*/g, "");
        }
        return tex;
    },

    /*
     * Callback for keyup events in tex field of dialog. Will call for
     * a new image from WebTex if we believe that the contents have
     * changed.
     */
    update : function() {
        var tw = TinyWebtexDialog,
            f = document.forms[0],
            ed = tinyMCEPopup.editor,
            size = f.size.value,
            old = ed.dom.get(f.uuid.value),
            tex;

        f.tex.value = tw.setStyle(f.tex.value, f.style.value),
        tex = f.tex.value.trim();

        if (tex == "") {
            // Expression is empty, reset status.
            tw.updateCounter();
            tw.isOk(true);

            if (old) {
                // Remove any old image.
                ed.dom.remove(old);
                ed.execCommand('mceRepaint', false);
            }
            return;
        }

        if (old &&
            tex == tw.getTex(old) &&
            size == tw.getSize(old)) {
            // No changes compared to old image.
            return;
        }

        // New or modified image.
        tw.callWebTex(
            ed.dom.create('img', {
                id : f.uuid.value,
                'src' : "{0}/WebTex?D={1}&tex={2}".format(tw.url, size, encodeURIComponent(tex)),
                'alt' : 'tex:' + tex,
                'class' : 'webtex'
            })
        );
    },

    /*
     * Inserts string at cursor in tex buffer.
     */
    insertAtCursor : function(str) {
        var tex = document.forms[0].tex,
            end,
            range;

        if (typeof tex.selectionStart != "undefined" && typeof tex.selectionEnd != "undefined") {
            end = tex.selectionEnd;
            tex.value = tex.value.slice(0, end) + str + tex.value.slice(end);
            tex.selectionStart = tex.selectionEnd = end + str.length;
        } else if (typeof document.selection != "undefined" && typeof document.selection.createRange != "undefined") {
            // IE <= 8.
            tex.focus();
            range = document.selection.createRange();
            range.collapse(false);
            range.text = str;
            range.select();
        }
    },

    closeMenues: function() {
        var menues = document.getElementsByClassName("twMenuPane"),
            i;

        for (i = 0; i < menues.length; i++) {
            menues.item(i).style.display = "none";
        }
    },

    initShortcuts : function() {
        var tw = this,
            entries = document.getElementsByClassName("twMenuEntry"),
            menues = document.getElementsByClassName("twMenu"),
            panes = document.getElementsByClassName("twMenuPane"),
            i;

        for (i = 0; i < panes.length; i++) {
            panes.item(i).onmouseout = function() {
                tw.timer = setTimeout(tw.closeMenues, 150);
            };
            panes.item(i).onmouseover = function() {
                clearTimeout(tw.timer);
            };
        }
        for (i = 0; i < entries.length; i++) {
            entries.item(i).onclick = function() {
                tw.closeMenues();
                tw.insertAtCursor(this.title);
                tw.update();
                document.forms[0].tex.focus();
                return false;
            };
        }
        for (i = 0; i < menues.length; i++) {
            menues.item(i).onclick = function() {
                var el = document.getElementById(this.getAttribute('href').substr(1));
                if (el.style.display == "block") {
                    tw.closeMenues();
                } else {
                    tw.closeMenues();
                    el.style.display = "block";
                }
                return false;
            };
        }
    },

    /*
     * Callback for the done button in dialog.
     */
    done : function() {
        tinyMCEPopup.close();
    }
};

tinyMCEPopup.onInit.add(TinyWebtexDialog.init, TinyWebtexDialog);<|endoftext|>


<commit_before>// @flow
// React-native tooling assumes this file is here, so we just require our real entry point
import 'core-js/es6/object'  // required for babel-plugin-transform-builtin-extend in RN Android
import 'core-js/es6/array'  // required for emoji-mart in RN Android
import 'core-js/es6/string'  // required for emoji-mart in RN Android
import {load} from './index.native'

load()
<commit_msg>Polyfill Map on Android<commit_after>// @flow
// React-native tooling assumes this file is here, so we just require our real entry point
import 'core-js/es6/object'  // required for babel-plugin-transform-builtin-extend in RN Android
import 'core-js/es6/array'   // required for emoji-mart in RN Android
import 'core-js/es6/string'  // required for emoji-mart in RN Android
import 'core-js/es6/map'     // required for FlatList in RN Android
import {load} from './index.native'

load()
<|endoftext|>


<commit_before>/**
 * Created by Liusongsen Yang on 1/17/2017.
 */
var md5 = require('md5');
var path = require('path');
var util = require('util');
var mustache = require('mustache');
var minimist = require('minimist');
var multer = require('multer');
var fs = require('fs');

module.exports = function (argv, callback) {
    var filePath = 'test/mock/';
    var ignoredParams = '';
    var debug = argv.debug || true;
    var localAppDir = argv.localAppDir || __dirname;
    var extensions = argv.extensions||'';
    var responseObj = {};

    var record = function (message, severity) {
        if (debug) {
            console.log(message);
        } else if (severity > 0 && !debug) {
            console.log(message);
        }
    };

    if (extensions != '') {
        extensions = require(path.join(localAppDir, extensions));
    } else {
        extensions= null;
    }

    var localAppMockAPI = function (req, res, next) {
        debugger;
        if (req.path == '/mock/fakerespond') {

            if (req.body.id == undefined || req.body.responseText == undefined) {
                registerMock(req.body);
                record("Mock body registered for: " + req.body.requestPath, 0);
                res.writeHead(200, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"success","message":"mock body registered"}');
                return res.end();
            } else {
                record("Provided both template ID and response text", 1);
                res.writeHead(500, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"failed","message":"Provided both template ID and response text"}');
                return res.end();
            }

        } else if (req.path == '/mock/fakePOSTrespond') {
            if (req.body.id == undefined || req.body.responseText == undefined) {
                registerMock(req.body);
                record("Mock body and Unique ID for POST request registered for: " + req.body.requestPath, 0);
                res.writeHead(200, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"success","message":"mock body registered"}');
                return res.end();
            } else {
                record("Provided both template ID and response text", 1);
                res.writeHead(500, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"failed","message":"Provided both template ID and response text"}');
                return res.end();
            }

        } else if (req.path == '/mock/interceptxhr') {

            registerInterceptXHR(req.body);
            record("Intercept registered for: " + req.body.requestPath, 0);
            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });
            res.write('{"status":"success","message":"intercept XHR registered"}');

            return res.end();

        } else if (req.path == '/mock/getinterceptedxhr') {

            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });

            res.write(JSON.stringify(getInterceptedXHR(req.body)));
            delete interceptXHRMap[req.body.requestPath];

            record("Sent intercepted XHR for: " + req.body.requestPath, 0);
            return res.end();

        } else if (req.path == '/setIgnoreFlag') {

            setIgnore(req.body.requestPath)
            record("Set ignored flag for: " + req.body.requestPath, 0);

            return res.end();
        } else if (req.path == '/unregisterMock') {

            unregisterMock(req.body.requestPath);
            record("Unregisters path for: " + req.body.requestPath, 0);

            return res.end();
        } else if (req.path == '/mock/template') {

            record("Registered template for: " + req.body.id, 0);

            registerTemplate(req.body);
            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });
            res.write('{"status":"success","message":"template registered"}');

            return res.end();

        } else if (isFakeRespond(req)) {
            var post;
            if (req.method === 'POST') {
                var body = {};
                req.on('data', function (data) {
                    body += data;
                });
                req.on('end', function () {
                    post = body;
                });
            }
            if (req.method === 'POST') {
                var mockReqRespMapKey = req._parsedUrl.pathname + md5(JSON.stringify(req.body));
                responseObj = mockReqRespMap[mockReqRespMapKey];

                if (responseObj === undefined) {
                    mockReqRespMapKey = req.url + md5(JSON.stringify(req.body));
                    if (mockReqRespMapKey.indexOf("?") >= 0)
                        mockReqRespMapKey = reparsePath(mockReqRespMapKey);
                    responseObj = mockReqRespMap[req.url + md5(JSON.stringify(req.body))];

                }

            } else {

                mockReqRespMapKey = req._parsedUrl.pathname;
                responseObj = mockReqRespMap[mockReqRespMapKey];
                if (responseObj === undefined) {
                    mockReqRespMapKey = req.url;
                    if (mockReqRespMapKey.indexOf("?") >= 0)
                        mockReqRespMapKey = reparsePath(mockReqRespMapKey);
                    responseObj = mockReqRespMap[req.url];

                }
            }

            if (responseObj!== undefined && responseObj["id"] !== undefined) {
                var template = templateMap[responseObj["id"]];
                if (template === undefined) {
                    res.writeHead(500, {
                        'Content-Type': 'application/json'
                    });
                    res.write('{"status":"failed","message":"There is no template for the provided ID"}');
                    return res.end();
                }
                var pairs = responseObj["keyValues"];
                if (typeof pairs === 'string') {
                    pairs = JSON.parse(pairs);
                }
                var output = mustache.render(template, pairs);
                res.writeHead(responseObj["statusCode"], {
                    'Content-Type': responseObj["contentType"],
                    'Access-Control-Allow-Origin': '*'
                });

                if (responseObj["eval"] !== undefined) {
                    var f = eval("(" + responseObj["eval"] + ")");
                    res.write(f(req, output), post);
                } else {
                    res.write(output);
                }

                record("Responded with mock for: " + mockReqRespMapKey, 0);

            } else {
                res.writeHead(responseObj["statusCode"]||200, responseObj["header"]);
                var responseText = "";
                if (responseObj["responseFile"] !== undefined) {
                    responseText = fs.readFileSync(responseObj["responseFile"]);

                } else {
                    responseText = responseObj["responseText"];
                }

                if (responseObj["delayTime"] > 0)
                    sleep(responseObj["delayTime"]);
                if (responseObj["eval"] !== undefined) {
                    var f = eval("(" + responseObj["eval"] + ")");
                    res.write(f(req, responseText), post);
                } else {
                    res.write(responseText);
                }

                record("Responded with mock for: " + mockReqRespMapKey, 0);
            }
            return res.end();
        } else if (isInterceptXHR(req)) {
            if (req.method === 'POST') {
                addInterceptedXHR(req, req.body);
            } else {
                addInterceptedXHR(req, null);
            }

            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });
            res.write('{"status":"success","message":"XHR intercepted"}');

            record("Intercepted XHR for: " + req.url, 0);
            return res.end();
        } else {
            if (extensions) {
                var options = {
                    req: req,
                    res: res,
                    fs: fs,
                    localAppDir: localAppDir,
                    filePath: filePath
                };
                extensions.customUrlParsing(options);

            } else {
                //looks like it is not needed anymore
                //localApp.use(express.static(localAppDir + filePath));
                return next();

            }
        }
    };

    /**
     * Used to register mocks for mock API calls
     */
    var mockReqRespMap = {};

    /**
     * Used to for XHR interceptions
     */
    var interceptXHRMap = {};

    /**
     * Used for responding with a completed template
     */
    var templateMap = {};

    /**
     * Un-register the mock in the mockReqRespMap
     *
     * @param mapKey => URL that will be deleted from the memory, if empty will wipe out all the registered mock response.
     */
    function unregisterMock(mapKey) {
        if (mapKey !== "") {
            delete mockReqRespMap[mapKey];
        }
        else {
            mockReqRespMap = {};
        }

    }

    /**
     * Registers the mock into mockReqRespMap
     *
     * @param post => contains the fake response body
     */
    function registerMock(post) {

        var responseObj = {};
        responseObj["statusCode"] = parseInt(post.statusCode) || 200;
        responseObj["header"] = post.header || {
                'Content-Type': post.contentType || 'application/json',
                'Access-Control-Allow-Origin': '*'
            };
        responseObj["contentType"] = post.contentType || "application/json";
        if (typeof post.responseText == 'object') {

            responseObj["responseText"] = JSON.stringify(post.responseText);

        } else {
            responseObj["responseText"] = post.responseText || "This is a fake response";
        }

        responseObj["responseFile"] = post.responseFile;

        responseObj["id"] = post.id;
        responseObj["keyValues"] = post.keyValues || {};
        responseObj["eval"] = post.eval;
        responseObj["delayTime"] = parseInt(post.delayTime) || 0;

        var requestPath = post.requestPath;
        if (post.requestJSONBody == '' || post.requestJSONBody == null) {

            mockReqRespMap[requestPath] = responseObj;

        } else {
            responseObj["requestJSONBody"] = post.requestJSONBody;
            var uniqueID = md5(JSON.stringify(post.requestJSONBody));
            mockReqRespMap[requestPath + uniqueID] = responseObj;
        }
    }

    /**
     * Registers the mock into mockReqRespMap
     *
     * @param req =>
     *            contains the mock api call (request query string contains request
     *            path, fake status code, fake content type)
     * @param post =>
     *            contains the fake response
     */
    function registerTemplate(post) {
        templateMap[post.id] = post.template;
        record("Registered template: " + post.template, 0);

    }

    /**
     * Registers the interception XHRs into interceptXHRMap
     *
     * @param req => contains the mock api call (request query string contains request path)
     */
    function registerInterceptXHR(body) {
        var interceptedXHRs = [];
        var requestPath = body.requestPath;
        interceptXHRMap[requestPath] = interceptedXHRs;
    }

    /**
     * Saves intercepted XHR (url, method, body only) into interceptXHRMap
     *
     * @param req => XHR
     * @param post => post body of the request (if any)
     */
    function addInterceptedXHR(req, post) {
        var xhrObj = {};
        var lightXHR = {};
        lightXHR["url"] = req.url;
        lightXHR["method"] = req.method;
        xhrObj["xhr"] = lightXHR;
        xhrObj["post"] = post;

        if (interceptXHRMap[req.url] != undefined) {
            interceptXHRMap[req.url].push(xhrObj);
        } else {
            interceptXHRMap[req._parsedUrl.pathname].push(xhrObj);
        }
    }

    /**
     * Returns the intercepted XHRs
     *
     * @param req => XHR containing request path to look up (request query string contains request path)
     * @return returns object containing list of XHRs with key xhr_#
     */
    function getInterceptedXHR(req) {
        var requestPath = req.requestPath;
        var interceptedXHRs = interceptXHRMap[requestPath];

        var interceptedXHRsObj = {};
        var counter = 1;
        if (interceptedXHRs != undefined) {
            for (var i = 0; i < interceptedXHRs.length; i++) {
                interceptedXHRsObj["xhr_" + counter] = interceptedXHRs[i];
                counter++;
            }
        }

        return interceptedXHRsObj;
    }

    /**
     * Determines whether the request made by the path requires mock response by
     * checking mockReqRespMap.
     *
     * @param req => XHR
     * @return true/false
     */
    function isFakeRespond(req) {
        var temp = req.url.toString();
        var uniqueID = md5(JSON.stringify(req.body));
        if (temp.indexOf("?") >= 0)
            req.url = reparsePath(temp);
        if (req.method === 'POST' && ((req.url in mockReqRespMap) && (mockReqRespMap[req.url] !== undefined)) &&
            ((!((req.url + uniqueID) in mockReqRespMap)) && (mockReqRespMap[req.url + uniqueID] === undefined))) {
            mockReqRespMap[req.url + uniqueID] = mockReqRespMap[req.url];
            delete mockReqRespMap[req.url];
            console.log('This is POST call, but we are mocking it as a GET method, please use setMockPOSTRespond function to mock the data!!');
        }
        if (((req.url in mockReqRespMap) && (mockReqRespMap[req.url] !== undefined)) ||
            ((req._parsedUrl.pathname in mockReqRespMap) && (mockReqRespMap[req._parsedUrl.pathname] !== undefined)) ||
            (((req.url + uniqueID) in mockReqRespMap) && (mockReqRespMap[req.url + uniqueID] !== undefined))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Determines whether the request made by the path requires interception.
     *
     * @param req => XHR
     * @return true/false
     */
    function isInterceptXHR(req) {
        if (((req.url in interceptXHRMap) && (interceptXHRMap[req.url] !== undefined)) ||
            ((req._parsedUrl.pathname in interceptXHRMap) && (interceptXHRMap[req._parsedUrl.pathname] !== undefined))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * set up the root for the mock response using file system.
     * comment out by KCH due to not fully functional, will be implemented for next release.
     * @param mockPath => root of the mock files.
     *
     */
// function setMockFilePathFunc(mockPath) {
//  	filePath = mockPath;
// }

    /**
     * set up the parameter that should be ignored when retrieving mock responses, for example, a random generated cache buster.
     * @param params => parameters in the url that needs to be ignored.
     *
     */
    function setIgnore(params) {
        ignoredParams += params;
    }

    /**
     * set up delay time of a certain response. Not exposed to the client.
     * @param time => time to be delayed, represented in millisecond.
     *
     */
    function sleep(time) {
        var stop = new Date().getTime();
        while (new Date().getTime() < stop + parseInt(time)) {

        }
    }

    /**
     * Supporting function to parse the the URL to ignore the parameters that user don't need. Not exposed to the client.
     *
     */
    function reparsePath(oldpath) {
        if (oldpath.indexOf("?") >= 0) {
            var vars = oldpath.split("?")[1].split("&");
            var result = oldpath.split("?")[0] + '?';
            var firstFlag = 0;
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split("=");
                if (ignoredParams.search(pair[0]) < 0) {
                    if (firstFlag === 0) {
                        result = result + pair[0] + '=' + pair[1];
                        firstFlag = 1;
                    }
                    else {
                        result = result + '&' + pair[0] + '=' + pair[1];
                    }
                }
            }
            return result;
        }
        else {
            return oldpath;
        }
    }
    record(["MSL launched from here: ", localAppDir].join(" "), 1);
    record(["Process ID: ", process.pid].join(" "), 1);
    record(["Debug Mode: ", debug].join(" "), 1);

    return localAppMockAPI;
};<commit_msg>refactoring<commit_after>/**
 * Created by Liusongsen Yang on 1/17/2017.
 */
var md5 = require('md5');
var path = require('path');
var util = require('util');
var mustache = require('mustache');
var minimist = require('minimist');
var multer = require('multer');
var fs = require('fs');

module.exports = function (argv, callback) {
    var filePath = 'test/mock/';
    var ignoredParams = '';
    var debug = argv.debug || true;
    var localAppDir = argv.localAppDir || __dirname;
    var extensions = argv.extensions||'';
    var responseObj = {};

    var record = function (message, severity) {
        if (debug) {
            console.log(message);
        } else if (severity > 0 && !debug) {
            console.log(message);
        }
    };

    if (extensions != '') {
        extensions = require(path.join(localAppDir, extensions));
    } else {
        extensions= null;
    }

    var localAppMockAPI = function (req, res, next) {
        debugger;
        if (req.path == '/mock/fakerespond') {

            if (req.body.id == undefined || req.body.responseText == undefined) {
                registerMock(req.body);
                record("Mock body registered for: " + req.body.requestPath, 0);
                res.writeHead(200, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"success","message":"mock body registered"}');
                return res.end();
            } else {
                record("Provided both template ID and response text", 1);
                res.writeHead(500, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"failed","message":"Provided both template ID and response text"}');
                return res.end();
            }

        } else if (req.path == '/mock/fakePOSTrespond') {
            if (req.body.id == undefined || req.body.responseText == undefined) {
                registerMock(req.body);
                record("Mock body and Unique ID for POST request registered for: " + req.body.requestPath, 0);
                res.writeHead(200, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"success","message":"mock body registered"}');
                return res.end();
            } else {
                record("Provided both template ID and response text", 1);
                res.writeHead(500, {
                    'Content-Type': 'application/json'
                });
                res.write('{"status":"failed","message":"Provided both template ID and response text"}');
                return res.end();
            }

        } else if (req.path == '/mock/interceptxhr') {

            registerInterceptXHR(req.body);
            record("Intercept registered for: " + req.body.requestPath, 0);
            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });
            res.write('{"status":"success","message":"intercept XHR registered"}');

            return res.end();

        } else if (req.path == '/mock/getinterceptedxhr') {

            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });

            res.write(JSON.stringify(getInterceptedXHR(req.body)));
            delete interceptXHRMap[req.body.requestPath];

            record("Sent intercepted XHR for: " + req.body.requestPath, 0);
            return res.end();

        } else if (req.path == '/setIgnoreFlag') {

            setIgnore(req.body.requestPath)
            record("Set ignored flag for: " + req.body.requestPath, 0);

            return res.end();
        } else if (req.path == '/unregisterMock') {

            unregisterMock(req.body.requestPath);
            record("Unregisters path for: " + req.body.requestPath, 0);

            return res.end();
        } else if (req.path == '/mock/template') {

            record("Registered template for: " + req.body.id, 0);

            registerTemplate(req.body);
            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });
            res.write('{"status":"success","message":"template registered"}');

            return res.end();

        } else if (isFakeRespond(req)) {
            var post;
            if (req.method === 'POST') {
                var body = {};
                req.on('data', function (data) {
                    body += data;
                });
                req.on('end', function () {
                    post = body;
                });
            }
            if (req.method === 'POST') {
                var mockReqRespMapKey = req._parsedUrl.pathname + md5(JSON.stringify(req.body));
                responseObj = mockReqRespMap[mockReqRespMapKey];

                if (responseObj === undefined) {
                    mockReqRespMapKey = req.url + md5(JSON.stringify(req.body));
                    if (mockReqRespMapKey.indexOf("?") >= 0)
                        mockReqRespMapKey = reparsePath(mockReqRespMapKey);
                    responseObj = mockReqRespMap[req.url + md5(JSON.stringify(req.body))];

                }

            } else {

                mockReqRespMapKey = req._parsedUrl.pathname;
                responseObj = mockReqRespMap[mockReqRespMapKey];
                if (responseObj === undefined) {
                    mockReqRespMapKey = req.url;
                    if (mockReqRespMapKey.indexOf("?") >= 0)
                        mockReqRespMapKey = reparsePath(mockReqRespMapKey);
                    responseObj = mockReqRespMap[req.url];

                }
            }

            if (responseObj!== undefined && responseObj["id"] !== undefined) {
                var template = templateMap[responseObj["id"]];
                if (template === undefined) {
                    res.writeHead(500, {
                        'Content-Type': 'application/json'
                    });
                    res.write('{"status":"failed","message":"There is no template for the provided ID"}');
                    return res.end();
                }
                var pairs = responseObj["keyValues"];
                if (typeof pairs === 'string') {
                    pairs = JSON.parse(pairs);
                }
                var output = mustache.render(template, pairs);
                res.writeHead(responseObj["statusCode"], {
                    'Content-Type': responseObj["contentType"],
                    'Access-Control-Allow-Origin': '*'
                });

                if (responseObj["eval"] !== undefined) {
                    var f = eval("(" + responseObj["eval"] + ")");
                    res.write(f(req, output), post);
                } else {
                    res.write(output);
                }

                record("Responded with mock for: " + mockReqRespMapKey, 0);

            } else {
                res.writeHead(responseObj["statusCode"]||200, responseObj["header"]);
                var responseText = "";
                if (responseObj["responseFile"] !== undefined) {
                    responseText = fs.readFileSync(responseObj["responseFile"]);

                } else {
                    responseText = responseObj["responseText"];
                }

                if (responseObj["delayTime"] > 0)
                    sleep(responseObj["delayTime"]);
                if (responseObj["eval"] !== undefined) {
                    var f = eval("(" + responseObj["eval"] + ")");
                    res.write(f(req, responseText), post);
                } else {
                    res.write(responseText);
                }

                record("Responded with mock for: " + mockReqRespMapKey, 0);
            }
            return res.end();
        } else if (isInterceptXHR(req)) {
            if (req.method === 'POST') {
                addInterceptedXHR(req, req.body);
            } else {
                addInterceptedXHR(req, null);
            }

            res.writeHead(200, {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            });
            res.write('{"status":"success","message":"XHR intercepted"}');

            record("Intercepted XHR for: " + req.url, 0);
            return res.end();
        } else {
            if (extensions) {
                var options = {
                    req: req,
                    res: res,
                    fs: fs,
                    localAppDir: localAppDir,
                    filePath: filePath
                };
                extensions.customUrlParsing(options);

            } else {
                //looks like it is not needed anymore
                //localApp.use(express.static(localAppDir + filePath));
                return next();

            }
        }
    };

    /**
     * Used to register mocks for mock API calls
     */
    var mockReqRespMap = {};

    /**
     * Used to for XHR interceptions
     */
    var interceptXHRMap = {};

    /**
     * Used for responding with a completed template
     */
    var templateMap = {};

    /**
     * Un-register the mock in the mockReqRespMap
     *
     * @param mapKey => URL that will be deleted from the memory, if empty will wipe out all the registered mock response.
     */
    function unregisterMock(mapKey) {
        if (mapKey !== "") {
            delete mockReqRespMap[mapKey];
        }
        else {
            mockReqRespMap = {};
        }

    }

    /**
     * Registers the mock into mockReqRespMap
     *
     * @param post => contains the fake response body
     */
    function registerMock(post) {

        var responseObj = {};
        responseObj["statusCode"] = parseInt(post.statusCode) || 200;
        responseObj["header"] = post.header || {
                'Content-Type': post.contentType || 'application/json',
                'Access-Control-Allow-Origin': '*'
            };
        responseObj["contentType"] = post.contentType || "application/json";
        if (typeof post.responseText == 'object') {

            responseObj["responseText"] = JSON.stringify(post.responseText);

        } else {
            responseObj["responseText"] = post.responseText || "This is a fake response";
        }

        responseObj["responseFile"] = post.responseFile;

        responseObj["id"] = post.id;
        responseObj["keyValues"] = post.keyValues || {};
        responseObj["eval"] = post.eval;
        responseObj["delayTime"] = parseInt(post.delayTime) || 0;

        var requestPath = post.requestPath;
        if (post.requestJSONBody == '' || post.requestJSONBody == null) {

            mockReqRespMap[requestPath] = responseObj;

        } else {
            responseObj["requestJSONBody"] = post.requestJSONBody;
            var uniqueID = md5(JSON.stringify(post.requestJSONBody));
            mockReqRespMap[requestPath + uniqueID] = responseObj;
        }
    }

    /**
     * Registers the mock into mockReqRespMap
     *
     * @param req =>
     *            contains the mock api call (request query string contains request
     *            path, fake status code, fake content type)
     * @param post =>
     *            contains the fake response
     */
    function registerTemplate(post) {
        templateMap[post.id] = post.template;
        record("Registered template: " + post.template, 0);

    }

    /**
     * Registers the interception XHRs into interceptXHRMap
     *
     * @param req => contains the mock api call (request query string contains request path)
     */
    function registerInterceptXHR(body) {
        var interceptedXHRs = [];
        var requestPath = body.requestPath;
        interceptXHRMap[requestPath] = interceptedXHRs;
    }

    /**
     * Saves intercepted XHR (url, method, body only) into interceptXHRMap
     *
     * @param req => XHR
     * @param post => post body of the request (if any)
     */
    function addInterceptedXHR(req, post) {
        var xhrObj = {};
        var lightXHR = {};
        lightXHR["url"] = req.url;
        lightXHR["method"] = req.method;
        xhrObj["xhr"] = lightXHR;
        xhrObj["post"] = post;

        if (interceptXHRMap[req.url] != undefined) {
            interceptXHRMap[req.url].push(xhrObj);
        } else {
            interceptXHRMap[req._parsedUrl.pathname].push(xhrObj);
        }
    }

    /**
     * Returns the intercepted XHRs
     *
     * @param req => XHR containing request path to look up (request query string contains request path)
     * @return returns object containing list of XHRs with key xhr_#
     */
    function getInterceptedXHR(req) {
        var requestPath = req.requestPath;
        var interceptedXHRs = interceptXHRMap[requestPath];

        var interceptedXHRsObj = {};
        var counter = 1;
        if (interceptedXHRs != undefined) {
            for (var i = 0; i < interceptedXHRs.length; i++) {
                interceptedXHRsObj["xhr_" + counter] = interceptedXHRs[i];
                counter++;
            }
        }

        return interceptedXHRsObj;
    }

    /**
     * Determines whether the request made by the path requires mock response by
     * checking mockReqRespMap.
     *
     * @param req => XHR
     * @return true/false
     */
    function isFakeRespond(req) {
        var temp = req.url.toString();
        var uniqueID = md5(JSON.stringify(req.body));
        if (temp.indexOf("?") >= 0)
            req.url = reparsePath(temp);
        if (req.method === 'POST' && ((req.url in mockReqRespMap) && (mockReqRespMap[req.url] !== undefined)) &&
            ((!((req.url + uniqueID) in mockReqRespMap)) && (mockReqRespMap[req.url + uniqueID] === undefined))) {
            mockReqRespMap[req.url + uniqueID] = mockReqRespMap[req.url];
            delete mockReqRespMap[req.url];
            console.log('This is POST call, but we are mocking it as a GET method, please use setMockPOSTRespond function to mock the data!!');
        }
        if (((req.url in mockReqRespMap) && (mockReqRespMap[req.url] !== undefined)) ||
            ((req._parsedUrl.pathname in mockReqRespMap) && (mockReqRespMap[req._parsedUrl.pathname] !== undefined)) ||
            (((req.url + uniqueID) in mockReqRespMap) && (mockReqRespMap[req.url + uniqueID] !== undefined))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Determines whether the request made by the path requires interception.
     *
     * @param req => XHR
     * @return true/false
     */
    function isInterceptXHR(req) {
        if (((req.url in interceptXHRMap) && (interceptXHRMap[req.url] !== undefined)) ||
            ((req._parsedUrl.pathname in interceptXHRMap) && (interceptXHRMap[req._parsedUrl.pathname] !== undefined))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * set up the root for the mock response using file system.
     * comment out by KCH due to not fully functional, will be implemented for next release.
     * @param mockPath => root of the mock files.
     *
     */
// function setMockFilePathFunc(mockPath) {
//  	filePath = mockPath;
// }

    /**
     * set up the parameter that should be ignored when retrieving mock responses, for example, a random generated cache buster.
     * @param params => parameters in the url that needs to be ignored.
     *
     */
    function setIgnore(params) {
        ignoredParams += params;
    }

    /**
     * set up delay time of a certain response. Not exposed to the client.
     * @param time => time to be delayed, represented in millisecond.
     *
     */
    function sleep(time) {
        var stop = new Date().getTime();
        while (new Date().getTime() < stop + parseInt(time)) {

        }
    }

    /**
     * Supporting function to parse the the URL to ignore the parameters that user don't need. Not exposed to the client.
     *
     */
    function reparsePath(oldpath) {
        if (oldpath.indexOf("?") >= 0) {
            var vars = oldpath.split("?")[1].split("&");
            var result = oldpath.split("?")[0] + '?';
            var firstFlag = 0;
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split("=");
                if (ignoredParams.search(pair[0]) < 0) {
                    if (firstFlag === 0) {
                        result = result + pair[0] + '=' + pair[1];
                        firstFlag = 1;
                    }
                    else {
                        result = result + '&' + pair[0] + '=' + pair[1];
                    }
                }
            }
            return result;
        }
        else {
            return oldpath;
        }
    }
    record(["MSL launched from here: ", localAppDir].join(" "), 1);
    record(["Process ID: ", process.pid].join(" "), 1);
    record(["Debug Mode: ", debug].join(" "), 1);

    return localAppMockAPI;
};<|endoftext|>


<commit_before>let builder = require('focus').component.builder;
let React = require('react');
let type = require('focus').component.types;
let requestStore = require('focus').network.builtInStore();
let assign = require('object-assign');
let ProgressBar = require('../../common/progress-bar').component;
let Icon = require('../../common/icon').component;
let LoadingBarMixin = {
  /** @inheriteddoc */
  getInitialState() {
    return this._getStateFromStore();
  },
  /** @inheriteddoc */
  componentWillMount() {
    requestStore.addUpdateRequestListener(this._handleRequestsUpdate);
    requestStore.addClearRequestsListener(this._handleClearRequests);
  },
  /** @inheriteddoc */
  componentWillUnMount: function cartridgeWillUnMount(){
    requestStore.removeUpdateRequestListener(this._handleRequestsUpdate);
    requestStore.removeClearRequestsListener(this._handleClearRequests);
  },
  _getStateFromStore: function getCartridgeStateFromStore(){
    return requestStore.getRequests();
  },
  _handleRequestsUpdate: function _handlePushMessage(messageId){
    this.setState(this._getStateFromStore());
  },
  _handleClearRequests(){
    this.setState({requests: {}});
  },
  /** @inheriteddoc */
  render: function renderProgressBar() {
    var completed  = +((this.state.total - this.state.pending)/this.state.total)*100;
    return (

        <ProgressBar completed={completed} />

<Icon name='circle-o-notch' other='fa-li fa-spin'/> pending {this.state.pending}

<Icon name='circle-thin' other='fa-li'/> cancelled {this.state.cancelled}

<Icon name='check' other='fa-li'/> success {this.state.success}

<Icon name='ban' other='fa-li'/>error {this.state.error}

<Icon name='plus-square-o' other='fa-li'/>total {this.state.total}

    );
  }
};

module.exports = builder(LoadingBarMixin);
<commit_msg>[loading bar is invisible when completion is above 100]<commit_after>let builder = require('focus').component.builder;
let React = require('react');
let type = require('focus').component.types;
let requestStore = require('focus').network.builtInStore();
let assign = require('object-assign');
let ProgressBar = require('../../common/progress-bar').component;
let Icon = require('../../common/icon').component;
let LoadingBarMixin = {
  /** @inheriteddoc */
  getInitialState() {
    return this._getStateFromStore();
  },
  /** @inheriteddoc */
  componentWillMount() {
    requestStore.addUpdateRequestListener(this._handleRequestsUpdate);
    requestStore.addClearRequestsListener(this._handleClearRequests);
  },
  /** @inheriteddoc */
  componentWillUnMount: function cartridgeWillUnMount(){
    requestStore.removeUpdateRequestListener(this._handleRequestsUpdate);
    requestStore.removeClearRequestsListener(this._handleClearRequests);
  },
  _getStateFromStore: function getCartridgeStateFromStore(){
    return requestStore.getRequests();
  },
  _handleRequestsUpdate: function _handlePushMessage(messageId){
    this.setState(this._getStateFromStore());
  },
  _handleClearRequests(){
    this.setState({requests: {}});
  },
  /** @inheriteddoc */
  render: function renderProgressBar() {
    var completed  = +((this.state.total - this.state.pending)/this.state.total)*100;
    var visible = false;
    if(completed < 100){
      visible = true;
    }
    return (

        {visible && <ProgressBar completed={completed} />}

<Icon name='circle-o-notch' other='fa-li fa-spin'/> pending {this.state.pending}

<Icon name='circle-thin' other='fa-li'/> cancelled {this.state.cancelled}

<Icon name='check' other='fa-li'/> success {this.state.success}

<Icon name='ban' other='fa-li'/>error {this.state.error}

<Icon name='plus-square-o' other='fa-li'/>total {this.state.total}

    );
  }
};

module.exports = builder(LoadingBarMixin);
<|endoftext|>


<commit_before>// This file is part of GKSimproved.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
modules.endless_scrolling = {
	name: "endless_scrolling",
	dText: "Endless scrolling",
	pages: [
		{ path_name: "/", options: {
			loading: '#pager_index', path: '/browse/', domExtract: "#torrent_list tr", domInsertion: "#torrent_list"
		} },
		{ path_name: "/browse/", options: {
			loading: '.pager_align', domExtract: "#torrent_list tr", domInsertion: "#torrent_list" , lastPage: ".pager_align", pageModifier: -1
		} },
		{ path_name: "/sphinx/", options: {
			loading: '.pager_align', domExtract: "#torrent_list tr", domInsertion: "#torrent_list", canSuggest: true, lastPage: ".pager_align", pageModifier: -1
		} },
		{ path_name: "/forums.php", params: { action: 'viewforum' }, options: {
			loading: '.thin table', loadingAfter: true, domExtract: 'tbody tr', domInsertion: '.thin tr:last', insertAfter: true, scrollOffset: 180, stopInsertBottomOffset: 100, lastPage: '.linkbox:nth(2)', lastPageRegex: /\[(\d+)\]\s*$/, endOfStream: 'No posts to display!'
		} },
		{ path_name: "/forums.php", params: { action: 'viewtopic' }, options: {
			loading: '.thin table:last', loadingAfter: true, domExtract: '.thin table', domInsertion: '.thin table:last', insertAfter: true, scrollOffset: 600, stopInsertBottomOffset: 100, lastPage: '.linkbox:first', lastPageRegex: /\[(\d+)\]\s*$/
		} },
		{ path_name: "/m/peers/snatched", options: {
			loading: '.pager_align', lastPage: ".pager_align:first", domExtract: ".table100 tbody tr", domInsertion: ".table100 tbody", cancelQ: true, pageModifier: -1
		} },
		{ path_name: "/logs/", options: {
			loading: '.pager_align', lastPage: '.pager_align', domExtract: "tbody tr", domInsertion: "tbody", pageModifier: -1
		} },
		{ path_name: "/req/", options: {
			loading: '.pager_align', lastPage: '.pager_align', domExtract: "#requests_list tbody tr:not(:first)", domInsertion: "#requests_list tbody", pageModifier: -1
		} },
		{ path_name: "/m/images/(page)?", options: {
			loading: '.pager_align', lastPage: '.pager_align', domExtract: "#imageslist div", domInsertion: "#imageslist", cancelQ: true, pageModifier: -1
		} }

	],
	loaded: false,
	loadModule: function(mOptions) {
		this.loaded = true;
		var module_name = this.name;
		var dbg = function(str) {
			_dbg(module_name, str);
		};

		dbg("[Init] Loading module");
		// Loading all functions used

		var maxPage = false;
		var getMaxPage = function() {
			if(!mOptions.lastPage) {
				return;
			}

			var pagesList = $(mOptions.lastPage);
			if(!pagesList.length || !pagesList.text().match(/\S/)) {
				maxPage = true;
			}
			else {
				var lastPageRegex = mOptions.lastPageRegex ? mOptions.lastPageRegex : /(\d+) ?$/;
				var lastPageMatch = pagesList.text().match(lastPageRegex);
				if(!lastPageMatch) {
					maxPage = true
				}
				else {
					maxPage = Number(lastPageMatch[1]) + (mOptions.pageModifier ? mOptions.pageModifier : 0);
				}
			}
		};

		var defaultScrollOffset = 200;
		var backTopButtonOffset = 100;
		var loadingPage = false;
		var wentToPageBottom = false;
		var nextPage = (url.params && url.params.page ? Number(url.params.page) + 1 : 2 + (mOptions.pageModifier ? mOptions.pageModifier : 0));
		var jOnScroll = function() {
			if(!opt.get(module_name, "endless_scrolling") || ignoreScrolling || avoidEndlessScrolling) {
				return;
			}

			if(document[$.browser.mozilla ? "documentElement" : "body"].scrollTop > backTopButtonOffset) {
				$("#backTopButton").show();
			}
			else {
				$("#backTopButton").hide();
			}

			if(maxPage !== false && (maxPage === true || nextPage > maxPage)) {
				return;
			}

			if(opt.get(module_name, "pause_scrolling") && document[$.browser.mozilla ? "documentElement" : "body"].scrollTop + window.innerHeight >= document.documentElement.scrollHeight) {
				dbg("[EndlessScrolling] Stop inserting, got to page bottom");
				wentToPageBottom = true;
			}

			dbg("[EndlessScrolling] Scrolled");
			if((document[$.browser.mozilla ? "documentElement" : "body"].scrollTop + window.innerHeight > document.documentElement.scrollHeight - (mOptions.scrollOffset ? mOptions.scrollOffset : defaultScrollOffset)) && !loadingPage) {
				dbg("[EndlessScrolling] Loading next page");
				loadingPage = true;

				var nextUrl = url;
				nextUrl.path = mOptions.path ? mOptions.path : nextUrl.path;
				nextUrl.params = nextUrl.params ? nextUrl.params : {};
				nextUrl.cancelQ = (mOptions.cancelQ ? mOptions.cancelQ : (nextUrl.cancelQ ? nextUrl.cancelQ : false));
				nextUrl.params.page = nextPage;
				var loadingP = '

Réticulation des méta-données de la page suivante
';
				if(mOptions.loadingAfter) {
					$(mOptions.loading).after(loadingP);
				}
				else {
					$(mOptions.loading).before(loadingP);
				}
				grabPage(nextUrl, function(data) {
					insertionData = $(data).find(mOptions.domExtract)
					dbg("[EndlessScrolling] Grab ended")
					if(insertionData && insertionData.length && !(mOptions.endOfStream && insertionData.text().indexOf(mOptions.endOfStream) != -1)) {
						insertAjaxData(insertionData);
					}
					else {
						dbg("[EndlessScrolling] No more data");
						$(".page_loading").text("Plus rien en vue cap'tain !");
					}
				});
			}
		};

		var insertAjaxData = function(data) {
			if(wentToPageBottom) {
				dbg("[EndlessScrolling] Waiting for user confirmation in order to insert more");
				$(".page_loading").html('Reprendre l\'endless scrolling');
				$(".resume_endless_scrolling").click(function(e) {
					wentToPageBottom = false;
					e.preventDefault();
					insertAjaxData(data);
					return false;
				});
				return;
			}
			dbg("[EndlessScrolling] Got data - Inserting");
			if(mOptions.insertAfter) {
				$(mOptions.domInsertion).after(data);
			}
			else {
				$(mOptions.domInsertion).append(data);
			}
			nextPage++;
			loadingPage = false;
			$(".page_loading").remove();
			$(document).trigger("endless_scrolling_insertion_done");
			dbg("[EndlessScrolling] Ended");
		};

		dbg("[Init] Starting");
		// Execute functions

		getMaxPage();
		dbg("[endless_scrolling] url relative pages : " + (url.params && url.params.page ? url.params.page : 0) + "/" + maxPage);
		$(document).scroll(jOnScroll);

		var generateTooltip = function() {
			return modules[module_name].pages.map(function(page){
				return page.path_name + (page.params ? '?' + $.map(page.params, function(value, query) {
					return query + '=' + value;
				}).join('&') : '');
			}).join('\n');
		};
		opt.setData(module_name, "endless_scrolling", "tooltip", generateTooltip());

		dbg("[Init] Ready");
	},
};<commit_msg>Tweaked /m/images/ endless scrolling<commit_after>// This file is part of GKSimproved.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
modules.endless_scrolling = {
	name: "endless_scrolling",
	dText: "Endless scrolling",
	pages: [
		{ path_name: "/", options: {
			loading: '#pager_index', path: '/browse/', domExtract: "#torrent_list tr", domInsertion: "#torrent_list"
		} },
		{ path_name: "/browse/", options: {
			loading: '.pager_align', domExtract: "#torrent_list tr", domInsertion: "#torrent_list" , lastPage: ".pager_align", pageModifier: -1
		} },
		{ path_name: "/sphinx/", options: {
			loading: '.pager_align', domExtract: "#torrent_list tr", domInsertion: "#torrent_list", canSuggest: true, lastPage: ".pager_align", pageModifier: -1
		} },
		{ path_name: "/forums.php", params: { action: 'viewforum' }, options: {
			loading: '.thin table', loadingAfter: true, domExtract: 'tbody tr', domInsertion: '.thin tr:last', insertAfter: true, scrollOffset: 180, stopInsertBottomOffset: 100, lastPage: '.linkbox:nth(2)', lastPageRegex: /\[(\d+)\]\s*$/, endOfStream: 'No posts to display!'
		} },
		{ path_name: "/forums.php", params: { action: 'viewtopic' }, options: {
			loading: '.thin table:last', loadingAfter: true, domExtract: '.thin table', domInsertion: '.thin table:last', insertAfter: true, scrollOffset: 600, stopInsertBottomOffset: 100, lastPage: '.linkbox:first', lastPageRegex: /\[(\d+)\]\s*$/
		} },
		{ path_name: "/m/peers/snatched", options: {
			loading: '.pager_align', lastPage: ".pager_align:first", domExtract: ".table100 tbody tr", domInsertion: ".table100 tbody", cancelQ: true, pageModifier: -1
		} },
		{ path_name: "/logs/", options: {
			loading: '.pager_align', lastPage: '.pager_align', domExtract: "tbody tr", domInsertion: "tbody", pageModifier: -1
		} },
		{ path_name: "/req/", options: {
			loading: '.pager_align', lastPage: '.pager_align', domExtract: "#requests_list tbody tr:not(:first)", domInsertion: "#requests_list tbody", pageModifier: -1
		} },
		{ path_name: "/m/images/", options: {
			loading: '.pager_align', lastPage: '.pager_align', domExtract: "#imageslist div", domInsertion: "#imageslist", cancelQ: true, pageModifier: -1
		} }

	],
	loaded: false,
	loadModule: function(mOptions) {
		this.loaded = true;
		var module_name = this.name;
		var dbg = function(str) {
			_dbg(module_name, str);
		};

		dbg("[Init] Loading module");
		// Loading all functions used

		var maxPage = false;
		var getMaxPage = function() {
			if(!mOptions.lastPage) {
				return;
			}

			var pagesList = $(mOptions.lastPage);
			if(!pagesList.length || !pagesList.text().match(/\S/)) {
				maxPage = true;
			}
			else {
				var lastPageRegex = mOptions.lastPageRegex ? mOptions.lastPageRegex : /(\d+) ?$/;
				var lastPageMatch = pagesList.text().match(lastPageRegex);
				if(!lastPageMatch) {
					maxPage = true
				}
				else {
					maxPage = Number(lastPageMatch[1]) + (mOptions.pageModifier ? mOptions.pageModifier : 0);
				}
			}
		};

		var defaultScrollOffset = 200;
		var backTopButtonOffset = 100;
		var loadingPage = false;
		var wentToPageBottom = false;
		var nextPage = (url.params && url.params.page ? Number(url.params.page) + 1 : 2 + (mOptions.pageModifier ? mOptions.pageModifier : 0));
		var jOnScroll = function() {
			if(!opt.get(module_name, "endless_scrolling") || ignoreScrolling || avoidEndlessScrolling) {
				return;
			}

			if(document[$.browser.mozilla ? "documentElement" : "body"].scrollTop > backTopButtonOffset) {
				$("#backTopButton").show();
			}
			else {
				$("#backTopButton").hide();
			}

			if(maxPage !== false && (maxPage === true || nextPage > maxPage)) {
				return;
			}

			if(opt.get(module_name, "pause_scrolling") && document[$.browser.mozilla ? "documentElement" : "body"].scrollTop + window.innerHeight >= document.documentElement.scrollHeight) {
				dbg("[EndlessScrolling] Stop inserting, got to page bottom");
				wentToPageBottom = true;
			}

			dbg("[EndlessScrolling] Scrolled");
			if((document[$.browser.mozilla ? "documentElement" : "body"].scrollTop + window.innerHeight > document.documentElement.scrollHeight - (mOptions.scrollOffset ? mOptions.scrollOffset : defaultScrollOffset)) && !loadingPage) {
				dbg("[EndlessScrolling] Loading next page");
				loadingPage = true;

				var nextUrl = url;
				nextUrl.path = mOptions.path ? mOptions.path : nextUrl.path;
				nextUrl.params = nextUrl.params ? nextUrl.params : {};
				nextUrl.cancelQ = (mOptions.cancelQ ? mOptions.cancelQ : (nextUrl.cancelQ ? nextUrl.cancelQ : false));
				nextUrl.params.page = nextPage;
				var loadingP = '

Réticulation des méta-données de la page suivante
';
				if(mOptions.loadingAfter) {
					$(mOptions.loading).after(loadingP);
				}
				else {
					$(mOptions.loading).before(loadingP);
				}
				grabPage(nextUrl, function(data) {
					insertionData = $(data).find(mOptions.domExtract)
					dbg("[EndlessScrolling] Grab ended")
					if(insertionData && insertionData.length && !(mOptions.endOfStream && insertionData.text().indexOf(mOptions.endOfStream) != -1)) {
						insertAjaxData(insertionData);
					}
					else {
						dbg("[EndlessScrolling] No more data");
						$(".page_loading").text("Plus rien en vue cap'tain !");
					}
				});
			}
		};

		var insertAjaxData = function(data) {
			if(wentToPageBottom) {
				dbg("[EndlessScrolling] Waiting for user confirmation in order to insert more");
				$(".page_loading").html('Reprendre l\'endless scrolling');
				$(".resume_endless_scrolling").click(function(e) {
					wentToPageBottom = false;
					e.preventDefault();
					insertAjaxData(data);
					return false;
				});
				return;
			}
			dbg("[EndlessScrolling] Got data - Inserting");
			if(mOptions.insertAfter) {
				$(mOptions.domInsertion).after(data);
			}
			else {
				$(mOptions.domInsertion).append(data);
			}
			nextPage++;
			loadingPage = false;
			$(".page_loading").remove();
			$(document).trigger("endless_scrolling_insertion_done");
			dbg("[EndlessScrolling] Ended");
		};

		dbg("[Init] Starting");
		// Execute functions

		getMaxPage();
		dbg("[endless_scrolling] url relative pages : " + (url.params && url.params.page ? url.params.page : 0) + "/" + maxPage);
		$(document).scroll(jOnScroll);

		var generateTooltip = function() {
			return modules[module_name].pages.map(function(page){
				return page.path_name + (page.params ? '?' + $.map(page.params, function(value, query) {
					return query + '=' + value;
				}).join('&') : '');
			}).join('\n');
		};
		opt.setData(module_name, "endless_scrolling", "tooltip", generateTooltip());

		dbg("[Init] Ready");
	},
};<|endoftext|>


<commit_before>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import queryString from 'query-string';
import makeQueryFunction from '@folio/stripes-components/util/makeQueryFunction';
import { stripesShape } from '@folio/stripes-core/src/Stripes';
import SearchAndSort from'./lib/SearchAndSort';
import { toUserAddresses } from './converters/address';
import packageInfo from './package';

// XXX Keep in sync with the value from <SearchAndSort>. Later, we will find a better way to share the value
const INITIAL_RESULT_COUNT = 30;

const filterConfig = [
  {
    label: 'Status',
    name: 'active',
    cql: 'active',
    values: [
      { name: 'Active', cql: 'true' },
      { name: 'Inactive', cql: 'false' },
    ],
  },
  {
    label: 'Patron group',
    name: 'pg',
    cql: 'patronGroup',
    values: [], // will be filled in by componentWillUpdate
  },
];

class Users extends React.Component {
  static propTypes = {
    stripes: stripesShape.isRequired,
    resources: PropTypes.shape({
      patronGroups: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      addressTypes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      users: PropTypes.shape({
        hasLoaded: PropTypes.bool.isRequired,
        other: PropTypes.shape({
          totalRecords: PropTypes.number.isRequired,
        }),
        isPending: PropTypes.bool.isPending,
        successfulMutations: PropTypes.arrayOf(
          PropTypes.shape({
            record: PropTypes.shape({
              id: PropTypes.string.isRequired,
              username: PropTypes.string.isRequired,
            }).isRequired,
          }),
        ),
      }),
      userCount: PropTypes.number,
      notes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
    }).isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
      search: PropTypes.string,
    }).isRequired,
    match: PropTypes.shape({
      path: PropTypes.string.isRequired,
    }).isRequired,
    mutator: PropTypes.shape({
      userCount: PropTypes.shape({
        replace: PropTypes.func,
      }),
      users: PropTypes.shape({
        POST: PropTypes.func,
      }),
    }).isRequired,
    okapi: PropTypes.shape({
      url: PropTypes.string.isRequired,
      tenant: PropTypes.string.isRequired,
      token: PropTypes.string.isRequired,
    }).isRequired,
    onSelectRow: PropTypes.func,
    disableUserCreation: PropTypes.bool,
  };

  static manifest = Object.freeze({
    userCount: { initialValue: INITIAL_RESULT_COUNT },
    users: {
      type: 'okapi',
      records: 'users',
      recordsRequired: '%{userCount}',
      perRequest: 30,
      path: 'users',
      GET: {
        params: {
          query: makeQueryFunction(
            'username=*',
            'username="$QUERY*" or personal.firstName="$QUERY*" or personal.lastName="$QUERY*" or personal.email="$QUERY*" or barcode="$QUERY*" or id="$QUERY*" or externalSystemId="$QUERY*"',
            {
              Status: 'active',
              Name: 'personal.lastName personal.firstName',
              'Patron Group': 'patronGroup.group',
              Username: 'username',
              Barcode: 'barcode',
              Email: 'personal.email',
            },
            filterConfig,
          ),
        },
        staticFallback: { params: {} },
      },
    },
    patronGroups: {
      type: 'okapi',
      path: 'groups',
      records: 'usergroups',
    },
    addressTypes: {
      type: 'okapi',
      path: 'addresstypes',
      records: 'addressTypes',
    },
  });

  constructor(props) {
    super(props);

    this.state = {};
    this.okapi = props.okapi;

    this.connectedSearchAndSort = props.stripes.connect(SearchAndSort);

    const logger = props.stripes.logger;
    this.log = logger.log.bind(logger);

    this.anchoredRowFormatter = this.anchoredRowFormatter.bind(this);

    this.resultsList = null;
  }

  componentWillUpdate() {
    const pg = (this.props.resources.patronGroups || {}).records || [];

    if (pg && pg.length) {
      filterConfig[1].values = pg.map(rec => ({ name: rec.group, cql: rec.id }));
    }
  }

  getRowURL(rowData) {
    return `/users/view/${rowData.id}${this.props.location.search}`;
  }

  // custom row formatter to wrap rows in anchor tags.
  anchoredRowFormatter(
    { rowIndex,
      rowClass,
      rowData,
      cells,
      rowProps,
      labelStrings,
    },
  ) {
    return (
      <a
        href={this.getRowURL(rowData)} key={`row-${rowIndex}`}
        aria-label={labelStrings && labelStrings.join('...')}
        role="listitem"
        className={rowClass}
        {...rowProps}
      >
        {cells}
      </a>
    );
  }

  render() {
    const props = this.props;
    const urlQuery = queryString.parse(props.location.search || '');
    const initialPath = (_.get(packageInfo, ['stripes', 'home']) ||
                         _.get(packageInfo, ['stripes', 'route']));

    return (<this.connectedSearchAndSort
      stripes={props.stripes}
      okapi={props.okapi}
      initialPath={initialPath}
      filterConfig={filterConfig}
      parentResources={props.resources}
      parentMutator={props.mutator}
      onSelectRow={props.onSelectRow}
      path={props.location.pathname}
      urlQuery={urlQuery}
    />);
  }
}

export default Users;
<commit_msg>Vertical whitespace<commit_after>import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import queryString from 'query-string';
import makeQueryFunction from '@folio/stripes-components/util/makeQueryFunction';
import { stripesShape } from '@folio/stripes-core/src/Stripes';
import SearchAndSort from'./lib/SearchAndSort';
import packageInfo from './package';

const INITIAL_RESULT_COUNT = 30;
const RESULT_COUNT_INCREMENT = 30;

const filterConfig = [
  {
    label: 'Status',
    name: 'active',
    cql: 'active',
    values: [
      { name: 'Active', cql: 'true' },
      { name: 'Inactive', cql: 'false' },
    ],
  },
  {
    label: 'Patron group',
    name: 'pg',
    cql: 'patronGroup',
    values: [], // will be filled in by componentWillUpdate
  },
];

class Users extends React.Component {
  static propTypes = {
    stripes: stripesShape.isRequired,
    resources: PropTypes.shape({
      patronGroups: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      addressTypes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
      users: PropTypes.shape({
        hasLoaded: PropTypes.bool.isRequired,
        other: PropTypes.shape({
          totalRecords: PropTypes.number.isRequired,
        }),
        isPending: PropTypes.bool.isPending,
        successfulMutations: PropTypes.arrayOf(
          PropTypes.shape({
            record: PropTypes.shape({
              id: PropTypes.string.isRequired,
              username: PropTypes.string.isRequired,
            }).isRequired,
          }),
        ),
      }),
      userCount: PropTypes.number,
      notes: PropTypes.shape({
        records: PropTypes.arrayOf(PropTypes.object),
      }),
    }).isRequired,
    history: PropTypes.shape({
      push: PropTypes.func.isRequired,
    }).isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string.isRequired,
      search: PropTypes.string,
    }).isRequired,
    match: PropTypes.shape({
      path: PropTypes.string.isRequired,
    }).isRequired,
    mutator: PropTypes.shape({
      userCount: PropTypes.shape({
        replace: PropTypes.func,
      }),
      users: PropTypes.shape({
        POST: PropTypes.func,
      }),
    }).isRequired,
    okapi: PropTypes.shape({
      url: PropTypes.string.isRequired,
      tenant: PropTypes.string.isRequired,
      token: PropTypes.string.isRequired,
    }).isRequired,
    onSelectRow: PropTypes.func,
    disableUserCreation: PropTypes.bool,
  };

  static manifest = Object.freeze({
    userCount: { initialValue: INITIAL_RESULT_COUNT },
    users: {
      type: 'okapi',
      records: 'users',
      recordsRequired: '%{userCount}',
      perRequest: 30,
      path: 'users',
      GET: {
        params: {
          query: makeQueryFunction(
            'username=*',
            'username="$QUERY*" or personal.firstName="$QUERY*" or personal.lastName="$QUERY*" or personal.email="$QUERY*" or barcode="$QUERY*" or id="$QUERY*" or externalSystemId="$QUERY*"',
            {
              Status: 'active',
              Name: 'personal.lastName personal.firstName',
              'Patron Group': 'patronGroup.group',
              Username: 'username',
              Barcode: 'barcode',
              Email: 'personal.email',
            },
            filterConfig,
          ),
        },
        staticFallback: { params: {} },
      },
    },
    patronGroups: {
      type: 'okapi',
      path: 'groups',
      records: 'usergroups',
    },
    addressTypes: {
      type: 'okapi',
      path: 'addresstypes',
      records: 'addressTypes',
    },
  });

  constructor(props) {
    super(props);

    this.state = {};
    this.okapi = props.okapi;

    this.connectedSearchAndSort = props.stripes.connect(SearchAndSort);

    const logger = props.stripes.logger;
    this.log = logger.log.bind(logger);

    this.anchoredRowFormatter = this.anchoredRowFormatter.bind(this);

    this.resultsList = null;
  }

  componentWillUpdate() {
    const pg = (this.props.resources.patronGroups || {}).records || [];

    if (pg && pg.length) {
      filterConfig[1].values = pg.map(rec => ({ name: rec.group, cql: rec.id }));
    }
  }

  getRowURL(rowData) {
    return `/users/view/${rowData.id}${this.props.location.search}`;
  }

  // custom row formatter to wrap rows in anchor tags.
  anchoredRowFormatter(
    { rowIndex,
      rowClass,
      rowData,
      cells,
      rowProps,
      labelStrings,
    },
  ) {
    return (
      <a
        href={this.getRowURL(rowData)} key={`row-${rowIndex}`}
        aria-label={labelStrings && labelStrings.join('...')}
        role="listitem"
        className={rowClass}
        {...rowProps}
      >
        {cells}
      </a>
    );
  }

  render() {
    const props = this.props;
    const urlQuery = queryString.parse(props.location.search || '');
    const initialPath = (_.get(packageInfo, ['stripes', 'home']) ||
                         _.get(packageInfo, ['stripes', 'route']));

    return (<this.connectedSearchAndSort
      stripes={props.stripes}
      okapi={props.okapi}
      initialPath={initialPath}
      filterConfig={filterConfig}
      initialResultCount={INITIAL_RESULT_COUNT}
      resultCountIncrement={RESULT_COUNT_INCREMENT}
      parentResources={props.resources}
      parentMutator={props.mutator}
      onSelectRow={props.onSelectRow}
      path={props.location.pathname}
      urlQuery={urlQuery}
    />);
  }
}

export default Users;
<|endoftext|>