Source: core/path.js

/**
 * @namespace pc.path
 * @description File path API
 */
pc.path = function () {
    return {
        /**
         * The character that separates path segments
         * @name pc.path.delimiter
         */
        delimiter: "/",
        /**
         * @function
         * @name pc.path.join
         * @description Join two sections of file path together, insert a delimiter if needed.
         * @param {String} one First part of path to join.
         * @param {String} two Second part of path to join.
         * @returns {String} The joined file path.
         */
        join: function () {
            var index;
            var num = arguments.length;
            var result = arguments[0];

            for (index = 0; index < num - 1; ++index) {
                var one = arguments[index];
                var two = arguments[index + 1];
                if (!pc.isDefined(one) || !pc.isDefined(two)) {
                    throw new Error("undefined argument to pc.path.join");
                }
                if (two[0] === pc.path.delimiter) {
                    result = two;
                    continue;
                }

                if (one && two && one[one.length - 1] !== pc.path.delimiter && two[0] !== pc.path.delimiter) {
                    result += (pc.path.delimiter + two);
                } else {
                    result += (two);
                }
            }

            return result;
        },

        /**
         * @function
         * @name  pc.path.normalize
         * @description  Normalize the path by removing '.' and '..' instances
         * @param  {String} path The path to normalize
         * @returns {String} The normalized path
         */
        normalize: function (path) {
            var lead = path.startsWith(pc.path.delimiter);
            var trail = path.endsWith(pc.path.delimiter);

            var parts = path.split('/');

            var result = '';

            var cleaned = [];

            for (var i = 0; i < parts.length; i++) {
                if (parts[i] === '') continue;
                if (parts[i] === '.') continue;
                if (parts[i] === '..' && cleaned.length > 0) {
                    cleaned = cleaned.slice(0, cleaned.length - 2);
                    continue;
                }

                if (i > 0) cleaned.push(pc.path.delimiter);
                cleaned.push(parts[i]);
            }


            result = cleaned.join('');
            if (!lead && result[0] === pc.path.delimiter) {
                result = result.slice(1);
            }

            if (trail && result[result.length - 1] !== pc.path.delimiter) {
                result += pc.path.delimiter;
            }

            return result;
        },

        /**
         * @function
         * @name pc.path.split
         * @description Split the pathname path into a pair [head, tail] where tail is the final part of the path
         * after the last delimiter and head is everything leading up to that. tail will never contain a slash
         * @param {String} path The path to split.
         * @returns {Array} The split path which is an array of two strings, the path and the filename.
         */
        split: function (path) {
            var parts = path.split(pc.path.delimiter);
            var tail = parts.slice(parts.length - 1)[0];
            var head = parts.slice(0, parts.length - 1).join(pc.path.delimiter);
            return [head, tail];
        },

        /**
         * @function
         * @name pc.path.getBasename
         * @description Return the basename of the path. That is the second element of the pair returned by
         * passing path into {@link pc.path.split}.
         * @param {String} path The path to process.
         * @returns {String} The basename.
         * @example
         * pc.path.getBasename("/path/to/file.txt"); // returns "path.txt"
         * pc.path.getBasename("/path/to/dir"); // returns "dir"
         */
        getBasename: function (path) {
            return pc.path.split(path)[1];
        },

        /**
         * @function
         * @name pc.path.getDirectory
         * @description Get the directory name from the path. This is everything up to the final instance of pc.path.delimiter.
         * @param {String} path The path to get the directory from
         * @returns {String} The directory part of the path.
         */
        getDirectory: function (path) {
            var parts = path.split(pc.path.delimiter);
            return parts.slice(0, parts.length - 1).join(pc.path.delimiter);
        },

        getExtension: function (path) {
            var ext = path.split('?')[0].split('.').pop();
            if (ext !== path) {
                return "." + ext;
            }
            return "";
        },

        isRelativePath: function (s) {
            return s.charAt(0) !== "/" && s.match(/:\/\//) === null;
        },

        extractPath: function (s) {
            var path = ".",
                parts = s.split("/"),
                i = 0;

            if (parts.length > 1) {
                if (pc.path.isRelativePath(s) === false) {
                    path = "";
                }
                for (i = 0; i < parts.length - 1; ++i) {
                    path += "/" + parts[i];
                }
            }
            return path;
        }
    };
}();