dlueth/qoopido.nucleus

View on GitHub
src/url.js

Summary

Maintainability
B
6 hrs
Test Coverage
/**
 * @use /demand/abstract/uuid
 * @use /demand/function/iterate
 */

(function(global, document) {
    'use strict';

    function definition(abstractUuid, iterate) {
        var objectDefineProperties    = Object.defineProperties,
            storage                   = {},
            regexMatchLeadingSlash    = /^\//,
            regexMatchParameter       = /[?&]?([^=]+)=([^&]*)/g,
            regexMatchSpaces          = /%20/g,
            regexMatchOpeningBrackets = /%5B/g,
            regexMatchClosingBrackets = /%5D/g,
            regexMatchLocal           = new RegExp('^' + resolveCurrent(), 'i'),
            regexMatchDefaults        = {
                'http:':  /:80$/,
                'https:': /:443$/,
                'ftp:':   /:21$/
            };

        function resolveCurrent() {
            var resolver = document.createElement('a');

            resolver.href = '/';

            return resolver.href.slice(0, -1);
        }

        function getLinkProperty(uuid, property) {
            var properties;

            if(properties = storage[uuid]) {
                return properties.link[property];
            }
        }

        function setLinkProperty(uuid, property, value) {
            var properties;

            if(properties = storage[uuid]) {
                properties.link[property] = value;
            }
        }

        function Parameter(uuid) {
            var self = this;

            objectDefineProperties(self, { uuid: { value: uuid } });

            Parameter.update(self);
        }

        Parameter.prototype = {
            /* only for reference
            uuid: null
            */
            get: function(name) {
                return storage[this.uuid].parameter[name];
            },
            set: function(name, value) {
                storage[this.uuid].parameter[name] = value;

                Parameter.serialize(this);
            },
            remove: function(name) {
                delete storage[this.uuid].parameter[name];

                Parameter.serialize(this);
            }
        };

        Parameter.update = function(context) {
            var properties = storage[context.uuid],
                parameter  = properties.parameter,
                search     = properties.link.search.split('+').join(' '),
                tokens;

            iterate(parameter, function(property) {
                delete parameter[property];
            });

            while(tokens = regexMatchParameter.exec(search)) {
                parameter[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
            }
        };

        Parameter.serialize = function(context) {
            var properties = storage[context.uuid],
                parameter  = properties.parameter,
                result     = '';

            iterate(parameter, function(property, value) {
                result += (!result ? '?' : '&') + encodeURIComponent(property) + '=' + encodeURIComponent(value);
            });

            properties.link.search = result
                .replace(regexMatchSpaces, '+')
                .replace(regexMatchOpeningBrackets, '[')
                .replace(regexMatchClosingBrackets, ']');
        };

        function Url(url) {
            var self = abstractUuid.call(this),
                link = document.createElement('a');

            link.href = url;

            storage[self.uuid] = {
                link:      link,
                parameter: {}
            };

            objectDefineProperties(self, {
                toString:  { value: function() { return link.toString() } },
                valueOf:   { value: function() { return link.valueOf(); } },
                parameter: { value: new Parameter(self.uuid) }
            });

            return self;
        }

        Url.prototype = {
            get local() {
                var value;

                if(value = getLinkProperty(this.uuid, 'href')) {
                    return regexMatchLocal.test(value);
                }
            },
            get href() {
                return getLinkProperty(this.uuid, 'href');
            },
            set href(value) {
                setLinkProperty(this.uuid, 'href', value);
            },
            get origin() {
                var self = this;

                return getLinkProperty(self.uuid, 'origin') || self.protocol + '//' + self.host;
            },
            get protocol() {
                return getLinkProperty(this.uuid, 'protocol');
            },
            set protocol(value) {
                setLinkProperty(this.uuid, 'protocol', value);
            },
            get username() {
                return getLinkProperty(this.uuid, 'username');
            },
            set username(value) {
                setLinkProperty(this.uuid, 'username', value);
            },
            get password() {
                return getLinkProperty(this.uuid, 'password');
            },
            set password(value) {
                setLinkProperty(this.uuid, 'password', value);
            },
            get host() {
                var value, protocol;

                if(value = getLinkProperty(this.uuid, 'host')) {
                    protocol = getLinkProperty(this.uuid, 'protocol');

                    return regexMatchDefaults[protocol] ? value.replace(regexMatchDefaults[protocol], '') : value;
                }
            },
            set host(value) {
                setLinkProperty(this.uuid, 'host', value);
            },
            get hostname() {
                return getLinkProperty(this.uuid, 'hostname');
            },
            set hostname(value) {
                setLinkProperty(this.uuid, 'hostname', value);
            },
            get port() {
                return getLinkProperty(this.uuid, 'port');
            },
            set port(value) {
                setLinkProperty(this.uuid, 'port', value);
            },
            get pathname() {
                var value;

                if(value = getLinkProperty(this.uuid, 'pathname')) {
                    return regexMatchLeadingSlash.test(value) ? value : '/' + value;
                }
            },
            set pathname(value) {
                setLinkProperty(this.uuid, 'pathname', value);
            },
            get search() {
                return getLinkProperty(this.uuid, 'search');
            },
            set search(value) {
                setLinkProperty(this.uuid, 'search', value);

                Parameter.update.call(this);
            },
            get hash() {
                return getLinkProperty(this.uuid, 'hash');
            },
            set hash(value) {
                setLinkProperty(this.uuid, 'hash', value);
            }
        };

        return Url.extends(abstractUuid);
    }

    provide([ '/demand/abstract/uuid', '/demand/function/iterate' ], definition);
}(this, document));