コード例 #1
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class Embedded(Widget):
    enabled = xom.Boolean(default=True)
    # font
    height = xom.Integer(default=200)
    group_name = xom.String(default="")
    macros = Macros()
    file = xom.String(default="")
    resize = xom.Enum(["none", "content", "widget"])
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=300)

    def setParentMacros(self, macros):
        """ Invoke base class function and also update opi_path field. """
        super(Embedded, self).setParentMacros(macros)

        m = {}
        if self.macros.include_parent_macros:
            m = dict(macros)
        # Same macros defined in widget overwrite parent macros
        for k, v in self.macros.items():
            m[k] = Macros.replace(v, m)

        if m and self.file:
            # Turn into a query string
            self.file += "?"
            escape = tornado.escape.url_escape
            self.file += "&".join("{0}={1}".format(escape(k), escape(v))
                                  for k, v in m.iteritems())
コード例 #2
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class LinkingContainer(Widget):
    background_color = Color()
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color()
    group_name = xom.String(default="")
    macros = Macros()
    opi_file = xom.String(default="")
    resize_behaviour = xom.Enum(["resize", "fit", "crop", "scroll"])
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)

    def setParentMacros(self, macros):
        """ Invoke base class function and also update opi_path field. """
        super(LinkingContainer, self).setParentMacros(macros)

        m = {}
        if self.macros.include_parent_macros:
            m = dict(macros)
        # Same macros defined in widget overwrite parent macros
        for k, v in self.macros.items():
            m[k] = Macros.replace(v, m)

        if m and self.opi_file:
            # Turn into a query string
            self.opi_file += "?"
            escape = tornado.escape.url_escape
            self.opi_file += "&".join("{0}={1}".format(escape(k), escape(v))
                                      for k, v in m.iteritems())
コード例 #3
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class TextEntry(Widget):
    alarm_border = xom.Boolean(default=True)
    background_color = Color(default={"red": 128, "green": 255, "blue": 255})
    height = xom.Integer(default=20)
    # font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    format_type = xom.Enum(FORMAT_TYPES, tagname="format")
    multi_line = xom.Boolean(default=False)
    precision = xom.Integer(default=-1)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    show_units = xom.Boolean(default=True)
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=100)
    wrap_words = xom.Boolean(default=False)

    def parse(self, node):
        done = super(TextEntry, self).parse(node)

        if self.precision == -1:
            fmt = "%s"
        else:
            fmt = self.format_type.get()
            precision = self.precision
            if self.format_type.index == 5:
                precision = 8

            fmt = fmt.format(precision)
        self.setField("value_format", fmt)

        return done
コード例 #4
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class TextUpdate(Widget):
    alarm_border = xom.Boolean(default=True)
    background_color = Color(default={"red": 240, "green": 240, "blue": 240})
    # font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    format_type = xom.Enum(FORMAT_TYPES, tagname="format")
    height = xom.Integer(default=20)
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN)
    precision = xom.Integer(default=-1)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    rotation = xom.Enum(["0deg", "90deg", "180deg", "270deg"])
    show_units = xom.Boolean(default=True)
    tooltip = xom.String(default="")
    transparent = xom.Boolean(default=False)
    vertical_alignment = xom.Enum(VERTICAL_ALIGN)
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=100)
    wrap_words = xom.Boolean(default=True)

    def parse(self, node):
        done = super(TextUpdate, self).parse(node)

        if self.precision == -1:
            fmt = "%s"
        else:
            fmt = self.format_type.get()
            precision = self.precision
            if self.format_type.index == 5:
                precision = 8

            fmt = fmt.format(precision)
        self.setField("value_format", fmt)

        return done
コード例 #5
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class BoolButton(Widget):
    background_color = Color()
    bit = xom.Integer(default=0)
    border_alarm_sensitive = xom.Boolean(default=False)
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    confirm_message = xom.String(default="")
    data_type = xom.Enum(["bit", "enum"])
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color()
    labels_from_pv = xom.Boolean(default=False)
    off_color = Color()
    off_label = xom.String(default="")
    off_state = xom.String(default="")
    on_color = Color()
    on_label = xom.String(default="")
    on_state = xom.String(default="")
    password = xom.String(default="")
    push_action_index = xom.Integer(default=0)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    released_action_index = xom.Integer(default=0)
    show_boolean_label = xom.Boolean(default=True)
    show_confirm_dialog = xom.Enum(["no", "both", "push", "release"])
    show_led = xom.Boolean(default=True)
    square_button = xom.Boolean(default=False)
    toggle_button = xom.Boolean(default=True)
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
コード例 #6
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class MultiStateLED(Widget):
    alarm_border = xom.Boolean(default=False)
    #font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    height = xom.Integer(default=20)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    square = xom.Boolean(default=False)
    states = MultiStateLEDList()
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=20)
コード例 #7
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class CheckBox(Widget):
    alarm_border = xom.Boolean(default=True)
    bit = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    # font
    height = xom.Integer(default=20)
    label = xom.String(default="")
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=100)
コード例 #8
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class Widget(xom.Model):
    """ Base Widget model with common fields. """

    widget_type = xom.String()
    name = xom.String(default="")
    x = xom.Integer(default=0)
    y = xom.Integer(default=0)
    width = xom.Integer(default=100)
    height = xom.Integer(default=40)
    actions = ActionList()

    def __init__(self, typeId=None, **kwds):
        self._typeId = (self.__class__.__name__ if typeId is None else typeId)
        super(Widget, self).__init__(**kwds)

    @staticmethod
    def parseType(node):
        # Remap some container names to match code
        widgetMap = {
            "checkbox": "CheckBox",
            "groupingContainer": "GroupingContainer",
            "linkingContainer": "LinkingContainer",
        }

        for key, value in node.attributes.items():
            if key == "typeId":
                if value.startswith("org.csstudio.opibuilder."):
                    widgetType = value.split(".")[-1]
                    return widgetMap.get(widgetType, widgetType)

                raise ValueError("<{0}> Invalid attribute typeId {1}".format(
                    node.nodeName, value))
        else:
            raise ValueError("<{0}> Missing attribute typeId".format(
                node.nodeName))

    def getType(self):
        return self._typeId

    def parse(self, node):
        """ Overloaded function makes sure we're parsing <widget> node. """

        typeId = Widget.parseType(node)
        if self._typeId != typeId and self._typeId != "Widget":
            raise ValueError("<{0}> Attribute typeId mismatch {1}!={2}".format(
                node.nodeName, typeId, self._typeId))

        return super(Widget, self).parse(node)

    def setParentMacros(self, macros):
        """ Called from converter to recognize additional macros provided by parent. """
        for action in self.actions:
            action.addMacros(macros)
コード例 #9
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class ActionButton(Widget):
    background_color = Color(default={"red": 210, "green": 210, "blue": 210})
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    height = xom.Integer(default=30)
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN, default=0)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    rotation = xom.Enum(["0deg", "90deg", "180deg", "270deg"])
    text = xom.String(default="$(actions)")
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=100)
コード例 #10
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class Label(Widget):
    background_color = Color(default={"red": 255, "green": 255, "blue": 255})
    # font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    height = xom.Integer(default=20)
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN, default=0)
    rotation = xom.Enum(["0deg", "90deg", "180deg", "270deg"])
    text = xom.String(default="")
    tooltip = xom.String(default="")
    transparent = xom.Boolean(default=False)
    vertical_alignment = xom.Enum(VERTICAL_ALIGN, default=0)
    visible = xom.Boolean(default=True)
    wrap_words = xom.Boolean(default=True)
    width = xom.Integer(default=100)
コード例 #11
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class CheckBox(Widget):
    background_color = Color()
    bit = xom.Integer(default=0)
    border_alarm_sensitive = xom.Boolean(default=False)
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color()
    label = xom.String(default="")
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
コード例 #12
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
        class LEDState(xom.Model):
            value = xom.Number()
            label = xom.String(default="")
            color = Color()

            def parse(self, node):
                pass
コード例 #13
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class Label(Widget):
    """ Label widget handler. """

    background_color = Color()
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color()
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN, default=0)
    text = xom.String(default="")
    tooltip = xom.String(default="")
    transparent = xom.Boolean(default=False)
    vertical_alignment = xom.Enum(VERTICAL_ALIGN, default=0)
    visible = xom.Boolean(default=True)
    wrap_words = xom.Boolean(default=True)
コード例 #14
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class Widget(xom.Model):
    """ Base Widget model with common fields. """

    actions = ActionList()
    name = xom.String(default="")
    x = xom.Integer(default=0)
    y = xom.Integer(default=0)
    height = xom.Integer(default=-1)
    width = xom.Integer(default=-1)

    def __init__(self, typeId=None, **kwds):
        self._typeId = (self.__class__.__name__ if typeId is None else typeId)
        super(Widget, self).__init__(**kwds)
        self.setField("widget_type", self._typeId)

    @staticmethod
    def parseType(node):
        # Remap some container names to match code
        widgetMap = {
            "action_button": "ActionButton",
            "bool_button": "BoolButton",
            "checkbox": "CheckBox",
            "embedded": "Embedded",
            "group": "Group",
            "label": "Label",
            "led": "LED",
            "multi_state_led": "MultiStateLED",
            "textentry": "TextEntry",
            "textupdate": "TextUpdate",
        }

        for key, value in node.attributes.items():
            if key == "type":
                return widgetMap.get(value, value)
        else:
            raise ValueError("<{0}> Missing attribute type".format(
                node.nodeName))

    def getType(self):
        return self._typeId

    def parse(self, node):
        """ Overloaded function makes sure we're parsing <widget> node. """

        typeId = Widget.parseType(node)
        if self._typeId != typeId:
            if self._typeId != "Widget":
                raise ValueError(
                    "<{0}> Attribute type mismatch {1}!={2}".format(
                        node.nodeName, typeId, self._typeId))
            self._typeId = typeId
            self.setField("widget_type", self._typeId)

        return super(Widget, self).parse(node)

    def setParentMacros(self, macros):
        """ Called from converter to recognize additional macros provided by parent. """
        pass
コード例 #15
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class TextInput(Widget):
    background_color = Color()
    border_alarm_sensitive = xom.Boolean(default=False)
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    #font
    foreground_color = Color()
    format_type = xom.Integer(default=0)
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN)
    limits_from_pv = xom.Boolean(default=False)
    maximum = xom.Number(default=100)
    minimum = xom.Number(default=0)
    multiline_input = xom.Boolean(default=False)
    precision = xom.Integer(default=0)
    precision_from_pv = xom.Boolean(default=True)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    selector_type = xom.Enum(["none", "file", "datetime"])
    show_units = xom.Boolean(default=True)
    text = xom.String(default="")
    tooltip = xom.String(default="")
    transparent = xom.Boolean(default=False)
    visible = xom.Boolean(default=True)

    def parse(self, node):
        super(TextInput, self).parse(node)

        if self.precision_from_pv:
            fmt = None
        else:
            try:
                fmt = FORMAT_TYPES[self.format_type]
            except:
                fmt = FORMAT_TYPES[0]

            precision = self.precision
            if self.format_type == 5:
                precision = 8

            fmt = fmt.format(precision)
        self.setField("value_format", fmt)

        return True
コード例 #16
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class ActionButton(Widget):
    background_color = Color()
    border_alarm_sensitive = xom.Boolean(default=False)
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color()
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN, default=0)
    push_action_index = xom.Integer(default=0)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    style = xom.Integer(default=0)
    text = xom.String(default="")
    toggle_button = xom.Boolean(default=False)
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
コード例 #17
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class LED(Widget):
    alarm_border = xom.Boolean(default=False)
    bit = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    #font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    height = xom.Integer(default=20)
    labels_from_pv = xom.Boolean(default=False)
    off_label = xom.String(default="")
    off_color = Color(default={"red": 60, "green": 100, "blue": 60})
    on_label = xom.String(default="")
    on_color = Color(default={"red": 60, "green": 255, "blue": 60})
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    square = xom.Boolean(default=False)
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    width = xom.Integer(default=20)
コード例 #18
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class OpenPathAction(Action):
    path = xom.String()

    def parse(self, node):
        """ Overloaded method does all from base class plus escapes path parameter. """
        super(OpenPathAction, self).parse(node)

        self.path = tornado.escape.url_escape(self.path, False)

        return True
コード例 #19
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class Action(xom.Model):
    """ Model for a single action. """
    type = xom.String(tagname="__self__", attrname="type")
    description = xom.String(default="")

    def __init__(self, **kwds):
        kwds["tagname"] = "action"
        super(Action, self).__init__(**kwds)

    def addMacros(self, macros):
        """ Add all available macros to actions that need pass macros along.

        This is a hook for actions that open new display and need to pass
        macros to it. Derived classes should not use this hook for replace
        existing macros, that is done by Converter.

        Default is no action.
        """
        pass
コード例 #20
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class TextUpdate(Widget):
    """ TextUpdate widget handler. """

    background_color = Color()
    border_alarm_sensitive = xom.Boolean(default=False)
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    foreground_color = Color()
    format_type = xom.Integer(default=0)
    horizontal_alignment = xom.Enum(HORIZONTAL_ALIGN)
    precision = xom.Integer(default=0)
    precision_from_pv = xom.Boolean(default=True)
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    show_units = xom.Boolean(default=True)
    text = xom.String(default="")
    tooltip = xom.String(default="")
    transparent = xom.Boolean(default=False)
    vertical_alignment = xom.Enum(VERTICAL_ALIGN)
    visible = xom.Boolean(default=True)
    wrap_words = xom.Boolean(default=True)

    def parse(self, node):
        done = super(TextUpdate, self).parse(node)

        if self.precision_from_pv:
            fmt = None
        else:
            try:
                fmt = FORMAT_TYPES[self.format_type]
            except:
                fmt = FORMAT_TYPES[0]

            precision = self.precision
            if self.format_type == 5:
                precision = 8

            fmt = fmt.format(precision)
        self.setField("value_format", fmt)

        return done
コード例 #21
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class Group(Widget):
    background_color = Color(default={"red": 255, "green": 255, "blue": 255})
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color(default={"red": 0, "green": 0, "blue": 0})
    height = xom.Integer(default=200)
    macros = Macros()
    style = xom.Enum(["group", "title", "border", "none"])
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    widgets = WidgetList(tagname="widget", default=[])
    width = xom.Integer(default=300)
コード例 #22
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class GroupingContainer(Widget):
    background_color = Color()
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    enabled = xom.Boolean(default=True)
    # font
    foreground_color = Color()
    lock_children = xom.Boolean(default=False)
    macros = Macros()
    show_scrollbar = xom.Boolean(default=True)
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)
    widgets = WidgetList(tagname="widget", default=[])
コード例 #23
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class Display(xom.Model):
    """ Model for the main Display widget. """

    actions = ActionList()
    macros = Macros()
    name = xom.String(default="")
    widgets = WidgetList(tagname="widget", default=[])

    def __init__(self, **kwds):
        super(Display, self).__init__(tagname="display", **kwds)
        self.setField("widget_type", "Display")

    def getType(self):
        return "Display"

    def setParentMacros(self, macros):
        """ Called from converter to recognize additional macros provided by parent. """
        for action in self.actions:
            action.addMacros(macros)
コード例 #24
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class WritePvAction(Action):
    pv_name = xom.String()
    value = xom.String()
コード例 #25
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
 class LEDState(xom.Model):
     value = xom.Number(default=0)
     label = xom.String(default="")
     color = Color()
コード例 #26
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class LED(Widget):
    background_color = Color()
    bit = xom.Integer(default=0)
    border_alarm_sensitive = xom.Boolean(default=False)
    border_color = Color()
    border_style = xom.Enum(BORDER_STYLES)
    border_width = xom.Integer(default=0)
    data_type = xom.Enum(["bit", "enum"])
    enabled = xom.Boolean(default=True)
    #font
    foreground_color = Color()
    pv_name = xom.String(default="")
    pv_value = xom.String(default="")
    show_boolean_label = xom.Boolean(default=False)
    square_led = xom.Boolean(default=False)
    state_count = xom.Integer(default=2)
    tooltip = xom.String(default="")
    visible = xom.Boolean(default=True)

    def parse(self, node):
        """ Turn LED XML nodes into a list of states.

        Handles multi-LED and 2-LED cases. Dynamically adds state_count and
        states fields to this Model. states field is sorted list of Models
        containing sub-fields value,color,label for each state.
        """
        super(LED, self).parse(node)

        class LEDState(xom.Model):
            value = xom.Number()
            label = xom.String(default="")
            color = Color()

            def parse(self, node):
                pass

        # Read number of total states, default to 2 for off/on LED
        count = xom.Integer(tagname="state_count", default=2)
        child = node.firstChild
        while child is not None:
            if child.nodeName == "state_count":
                count.parse(child)
                break
            child = child.nextSibling
        self.setField("state_count", count.get())

        # Create a list of all state models
        states = []
        for i in range(self.state_count):
            state = LEDState()
            states.append(state)

        # Parse nodes describing states - handles 2 state LED as well as n-state LED
        child = node.firstChild
        while child is not None:
            try:
                check, fieldname, i = child.nodeName.split("_")
                i = int(i)
                if check == "state":
                    if fieldname == "color":
                        states[i].color.setTagName(child.nodeName, True)
                        states[i].color.parse(child)
                    elif fieldname == "label":
                        states[i].label.setTagName(child.nodeName, True)
                        states[i].label.parse(child)
                    elif fieldname == "value":
                        states[i].value.setTagName(child.nodeName, True)
                        states[i].value.parse(child)
            except:
                if child.nodeName == "off_color":
                    states[0].color.setTagName(child.nodeName, True)
                    states[0].color.parse(child)
                    states[0].value.setDefault(0)
                elif child.nodeName == "off_label":
                    states[0].label.setTagName(child.nodeName, True)
                    states[0].label.parse(child)
                    states[0].value.setDefault(0)
                elif child.nodeName == "on_color":
                    states[1].color.setTagName(child.nodeName, True)
                    states[1].color.parse(child)
                    states[1].value.setDefault(1)
                elif child.nodeName == "on_label":
                    states[1].label.setTagName(child.nodeName, True)
                    states[1].label.parse(child)
                    states[1].value.setDefault(1)

            child = child.nextSibling

        # Transform fields into Python objects
        for i in range(self.state_count):
            states[i].setField("label", states[i].label.get())
            states[i].setField("value", states[i].value.get())

        # Assign 'states' field
        self.setField("states", states)
        return True
コード例 #27
0
ファイル: bob.py プロジェクト: hinxx/WebEPICS
class Action(opi.Action):
    description = xom.String(default="")
コード例 #28
0
ファイル: opi.py プロジェクト: hinxx/WebEPICS
class WritePvAction(Action):
    pv_name = xom.String()
    value = xom.String()
    timeout = xom.Integer(default=10)
    confirm_message = xom.String(default="")