text:
#include "config.h"

#include "gom/gomwidget.h"

#include "gom/dom/gomdocumentevent.h"
#include "gom/dom/gomdombuiltins.h"
#include "gom/dom/gomdomexception.h"
#include "gom/dom/gomelement.h"
#include "gom/dom/gomeventtarget.h"
#include "gom/dom/gomkeyboardevent.h"
#include "gom/dom/gommouseevent.h"
#include "gom/dom/gomuievent.h"
#include "gom/gomeventtargetinternal.h"
#include "gom/gomglist.h"
#include "gom/gomjselement.h"
#include "gom/gomjsobject.h"
#include "gom/gomkeyboardevt.h"
#include "gom/gomlistenerlist.h"
#include "gom/gomnodeinternal.h"
#include "gom/gomobject.h"

#include "gommacros.h"

#include <gtk/gtk.h>

#include <strings.h>
#include <stdlib.h>
#include <limits.h>

enum {
    PROP_TAG_NAME = 1078,
    PROP_NODE_VALUE,
    PROP_NODE_TYPE,
    PROP_PARENT_NODE,
    PROP_CHILD_NODES,
    PROP_FIRST_CHILD,
    PROP_LAST_CHILD,
    PROP_PREVIOUS_SIBLING,
    PROP_NEXT_SIBLING,
    PROP_ATTRIBUTES,
    PROP_OWNER_DOCUMENT,
    PROP_NAMESPACE_URI,
    PROP_PREFIX,
    PROP_LOCAL_NAME,
};

static void (*_gtk_widget_set_property) (GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
static void (*_gtk_widget_get_property) (GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);

static void     (*_gtk_widget_constructed) (GObject *object);

GQuark gom_widget_private_quark (void);
GOM_DEFINE_QUARK (widget_private);

typedef struct {
    GomDocument     *owner_document;
    GomListenerList *listeners;
    GomNode         *next_sibling;
    GomNode         *parent_node;
    GomNode         *prev_sibling;
    char            *namespace_uri;
    char            *prefix;
    char            *tag_name;
    GdkRectangle     allocation;
    guint            click_state;
    guint            constructed    : 1;
    guint            dirty_children : 1;
} GomWidgetPrivate;

static void
free_priv (gpointer data)
{
    GomWidgetPrivate *priv = data;
    if (priv->tag_name) {
        g_print (GOM_LOC ("Freeing a <%s>'s privates\n"), priv->tag_name);
    }
    GOM_UNSET_WEAK (priv->owner_document);
    gom_listener_list_free (priv->listeners);
    g_free (priv->namespace_uri);
    g_free (priv->prefix);
    g_free (priv->tag_name);
    g_free (priv);
}

static GomWidgetPrivate *
get_priv (gpointer obj)
{
    GomWidgetPrivate *priv;
    priv = g_object_get_qdata (obj, gom_widget_private_quark ());
    if (!priv) {
        priv = g_new0 (GomWidgetPrivate, 1);
        priv->listeners = gom_listener_list_new ();
        g_object_set_qdata_full (obj, gom_widget_private_quark (), priv, free_priv);
    }
    return priv;
}
#define PRIV(i) get_priv (i)

static GomNode *
widget_insert_before (GomNode *node,
                      GomNode *new_child,
                      GomNode *ref_child,
                      GError  **error)
{
    GOM_NOT_IMPLEMENTED_ERROR (error);
    return NULL;
}

static GomNode *
widget_replace_child (GomNode *node,
                      GomNode *new_child,
                      GomNode *ref_child,
                      GError  **error)
{
    GOM_NOT_IMPLEMENTED_ERROR (error);
    return NULL;
}

static GomNode *
widget_remove_child (GomNode *node,
                     GomNode *old_child,
                     GError  **error)
{
    GOM_NOT_IMPLEMENTED_ERROR (error);
    return NULL;
}

typedef struct {
    GomNode *node;
    GomNode *new_child;
    GError **error;
} AppendChildData;

static void
append_child_attrs_foreach (gpointer key, gpointer value, gpointer user_data)
{
    AppendChildData *data = user_data;
    GParamSpec *spec;
    GError *error = NULL;
    GValue gval = { 0 };

    spec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (data->node), key);
    if (!spec ||
        G_TYPE_FUNDAMENTAL (G_PARAM_SPEC_VALUE_TYPE (spec)) == G_TYPE_OBJECT) {
        return;
    }
#if 0
    g_print ("found child property %s.%s on %s\n",
             g_type_name (G_TYPE_FROM_INSTANCE (data->node)),
             spec->name,
             g_type_name (G_TYPE_FROM_INSTANCE (data->new_child)));
#endif
    if (gtk_builder_value_from_string (NULL, spec, g_value_get_string (value), &gval, &error)) {
        gtk_container_child_set_property (GTK_CONTAINER (data->node),
                                          GTK_WIDGET (data->new_child),
                                          spec->name,
                                          &gval);
        g_value_unset (&gval);
    } else {
        g_print ("Error getting value_from_string: %s\n",
                 error->message);
        if (data->error && *data->error) {
            g_error_free (error);
        } else {
            g_propagate_error (data->error, error);
        }
    }
}

static GomNode *
widget_append_child (GomNode *node,
                     GomNode *new_child,
                     GError  **error)
{
    AppendChildData data;

    if (!GTK_IS_CONTAINER (node)) {
        g_set_error (error,
                     GOM_DOM_EXCEPTION_ERROR,
                     GOM_HIERCHY_REQUEST_ERR,
                     "Cannot append to a parent that is not a GtkContainer");
        return new_child;
    }
    if (!GTK_IS_WIDGET (new_child)) {
        if (!GOM_IS_TEXT (new_child)) {
            g_set_error (error,
                         GOM_DOM_EXCEPTION_ERROR,
                         GOM_HIERCHY_REQUEST_ERR,
                         "Cannot append a child that is not a GtkWidget");
        }
        return new_child;
    }

    gtk_container_add (GTK_CONTAINER (node), GTK_WIDGET (new_child));

    data.node = node;
    data.new_child = new_child;
    data.error = error;

    gom_object_attributes_foreach (G_OBJECT (new_child), append_child_attrs_foreach, &data);

    if (GOM_IS_NODE_INTERNAL (new_child)) {
        gom_node_internal_set_parent (GOM_NODE_INTERNAL (new_child), node);
    }

    /* gtk_container_add() doesn't ref, but append_child() should, so
     * ref our child */
    return g_object_ref (new_child);
}

static gboolean
widget_has_child_nodes (GomNode *node)
{
    gboolean has_child_nodes = FALSE;

    if (GTK_IS_CONTAINER (node)) {
        GList *children = gtk_container_get_children (GTK_CONTAINER (node));
        has_child_nodes = g_list_length (children) > 0;
        g_list_free (children);
    }
    return has_child_nodes;
}

static GomNode *
widget_clone_node (GomNode *node, gboolean deep)
{
    GOM_NOT_IMPLEMENTED;
    return NULL;
}

static void
widget_normalize (GomNode *node)
{
    GOM_NOT_IMPLEMENTED;
}

static gboolean
widget_is_supported (GomNode    *node,
                     const char *feature,
                     const char *version)
{
    GOM_NOT_IMPLEMENTED;
    return FALSE;
}

static gboolean
widget_has_attributes (GomNode *node)
{
    return TRUE;
}

static char *
widget_get_attribute_ns (GomElement *elem, const char *namespace_uri, const char *name)
{
    GParamSpec *spec;
    GValue gval = { 0 };
    const GValue *gvalp;
    char *ret = NULL;

    spec = g_object_class_find_property (G_OBJECT_GET_CLASS (elem), name);
    if (spec) {
        g_value_init (&gval, G_TYPE_STRING);
        g_object_get_property (G_OBJECT (elem), name, &gval);
        if (G_VALUE_HOLDS (&gval, G_TYPE_STRING)) {
            ret = g_value_dup_string (&gval);
        }
        g_value_unset (&gval);
    } else {
        gvalp = gom_object_get_attribute (G_OBJECT (elem), name);
        if (gvalp) {
            if (G_VALUE_HOLDS_STRING (gvalp)) {
                ret = g_value_dup_string (gvalp);
            }
        }
    }

    return ret;
}

static char *
widget_get_attribute (GomElement *elem, const char *name)
{
    return widget_get_attribute_ns (elem, NULL, name);
}

static void
widget_set_attribute_ns (GomElement *elem,
                         const char *namespace_uri,
                         const char *qualified_name,
                         const char *value,
                         GError **error)
{
    GParamSpec *spec;
    GValue gval = { 0 };

    spec = g_object_class_find_property (G_OBJECT_GET_CLASS (elem), qualified_name);
    if (spec) {
        if (G_TYPE_FUNDAMENTAL (G_PARAM_SPEC_VALUE_TYPE (spec)) == G_TYPE_OBJECT) {
            g_set_error (error, GOM_DOM_EXCEPTION_ERROR,
                         GOM_INVALID_ATTRIBUTE_TYPE_ERR,
                         "Attribute %s.%s is a %s, which a string cannot be converted to",
                         g_type_name (spec->owner_type), qualified_name,
                         g_type_name (G_PARAM_SPEC_VALUE_TYPE (spec)));
        } else if (gtk_builder_value_from_string (NULL, spec, value, &gval, error)) {
            g_object_set_property (G_OBJECT (elem), qualified_name, &gval);
        }
    } else {
        g_value_init (&gval, G_TYPE_STRING);
        g_value_set_string (&gval, value);
        gom_object_set_attribute (G_OBJECT (elem), qualified_name, &gval);
    }
    g_value_unset (&gval);
}

static void
widget_set_attribute (GomElement *elem, const char *name, const char *value, GError **error)
{
    widget_set_attribute_ns (elem, NULL, name, value, error);
}

static void
widget_remove_attribute_ns (GomElement *elem,
                                const char *namespace_uri,
                                const char *local_name,
                                GError    **error)
{
    GOM_NOT_IMPLEMENTED_ERROR (error);
}

static void
widget_remove_attribute (GomElement *elem, const char *name, GError **error)
{
    widget_remove_attribute_ns (elem, NULL, name, error);
}

static GomAttr *
widget_get_attribute_node_ns (GomElement *elem,
                              const char *namespace_uri,
                              const char *local_name)
{
    GOM_NOT_IMPLEMENTED;
    return NULL;
}

static GomAttr *
widget_get_attribute_node (GomElement *elem, const char *name)
{
    return widget_get_attribute_node_ns (elem, NULL, name);
}

static GomAttr *
widget_set_attribute_node_ns (GomElement *elem, GomAttr *new_atr, GError **error)
{
    GOM_NOT_IMPLEMENTED_ERROR (error);
    return new_atr;
}

static GomAttr *
widget_set_attribute_node (GomElement *elem, GomAttr *new_atr, GError **error)
{
    return widget_set_attribute_node_ns (elem, new_atr, error);
}

static GomAttr *
widget_remove_attribute_node (GomElement *elem, GomAttr *old_attr, GError **error)
{
    GOM_NOT_IMPLEMENTED_ERROR (error);
    return old_attr;
}

static GomNodeList *
widget_get_elements_by_tag_name_ns (GomElement *elem, const char *namespace_uri, const char *name)
{
    GOM_NOT_IMPLEMENTED;
    return NULL;
}

static GomNodeList *
widget_get_elements_by_tag_name (GomElement *elem, const char *name)
{
    return widget_get_elements_by_tag_name_ns (elem, NULL, name);
}

static gboolean
widget_has_attribute_ns (GomElement *elem, const char *namespace_uri, const char *local_name)
{
    GOM_NOT_IMPLEMENTED;
    return FALSE;
}

static gboolean
widget_has_attribute (GomElement *elem, const char *name)
{
    return widget_has_attribute_ns (elem, NULL, name);
}

static void
gom_widget_get_property (GObject    *object,
                         guint       property_id,
                         GValue     *value,
                         GParamSpec *pspec)
{
    GomWidgetPrivate *priv = PRIV (object);
    GList *children = NULL;
    GList *li = NULL;

    switch (property_id) {
    case PROP_TAG_NAME:
        g_value_set_string (value, priv->tag_name);
        break;
    case PROP_NODE_VALUE:
        g_value_set_string (value, NULL);
        break;
    case PROP_NODE_TYPE:
        g_value_set_enum (value, GOM_ELEMENT_NODE);
        break;
    case PROP_OWNER_DOCUMENT:
        g_value_set_object (value, priv->owner_document);
        break;
    case PROP_NAMESPACE_URI:
        g_value_set_string (value, priv->namespace_uri);
        break;
    case PROP_PREFIX:
        g_value_set_string (value, priv->prefix);
        break;
    case PROP_LOCAL_NAME:
        g_value_set_static_string (value, g_type_name (G_TYPE_FROM_INSTANCE (object)));
        break;
    case PROP_PARENT_NODE:
        g_value_set_object (value, priv->parent_node);
        break;
    case PROP_CHILD_NODES: {
	GomNodeList *nodes;
	GList *li;
	li = GTK_IS_CONTAINER (object)
	    ? gtk_container_get_children (GTK_CONTAINER (object))
	    : NULL;
	nodes = g_object_new (GOM_TYPE_G_LIST, "nodes", li, NULL);
	g_value_set_object (value, nodes);
	g_object_unref (nodes);
	g_list_free (li);
	break;
    }
    case PROP_FIRST_CHILD:
        if (GTK_IS_CONTAINER (object)) {
            children = gtk_container_get_children (GTK_CONTAINER (object));
        }
        g_value_set_object (value, children ? children->data : NULL);
        g_list_free (children);
        break;
    case PROP_LAST_CHILD:
        if (GTK_IS_CONTAINER (object)) {
            children = gtk_container_get_children (GTK_CONTAINER (object));
            li = g_list_last (children);
        }
        g_value_set_object (value, li ? li->data : NULL);
        g_list_free (children);
        break;
    case PROP_PREVIOUS_SIBLING:
        if (GOM_IS_NODE_INTERNAL (priv->parent_node)) {
            gom_node_internal_sibling_requested (GOM_NODE_INTERNAL (priv->parent_node), GOM_NODE (object));
        }
        g_value_set_object (value, priv->prev_sibling);
        break;
    case PROP_NEXT_SIBLING:
        if (GOM_IS_NODE_INTERNAL (priv->parent_node)) {
            gom_node_internal_sibling_requested (GOM_NODE_INTERNAL (priv->parent_node), GOM_NODE (object));
        }
        g_value_set_object (value, priv->next_sibling);
        break;
    case PROP_ATTRIBUTES:
        g_value_set_object (value, NULL);
        break;
    default:
        if (_gtk_widget_get_property) {
            _gtk_widget_get_property (object, property_id, value, pspec);
        }
        break;
    }
}

static void
update_tag_name (GObject *object)
{
    GomWidgetPrivate *priv = PRIV (object);
    if (priv->namespace_uri) {
        if (priv->prefix && !strcmp (priv->prefix, "xml") &&
            strcmp (priv->namespace_uri, "http://www.w3.org/XML/1998/namespace")) {
            g_warning (GOM_LOC ("invalid prefix '%s' for namespace '%s' on %p"),
                       priv->prefix, priv->namespace_uri, object);
            g_free (priv->prefix);
            priv->prefix = NULL;
            return;
        }
    } else if (priv->prefix) {
        g_warning (GOM_LOC ("prefix set without namespace on %p"), object);
        return;
    } else {
        return;
    }
    g_free (priv->tag_name);
    priv->tag_name = priv->prefix
        ? g_strconcat (priv->prefix, ":", g_type_name (G_TYPE_FROM_INSTANCE (object)), NULL)
        : g_strdup (g_type_name (G_TYPE_FROM_INSTANCE (object)));
}

static void
gom_widget_set_property (GObject      *object,
                         guint         property_id,
                         const GValue *value,
                         GParamSpec   *pspec)
{
    GomWidgetPrivate *priv = PRIV (object);

    switch (property_id) {
    case PROP_OWNER_DOCUMENT:
        GOM_SET_WEAK (priv->owner_document, g_value_get_object (value));
        break;
    case PROP_NAMESPACE_URI:
        priv->namespace_uri = g_value_dup_string (value);
        break;
    case PROP_PREFIX:
        g_free (priv->prefix);
        priv->prefix = g_value_dup_string (value);
        if (priv->constructed) {
            update_tag_name (object);
        }
    default:
        if (_gtk_widget_set_property) {
            _gtk_widget_set_property (object, property_id, value, pspec);
        }
        break;
    }
}

/*
 Triple-clicks are very similar to double-clicks, except that
 GDK_3BUTTON_PRESS is inserted after the third click. The order of the
 events is:

 1. GDK_BUTTON_PRESS
 2. GDK_BUTTON_RELEASE
 3. GDK_BUTTON_PRESS
 4. GDK_2BUTTON_PRESS
 5. GDK_BUTTON_RELEASE
 6. GDK_BUTTON_PRESS
 7. GDK_3BUTTON_PRESS
 8. GDK_BUTTON_RELEASE

Meanwhile, for the DOM...

 The click event occurs when the pointing device button is clicked
 over an element. A click is defined as a mousedown and mouseup over
 the same screen location. The sequence of these events is:

 1. mousedown
 2. mouseup
 3. click

 If multiple clicks occur at the same screen location, the sequence
 repeats with the detail attribute incrementing with each repetition.
*/

#define INIT_MOUSE_EVENT(name, f)                                       \
    G_STMT_START {                                                      \
        if (GOM_IS_DOCUMENT_EVENT (priv->owner_document)) {             \
            evt = gom_document_event_create_event (                     \
                GOM_DOCUMENT_EVENT (priv->owner_document),              \
                "MouseEvent", &error);                                  \
        }                                                               \
        if (GOM_IS_MOUSE_EVENT (evt)) {                                 \
            gom_mouse_event_init_mouse_event (                          \
                GOM_MOUSE_EVENT (evt),                                  \
                (name), TRUE, event->type != GDK_MOTION_NOTIFY,         \
                NULL, priv->click_state,                                \
                event->f.x_root,                                        \
                event->f.y_root,                                        \
                event->f.x,                                             \
                event->f.y,                                             \
                event->f.state & GDK_CONTROL_MASK,                      \
                event->f.state & GDK_MOD1_MASK,                         \
                event->f.state & GDK_SHIFT_MASK,                        \
                event->f.state & GDK_META_MASK,                         \
                (event->f.state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) >> 8, \
                NULL);                                                  \
        }                                                               \
    } G_STMT_END

#define INIT_KEY_EVENT(name)                                            \
    G_STMT_START {                                                      \
        if (GOM_IS_DOCUMENT_EVENT (priv->owner_document)) {             \
            evt = gom_document_event_create_event (                     \
                GOM_DOCUMENT_EVENT (priv->owner_document),              \
                "KeyboardEvent", &error);                               \
        }                                                               \
        if (GOM_IS_KEYBOARD_EVENT (evt)) {                              \
            modifiers = gom_keyboard_evt_string_from_state (event->key.state); \
            gom_keyboard_evt_keyval_to_key_identifier (event->key.keyval, &key_identifier, &key_location); \
            gom_keyboard_event_init_keyboard_event (                    \
                GOM_KEYBOARD_EVENT (evt),                               \
                (name), TRUE, TRUE,                                     \
                NULL,                                                   \
                key_identifier, key_location, modifiers);               \
            g_free (modifiers);                                         \
        }                                                               \
    } G_STMT_END

static gboolean
gom_widget_event (GtkWidget *widget, GdkEvent *event)
{
    GomEvent *evt = NULL;
    GomWidgetPrivate *priv;
    GError *error = NULL;

    char *modifiers;
    const char *key_identifier;
    GomKeyLocationCode key_location;

    static GdkEventType  last_type  = 0;
    static GdkEvent     *last_event = NULL;

    if (event == last_event && event->type == last_type) {
        return FALSE;
    }

    switch (event->type) {
    case GDK_MOTION_NOTIFY:
        priv = PRIV (widget);
        priv->click_state = 0;
        INIT_MOUSE_EVENT (GOM_MOUSEMOVE, motion);
        break;
    case GDK_BUTTON_PRESS:
        priv = PRIV (widget);
        ++priv->click_state;
        INIT_MOUSE_EVENT (GOM_MOUSEDOWN, button);
        break;
    case GDK_BUTTON_RELEASE:
        priv = PRIV (widget);
        INIT_MOUSE_EVENT (GOM_MOUSEUP, button);
        break;
    case GDK_ENTER_NOTIFY:
        priv = PRIV (widget);
        INIT_MOUSE_EVENT (GOM_MOUSEOVER, crossing);
        break;
    case GDK_LEAVE_NOTIFY:
        priv = PRIV (widget);
        INIT_MOUSE_EVENT (GOM_MOUSEOUT, crossing);
        break;
    case GDK_KEY_PRESS:
        if (GTK_IS_WINDOW (widget) && GTK_WINDOW (widget)->focus_widget) {
            widget = GTK_WINDOW (widget)->focus_widget;
        }
        priv = PRIV (widget);
        INIT_KEY_EVENT (GOM_KEYDOWN);
        break;
    case GDK_KEY_RELEASE:
        if (GTK_IS_WINDOW (widget) && GTK_WINDOW (widget)->focus_widget) {
            widget = GTK_WINDOW (widget)->focus_widget;
        }
        priv = PRIV (widget);
        INIT_KEY_EVENT (GOM_KEYUP);
        break;
    default:
        break;
    }

    if (!evt) {
        return FALSE;
    }

    g_assert (GOM_IS_EVENT (evt));

    last_type  = event->type;
    last_event = event;

    g_object_ref (widget);
    gom_listener_list_dispatch_event (GOM_EVENT_TARGET (widget), evt, &error);
    if (error) {
        g_print (GOM_LOC ("Error dispatching event: %s\n"),
                  error->message);
        g_clear_error (&error);
    }
    if (gom_event_is_default_prevented (evt)) {
        g_object_unref (evt);
        g_object_unref (widget);
        return TRUE;
    }
    g_object_unref (evt);
    if (event->type == GDK_BUTTON_RELEASE && priv->click_state) {
        evt = NULL;
        INIT_MOUSE_EVENT (GOM_CLICK, button);
        if (!evt) {
            g_object_unref (widget);
            return FALSE;
        }
        gom_listener_list_dispatch_event (GOM_EVENT_TARGET (widget), evt, &error);
        if (error) {
            g_print (GOM_LOC ("Error dispatching event: %s\n"),
                      error->message);
            g_clear_error (&error);
        }
        if (gom_event_is_default_prevented (evt)) {
            g_object_unref (evt);
            g_object_unref (widget);
            return TRUE;
        }
        g_object_unref (evt);
    }
    g_object_unref (widget);
    return FALSE;
}

static void
gom_widget_event_after (GtkWidget *widget, GdkEvent *event)
{
    GomEvent *evt = NULL;
    GomWidgetPrivate *priv;
    GError *error = NULL;

    static GdkEventType  last_type  = 0;
    static GdkEvent     *last_event = NULL;

    if (event == last_event && event->type == last_type) {
        return;
    }

    switch (event->type) {
    case GDK_FOCUS_CHANGE:
        priv = PRIV (widget);
        if (GOM_IS_DOCUMENT_EVENT (priv->owner_document)) {
            evt = gom_document_event_create_event (GOM_DOCUMENT_EVENT (priv->owner_document),
                                                   "UIEvent", &error);
        }
        if (GOM_IS_UI_EVENT (evt)) {
            gom_ui_event_init_ui_event_ns (GOM_UI_EVENT (evt),
                                           GOM_EVENTS_NAMESPACE_URI,
                                           event->focus_change.in ? GOM_DOM_FOCUS_IN : GOM_DOM_FOCUS_OUT,
                                           TRUE, FALSE,
                                           NULL, 0);
        }
        break;
    default:
        break;
    }

    if (!evt) {
        return;
    }

    g_assert (GOM_IS_EVENT (evt));

    last_type  = event->type;
    last_event = event;

    gom_listener_list_dispatch_event (GOM_EVENT_TARGET (widget), evt, &error);
    if (error) {
        g_print (GOM_LOC ("Error dispatching event: %s\n"),
                  error->message);
        g_clear_error (&error);
    }
    g_object_unref (evt);
}

static void
widget_add_event_listener_ns (GomEventTarget   *event_target,
                              const char       *namespace_uri,
                              const char       *type,
                              GomEventListener *listener,
                              gboolean          use_capture,
                              GObject          *evt_group)
{
    gom_listener_list_add_event_listener (
        PRIV (event_target)->listeners, namespace_uri,
        type, listener, use_capture, evt_group);
}

static void
widget_add_event_listener (GomEventTarget   *target,
                           const char       *type,
                           GomEventListener *listener,
                           gboolean          use_capture)
{
    gom_listener_list_add_event_listener (
        PRIV (target)->listeners, NULL,
        type, listener, use_capture, NULL);
}

static void
widget_remove_event_listener_ns (GomEventTarget   *target,
                                 const char       *namespace_uri,
                                 const char       *type,
                                 GomEventListener *listener,
                                 gboolean          use_capture)
{
    gom_listener_list_remove_event_listener (
        PRIV (target)->listeners, namespace_uri,
        type, listener, use_capture);
}

static void
widget_remove_event_listener (GomEventTarget   *target,
                              const char       *type,
                              GomEventListener *listener,
                              gboolean          use_capture)
{
    gom_listener_list_remove_event_listener (
        PRIV (target)->listeners, NULL,
        type, listener, use_capture);
}

static gboolean
widget_has_event_listener_ns (GomEventTarget   *target,
                              const char       *namespace_uri,
                              const char       *type)
{
    return gom_listener_list_has_event_listener (PRIV (target)->listeners, namespace_uri, type);
}

static gboolean
widget_will_trigger_ns (GomEventTarget   *target,
                        const char       *namespace_uri,
                        const char       *type)
{
    return gom_listener_list_will_trigger (PRIV (target)->listeners, target, namespace_uri, type);
}

static gboolean
widget_dispatch_event (GomEventTarget *target,
                       GomEvent       *evt,
                       GError        **error)
{
    GdkEvent event = { 0 };
    gboolean ctrlKey, shiftKey, altKey, metaKey;
    GdkKeymapKey *keys;
    gint n_keys;
    GomKeyLocationCode location;
    char *identifier;
    long timestamp;
    char *type;

    g_object_get (evt, "type", &type, NULL);

    if (GOM_IS_KEYBOARD_EVENT (evt)) {
        if (!strcmp (type, GOM_KEYDOWN)) {
            event.any.type = GDK_KEY_PRESS;
        } else if (!strcmp (type, GOM_KEYUP)) {
            event.any.type = GDK_KEY_RELEASE;
        } else {
            goto dispatch;
        }
        g_object_get (evt,
                      "key-identifier", &identifier,
                      "key-location",   &location,
                      "ctrl-key",       &ctrlKey,
                      "shift-key",      &shiftKey,
                      "alt-key",        &altKey,
                      "meta-key",       &metaKey,
                      "time-stamp",     &timestamp,
                      NULL);
        if (!gom_keyboard_evt_key_identifier_to_keyval (identifier, location,
                                                        &event.key.keyval)) {
            g_free (identifier);
            goto dispatch;
        }
        if (!gdk_keymap_get_entries_for_keyval (NULL, event.key.keyval, &keys, &n_keys)) {
            g_free (identifier);
            goto dispatch;
        }
        g_free (identifier);

        event.key.hardware_keycode = keys[0].keycode;
        event.key.group = keys[0].group;
        g_free (keys);

        event.key.window = GTK_WIDGET (target)->window;
        event.key.send_event = FALSE;
        event.key.time = timestamp;
        if (ctrlKey) {
            event.key.state |= GDK_CONTROL_MASK;
        }
        if (shiftKey) {
            event.key.state |= GDK_SHIFT_MASK;
        }
        if (altKey) {
            event.key.state |= GDK_MOD1_MASK;
        }
        if (metaKey) {
            event.key.state |= GDK_META_MASK;
        }
    } else if (GOM_IS_MOUSE_EVENT (evt)) {

    } else if (GOM_IS_UI_EVENT (evt)) {

    } else {

    }

    if (event.any.type) {
        g_free (type);
        gtk_main_do_event (&event);
        return TRUE;
    }
dispatch:
    g_free (type);
    return gom_listener_list_dispatch_event (target, evt, error);
}

static void
widget_set_parent (GomNodeInternal *child, GomNode *parent)
{
    GomWidgetPrivate *priv = PRIV (child);
#if 0
    char *child_name, *parent_name = NULL;
    g_object_get (child, "node-name", &child_name, NULL);
    if (parent) {
        g_object_get (parent, "node-name", &parent_name, NULL);
    }
    g_print (GOM_LOC ("<%s %p> <%s %p>\n"),
             child_name, child,
             parent_name ? parent_name : "(no parent)", parent);
    g_free (child_name);
    g_free (parent_name);
#endif
    priv->next_sibling = NULL;
    priv->prev_sibling = NULL;
    priv->parent_node  = parent;
}

static void
widget_set_next_sibling (GomNodeInternal *child, GomNode *sibling)
{
#if 0
    char *child_name, *sibling_name = NULL;
    g_object_get (child, "node-name", &child_name, NULL);
    if (sibling) {
        g_object_get (sibling, "node-name", &sibling_name, NULL);
    }
    g_print (GOM_LOC ("<%s %p> <%s %p>\n"),
             child_name, child,
             sibling_name ? sibling_name : "(no sibling)", sibling);
    g_free (child_name);
    g_free (sibling_name);
#endif
    PRIV (child)->next_sibling = sibling;
}

static void
widget_set_prev_sibling (GomNodeInternal *child, GomNode *sibling)
{
#if 0
    char *child_name, *sibling_name = NULL;
    g_object_get (child, "node-name", &child_name, NULL);
    if (sibling) {
        g_object_get (sibling, "node-name", &sibling_name, NULL);
    }
    g_print (GOM_LOC ("<%s %p> <%s %p>\n"),
             child_name, child,
             sibling_name ? sibling_name : "(no sibling)", sibling);
    g_free (child_name);
    g_free (sibling_name);
#endif
    PRIV (child)->prev_sibling = sibling;
}

static void
widget_sibling_requested (GomNodeInternal *node, GomNode *child)
{
    GomWidgetPrivate *priv = PRIV (node);
    GList *children;
    GList *li;
    g_assert (GTK_IS_CONTAINER (node));
    if (!priv->dirty_children) {
        return;
    }
    children = gtk_container_get_children (GTK_CONTAINER (node));
    for (li = children; li; li = li->next) {
        if (GOM_IS_NODE_INTERNAL (li->data)) {
            gom_node_internal_set_prev_sibling (li->data, li->prev ? li->prev->data : NULL);
            gom_node_internal_set_next_sibling (li->data, li->next ? li->next->data : NULL);
        }
    }
    g_list_free (children);
    priv->dirty_children = 0;
}

static void
widget_dispatch_listeners (GomEventTargetInternal *current_target,
                           GomEvent               *evt,
                           const char             *namespace_uri,
                           const char             *type_name,
                           GomPhaseType            phase)
{
    gom_listener_list_dispatch_listeners (
        PRIV (current_target)->listeners,
        GOM_EVENT_TARGET (current_target),
        evt, namespace_uri, type_name, phase);
}

static GomEventTarget *
widget_get_parent_target (GomEventTargetInternal *target)
{
    GomWidgetPrivate *priv = PRIV (target);

    return priv->parent_node ? GOM_EVENT_TARGET (priv->parent_node) : NULL;
}

static void
widget_dirty_children (GtkContainer *container, GtkWidget *widget, gpointer data)
{
    PRIV (container)->dirty_children = 1;
}

static void
widget_activate (GtkWidget *w, gpointer data)
{
    GomWidgetPrivate *priv = PRIV (w);
    GomEvent *event;
    GError   *error = NULL;
    if (!GOM_IS_DOCUMENT_EVENT (priv->owner_document)) {
        return;
    }
    event = gom_document_event_create_event (GOM_DOCUMENT_EVENT (priv->owner_document),
                                             "UIEvent", &error);
    if (!event) {
        g_printerr (GOM_LOC ("could not create activate events: %s\n"),
                     error->message);
        g_clear_error (&error);
        return;
    }
    if (!GOM_IS_UI_EVENT (event)) {
        g_printerr (GOM_LOC ("event is a %s, but not a UIEvent\n"),
                    g_type_name (G_TYPE_FROM_INSTANCE (event)));
        g_object_unref (event);
        return;
    }
    gom_ui_event_init_ui_event_ns (GOM_UI_EVENT (event),
                                   GOM_EVENTS_NAMESPACE_URI,
                                   GOM_DOM_ACTIVATE,
                                   TRUE, TRUE, NULL, 0);
    gom_listener_list_dispatch_event (GOM_EVENT_TARGET (w), event, &error);
    g_object_unref (event);
    if (error) {
        g_printerr (GOM_LOC ("Error dispatching event: %s\n"),
                     error->message);
        g_clear_error (&error);
    }
}

static void
widget_size_allocate (GtkWidget *widget, GdkRectangle *rect, gpointer data)
{
    GomWidgetPrivate *priv = PRIV (widget);
    GomEvent *event;
    GError *error = NULL;

    if (rect->width  == priv->allocation.width &&
        rect->height == priv->allocation.height) {
        return;
    }
    if (!GOM_IS_DOCUMENT_EVENT (priv->owner_document)) {
        return;
    }
    priv->allocation = *rect;
    event = gom_document_event_create_event (GOM_DOCUMENT_EVENT (priv->owner_document),
                                             "UIEvent", &error);
    if (!event) {
        g_printerr (GOM_LOC ("could not create activate events: %s\n"),
                     error->message);
        g_clear_error (&error);
        return;
    }
    if (!GOM_IS_UI_EVENT (event)) {
        g_printerr (GOM_LOC ("event is a %s, but not a UIEvent\n"),

                    g_type_name (G_TYPE_FROM_INSTANCE (event)));
        g_object_unref (event);
        return;
    }
    gom_ui_event_init_ui_event_ns (GOM_UI_EVENT (event),
                                   GOM_EVENTS_NAMESPACE_URI,
                                   GOM_RESIZE,
                                   TRUE, FALSE, NULL, 0);
    gom_listener_list_dispatch_event (GOM_EVENT_TARGET (widget), event, &error);
    g_object_unref (event);
    if (error) {
        g_printerr (GOM_LOC ("Error dispatching event: %s\n"),
                     error->message);
        g_clear_error (&error);
    }
}

static void
widget_scroll (GtkWidget *widget, GtkAdjustment *adjust)
{
    GomWidgetPrivate *priv;
    GomEvent *event;
    GError *error = NULL;
    GtkWidget *child;

    if (GTK_IS_BIN (widget)) {
        child = gtk_bin_get_child (GTK_BIN (widget));
    } else {
        child = widget;
    }
    if (!child) {
        g_print (GOM_LOC ("returning\n"));
        return;
    }

    priv = PRIV (child);
    if (!GOM_IS_DOCUMENT_EVENT (priv->owner_document)) {
        g_print (GOM_LOC ("returning\n"));
        return;
    }
    event = gom_document_event_create_event (GOM_DOCUMENT_EVENT (priv->owner_document),
                                             "UIEvent", &error);
    if (!event) {
        g_printerr (GOM_LOC ("could not create activate events: %s\n"),
                     error->message);
        g_clear_error (&error);
        return;
    }
    if (!GOM_IS_UI_EVENT (event)) {
        g_printerr (GOM_LOC ("event is a %s, but not a UIEvent\n"),
                    g_type_name (G_TYPE_FROM_INSTANCE (event)));
        g_object_unref (event);
        return;
    }
    gom_ui_event_init_ui_event_ns (GOM_UI_EVENT (event),
                                   GOM_EVENTS_NAMESPACE_URI,
                                   GOM_SCROLL,
                                   TRUE, FALSE, NULL, 0);
    gom_listener_list_dispatch_event (GOM_EVENT_TARGET (child), event, &error);
    g_object_unref (event);
    if (error) {
        g_printerr (GOM_LOC ("Error dispatching event: %s\n"),
                     error->message);
        g_clear_error (&error);
    }
}

static void
widget_scroll_adjustment_notify (GObject *obj, GParamSpec *arg1, gpointer data)
{
    GtkAdjustment *adjust = NULL;
    g_object_get (obj, arg1->name, &adjust, NULL);
    if (adjust) {
        g_signal_connect_swapped (adjust, "value-changed", G_CALLBACK (widget_scroll), obj);
    }
}

GOM_IMPLEMENT (NODE,                  node,                  widget);
GOM_IMPLEMENT (ELEMENT,               element,               widget);
GOM_IMPLEMENT (EVENT_TARGET,          event_target,          widget);
GOM_IMPLEMENT (NODE_INTERNAL,         node_internal,         widget);
GOM_IMPLEMENT (EVENT_TARGET_INTERNAL, event_target_internal, widget);

static void
gom_widget_constructed (GObject *object)
{
    GomWidgetPrivate *priv = PRIV (object);

    if (_gtk_widget_constructed) {
        _gtk_widget_constructed (object);
    }

#if 0
    if (!priv->owner_document) {
        g_warning (GOM_LOC ("%s %p: No document set"),
                   g_type_name (G_TYPE_FROM_INSTANCE (object)),
                   object);
    }
#endif

    update_tag_name (object);

    gtk_widget_add_events (GTK_WIDGET (object),
                           GDK_BUTTON_PRESS_MASK  | GDK_BUTTON_RELEASE_MASK |
                           GDK_ENTER_NOTIFY_MASK  | GDK_LEAVE_NOTIFY_MASK   |
                           GDK_KEY_PRESS_MASK     | GDK_KEY_RELEASE_MASK    |
                           GDK_FOCUS_CHANGE_MASK  | GDK_POINTER_MOTION_HINT_MASK);

    if (GTK_IS_CONTAINER (object)) {
        g_signal_connect (object, "add",    G_CALLBACK (widget_dirty_children), NULL);
        g_signal_connect (object, "remove", G_CALLBACK (widget_dirty_children), NULL);
    }
    if (GTK_IS_BUTTON (object)) {
        g_signal_connect (object, "clicked", G_CALLBACK (widget_activate), NULL);
    }
    if (GTK_IS_ENTRY (object)) {
        g_signal_connect (object, "activate", G_CALLBACK (widget_activate), NULL);
    }
    if (GTK_IS_WINDOW (object)) {
        g_signal_connect (object, "size-allocate", G_CALLBACK (widget_size_allocate), NULL);
    }
    if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), "hadjustment") &&
        g_object_class_find_property (G_OBJECT_GET_CLASS (object), "vadjustment")) {
        g_signal_connect (object, "notify::hadjustment", G_CALLBACK (widget_scroll_adjustment_notify), NULL);
        g_signal_connect (object, "notify::vadjustment", G_CALLBACK (widget_scroll_adjustment_notify), NULL);
    }
    g_signal_connect (object, "event",       G_CALLBACK (gom_widget_event),       NULL);
    g_signal_connect (object, "event-after", G_CALLBACK (gom_widget_event_after), NULL);

    priv->constructed = 1;
}

static gpointer
gom_widget_init_once (gpointer data)
{
    GObjectClass      *oclass;
    GType type = 0;

    static const GInterfaceInfo node_info = {
        widget_implement_node,
        NULL, /* interface_finalize */
        NULL  /* interface_data */
    };
    static const GInterfaceInfo element_info = {
        widget_implement_element,
        NULL, /* interface_finalize */
        NULL  /* interface_data */
    };
    static const GInterfaceInfo target_info = {
        widget_implement_event_target,
        NULL,
        NULL,
    };
    static const GInterfaceInfo node_internal_info = {
        widget_implement_node_internal,
        NULL,
        NULL
    };
    static const GInterfaceInfo target_internal_info = {
        widget_implement_event_target_internal,
        NULL,
        NULL
    };

    oclass = g_type_class_ref (GTK_TYPE_WIDGET);

    _gtk_widget_get_property = oclass->get_property;
    oclass->get_property = gom_widget_get_property;

    _gtk_widget_set_property = oclass->set_property;
    oclass->set_property = gom_widget_set_property;

    _gtk_widget_constructed = oclass->constructed;
    oclass->constructed = gom_widget_constructed;

    g_object_class_install_property (
        oclass, PROP_TAG_NAME,
        g_param_spec_string ("node-name", NULL,
                             "The name of this node, depending on its type",
                             NULL,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_NODE_VALUE,
        g_param_spec_string ("node-value", NULL,
                             "The value of this node, depending on its type",
                             NULL,
                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_NODE_TYPE,
        g_param_spec_enum ("node-type", NULL,
                           "A code representing the type of the underlying object",
                           GOM_TYPE_NODE_TYPE, GOM_ELEMENT_NODE,
                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_PARENT_NODE,
        g_param_spec_object ("parent-node", NULL,
                             "The parent of this node.",
                             GOM_TYPE_NODE,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_CHILD_NODES,
        g_param_spec_object ("child-nodes", NULL,
                             "A NodeList that contains all children of this node.",
                             GOM_TYPE_NODE_LIST,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_FIRST_CHILD,
        g_param_spec_object ("first-child", NULL,
                             "The first child of this node.",
                             GOM_TYPE_NODE,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_LAST_CHILD,
        g_param_spec_object ("last-child", NULL,
                             "The last child of this node.",
                             GOM_TYPE_NODE,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_PREVIOUS_SIBLING,
        g_param_spec_object ("previous-sibling", NULL,
                             "The node immediately preceding this node.",
                             GOM_TYPE_NODE,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_NEXT_SIBLING,
        g_param_spec_object ("next-sibling", NULL,
                             "The node immediately following this node.",
                             GOM_TYPE_NODE,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_ATTRIBUTES,
        g_param_spec_object ("attributes", NULL,
                             "A NamedNodeMap containing the attributes of this node (if it is an Element) or null otherwise.",
                             GOM_TYPE_NAMED_NODE_MAP,
                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_OWNER_DOCUMENT,
        g_param_spec_object ("owner-document", NULL,
                             "The Document object associated with this node.",
                             GOM_TYPE_DOCUMENT,
                             G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_NAMESPACE_URI,
        g_param_spec_string ("namespace-u-r-i", "namespace-uri",
                             "The namespace URI of this node, or null if it is unspecified.",
                             NULL,
                             G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_PREFIX,
        g_param_spec_string ("prefix", NULL,
                             "The namespace prefix of this node, or null if it is unspecified.",
                             NULL,
                             G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_LOCAL_NAME,
        g_param_spec_string ("local-name", NULL,
                             "Returns the local part of the qualified name of this node.",
                             NULL,
                             G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (
        oclass, PROP_TAG_NAME,
        g_param_spec_string ("tag-name", NULL,
                             "The name of the element.",
                             NULL,
                             G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_type_add_interface_static (GTK_TYPE_WIDGET, GOM_TYPE_EVENT_TARGET,          &target_info);
    g_type_add_interface_static (GTK_TYPE_WIDGET, GOM_TYPE_EVENT_TARGET_INTERNAL, &target_internal_info);
    g_type_add_interface_static (GTK_TYPE_WIDGET, GOM_TYPE_NODE,                  &node_info);
    g_type_add_interface_static (GTK_TYPE_WIDGET, GOM_TYPE_ELEMENT,               &element_info);
    g_type_add_interface_static (GTK_TYPE_WIDGET, GOM_TYPE_NODE_INTERNAL,         &node_internal_info);

#define WIDGET(w) type ^= w;
#include "gomwidgets.c"
#undef WIDGET

    return GSIZE_TO_POINTER (type);
}

void
gom_widget_init (void)
{
    static GOnce init_once = G_ONCE_INIT;
    g_once (&init_once, gom_widget_init_once, NULL);
}


Finally there are the results of the XXII Ural Championship in competitive programming. The Plushki team took 9th place, entered the top ten.
The XXII Ural Championship in competitive programming is held this year in Ufa. Traditionally Plushki team, represented by Denis Tatarinov and Kirill Bulatov, takes part in the Championship.
On the 21-22 April in ITSU held programming contests the tour was sponsored by the Ardecs company. The sponsor, the Ardecs company, has awarded winners of the qualifying round, all participants received valuable gifts.
VIII International open championship final in sport programming of BSUIR was held at the Faculty of Computer Systems and Networks in Belarusian State University of Informatics and Radioelectronics with the support of Ministry of Education of Belarus.
Dear Defenders of the Fatherland please accept our wishes for good health, success, and well-being.
The opening of regular programmers' tournament was started in ISTU on 10 February. Tournament of young programmers, which ISTU organization in cooperation with M.V. Lomonosov Moscow State University, will last nearly two weeks.
Today our company celebrates 7 successful years on the IT market!
This is a remarkable occasion for us — Today Ardecs celebrates its 7 years anniversary!
Business processes of the company Satori Ltd. has been analysed and formalized, software system, which includes a constructor and calculator of orders, subsystem of task accomplishment, inventory accounting module, interaction with customers module, has been developped.
As a result, operating principles have conformed to Regulation of the Government of the Russian Federation of 23 September 2010 # 731.
Information system Independent registrar shall be used for independent data collection, logging, storage, control and audit information in an open online auction on trading platforms.


Bubbles loves pretty much any kind of craft project you put in front of her. It doesn't matter if it's painting, gluing, using stickers or pompoms or stampers – she's in.
Not too long ago the folks at Fibre Craft offered to send us some of their new Creative Hands craft totes to review. They're recommended for ages 6+ but once I took a look at the kits I decided that Bubbles would be able to complete most of them on her own, and some with a little help.
Have you ever reached for a drink just to wonder, "Is this mine?" It's easy to happen, especially in the hustle and bustle of a game day party. They make those little charms for wine glasses so I thought I'd make my own version for bottles.
Growing up crafts were one of my favorite things to do. I still enjoy being up to my elbows in glue and paint when I have the chance, so I'm particularly excited that Bubbles is showing interest in crafts, as well.
A little while ago the folks at Michael's asked if Bubbles and I would like to attend their fun annual Passport to Imagination series as their guest this summer. This year Michael's has partnered with seven of North America's best museums to create a museum road trip. Each day, kids will experience culture through crafts with projects inspired by world-class museum exhibits.


text:
import {Errors, JsonApiAction, Methods, PermissionClass, ReactWebPartView, WebPartView} from 'actions';
import React from "react";
import Combobox from 'react-widgets/lib/Combobox'

class MyApiAction extends JsonApiAction
{
    validate(json: any, errors: Errors): void
    {
        if (!json.name)
            errors.rejectValue("name", "Required");
        if (!("i" in json))
            errors.rejectValue("i", "Required");
        else if (isNaN(json.i = parseInt(json.i)))
            errors.rejectValue( "i", "Could not convert integer");
    }

    public handleGet(json:any, errors:Errors) : Object
    {
        console.log("<BeginAction.handleGet>");
        console.log("name="+json.name);

    	const ret:any =
		{
			message: 'Hello ' + this.user.getDisplayName(),
			method : 'GET',
			url : this.request.getRequestURI(),
            userAgent : this.request.getHeaders()['user-agent'],
			contextPath : this.request.getContextPath(),
			params : json
		};

        console.log("</BeginAction.handleGet>");
        return this.successResponse(ret);
	}

	public handlePost(json:any, errors:Errors)
    {
        return this.handleGet(json,errors);
    }

	methodsAllowed:string[] = ['POST','GET'];
    requiresPermission:string[] = [PermissionClass.READ];
}

class SecondAction extends JsonApiAction
{
    public validate(json:any, errors:Errors) : void
    {
        console.log("json.name=" + json.name);
        if (!json.name)
            errors.rejectValue("name", "value is required");
        if (json.name == 'Fred')
            errors.rejectValue("name", "not that guy");
    }
    public handleGet(json:any, errors:Errors) : Object
    {
        return this.successResponse({name:json.name, answer:42});
    }

    methodsAllowed:string[] = ['GET'];
    requiresPermission:string[] = [PermissionClass.READ];
}

class QueryAction extends JsonApiAction
{
    public validate(json:any, errors:Errors) : void
    {
    }

    public handleGet(json:any, errors:Errors) : Object
    {
        console.log("<QueryAction.execute>");
/*
        let rs:Results = ServiceManager.getQueryService().select(
            {
                "schemaName":"core",
                "sql":"SELECT userId, email FROM core.Users"
            });
        let arr = [];
        while (rs.next())
        {
            console.log(rs.getString(2));
            arr.push({"userid":rs.getNumber(1), "email":rs.getString(2)});
        }
        rs.close();
 */

        console.log("</QueryAction.execute>");
        return {users:[{userid:'a',email:'a@acme.test'}]};
    }

    public handlePost(json:any, errors:Errors)
    {
        return this.handleGet(json,errors);
    }

    methodsAllowed:string[] = [ Methods.GET, Methods.POST ];
    requiresPermission:string[] = [PermissionClass.READ];
}

class HtmlView extends WebPartView
{
    render(params, writer)
    {
        writer.write("

Hello World

");
        return null;
    }
    requiresPermission:string[] = [PermissionClass.NONE];
}

function Hello(props)
{
    return
Hello, {props.name}
;
}

class ReactView extends ReactWebPartView
{
    getMarkup(json)
    {
        const names = ["matt","adam","kevin"];
        return(

                <link href="/labkey/react-widgets.css" type="text/css" rel="stylesheet" />

                    {names.map((name,index) => <Hello key={index} name={name}/>)}
                    <Combobox data={names}/>

        );
    }
    requiresPermission:string[] = [PermissionClass.NONE];
}

// differences between actions and views:
// a) wrapping of views in template
// b) default contentType
// c) rendering of unhandled exceptions

export default {
    actions:
    {
        myapi:  MyApiAction,            // class
        second: new SecondAction(),     // object
        query:  QueryAction
    },
    views:
    {
        html:   HtmlView,
        react:  ReactView
    }
};

redpajama_set_name: RedPajamaGithub


Crazy and Complicated Truss Designs Part 4 – Gould Design, Inc.
A while back I wrote a piece about crazy and complicated trusses that I have done over my career. A few weeks ago I came across another plan that I could add to that list. One of our customers recently gave a plan for a big a church and said here please design us something that works. Well what followed was quite insane if you ask me.
Like most big commercial buildings this plan had very large spans for trusses, by large, I mean 56' up to 64'. Typically this wouldn't pose an issue, on this plan they wanted vaulted trusses. By vaulting them, it completely took away the ability to piggy back them. As many of you may know, with trusses that large, deflection starts to play a huge part in design. Multiply that by 3 or 4 and you can see how the issues would arise.
This plan kind of was a rather difficult one to work through in the beginning. Inverted hips with little to no bearing on trusses that were spanning what seemed like an entire football field. The only thing saving this situation was what we were initially told by our customer: "Make it work any way you see fit, do what needs to be done." However, even that was difficult due to all the wide open rooms and little to no place to grab extra internal bearing where needed. After spending some time deliberating amongst myself and my colleagues, we came up with an idea to make it work. The solution was to design two half trusses and splice them together in the field, due to the overall height.
As you can see, the base truss on one wing of this building is 64' long and 20' tall. Now that's an enormous truss! One-piece, it is impossible to deliver to the job site. The biggest issue (as I had stated earlier) with them is the vertical deflection. This was a MAJOR problem. Trying to get the heels as low as I could go and figuring out how in the world they were going to build them. I couldn't piggy back them, as I needed as much depth as I could get. It became apparent to us that we were going to have to split them into two pieces, to be assembled in the field. So that's what we did!
As you can imagine a scissor mono works much easier than the full truss. Because of that and the fact that if we truly wanted to know if they would work, each half had to fully mimic the base. I even had to throw on a "K-Web" at the vertical to prevent shear and allow proper bracing.
So I designed a full-span truss with a quantity of "0" and then designed each "half" to match. But I wasn't done there! The "half" piece had to match lumber and plating, as if it was the full-span truss. So I had to manually upgrade each chord and plate. I am glad my colleagues pointed this out early on in the design.
As if that was not enough, there is another aspect to this plan that threw another wrench in the design: Attic space.
Because of the attic space the building owner desired, I had use parallel chord trusses for created an inverted hip in the center of the building. If dealing with the larger spans weren't enough, they want to do what?!?
Here are a couple of cut-through shots to give you an idea of what I mean.
As you can tell, what appeared to be a semi-simple, very large span truss job, turned into a semi-nightmare. This job was quite the challenge and I learned a lot from it.
Here is a view of all the different ceiling condition this plan has. To top it off, the building has an angle in the section where all this transition is going on! Have you ever done that type of design?
Almost nothing is the same. Two sections in this drawing have the trusses broken into 3 pieces because of the parallel chords! Here is a shot of the trusses pieced together, using the "snipping tool." NOTE: I only did one end, but the parallel vault is on both ends.
As you can see, I had my hands full trying to figure out what do on this one! In the end, I feel that I have come up with a good working solution for this building that suited the building owner's needs. While these are most certainly the ugliest trusses I have ever designed, they are definitely not the easiest to deal with.
Have you ever seen anything like this? What solution did you come up with to meet what was a seemingly impossible building design?


Discover on-sale women's Women's Hat & Hair Accessories from Mischa Lampert and similar brands at Shopbop.
Shop new casual styles like Mischa Lampert Stripe Beanie Hat that have just landed from this season's collection. Discover our wide selection of designer dresses, jackets, jeans, tops, shoes and more in our shopping section.


The iconic Smeg FAB range combines good looks with high-performance technology. With beautiful bold finishes and soft round curves reminiscent of 1950's retro design, there are many colour options and six size options, from the ultra spacious FAB50 down to the petite proportions of the FAB5 minibar, but also freezers, washing machines, dishwashers and small domestic appliances.
With the new double door refrigerator FAB50, Smeg updates one of the most successful products in its 50Style range.
The new FAB50 fridge, with improved energy performance and aesthetic design, appealing to anyone who needs large spaces while offering the design features and functionality that have always been the hallmarks of the company.
The performance of the FAB50 refrigerators has been improved all around, first and foremost in terms of energy efficiency: the refrigerator has achieved A++ class energy rating, leading to substantial energy savings.
The FAB50 fridges provides high-level performance while reducing maintenance to a minimum. The No-Frostair circulation system prevents the build-up of frost, mould and bad odours, thereby avoiding the need for tiresome defrosting and deep-cleaning operations.
LED lights provide excellent lighting and are longer lasting than the previous bulbs.
The Fresh Zone provides ideal storage conditions for fresh foods, which will last much longer as a result.


Players pick one of eight characters (four male and four female), select a personality and hit the tour.
Players pick one of eight characters (four male and four female), select a personality and hit the tour. From there they can play in tournaments, practice, play a quick round or play in local wireless mode with pals.
Play tournaments to go up in the rankings, which earns more money to buy better gear. As players customize their characters' clubs, spikes and other gear, their game will improve and they will unlock more and more courses.
The intuitive control of the game is the true special move, as it quickly immerses veteran golfers and novices alike. Players simply touch the screen to switch clubs, aim their shot, view camera angles or make menu selections. Once they're ready to swing, they just touch the club head, draw it back and swing it forward to make contact.
Adjusting the angle of the club face allows players to hook or slice the ball, and a precise swing will always pay off in the end.
For multiplayer games, players can play with up to four people over local wireless with a single game card using DS Download Play (or with multiple ones for more options in DS Local Area Network). Players also can download a demo to other Nintendo DS systems.
Experience the feel and finesse of real golf...the way it was meant to be played.
Touch Swing control: In this realistic golf game, swing control mimics real life. Players simply slide the stylus across the touch screen to strike the ball and send it flying. They can even curve their swing to slice or hook shots and alter the angle of the club head for sharpshooter-like accuracy.
More than 15 impressive 3-D courses, all with their own hazards and strategies, test players' skills. Players earn better gear by winning tournaments to help them compete.
With several game modes, character customization and wireless multiplayer support for up to four players with just one game card, there's something here for all golfers, no matter what their skill levels.


The Roche Conventions is a fairly simple respond to a 1NT open. The partner response of a 2 club overcall, shows 12-14 high card points, a balanced or semi-balanced hand and 3+ clubs. All other responses are the normal response to a 1NT open.
Pass 4+ clubs, no 5-card suit, and no interest in game.


>>>> Maybe an API for message distribution could be the way forward.
>>>> are examples. pud is another.
>>> Why is this more efficient than multicast?
>> unicast would have better performance and reliability than flooding.
>> examples. Maybe not always.
>> deamon, as discussed some time ago. Something for after v2.
> aggregation/jitter per multicast group ?
We have to take a look to SMF also. And maybe to separate distribution trees, if standards for such become available.
> I wonder if we could use the "openvswitch" kernel module for a more efficient multicast implementation.
Or whatever what is available. The mcastd or mcast kernel module would be compatible with other protocols.


Is incredible yet true that you can overcome chronic pain in the knees and joints by using natural arthritis remedies that do not have a long list of side effects. Joint pains or arthritis is a prevalent problem faced by people of all age groups - especially women, obese people and people past their 1960s. Nagging pains on different parts of the body like the wrists, toes, knees, shoulders are mostly brought on by constant tension on the muscles positioned in these areas. If you have high uric acid in blood, you can get extreme gout pains, characterized by pains on the lower end of the big toe. Older men and women generally lose calcium in their bones. This results in osteoarthritis, which is characterized by a nagging pain on the back and spine.
Deficiency in vital minerals in the body is causing arthritis, you can take Epsom salt as it has high this mineral content. Cod liver oil consists of essential oils which help to prevent degeneration of cartilages in the bones. Experts recommend consuming a generous amount of fruits and vegetables in the diet plan. Whole-meal goods, fish, green leafy vegetables, citrus fruits, and lots of fruits and vegetables that have vitamin c are some helpful natural arthritis remedies. Dairy products, such as milk and curd are useful in providing calcium to be able to the body. Bananas offer minerals that include calcium and phosphorous. However, ensure you do not have heart ailments, cholesterol, and all forms of diabetes. If you do, it is best to check what you eat with the doctor.
You strain yourself too much while having a nagging joint pain, you can damage your bones and cartilages even more. There are many natural arthritis remedies that lower inflammation of the joints, and consequently lessen joint pain. For instance, one tablespoon of flax seed at least 3 times a day can lower inflammation on the joints. Comparable natural cure to stop inflammation are cinnamon, alfalfa seeds, turmeric, and ginger.
An effective ointment or cream containing natural ingredients like menthol, Belladonna, Ignatia, MSM, Phosphorus, Rhus Tox, Naja as well as Lachesis Mutus is proven to bring instant relief from the soaring pain of arthritis. When you apply an ointment or cream that contains most of these pain relieving ingredients, it might be one of the best natural arthritis remedies you can find for yourself.


Choice of: Extra egg, Extra pho, Extra roundsteak, Extra beef flank, Extra brisket point and more.
Choice of: Roundsteak "Pho", Flank "Pho", Beef Tendon "Pho", Beef Ball "Pho", Chicken "pho" and more.
Choice of: Chicken Drumstick "pho", Mix Pho, Strawberry Pudding, Mango Pudding, Coconut Pudding and more.


This new build provides a G_SvcOauthAccessToken2 method where a ChilkatHashtable is passed for the 1st arg instead of each of the three claim set params ("iss", "scope", and "sub"). This is more open-ended and allows for future unforeseen args to be added.
I'll add this ability in the next few days. What programming language, operating system, .NET Framework, architecture, etc. do you need so I can provide the exact build?


From Makr Shakr TONI to Italdesign's Pop.Up Next. From Scribit to Lift-Bit.
The RoboTO event, setup in Turin, Italy throughout summer 2018, hosts several projects, each of them with a unique character and distinct purposes. But one similarity gathers all the designs exhibited. That is, they do all advocate the idea that new technologies can help us make our daily life easier, and perhaps even more fun.
RoboTO is created as a club that encourages meetings and discussions on issues such as automation, the future of work and urban life. We invite you to take part in this little experiment, which is open to everyone in Turin and beyond.
The projects exhibited at RoboTO, all make us imagine a different tomorrow; such as Makr Shakr 3.0, Makr Shakr's robotic bartender with mechanical arms, or Scribit, a small write&erase robot creating wall-drawings based on live content from the web, or Pop.Up Next, Italdesign's vision for a self-driving flying vehicle. In particular, with Makr Shakr 3.0, we imagine a future where everyone can become a skilled bartender able to personalize and create cocktails for his or her friends. In this way, machines become a tool to improve and augment social interactions.
Technology can be fun to play with. This summer, come at RoboTO innovation club to take part in the game.


As we have shared in the past, at My Olive Tree we love to spend time in the kitchen. One of our favorite joys is sharing our love for baking with the younger generation. We cannot stress the importance of teaching young children about the value of a healthy and nutritious diet. And because Jewish feasts center around traditional meals, the holiday season is always a perfect time to share both cultural practices, as well as our passion for health with our children.
Liete's Culinaria.com shared this excellent recipe. Although traditional Passover matzah is created in a kosher kitchen, we know that not everyone has that capability or desire. Still, kosher or not, we believe that creating your own matzah from scratch is a fun and educational way to prepare for the upcoming Passover festivities.
Preheat the oven to 500°F (260°C). Ideally, you would place a pizza stone on the bottom oven rack, but realistically a 10-by-15-inch baking sheet will work just dandy.
In a large bowl, mix together all the ingredients, using 3/4 cup water, until everything comes together to form a dough. If the dough seems dry, add a little more water, a few drops at a time. If you do not need the matzah to be kosher for Passover, let the dough rest for 10 to 15 minutes. If you do need the matzah to be kosher for Passover, proceed immediately to the next step so that you can finish everything in 18 minutes.
Divide the dough into 8 pieces. Flatten a piece slightly and pass it repeatedly through a pasta maker, reducing the thickness each time until you reach the minimum setting. Alternately, you can simply roll the dough as thinly as possible with a rolling pin on a lightly floured surface. Repeat with the remaining dough pieces.
Trim the rolled-out dough pieces into rectangles. (How many pieces of matzah you get depends on how thinly you rolled the dough.) Use a fork to prick holes in the surface of the dough. lf salted matzah is desired, brush or spray the dough surface lightly with water and sprinkle with salt to taste.
Carefully place some of the rectangles onto the pizza stone or baking sheet. They should fit snugly but should not touch. Bake until the surface of the matzah is golden brown and bubbly, 30 to 90 seconds. Using tongs, carefully flip the matzah pieces and continue to bake until the other side is golden browned and lightly blistered, 15 to 30 seconds. Keep careful and constant watch to keep the matzah from burning; the exact baking time will vary from oven to oven and will get longer with subsequent batches. You want to let the matzah get a few dots of light brown but do not let the matzah turn completely brown or it will taste burnt. Let it cool before serving.
When you purchase a bottle of My Olive Tree's Extra Virgin Olive Oil the proceeds from our oil go entirely toward our many humanitarian aid projects within the nation of Israel and beyond. We pray that you enjoy spending time with your families this month of April as we celebrate the incredible miracle of Pesach!


396 Motor Yacht is Absolutely beautiful in showroom condition with only 160 engine hours and 140 hours on the Kohler generator. Extremely well kept and continually up dated. New Manifolds and risers 2017. Must see. Call for appointment to inspect. No disappoints Guaranteed!
The updated raised walk deck design has allowed design changes that give the Carver 39 Motor Yacht a wider cabin - much roomier than one would expect. Adding to its appeal, the cabin living space has been combined into a spacious, open "great room" with plenty of salon seating and space for all sorts of extra amenities, like an end table, TV, and VCR. Two luxurious staterooms and two head compartments provide welcome privacy with the master head compartment providing separate head and shower compartments. Superb entertainment possibilities can be found everywhere, from the spacious cabin to the roomy bridge with its wet bar and L-shaped lounge - and the aft deck is no exception. Its integral bridge stairway and side deck wing doors provide convenient access, and the integral boarding platform with aft deck stairway and retractable boarding ladder make boarding easier.


text:
'QAnon shaman' Capitol rioter Jake Angeli on hunger strike because he can't get organic food in detention, mom says
Laura Gesualdi-Gilmore
1:48, 12 Jan 2021
Updated: 6:07, 12 Jan 2021
CAPITOL rioter Jake Angeli, also known as the "QAnon Shaman," won't eat in the detention center he's being held at because he can't get "all organic food."
Angeli, whose real name is Jacob Chansley, turned himself into the FBI on Saturday and made his first virtual court appearance on Monday.
'QAnon Shaman' Jake Angeli is not pleased with the food selection in the detention center he's being held atCredit: Reuters
Angeli was part of the attempted coup at the Capitol on January 6Credit: AFP or licensors
The Phoenix native, 33, was pictured many times during the coup Donald Trump's supporters attempted on January 6 in DC, as Congress was working to certify the results of the 2020 election.
Angeli stuck out among the sea of Trump flags and MAGA hats as he wore a furry, horned hood, American flag face paint, and showed up topless.
He's now been arrested and charged with knowingly entering or remaining in any restricted building or grounds without lawful authority and violent entry and disorderly conduct on Capitol grounds.
The public defender representing him on Monday said that the self-proclaimed shaman was on an "extremely restrictive" diet while he was in custody.
A total of five people died during or after the Capitol riotsCredit: Reuters
Angeli has said he 'didn't break any laws' while he was inside a federal building illegally Credit: AP:Associated Press
The defender suggested that Angeli wasn't eating for religious reasons, but the suspect's mother later said it was because "the detention facility won't feed him all organic food."
The judge presiding over the case said Angeli's refusal to eat was "deeply concerning" and asked the public defender to work with US Marshals to address the dietary concerns, according to ABC 15.
Angeli was one of several Trump supporters who stormed the Capitol last Wednesday, and have since been arrested.
Stay-at-home dad Adam Johnson, who was seen grinning in the building as he carried House Speaker Nancy Pelosi's lectern, was also arrested, as was Air Force veteran Larry Rendall Brock, Jr., who was seen in the building carrying zip-ties.
Richard Barnett, the man seen cheerfully putting his feet up on a desk in Pelosi's office, has also been arrested.
Snow totals revealed on map as winter storm batters New York and other states
CREEPY CRAWLY
PLAY-COY
I'm so beautiful men are too scared to date me, says Playboy model
TWITCH FIGHT
Ninja and Pokimane's drama explained
Angeli, who claims to be a shaman who can travel among dimensions to fight pedophiles, told Newsy last week that he was not worried about charges.
"I'm not really all that worried about it because, in all honesty ... I didn't break any laws. I walked through open doors," he said.
During his Monday hearing, Angeli did not make any statements regarding the charges against him.
Horned QAnon nut Jake Angeli who stormed Capitol says in wild interview he can 'travel to other dimensions to fight pedos'
US Capitol protests

redpajama_set_name: RedPajamaCommonCrawl


text:
/*
 * @module ClassHandler
 * Performs Different Element Class functions
 */

export class ClassHandler {

    // @param {HTMLElement} Element Selector
    // @param {String} classname
    // Adds one or more class names to the element.
    add(element: HTMLElement, className: string) {
		var name = ClassHandler.inputCheck(className);
			for(var i = 0; i < name.length ; i++){
				try{
					element.classList.add(name[i]);
				}
				catch(e){
					console.log(e.message);
					return 'Input Error: Space between class names. Sperate class names with comma(,) only'
				}
			}
    }

    // @param {HTMLElement} Element Selector
    // @param {String} classname
    // Removes one or more class names from the element.
    remove(element: HTMLElement, className: string) {
		var name = ClassHandler.inputCheck(className);
			for(var i = 0; i < name.length ; i++){
				try{
					element.classList.remove(name[i]);
				}
				catch(e){
					console.log(e.message);
					return 'Input Error: Space between class names. Sperate class names with comma(,) only'
				}
			}
    }

    // @param {HTMLElement} Element Selector
    // @param {String} classname
    // @return {Boolean}, indicating whether the element has the specified class name.
    has(element: HTMLElement, className: string):Boolean {
        return element.classList.contains(className);
    }

	// @param {HTMLElement} Element Selector
    // @param {String} classname
    // Toggles between a class name for the element.
	toggle(element:HTMLElement,className:string){
		element.classList.toggle(className)
	}

	// @param {String} class name
	// @private
	// @return {Object} with removed spaces and seperated on commas
	static inputCheck(input:string):Array<string>{
		var str = input.split(',');
		return str
	}
}


text:
Management & Operations
Chicago Transit to make Blue Line improvements
Mayor Rahm Emanuel and Gov. Pat Quinn announced a comprehensive improvement plan for the Chicago Transit Authority's (CTA) Blue Line O'Hare Branch — an overhaul that will provide faster travel times and updated stations while creating more than 1,300 good-paying jobs.
The $492 million plan, called "Your New Blue," includes several track and station improvement projects along a 12.5-mile stretch of the Blue Line O'Hare Branch between the Grand and Cumberland stations, as well as upgrades to the signal system between the Jefferson Park and O'Hare stations.
The improvements will provide faster and more comfortable and reliable commutes for the more than 80,000 customers who each weekday use stations along the branch, which had 25 million rides last year. This project is the largest comprehensive investment for this line since the O'Hare branch extension was built from Jefferson Park to O'Hare in 1983.
"This plan represents a huge investment in CTA riders and visitors traveling to Chicago via O'Hare and many vibrant communities along the North and Northwest Sides. It's also part of our latest efforts to modernize our transit system for the 21st century," said Mayor Emanuel.
Among the four-year plan's highlights:
Extensive station renovations at several stations.
Concrete platform repairs and installation of a new elevator at the Addison station to make it ADA accessible.
Repairs at the Irving Park, Montrose, Harlem and Cumberland stations.
Track improvements to eliminate slow zones in the Dearborn Subway (Grand to Division) and Milwaukee Subway (Damen to Belmont).
Track signal improvements between O'Hare and Jefferson Park, which will improve speed of travel.
Traction power upgrades to improve service and reliability.
Installation of new water management systems and repairs to ensure dry and clean subway stations.
Special track improvements near the O'Hare station and in the Rosemont rail yard to help reduce delays and provide for more efficient operations.
Upgrade of wireless infrastructure in the subway tunnels to provide customers and workers with faster and more reliable voice, data and Web service in the subway. Current infrastructure was built in 2005 and is inadequate and outdated.
Ridership on the O'Hare Branch is growing rapidly, with more than 25 million station entries in 2012. Weekday ridership has grown 25% over the past five years and 33% over the past 10 years — outpacing the growth of the rail system as a whole. Annual ridership on the O'Hare branch increased by 6.3 million rides over the past 10 years, the second highest for any branch after Red North Main, which added seven million rides.
Funding for Your New Blue is expected to come from local, state and federal sources. The first projects are expected to get under way in 2014. Project schedules are still being finalized, but the project is expected to begin with track work, following by station renovations, power upgrades and then signal improvements.
U.S. governors
Anticipating Your Fleet's Brake Maintenance Needs
Alstom unveils new brand identity
The "Mobility by Nature" tagline is the result of an internal poll in which more than 2,500 employees made proposals for expressing Alstom's vision.
METRO Magazine parent co. launches transportation, fleet association
Global Ground Transportation Institute is geared toward limousine, shuttle bus, motorcoach and autonomous vehicle industries.
Blacksburg Transit named 'Outstanding Transit System' of N. America
Virginia-based agency earned the award in the small urban system category characterized by annual ridership of four million or less.
IndyGo taps Santa Clara VTA exec as new president/CEO
Inez Evans will replace current IndyGo President/CEO Michael Terry in mid- to late August.
C-TRAN named North American mid-sized 'Transit System of the Year'
Vancouver, Wash.-based agency increased total ridership by 4.63% during the past two years, one of key achievements factored in.


LG GPad 8.3 vs LG GPad 7.0: Which one is the best?
Last year LG made a comeback in the tablet world with the surprisingly good and stylish LG GPad 8.3. The 2013 LG Tablet used premium materials and was high in specs. In 2014 the situation has changed for LG. The Korean firm has launched 3 new tablets (all with similar specifications) in different sizes: 7.0, 8.0 and 10.1. A few months ago I wrote a complete review about the the LG GPad 7.0 (click here to read the review).
For the last 2 weeks I have been using exclusively the LG GPad 8.3 (2013) and I am really impressed with the tablet. So I decided to launch a battle: LG GPad 8.3 vs LG GPad 7.0.
Are you ready? Here are the results, and hopefully it will help you if you are thinking about buying a LG tablet in the future.
I decided to compare both tablets on the following areas: Build quality and design, Screen, Battery Life, Speakers and UI- Overall performance and both tablets are running Android Kit Kat (LG GPad 8.3 has an upgrade ready).
The LG GPad 8.3 (2013) and the LG GPad 7.0 (2014) are very nice tablets. Both are well built and they feel more premium than any Samsung Galaxy, Lenovo tab, Acer tab (not that difficult!) or even the Nexus 7. The quality of material used on the tablets is different. The rear of the LG GPad 8.3 (2013) has a mix of premium metal and plastic which reminds me of the iPad mini. The LG GPad 8.3 (2013) is on the heavy side (338gr) but it feels very sturdy and premium. At less than £200 it is probably one of the best built Android tablets out there.
The LG GPad 7.0 (2014) is the new generation of LG Tablets. This time LG has decided to go full plastic. When we think about tablets made of plastic we immediately think about the range of Galaxy Tab by Samsung with quirky noises and cheap looking. The LG GPad 7.0 is in another league. The back is made of 100% plastic but it feels premium. In a way it looks like a big Nokia Lumia phone which is a good thing because Nokia is extremely good at building phones. The GPad 7.0 has a price tag of £100 and without a doubt it is the best built budget tablet.
Who is the winner? In my opinion the LG GPad 8.3 is the winner because the overall feeling is superior, but the LG GPad 7.0 is not far behind. Both tablets are impressive on the build quality side!
Apart from the obvious screen size difference (all the new LG tablets 2014 have the same screen resolution), you will immediately notice that one is full HD and one is 720p HD.
The screen on the LG GPad 8.3 (2013) is fantastic. The full HD is perfect for the size of the tablet and with a pixel density of 273 and a screen resolution of 1200x1900 you will love it. Viewing angles are excellent and the colour reproduction is amazing. The screen resolution is not as high as the Nexus 7 but colour reproduction is better in my opinion. I never found watching Netflix on Nexus 7 an amazing experience (I am sure I will upset a lot of people here!). Of course there are better screens out there, the iPad Mini retina display or the new Galaxy Tab S, but only the LG GPad 8.3 can give you such a great screen for less than £200.
This year LG has decided to downgrade some specs of their new range of tablets, and one of them is the screen resolution. It's a shame because at the end of the day, one of the most important aspects of a tablet is its screen resolution.
The LG GPad 7.0 has "only" a 1280x800 IPS display. Last year Motorola showed the world that a 720p display could be great (Moto G) and it looks like LG has learned from them. The screen of the GPad 7.0 is very nice, colours are vibrant and it has a very good viewing angle. Of course you will notice the difference if you put the GPad 8.3 next to the GPad 7.0, but for £100 you get a lot of screen for your money. There are so many £100 7" Android tablets with washed out screens (Lenovo, Acer for example), but don't worry because this is not the case.
The winner is obviously the GPad 8.3 (2013) with its beautiful HD screen, but the LG GPad 7.0 has one of the best (if not the best) screen for a budget tablet. Watching Netflix on the GPad 7.0 is a very enjoyable experience. It is obvious that LG knows a lot about screen quality.
The LG GPad 8.3 (2013) has a battery of 4.600mAh and the LG GPad 7.0 (2014) has a battery of 4.000mAh. I really think that the GPad 8.3 needs a much bigger battery. The full HD screen needs a lot of power and unfortunately the battery on the GPad 8.3 is on the short side. Obviously it all depends on what you do with your tablet. But I am a moderate tablet user (as I work almost all day on my Chromebook) but I find the LG GPad 8.3 will never last a full day. Worst still...it takes an awful long time to charge. Personally I am not really impressed with the battery of the LG GPad 8.3 (2013).
I always hated the poor sound quality of the iPad 1-2-3 and 4. A tablet is an excellent device for media consumption so good stereo speakers are very important, and unfortunately very often manufacturers don't put enough attention in that area. So far the best speakers on a tablet were the Blackberry Playbook, HP Touchpad, the Motorola Xoom, Nokia Lumia 2520 and some Samsung Galaxy Notes. So will this change with LG?
The LG GPad 8.3 (2013) has two stereo speakers located on the back of the device. They offer a good sound quality, loud and quite clear. We are still far from HiFi stereo sound but for a tablet it is actually good. You can easily watch a movie on Netflix or TV show on Hulu Plus using the internal speakers of this tablet.
With the LG GPad 7.0 (2014), LG has changed the speaker placement. Now they are located at the bottom of the device (by the USB charger plug). Unfortunately the result is not that great. The sound is very quiet and I always use my headphones to watch a movie with it.
Without any doubt the LG GPad 8.3 (2013) is the winner and the sound is much better.
In an Android world dominated by high specs processors, it feels like the tablet market is always left behind by sub par specification. It is easy to find outstanding Android Smartphones with great user experience but not with tablets. Android tablets always have some lags or hiccups, and this starts to get annoying. Maybe Google should start to develop a version of Android specially designed for tablets?
So what about the User Interface and the experience with LG?
The LG GPad 8.3 (2013) is now on Android Kit Kat and the overall performance is better than at launch. This tablet is powered by an older processor Qualcomm Snapdragon 600 but it doesn't really affect the overall performance. There is some feeling of lags especially on the Home screen and widgets, but I think this is due to the heavy LG UI. The LG UI on the GPAd 8.3 (2013) is heavy, childish, it feels like we are back in 2011! It's a shame because this beautiful tablet is "ruined" by a cartoonist UI. Apps launch quickly, browsing the web is fast, but it is the navigation through the UI that feels heavy. With LG you can customise almost everything, from the font to the android buttons. I am sure it is okay for some, but I want something nice and that performs well out of the box. That's not the case with the LG GPad 8.3 (2014).
The new LG GPad 7.0 (2014) is a different world! LG has learned that less is more and they have introduced a new UI which is more polished, refined and minimalist. The tablet is in fact very snappy and performs very well. There is slight lagging sometimes but nothing annoying. The feeling is that the LG GPad 7.0 is extremely fast and shouldn't be on the budget category. You will be really impressed by the overall experience of the new LG GPad 7.0 and the UI is very similar to the LG G3 smartphone.
For me the clear winner is the LG GPad 7.0 (2014) especially because I really dislike heavy UI.
This is an extremely difficult answer to give you right now. First I have to say that the LG comeback in the Android tablet world is perfect. The Korean firm has produced an excellent range of tablets at affordable prices. In fact the budget range of the 2014 GPads is far superior to any Samsung Galaxy Tab, Lenovo IdeaTab, Acer, etc…. Whatever tablet you choose, you won't be disappointed.
At £169 on Amazon, the LG GPad 8.3 (2013) is an excellent tablet that gives you a lot for your money. You get an amazing full HD screen, good performance, stylish look, excellent build quality, good speakers, and an OK camera. But you also have to deal with an average battery life and a heavy LG UI. However at £169 you will not find any better tablet right now. I will give this tablet a 4/5.
For £74 the LG GPad 7.0 (2014) is absolutely amazing. It is fast, snappy, stylish, with great performances. But for £100 LG had to do some cuts. The great screen is not full HD but it is still excellent. The speakers are not that great and the camera is really bad and the build quality is good but not as premium as the LG GPad 8.3 (2013). On the other hand you get a much better battery life and a much nicer and polished UI. I have to give this tablet a 4.5/5 because you get a lot for £100.
I hope this post will help you with future purchases. Hopefully soon LG will launch a more premium tablet range (especially with full HD screen) but so far LG is doing very good.


Come and experience the beauty of our parish community.
Join us as we advance in relationship with God and with one another. Kindle your faith, enrich our community with your individual gifts, and form strong and lasting relationships with other parishioners.
Call the parish office and arrange a time to meet with Fr. Redcay, who will share with you some of the vibrant happenings of St. Patrick Parish. We look forward to meeting you!
To join us as a registered member, please complete the form below and deliver it to the parish office.


text:
Toy Review: Mattel SDCC Exclusive Batman Batusi Action Figure
Let's do the Batusi! Top side panel of Batusi Batman box
Gleeps! It's Batman!
In our previous Bat-Post we revealed the first of the Batman TV series toys with the Batman Classic TV series Batman and Robin 2-pack. This week we review another classic re-enactment of the show with this Batusi TV Batman action figure.
This was originally a San Diego Comic-Con Exclusive action figure, but is now available at Matty Collector for $30.
Front panel of box #1 of the Batusi Batman action figure
The patrons of this discotheque look straight out of Mad Men
History of the Batusi
Actress Cathy Ferrar will be forever known as the "Gleeps Girl" after exclaiming the above dialogue during the premier episode of the Batman TV series "Hi Diddle Riddle." During that episode Batman and Robin follow a lead that winds them up at a discotheque called "What A Way To Go-Go." It's there that Batman reveals the dance that would help feed the fire to a craze known as Batmania.
Adam West describes the filming of this scene in his book 'Back to the Batcave':
When we did the discotheque scene, the drugged Batman slid onto the dance floor and did the Batusi with Jill St. John. As (Lorenzo) Semple had written the scene, the Batusi was a nondescript dance that simply enabled Batman to move along the revelers. I felt it should be a little more than that, and I worked it out on my own, made it a singular moment of madness inspired by the ongoing debate in the media about whether drug use was mind-expanding or debilitating. I wasn't trying to make a social judgement. I just felt it would be funny and abstract to have batman slightly out of his well-trained mind, losing control as a result of the drug.
Virtually everyone in the crew laughed when I finished the scene, but there was some front-office type who nervously jangled the change in his pocket and said he thought it was ridiculous.
"He's drugged," I yelled, as I Batusied in his direction. "What do you do when you're drugged?"
I didn't find out, because he turned and left. He never came back, either, and to this day I wonder what he and others like him thought when the Batusi became a national dance craze.
Mattel put some thought into this Batman action figure complete with TV diorama
You shake a pretty mean cape Batman and so does Mattel!
Overall, the Batusi Batman action figure is a welcomed addition to the line of Mattel Batman toys and it nicely displays in it's double boxed TV diorama. The figure itself is reminiscent of Kenner's Super Powers collection as it has a mechanism that causes Batman's arms to move to replicate the iconic dance.
Robin get his groove on watching Batman on the Bat Scope at the What a Way to Go-Go
The figure has many pluses, but those pluses also cause it to have minuses at the same time:
The art on the packaging is straight out of the sixties and the toy comes in not one, but two boxes. The second box that actually encompasses the figure is a mock television set that allows for Batman to dance in the box without removing it. The TV box has accurate quotes from the first episode like "In this little game, I am the ace of trump!" and the back depicts Robin parked in the Batmobile in front of the What a Way to Go-Go at 222 Glover Ave. I'm impressed with Mattel's attention to detail with this and that they actually did their homework.
The TV set box also plays jazzy surf rock music similar to the music that was played during the dance scene in the pilot episode. This was a clever feature even for Mattel if I do say so myself.
The action figure is a dead ringer for Adam West and is well detailed like its other Batman counterparts in the toy line.
With a push of a button you too can do the Batusi
I love the idea of the TV set box, but making it a 2-pack with an action figure of Jill St. John who portrayed the Riddler's Moll, Molly, would have been the ultimate win for this toy.
The music playing feature was also a win-win, but is not the actual song played in the series, which can be found on the Batman Exclusive Original Television Soundtrack Album on the track Holy Flypaper. Either Mattel didn't complete their homework or there was a licensing issue.
The figure is nicely detailed, as are the other figures in the series, but as usual it's basically a re-used mold. The difference between the other Batman's in this wave is the abdomen does not have articulation like the others. Also, Batman's fingers are forever molded giving the piece sign. Being able to swap out molded hands would have been a nice feature.
Rear packaging art of Box #1 of the Batman Batusi action figure
A quote from Adam West on the rear panel on box #1:
I created the BATUSI when everyone was shaking it to the WATUSI. In our television show, BATMAN just couldn't resist the music and his urge to get up and dance. And so the BATUSI was born. It immediately became a national dance craze and my moves were copied many times in many movies and TV shows. Do you hear the music? Let's dance the BATUSI! - Adam West
For the most part, I feel Mattel put their all into this set and the draw backs are probably unapparent to most Batman toy collectors and could be that only a Batman TV series freak like me would notice such things, but this toy has been 47 years in the making and I'll take what I can get.
Anyone else manage to pick up this Bat-Figure? Who remembers the clue that brought the Dynamic Duo to 222 Glover Ave?
Next week John reviews the Batmobile!
Labels: Action Figures , Batman , Mattel , Matty Collector , SDCC , Toys , TV Series
John Gaither August 19, 2013 at 2:48 AM
Awesome John! You got it congrats and it is such a fun toy from the looks of it and the package really is a big selling point of it. Such a great display piece.
John Sholtz August 19, 2013 at 7:36 PM
It really did turn out nice John Boy. It looks like it is already sold out on mattycollector.com. Hopefully, they have more soon if anyone else is hoping to get one.
Corey Sorenson August 20, 2013 at 9:00 AM
Great review John. I especially enjoyed your historical commentary of the episode.
It's been a LONG time since I've seen this show, so I don't remember the clue that tipped Batman and Robin off.
When Michael was 3 or 4, we used to watch old episodes on YouTube during my lunch break. Matthew would be a perfect age for this series and given his enjoyment of the Batman '66 comic book, it's disappointing that I can't share it with him.
John Sholtz August 20, 2013 at 10:01 PM
Thanks Corey. I love the history behind things.
Hopefully, with the latest licensing triumphs the Batman TV series will be available on DVD soon.
I watch it with my daughter all the time. When she was just over 2 years old she began singing the theme song and a guy asked me, "Is she signing the Batman theme? That's the coolest thing I've ever seen!" I wish I could have caught that on video.