from durus.utils import str_to_int8 from qp.fill.html import javascript, div from qp.fill.widget import StringWidget, CompositeWidget from qp.lib.util import integer from qp.pub.common import get_publisher class CallbackSelectWidget (CompositeWidget): """ This is a search-select widget that relies on a callback to provide the full list of items from which one will be selected. """ def __init__(self, name, *args, **kwargs): CompositeWidget.__init__(self, name, *args, **kwargs) self.key_id = "key_%s" % self.name self.description_id = "desc_%s" % self.name self.choices_id = "choices_%s" % self.name self.selected_id = "selected_%s" % self.name self.add(StringWidget, 'key', value=str(self.value_to_key(self.value)), id=self.key_id, style="display:none") self.add(StringWidget, 'description', value=self.value_to_description(self.value), id=self.description_id, autocomplete="off") callback_path = '/callback/user_select' @classmethod def callback:xml(klass): for user in sorted(get_publisher().gen_active_users(), key=lambda x: klass.value_to_description(x).lower()): div(klass.value_to_description(user), key=klass.value_to_key(user), css_class="callback_option") @classmethod def value_to_key(klass, value): if value is None: return '' return str_to_int8(value._p_durus_id) @classmethod def key_to_value(klass, key): if type(key) is not int: key = integer(key) if key is None or key < 0: return None return get_publisher().get_connection().get(key) @staticmethod def value_to_description(value): return str(value) def filter_js:xml(self): # Executed on focus and keyup events in the description widget. "function () {\n" " var query = document.getElementById('%s').value.toLowerCase();" % self.description_id " var choices = document.getElementById('%s');" % self.choices_id " var match = function (n) {return -1 != genlib.node_get_text_data_string(n).toLowerCase().indexOf(query)};" " genlib.classify_children(choices, match, 'matched');" " choices.style.display = 'block';" "}" def initialize_js:xml(self): # Executed when the callback results are here. "function (e) {\n" " var filter = %s;" % self.filter_js() " var description = document.getElementById('%s');" % self.description_id " var choices = document.getElementById('%s');" % self.choices_id " var key = document.getElementById('%s');" % self.key_id " description.onkeyup = filter;" " description.onfocus = filter;" " description.onclick = function () {this.value = '';};" " description.onblur = function () { setTimeout(function () {choices.style.display = 'none';}, 500)};" " var children = genlib.get_child_elements(e);" " for (var i in children) {\n" " var child = children[i];" " genlib.node_add_class(child, 'not_matched');" " child.onclick = function (e) {" " key.value = this.attributes.key.value;" " description.value = genlib.node_get_text_data_string(this);" " choices.style.display = 'none';" " };" " genlib.install_hovering(child);" " }" "}" def render_content:xml(self): div(self.get_widget('key').render(), div(id=self.selected_id), self.get_widget('description').render(), div(javascript( "genlib.load_from('%s', '%s', %s)\n" % (self.choices_id, self.callback_path, self.initialize_js())), css_class="choices", id=self.choices_id)) def _parse(self, request=None): CompositeWidget._parse(self, request=request) if self.get('key') and self.get('description'): self.value = self.key_to_value(self.get('key')) else: self.value = None class CallbackMultipleSelectWidget (CallbackSelectWidget): """ This is a search-select widget that relies on a callback to provide the full list of items from which a subset will be selected. """ def __init__(self, name, *args, **kwargs): CompositeWidget.__init__(self, name, *args, **kwargs) self.key_id = "key_%s" % self.name self.description_id = "desc_%s" % self.name self.choices_id = "choices_%s" % self.name self.add(StringWidget, 'key', value="".join(str(self.value_to_key(v)) + " " for v in self.value or []), id=self.key_id, style="display:none") self.add(StringWidget, 'description', value=None, id=self.description_id, autocomplete="off") self.selected_id = "selected_%s" % self.name def initialize_js:xml(self): # Executed when the callback results are here. "function (e) {\n" " var filter = %s;" % self.filter_js() " var description = document.getElementById('%s');" % self.description_id " var choices = document.getElementById('%s');" % self.choices_id " var key = document.getElementById('%s');" % self.key_id " var current_keys = key.value.split(' ');" " var selected = document.getElementById('%s');" % self.selected_id " var choices_children = genlib.get_child_elements(choices);" " description.onkeyup = filter;" " description.onfocus = filter;" " description.onclick = function () {this.value = '';};" " description.onblur = function () { setTimeout(function () {choices.style.display = 'none';}, 500)};" " for (var i in choices_children) {\n" " var child = choices_children[i];" " genlib.install_hovering(child);" " genlib.node_add_class(child, 'not_matched');" " var child_key = child.attributes.key.value;" " for (j in current_keys) {" " if (current_keys[j] == child_key) {" " genlib.node_add_class(child, 'already_selected');" " }" " };" " child.onclick = function (e) {" " genlib.node_add_class(this, 'already_selected');" " var this_key = this.attributes.key.value;" " key.value = key.value + this_key + ' ';" " description.value = '';" " choices.style.display = 'none';" " var selected_children = genlib.get_child_elements(selected);" " for (var k in selected_children) {" " var selected_child = selected_children[k];" " if (selected_child.attributes.key.value == this_key) {" " genlib.node_remove_class(selected_child, 'not_matched');" " genlib.node_add_class(selected_child, 'matched');" " break;" " }" " }" " }" " };" " genlib.node_replace_children(selected, genlib.get_child_elements(e.cloneNode(true)));" " var selected_children = genlib.get_child_elements(selected);" " for (var i in selected_children) {\n" " var child = selected_children[i];" " genlib.install_hovering(child);" " var child_key = child.attributes.key.value;" " for (j in current_keys) {" " if (current_keys[j] == child_key) {" " genlib.node_add_class(child, 'matched');" " genlib.node_remove_class(child, 'not_matched');" " genlib.node_remove_class(child, 'already_selected');" " }" " };" " child.onclick = function (e) {" " var this_key = this.attributes.key.value;" " key.value = key.value.replace(this_key + ' ', '');" " this.style.display = 'none';" " for (var k in genlib.get_child_elements(choices)) {" " var choices_child = choices_children[k];" " if (choices_child.attributes.key.value == this_key) {" " genlib.node_remove_class(choices_child, 'already_selected');" " break;" " }" " }" " }" " }" "}" def render_content:xml(self): div(self.get_widget('key').render(), div(id=self.selected_id, css_class="selected_choices"), self.get_widget('description').render(), div(javascript( "genlib.load_from('%s', '%s', %s)\n" % (self.choices_id, self.callback_path, self.initialize_js())), css_class="choices", id=self.choices_id)) def _parse(self, request=None): CompositeWidget._parse(self, request=request) if self.get('key'): self.value = [v for v in map(self.key_to_value, self.get('key').split()) if v is not None] else: self.value = None