Ox.InfoList = function(options, self) {

    self = self || {};
    var that = Ox.Element({}, self)
        .defaults({
            borderRadius: 0,
            defaultRatio: 1,
            draggable: false,
            id: '',
            item: null,
            items: null,
            keys: [],
            max: -1,
            min: 0,
            selected: [],
            size: 192,
            sort: [],
            unique: ''
        })
        .options(options || {})

    self.iconSize = Math.round(self.options.size * 2/3);
    self.itemHeight = self.options.size;

    that.$element = Ox.List({
            construct: constructItem,
            draggable: self.options.draggable,
            id: self.options.id,
            itemHeight: self.itemHeight,
            items: self.options.items,
            itemWidth: getItemWidth(),
            keys: self.options.keys,
            max: self.options.max,
            min: self.options.min,
            orientation: 'vertical',
            pageLength: 10,
            selected: self.options.selected,
            sort: self.options.sort,
            type: 'info',
            unique: self.options.unique
        }, Ox.clone(self))
        .addClass('OxInfoList')
        .bindEvent({
            select: function() {
                self.options.selected = that.$element.options('selected');
            }
        });

    updateKeys();

    function constructItem(data) {
        var isEmpty = Ox.isEmpty(data),
            data = !isEmpty
                ? self.options.item(data, self.options.sort, self.options.size)
                : {
                    icon: {
                        width: Math.round(self.iconSize * (
                            self.options.defaultRatio >= 1 ? 1 : self.options.defaultRatio
                        )),
                        height: Math.round(self.iconSize / (
                            self.options.defaultRatio <= 1 ? 1 : self.options.defaultRatio
                        ))
                    },
                    info: {}
                };
        Ox.Log('List', 'DATA -=-------', data)

        var $icon = Ox.Element()
                .css({
                    float: 'left',
                    width: '132px',
                    height: '192px',
                    margin: '4px 2px 4px 2px',
                    //background: 'blue'
                })
                .append(
                    Ox.IconItem(Ox.extend(data.icon, {
                            borderRadius: self.options.borderRadius,
                            iconHeight: self.iconSize,
                            iconWidth: self.iconSize,
                            imageHeight: data.icon.height,
                            imageWidth: data.icon.width,
                            isInfoList: true,
                            itemHeight: self.itemHeight,
                            itemWidth: 128
                        }))
                        .addClass('OxInfoIcon')
                        .css({
                            position: 'absolute'
                        })
                ),
            $info = Ox.Element()
                .css({
                    float: 'left',
                    width: getItemWidth() - 144 + 'px',
                    height: 196 + 'px',
                    //background: 'green'
                }),
            $infobox = Ox.Element()
                .css({
                    position: 'absolute',
                    width: getItemWidth() - 144 + 'px',
                    height: 196 + 'px',
                    marginTop: '-2px',
                    overflow: 'hidden'
                })
                .appendTo($info);
            $item = Ox.Element()
                .css({
                    width: getItemWidth() - 8 + 'px',
                    height: 196 + 'px',
                    margin: '4px',
                    //background: 'red'
                })
                .append($icon)
                .append($info);
        if (!isEmpty) {
            var $element = data.info.element(Ox.extend(data.info.options, {
                    width: getItemWidth() - 152
                }))
                .addClass('OxInfoElement');
            data.info.css && $element.css(data.info.css);
            data.info.events && $element.bindEvent(data.info.events);
            $element.addClass('OxId' + $element.id); // fixme: needed?
            $infobox.append($element);
        }
        return $item;
    }

    function getItemWidth(cached) {
        if (!cached) {
            self.cachedWidth = that.$element.width() - Ox.UI.SCROLLBAR_SIZE;
        } else if (!self.cachedWidth || self.cachedWidthTime < +new Date() - 5000) {
            self.cachedWidth = that.$element.width() - Ox.UI.SCROLLBAR_SIZE;
            self.cachedWidthTime = +new Date();
        }
        return self.cachedWidth;
    }

    function updateKeys() {
        self.options.keys = Ox.unique(Ox.merge(self.options.keys, [self.options.sort[0].key]));
        that.$element.options({
            keys: self.options.keys
        });
    }

    self.setOption = function(key, value) {
        if (key == 'items') {
            that.$element.options(key, value);
        } else if (key == 'selected') {
            Ox.Log('List', 'SELECTED', value)
            that.$element.options(key, value);
        } else if (key == 'sort') {
            that.$element.options(key, value);
        } else if (key == 'width') {
            var width = getItemWidth();
            $('.OxInfoElement').each(function() {
                var $parent = $(this).parent(),
                    id = parseInt(/OxId(.*?)$/.exec(this.className)[1]);
                $parent.css({width: width - 144});
                $parent.parent().css({width: width - 144});
                $parent.parent().parent().css({width: width - 8});
                Ox.Log('List', '@@@', this.className, id)
                Ox.UI.elements[id].options({width: width - 152});
            });
        }
    };

    that.closePreview = function() {
        that.$element.closePreview();
        return that;
    };

    that.infoOptions = function(id, options) {
        // ...
    }

    that.paste = function(data) {
        that.$element.paste(data);
        return that;
    };

    that.reloadList = function(stayAtPosition) {
        that.$element.reloadList(stayAtPosition);
        return that;
    };

    that.scrollToSelection = function() {
        that.$element.scrollToSelection();
        return that;
    };

    that.size = function() {
        that.$element.size();
    };

    that.sortList = function(key, operator) {
        self.options.sort = [{
            key: key,
            operator: operator
        }];
        updateKeys();
        that.$element.sortList(key, operator);
        return that;
    };

    that.value = function(id, key, value) {
        // fixme: make this accept id, {k: v, ...}
        if (arguments.length == 1) {
            return that.$element.value(id);
        } else if (arguments.length == 2) {
            return that.$element.value(id, key);
        } else {
            that.$element.value(id, key, value);
            return that;
        }
    };

    return that;

};