'use strict'; /*@ Ox.TreeList TreeList Object ([options[, self]]) -> TreeList Object options Options object data data to be parsed to items, needs documentation items array of items max maximum number of items that can be selected, -1 unlimited min minimum number of items that have to be selected selected selected ids width list width self shared private variable @*/ Ox.TreeList = function(options, self) { // fixme: expanding the last item should cause some scroll self = self || {}; var that = Ox.Element({}, self) .defaults({ data: null, items: [], max: 1, min: 0, selected: [], width: 'auto' }) .options(options || {}); if (self.options.data) { self.options.items = []; Ox.sort(Object.keys(self.options.data)).forEach(function(key) { self.options.items.push(parseData(key, self.options.data[key])); }); } that.setElement( self.$list = Ox.List({ _tree: true, construct: constructItem, itemHeight: 16, items: parseItems(), itemWidth: self.options.width, keys: ['expanded', 'id', 'items', 'level', 'title'], max: self.options.max, min: self.options.min, unique: 'id' }, Ox.clone(self)) .addClass('OxTextList OxTreeList') .css({ width: self.options.width + 'px', overflowY: 'scroll' }) .bindEvent({ anyclick: clickItem, select: selectItem, toggle: toggleItems }) ); self.options.selected.length && selectItem({ids: self.options.selected}); function clickItem(e) { var $target = $(e.target), $item, id, item; if ($target.is('.OxToggle')) { $item = $target.parent().parent(); id = $item.data('id'); item = getItemById(id); toggleItem(item, !item.expanded); } } function constructItem(data) { var $item = $('
').css({width: self.options.width + 'px'}), padding = (data.level + !data.items) * 16 - 8; if (data.level || !data.items) { $('
') .addClass('OxCell OxTarget') .css({ width: padding + 'px', }) .appendTo($item); } if (data.items) { $('
') .addClass('OxCell') .css({ width: '8px', }) .append( // fixme: need Ox.Icon() $('') .addClass('OxToggle') .attr({ src: Ox.UI.getImageURL( 'symbol' + (data.expanded ? 'Down' : 'Right') ) }) ) .appendTo($item); } $('
') .addClass('OxCell OxTarget' + (!data.items ? ' OxSelectable' : '')) .css({ width: (self.options.width - padding - 32 + !data.items * 16) + 'px' }) .html(data.title || '') .appendTo($item); return $item; } function getItemById(id, items, level) { var ret = null; items = items || self.options.items; level = level || 0; Ox.forEach(items, function(item) { if (item.id == id) { ret = Ox.extend(item, { level: level }); Ox.Break()(); } if (item.items) { ret = getItemById(id, item.items, level + 1); if (ret) { Ox.Break()(); } } }); return ret; } function getParent(id, items) { var ret; Ox.forEach(items, function(item) { if (item.items) { if (Ox.getObjectById(item.items, id)) { ret = item.id; } else { ret = getParent(id, item.items); } if (ret) { Ox.Break()(); } } }); return ret; } function parseData(key, value) { var ret = { expanded: false, id: Ox.uid().toString(), title: key.toString() + ': ' }, type = Ox.typeOf(value); if (type == 'array' || type == 'object') { ret.title += Ox.toTitleCase(type) + ' [' + Ox.len(value) + ']'; ret.items = Ox.sort( type == 'array' ? value.map(function(v, i) { return parseData(i, v); }) : Object.keys(value).map(function(k) { return parseData(k, value[k]); }) ); } else { ret.title += ( type == 'function' ? value.toString().split('{')[0] : JSON.stringify(value) .replace(/(^"|"$)/g, '"') ); } return ret; } function parseItems(items, level) { var ret = []; items = items || self.options.items; level = level || 0; items.forEach(function(item, i) { var item_ = Ox.extend({ level: level }, item, item.items ? { items: !!item.expanded ? parseItems(item.items, level + 1) : [] } : {} ); ret.push(item_); if (item.items) { ret = ret.concat(item_.items); } }); return ret; } function selectItem(data) { var id = data.ids[0], parent = id, parents = []; while (parent = getParent(parent, self.options.items)) { parents.push(parent); } parents = parents.reverse(); toggleItems({ expanded: true, ids: parents }); self.$list.options({selected: data.ids}) } function toggleItem(item, expanded) { var $img, pos; item.expanded = expanded; that.find('.OxItem').each(function() { var $item = $(this); if ($item.data('id') == item.id) { $img = $item.find('.OxToggle'); pos = $item.data('position'); return false; } }); //that.$element.value(item.id, 'expanded', expanded); $img.attr({ src: Ox.UI.getImageURL( 'symbol' + (expanded ? 'Down' : 'Right') ) }); expanded ? that.$element.addItems( pos + 1, parseItems(item.items, item.level + 1) ) : that.$element.removeItems( pos + 1, parseItems(item.items, item.level + 1).length ); } function toggleItems(data) { data.ids.forEach(function(id, i) { var item = getItemById(id); if (item.items && data.expanded != !!item.expanded) { toggleItem(item, data.expanded); } }); } self.setOption = function(key, value) { if (key == 'data') { // ... } else if (key == 'selected') { //self.$list.options({selected: value}); selectItem({ids: value}); self.$list.scrollToSelection(); } else if (key == 'width') { // ... } }; /*@ gainFocus gainFocus @*/ that.gainFocus = function() { self.$list.gainFocus(); return that; }; /*@ hasFocus hasFocus @*/ that.hasFocus = function() { return self.$list.hasFocus(); }; /*@ loseFocus loseFocus @*/ that.loseFocus = function() { self.$list.loseFocus(); return that; }; return that; };