/*
 * Author: Alexandre Havrileck (Oxyno-zeta)
 * Date: 22/10/16
 * Licence: See Readme
 */
/* ************************************* */
/* ********       IMPORTS       ******** */
/* ************************************* */


/* ************************************* */
/* ********      VARIABLES      ******** */
/* ************************************* */

const basicFunctionPattern = new RegExp(
    // eslint-disable-next-line prefer-template
    ''
    + /^function/.source
    + / *([$_a-zA-Z][$\w]*)?/.source // name
    + / *\([ \n]*([$_a-zA-Z][$\w]*(?:[ \n]*,[ \n]*[$_a-zA-Z][$\w]*)*)*?,?[ \n]*\)/.source // params
    + /[ \n]*{\n*(.*)[ \n]*}$/.source, // body
    's',
);

/* ************************************* */
/* ********  PRIVATE FUNCTIONS  ******** */
/* ************************************* */

/**
 * Try to regex match a string as a javascript function.
 * @param functionString {string} string to match
 * @param splitParams {boolean} whether to split parameters into an array
 * @returns {{name: string, params: string | string[], body: string} | null}
 */
function matchFunction(functionString, splitParams = false) {
    const match = basicFunctionPattern.exec(functionString);
    if (match === null) return null;
    return {
        name: match[1],
        params: splitParams ? commaSplit(match[2]) : match[2],
        body: match[3],
    };
}

/**
 * Split comma separated strings and trim surrounding whitespace.
 * @param string {string | undefined} a string of comma-separated strings
 * @returns {string[]} an array of elements that were separated by commas with
 *   surrounding whitespace trimmed. May be empty.
 */
function commaSplit(string) {
    if (!string) return [];
    return string.split(',').map(x => x.trim());
}

/**
 * Sanitize a string that might have a function definition.
 * Note that this is not a perfect solution, there are still
 * security flaws.
 * @param functionString {string} string to sanitize
 * @returns {Function | null}
 */
function sanitizeFunction(functionString) {
    /* This is not an exhaustive check by any means
     * For instance, function names may have a wide variety of
     * unicode characters and still be valid... oh well!
     *
     * TEST CASES:
     *
     * // Should match (single-line):
     * function() {}
     * function () {}
     * function myFunc(){}
     * function myFunc(arg1){}
     * function(arg1,arg2, arg3,  arg4) {}
     * function myFunc(arg1, arg2, arg3){}
     * function myFunc(arg1, arg2, arg3){console.log('something');}
     * function myFunc(arg1,){}
     * function myFunc(arg1, ){}
     * function myFunc(arg1) {if (true) {var moreCurlyBraces = 1;}}
     *
     * // Should match (multi-line):
     * function myFunc(arg1, arg2, arg3) {
     *     console.log('something');
     * }
     *
     * function myFunc() {
     *     console.log('test');
     *     if (true) {
     *         console.log('test2');
     *     }
     * }
     *
     * // Should not match (single-line):
     * anotherFunction()
     * function myFunc {}
     * function myFunc()); (anotherFunction()
     * function myFunc(){}, anotherFunction()
     */
    const match = matchFunction(functionString, true);
    if (!match) return null;

    // Here's the security flaw. We want this functionality for supporting
    // JSONP, so we've opted for the best attempt at maintaining some amount
    // of security. This should be a little better than eval because it
    // shouldn't automatically execute code, just create a function which can
    // be called later.
    // eslint-disable-next-line no-new-func
    const func = new Function(...match.params, match.body || '');
    func.displayName = match.name;
    return func;
}


/* ************************************* */
/* ********   PUBLIC FUNCTIONS  ******** */
/* ************************************* */

/**
 * Parse a string into either a function or a JSON element.
 * @param string {string} string to parse
 * @returns {Function | Object | Array | null | boolean | number | string}
 */
function parse(string) {
    // Try parsing (and sanitizing) a function
    const func = sanitizeFunction(string);
    if (func !== null) return func;

    try {
        return JSON.parse(string);
    } catch (e) {
        return string;
    }
}

/* ************************************* */
/* ********       EXPORTS       ******** */
/* ************************************* */
export default parse;

@@ -1770,43 +1770,95 @@
 %0A * 
-Sanitize a string that might have a
+Try creating an anonymous function from a string, or return null if it's%0A * not a valid
  fun
@@ -1906,24 +1906,23 @@
 t a 
-perfect solution
+completely safe
 , th
@@ -1934,19 +1934,16 @@
 re still
-%0A *
  securit
@@ -1949,16 +1949,54 @@
 ty flaws
+,%0A * but it is safer than using %60exec%60
 .%0A * @pa
@@ -2033,24 +2033,58 @@
 ring to 
-sa
+try to parse as a function%0A *   defi
 niti
-ze
+on
 %0A * @ret
@@ -2109,29 +2109,110 @@
 ull%7D
-%0A */%0Afunction sanitiz
+ an anonymous function if the string is a valid%0A *   function definition, else null%0A */%0Afunction creat
 eFun
@@ -4331,23 +4331,21 @@
  func = 
-sanitiz
+creat
 eFunctio

Rename sanitizeFunction to createFunction and update docstring.