Joyquery

Javascript library implementing emulation of CSS selectors lookup, as document.querySelector.

View the Project on GitHub jeremiah-shaulov/joyquery

Home
Features
Selectors
API reference
Adding extensions
Benchmark

See also:
Joyquery for PHP

Joyquery allows to add support for pseudo classes, which are not supported by the library. There are 3 standard classes that joyquery doesn't support by default: :active, :hover and :lang(). On this page i'll show how to add them. Also you are free to add your custom pseudo classes.

:active

if (!joyquery.FUNCTIONS.active)
{   function add_event(node, onwhat, callback, capture)
    {   if (node.addEventListener)
        {   if (onwhat.substr(0, 2) == 'on') onwhat = onwhat.substring(2);
            node.addEventListener(onwhat, callback, !!capture);
            return function()
            {   node.removeEventListener(onwhat, callback, !!capture);
            };
        }
        else
        {   node.attachEvent(onwhat, callback);
            return function()
            {   node.detachEvent(onwhat, callback);
            };
        }
    }

    var active_last_target = null;

    add_event
    (   document,
        'onmousedown',
        function(evt)
        {   active_last_target = (evt || window.event || {}).target;
            function done(evt)
            {   var true_evt = evt || window.event || {};
                if (true_evt.type!='mousemove' || true_evt.target!=active_last_target)
                {   active_last_target = null;
                    onmouseup();
                    onmousemove();
                }
            }
            var onmouseup = add_event(document, 'onmouseup', done);
            var onmousemove = add_event(document, 'onmousemove', done);
        }
    );

    joyquery.FUNCTIONS.active = function()
    {   var node = this.node;
        var last = active_last_target;
        while (last && last!=node)
        {   last = last.parentNode;
        }
        return last;
    };
}

:hover

if (!joyquery.FUNCTIONS.hover)
{   function add_event(node, onwhat, callback, capture)
    {   if (node.addEventListener)
        {   if (onwhat.substr(0, 2) == 'on') onwhat = onwhat.substring(2);
            node.addEventListener(onwhat, callback, !!capture);
            return function()
            {   node.removeEventListener(onwhat, callback, !!capture);
            };
        }
        else
        {   node.attachEvent(onwhat, callback);
            return function()
            {   node.detachEvent(onwhat, callback);
            };
        }
    }

    var hover_last_target = null;

    add_event
    (   document,
        'onmouseover',
        function(evt)
        {   hover_last_target = (evt || window.event || {}).target;
        }
    );
    add_event
    (   document,
        'onmouseout',
        function(evt)
        {   var target = (evt || window.event || {}).target;
            if (target==document.body || target==document.documentElement)
            {   hover_last_target = null;
            }
        }
    );

    joyquery.FUNCTIONS.hover = function()
    {   var node = this.node;
        var last = hover_last_target;
        while (last && last!=node)
        {   last = last.parentNode;
        }
        return last;
    };
}

:lang()

if (!joyquery.FUNCTIONS.lang)
{   var langs_checked = {};
    var langs_checked_length = 0;
    var document_language = null;

    joyquery.FUNCTIONS['lang.raw_argument'] = true;

    joyquery.FUNCTIONS.lang = function(code)
    {   var value;
        var node = this.node;
        while (!value && node)
        {   value = node.lang;
            node = node.parentNode;
        }
        if (!value)
        {   // lang attribute is not specified
            // is document in this language?
            if (!(code in langs_checked))
            {   try
                {   if (langs_checked_length >= 128)
                    {   langs_checked = {};
                        langs_checked_length = 0;
                    }
                    langs_checked[code] = !!document.querySelector('html:lang('+code+')');
                    langs_checked_length++;
                }
                catch (e)
                {
                }
            }
            if (code in langs_checked)
            {   return langs_checked[code]; // document.querySelector succeeded
            }
            // does document have a meta tag
            if (document_language == null)
            {   try
                {   var metas = document.getElementsByTagName('meta');
                    for (var i=metas.length-1; i>=0; i--)
                    {   if (metas[i].httpEquiv.toLowerCase() == 'content-language')
                        {   document_language = metas[i].content;
                            break;
                        }
                    }
                }
                catch (e)
                {
                }
                if (!document_language)
                {   document_language = navigator.language || navigator.userLanguage ||  '';
                }
            }
            value = document_language;
        }
        value = value.replace('_', '-').toLowerCase();
        code = code.toLowerCase();
        return value==code || value.substr(0, code.length+1)==code+'-';
    };
}

However, some browsers don't allow to determine default document language. If document.querySelector('html:lang('+code+')') fails, this function looks for a meta tag like this:

<meta http-equiv="Content-Language" content="he-IL"/>

If such tag also doesn't exist, the system language (navigator.language) is assumed to be the language of the document.