"""Wrapper around the jQuery UI library

Exposes a single object, jq, to manipulate the widgets designed in the library

This object supports :
- subscription : js[elt_id] returns an object matching the element with the
  specified id
- a method get(**kw). The only keyword currently supported is "selector". The
  method returns a list of instances of the class Element, each instance wraps
  the elements matching the CSS selector passed
  
  jq(selector="button") : returns instances of Element for all button tags

  The value can be a list or tuple of CSS selector strings :

  js(selector=("input[type=submit]","a")) : instances of Element for all
  "input" tags with attribute "type" set to "submit" + "a" tags (anchors)

Instances of Element have the same interface as the selections made by the
jQuery function $, with the additional methods provided by jQuery UI. For 
instance, to turn an element into a dialog :

jq[elt_id].dialog()

When jQuery UI methods expect a Javascript object, they can be passed as
key/value pairs :

jq['tags'].autocomplete(source=availableTags)

"""
from browser import html, document, window
import javascript

_path = __file__[:__file__.rfind('/')]+'/'

document <= html.LINK(rel="stylesheet",
    href=_path+'css/smoothness/jquery-ui.css')

# The scripts must be loaded in blocking mode, by using the function 
# load(script_url[, names]) in module javascript
# If we just add them to the document with script tags, eg :
#
# document <= html.SCRIPT(sciprt_url)
# _jqui = window.jQuery.noConflict(True)
#
# the name "jQuery" is not in the Javascript namespace until the script is 
# fully loaded in the page, so "window.jQuery" raises an exception

# Load jQuery and put name 'jQuery' in the global Javascript namespace
javascript.load(_path+'jquery-1.11.2.min.js', ['jQuery'])
javascript.load(_path+'jquery-ui.min.js')

_jqui = window.jQuery.noConflict(True)

_events = ['abort',
'beforeinput',
'blur',
'click',
'compositionstart',
'compositionupdate',
'compositionend',
'dblclick',
'error',
'focus',
'focusin',
'focusout',
'input',
'keydown',
'keyup',
'load',
'mousedown',
'mouseenter',
'mouseleave',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
'resize',
'scroll',
'select',
'unload']

class JQFunction:

    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kw):
        if kw:
            # keyword arguments are passed as a single Javascript object
            return self.func(*args, kw)
        else:
            return self.func(*args)

class Element:
    """Wrapper around the objects returned by jQuery selections"""

    def __init__(self, item):
        self.item = item

    def bind(self, event, callback):
        """Binds an event on the element to function callback"""
        getattr(self.item, event)(callback)

    def __getattr__(self, attr):
        res = getattr(self.item, attr)
        if attr in _events:
            # elt.click(f) is handled like elt.bind('click', f)
            return lambda f:self.bind(attr, f)
        if callable(res):
            res = JQFunction(res)
        return res

class jq:

    @staticmethod
    def get(**selectors):
        items = []
        for k,v in selectors.items():
            if k=='selector':
                if isinstance(v,[list, tuple]):
                    values = v
                else:
                    values = [v]
                for value in values:
                    items.append(Element(_jqui(value)))
            elif k=='element':
                items = Element(_jqui(v))
        return items
    
    @staticmethod
    def __getitem__(element_id):
        return jq.get(selector='#'+element_id)[0]
    