2011-11-05 16:46:53 +00:00
|
|
|
'use strict';
|
2012-05-21 10:38:18 +00:00
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-31 10:32:54 +00:00
|
|
|
Ox.Menu <f> Menu Object
|
2011-05-16 08:24:46 +00:00
|
|
|
options <o> Options object
|
2014-01-16 07:07:21 +00:00
|
|
|
edge <s> open to 'bottom' or 'right'
|
2011-05-16 08:24:46 +00:00
|
|
|
element <o> the element the menu is attached to
|
|
|
|
id <s> the menu id
|
|
|
|
items <a> array of menu items
|
|
|
|
mainmenu <o> the main menu this menu is part of, if any
|
|
|
|
offset <o> offset of the menu, in px
|
|
|
|
left <n> left
|
|
|
|
top <n> top
|
|
|
|
parent <o> the supermenu, if any
|
|
|
|
selected <b> the position of the selected item
|
|
|
|
size <s> 'large', 'medium' or 'small'
|
2012-07-04 11:29:18 +00:00
|
|
|
self <o> Shared private variable
|
|
|
|
([options[, self]]) -> <o:Ox.Element> Menu Object
|
|
|
|
change_groupId <!> {id, value} checked item of a group has changed
|
|
|
|
click_itemId <!> item not belonging to a group was clicked
|
|
|
|
click_menuId <!> {id, value} item not belonging to a group was clicked
|
|
|
|
deselect_menuId <!> {id, value} item was deselected not needed, not implemented
|
|
|
|
hide_menuId <!> menu was hidden
|
|
|
|
select_menuId <!> {id, value} item was selected
|
|
|
|
click <!> click
|
|
|
|
change <!> change
|
|
|
|
select <!> select
|
|
|
|
deselect <!> deselect
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
|
|
|
|
2011-04-22 22:03:10 +00:00
|
|
|
Ox.Menu = function(options, self) {
|
2011-08-09 17:00:39 +00:00
|
|
|
|
2011-06-19 17:48:32 +00:00
|
|
|
self = self || {};
|
|
|
|
var that = Ox.Element({}, self)
|
2011-12-21 13:42:47 +00:00
|
|
|
.defaults({
|
2014-01-16 07:07:21 +00:00
|
|
|
edge: 'bottom',
|
2011-12-21 13:42:47 +00:00
|
|
|
element: null,
|
|
|
|
id: '',
|
|
|
|
items: [],
|
|
|
|
mainmenu: null,
|
|
|
|
maxWidth: 0,
|
|
|
|
offset: {
|
|
|
|
left: 0,
|
|
|
|
top: 0
|
|
|
|
},
|
|
|
|
parent: null,
|
|
|
|
selected: -1,
|
|
|
|
size: 'medium' // fixme: remove
|
|
|
|
})
|
|
|
|
.options(options || {})
|
2012-05-28 19:35:41 +00:00
|
|
|
.update({
|
|
|
|
items: function() {
|
|
|
|
renderItems(self.options.items);
|
|
|
|
},
|
|
|
|
selected: function() {
|
|
|
|
that.$content.find('.OxSelected').removeClass('OxSelected');
|
|
|
|
selectItem(self.options.selected);
|
|
|
|
}
|
|
|
|
})
|
2011-12-21 13:42:47 +00:00
|
|
|
.addClass(
|
2014-01-16 07:07:21 +00:00
|
|
|
'OxMenu Ox' + Ox.toTitleCase(self.options.edge) +
|
2011-12-21 13:42:47 +00:00
|
|
|
' Ox' + Ox.toTitleCase(self.options.size)
|
|
|
|
)
|
2012-05-28 14:06:22 +00:00
|
|
|
.on({
|
2011-12-21 13:42:47 +00:00
|
|
|
click: click,
|
|
|
|
mouseenter: mouseenter,
|
|
|
|
mouseleave: mouseleave,
|
|
|
|
mousemove: mousemove,
|
|
|
|
mousewheel: mousewheel
|
|
|
|
})
|
|
|
|
.bindEvent({
|
|
|
|
key_up: selectPreviousItem,
|
|
|
|
key_down: selectNextItem,
|
|
|
|
key_left: selectSupermenu,
|
|
|
|
key_right: selectSubmenu,
|
|
|
|
key_escape: hideMenu,
|
|
|
|
key_enter: clickSelectedItem
|
|
|
|
});
|
2011-11-11 10:24:49 +00:00
|
|
|
|
|
|
|
self.itemHeight = self.options.size == 'small'
|
|
|
|
? 12 : self.options.size == 'medium' ? 16 : 20;
|
|
|
|
self.scrollSpeed = 1;
|
2011-04-22 22:03:10 +00:00
|
|
|
|
2011-12-21 13:42:47 +00:00
|
|
|
// render
|
2011-04-22 22:03:10 +00:00
|
|
|
that.items = [];
|
|
|
|
that.submenus = {};
|
|
|
|
that.$scrollbars = [];
|
|
|
|
that.$top = $('<div>')
|
|
|
|
.addClass('OxTop')
|
2012-06-26 16:21:39 +00:00
|
|
|
.appendTo(that);
|
2011-12-21 13:42:47 +00:00
|
|
|
that.$scrollbars.up = renderScrollbar('up')
|
2012-06-26 16:21:39 +00:00
|
|
|
.appendTo(that);
|
2011-04-22 22:03:10 +00:00
|
|
|
that.$container = $('<div>')
|
|
|
|
.addClass('OxContainer')
|
2012-06-26 16:21:39 +00:00
|
|
|
.appendTo(that);
|
2011-04-22 22:03:10 +00:00
|
|
|
that.$content = $('<table>')
|
|
|
|
.addClass('OxContent')
|
|
|
|
.appendTo(that.$container);
|
2011-12-21 13:42:47 +00:00
|
|
|
renderItems(self.options.items);
|
|
|
|
that.$scrollbars.down = renderScrollbar('down')
|
2012-06-26 16:21:39 +00:00
|
|
|
.appendTo(that);
|
2011-04-22 22:03:10 +00:00
|
|
|
that.$bottom = $('<div>')
|
|
|
|
.addClass('OxBottom')
|
2012-06-26 16:21:39 +00:00
|
|
|
.appendTo(that);
|
2011-04-22 22:03:10 +00:00
|
|
|
|
|
|
|
function click(event) {
|
|
|
|
var item,
|
|
|
|
position,
|
|
|
|
$target = $(event.target),
|
|
|
|
$parent = $target.parent();
|
|
|
|
// necessary for highlight
|
|
|
|
if ($parent.is('.OxCell')) {
|
|
|
|
$target = $parent;
|
|
|
|
$parent = $target.parent();
|
|
|
|
}
|
|
|
|
if ($target.is('.OxCell')) {
|
|
|
|
position = $parent.data('position');
|
|
|
|
item = that.items[position];
|
|
|
|
if (!item.options('disabled')) {
|
|
|
|
clickItem(position);
|
|
|
|
} else {
|
|
|
|
that.hideMenu();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
that.hideMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-24 10:13:33 +00:00
|
|
|
function clickItem(position, files) {
|
2011-04-22 22:03:10 +00:00
|
|
|
var item = that.items[position],
|
2012-01-02 19:06:36 +00:00
|
|
|
group = item.options('group'),
|
2011-04-22 22:03:10 +00:00
|
|
|
menu = self.options.mainmenu || self.options.parent || that,
|
2012-01-02 19:06:36 +00:00
|
|
|
offset,
|
2011-04-22 22:03:10 +00:00
|
|
|
toggled;
|
|
|
|
that.hideMenu();
|
|
|
|
if (!item.options('items').length) {
|
2011-09-17 23:25:22 +00:00
|
|
|
if (self.options.parent) {
|
2012-06-06 19:39:43 +00:00
|
|
|
self.options.parent.hideMenu(true).triggerEvent('click', Ox.extend({
|
|
|
|
id: item.options('id'),
|
2012-08-13 13:20:07 +00:00
|
|
|
title: parseTitle(item.options('title')[0])
|
2012-06-06 19:39:43 +00:00
|
|
|
}, files ? {files: files} : {}));
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
if (item.options('checked') !== null) {
|
2012-01-02 19:06:36 +00:00
|
|
|
if (group) {
|
|
|
|
offset = self.optionGroupOffset[group];
|
|
|
|
toggled = self.optionGroup[group].toggle(position - offset);
|
2011-04-22 22:03:10 +00:00
|
|
|
if (toggled.length) {
|
|
|
|
toggled.forEach(function(pos) {
|
2012-01-02 19:06:36 +00:00
|
|
|
that.items[pos + offset].toggleChecked();
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
menu.triggerEvent('change', {
|
|
|
|
id: item.options('group'),
|
2012-01-02 19:06:36 +00:00
|
|
|
checked: self.optionGroup[group].checked().map(function(pos) {
|
2011-04-22 22:03:10 +00:00
|
|
|
return {
|
2012-01-02 19:06:36 +00:00
|
|
|
id: that.items[pos + offset].options('id'),
|
|
|
|
title: Ox.isString(that.items[pos + offset].options('title')[0])
|
2012-08-13 13:20:07 +00:00
|
|
|
? parseTitle(that.items[pos + offset].options('title')[0]) : ''
|
2011-04-22 22:03:10 +00:00
|
|
|
};
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
item.toggleChecked();
|
|
|
|
menu.triggerEvent('change', {
|
|
|
|
checked: item.options('checked'),
|
|
|
|
id: item.options('id'),
|
2011-11-02 19:47:19 +00:00
|
|
|
title: Ox.isString(item.options('title')[0])
|
2012-08-13 13:20:07 +00:00
|
|
|
? parseTitle(item.options('title')[0]) : ''
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
2012-06-27 09:49:13 +00:00
|
|
|
// if item.options.files && !files, the click happened outside
|
|
|
|
// the title - as a workaround, we don't trigger a click event.
|
|
|
|
if (!item.options('file') || files) {
|
|
|
|
menu.triggerEvent('click', Ox.extend({
|
|
|
|
id: item.options('id'),
|
2012-08-13 13:20:07 +00:00
|
|
|
title: parseTitle(item.options('title')[0])
|
2012-06-27 09:49:13 +00:00
|
|
|
}, files ? {files: files} : {}));
|
|
|
|
}
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
if (item.options('title').length == 2) {
|
|
|
|
item.toggleTitle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-01 14:16:27 +00:00
|
|
|
function clickLayer() {
|
|
|
|
that.hideMenu();
|
|
|
|
}
|
|
|
|
|
2011-04-22 22:03:10 +00:00
|
|
|
function clickSelectedItem() {
|
|
|
|
// called on key.enter
|
|
|
|
if (self.options.selected > -1) {
|
|
|
|
clickItem(self.options.selected);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-21 13:42:47 +00:00
|
|
|
function getElement(id) {
|
|
|
|
// fixme: needed?
|
|
|
|
return $('#' + Ox.toCamelCase(options.id + '/' + id));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getItemPositionById(id) {
|
|
|
|
// fixme: this exists in ox.js by now
|
|
|
|
var position;
|
|
|
|
Ox.forEach(that.items, function(item, i) {
|
|
|
|
if (item.options('id') == id) {
|
|
|
|
position = i;
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-12-21 13:42:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
|
|
|
function hideMenu() {
|
2014-01-16 05:14:41 +00:00
|
|
|
// called on key.escape
|
2011-12-21 13:42:47 +00:00
|
|
|
that.hideMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
function isFirstEnabledItem() {
|
|
|
|
var ret = true;
|
|
|
|
Ox.forEach(that.items, function(item, i) {
|
|
|
|
if (i < self.options.selected && !item.options('disabled')) {
|
2012-05-22 07:11:26 +00:00
|
|
|
ret = false;
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-12-21 13:42:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isLastEnabledItem() {
|
|
|
|
var ret = true;
|
|
|
|
Ox.forEach(that.items, function(item, i) {
|
|
|
|
if (i > self.options.selected && !item.options('disabled')) {
|
2012-05-22 07:11:26 +00:00
|
|
|
ret = false;
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-12-21 13:42:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
function mouseenter() {
|
|
|
|
that.gainFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
function mouseleave() {
|
2012-06-27 09:49:13 +00:00
|
|
|
if (
|
|
|
|
self.options.selected > -1
|
|
|
|
&& !that.items[self.options.selected].options('items').length
|
|
|
|
) {
|
2011-12-21 13:42:47 +00:00
|
|
|
selectItem(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function mousemove(event) {
|
|
|
|
var item,
|
|
|
|
position,
|
|
|
|
$target = $(event.target),
|
2012-03-24 10:13:33 +00:00
|
|
|
$parent = $target.parent(),
|
|
|
|
$grandparent = $parent.parent();
|
2011-12-21 13:42:47 +00:00
|
|
|
if ($parent.is('.OxCell')) {
|
|
|
|
$target = $parent;
|
|
|
|
$parent = $target.parent();
|
2012-03-24 10:13:33 +00:00
|
|
|
} else if ($grandparent.is('.OxCell')) {
|
|
|
|
$target = $grandparent;
|
|
|
|
$parent = $target.parent();
|
2011-12-21 13:42:47 +00:00
|
|
|
}
|
|
|
|
if ($target.is('.OxCell')) {
|
|
|
|
position = $parent.data('position');
|
|
|
|
item = that.items[position];
|
|
|
|
if (!item.options('disabled') && position != self.options.selected) {
|
|
|
|
selectItem(position);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mouseleave();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function mousewheel(e, delta, deltaX, deltaY) {
|
|
|
|
var $scrollbar;
|
|
|
|
if (deltaY && !$(e.target).is('.OxScrollbar')) {
|
|
|
|
$scrollbar = that.$scrollbars[deltaY < 0 ? 'down' : 'up'];
|
|
|
|
Ox.loop(0, Math.abs(deltaY), function() {
|
|
|
|
if ($scrollbar.is(':visible')) {
|
|
|
|
$scrollbar.trigger('mouseenter').trigger('mouseleave');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-13 13:20:07 +00:00
|
|
|
function parseTitle(title) {
|
|
|
|
return Ox.decodeHTMLEntities(Ox.stripTags(title));
|
|
|
|
}
|
|
|
|
|
2011-12-21 13:42:47 +00:00
|
|
|
function renderItems(items) {
|
2011-04-22 22:03:10 +00:00
|
|
|
|
2012-01-02 19:06:36 +00:00
|
|
|
var offset = 0;
|
|
|
|
|
2011-04-22 22:03:10 +00:00
|
|
|
that.$content.empty();
|
|
|
|
scrollMenuUp();
|
|
|
|
|
2012-01-02 19:06:36 +00:00
|
|
|
self.optionGroup = {};
|
|
|
|
self.optionGroupOffset = {};
|
2011-04-22 22:03:10 +00:00
|
|
|
items.forEach(function(item, i) {
|
|
|
|
if (item.group) {
|
2011-11-10 09:54:33 +00:00
|
|
|
items[i] = item.items.map(function(v) {
|
2011-09-09 10:41:13 +00:00
|
|
|
return Ox.extend(v, {
|
2011-04-22 22:03:10 +00:00
|
|
|
group: item.group
|
|
|
|
});
|
|
|
|
});
|
2012-01-02 19:06:36 +00:00
|
|
|
self.optionGroup[item.group] = new Ox.OptionGroup(
|
2011-11-10 09:54:33 +00:00
|
|
|
items[i].filter(function(v) {
|
|
|
|
return 'id' in v;
|
|
|
|
}),
|
2011-04-22 22:03:10 +00:00
|
|
|
'min' in item ? item.min : 1,
|
|
|
|
'max' in item ? item.max : 1
|
|
|
|
);
|
2012-01-03 07:35:22 +00:00
|
|
|
self.optionGroupOffset[item.group] = offset;
|
|
|
|
offset += items[i].length;
|
|
|
|
} else if ('id' in item) {
|
|
|
|
offset += 1;
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
items = Ox.flatten(items);
|
|
|
|
|
|
|
|
that.items = [];
|
|
|
|
items.forEach(function(item) {
|
|
|
|
var position;
|
2011-12-22 15:47:46 +00:00
|
|
|
if ('id' in item) {
|
2011-11-04 22:14:30 +00:00
|
|
|
that.items.push(
|
2012-06-17 17:11:23 +00:00
|
|
|
Ox.MenuItem(Ox.extend(Ox.clone(item), {
|
2011-11-04 22:14:30 +00:00
|
|
|
maxWidth: self.options.maxWidth,
|
|
|
|
menu: that,
|
|
|
|
position: position = that.items.length
|
|
|
|
}))
|
|
|
|
.data('position', position)
|
|
|
|
.appendTo(that.$content)
|
|
|
|
); // fixme: jquery bug when passing {position: position}? does not return the object?;
|
2011-04-22 22:03:10 +00:00
|
|
|
if (item.items) {
|
2011-06-19 17:48:32 +00:00
|
|
|
that.submenus[item.id] = Ox.Menu({
|
2014-01-16 07:07:21 +00:00
|
|
|
edge: 'right',
|
2011-04-22 22:03:10 +00:00
|
|
|
element: that.items[position],
|
|
|
|
id: Ox.toCamelCase(self.options.id + '/' + item.id),
|
|
|
|
items: item.items,
|
|
|
|
mainmenu: self.options.mainmenu,
|
|
|
|
offset: {
|
|
|
|
left: 0,
|
|
|
|
top: -4
|
|
|
|
},
|
|
|
|
parent: that,
|
2012-05-26 15:48:19 +00:00
|
|
|
size: self.options.size
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
2011-12-21 13:42:47 +00:00
|
|
|
that.$content.append(renderSpace());
|
|
|
|
that.$content.append(renderLine());
|
|
|
|
that.$content.append(renderSpace());
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!that.is(':hidden')) {
|
|
|
|
that.hideMenu();
|
|
|
|
that.showMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-12-21 13:42:47 +00:00
|
|
|
function renderLine() {
|
2011-04-22 22:03:10 +00:00
|
|
|
return $('<tr>').append(
|
|
|
|
$('<td>', {
|
|
|
|
'class': 'OxLine',
|
|
|
|
colspan: 5
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2011-12-21 13:42:47 +00:00
|
|
|
function renderScrollbar(direction) {
|
2011-04-22 22:03:10 +00:00
|
|
|
var interval,
|
|
|
|
speed = direction == 'up' ? -1 : 1;
|
|
|
|
return $('<div/>', {
|
|
|
|
'class': 'OxScrollbar Ox' + Ox.toTitleCase(direction),
|
|
|
|
html: Ox.UI.symbols['triangle_' + direction],
|
|
|
|
click: function() { // fixme: do we need to listen to click event?
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
mousedown: function() {
|
2011-11-11 10:24:49 +00:00
|
|
|
self.scrollSpeed = 2;
|
2011-04-22 22:03:10 +00:00
|
|
|
return false;
|
|
|
|
},
|
|
|
|
mouseenter: function() {
|
2011-11-11 10:24:49 +00:00
|
|
|
self.scrollSpeed = 1;
|
2011-04-22 22:03:10 +00:00
|
|
|
var $otherScrollbar = that.$scrollbars[direction == 'up' ? 'down' : 'up'];
|
|
|
|
$(this).addClass('OxSelected');
|
|
|
|
if ($otherScrollbar.is(':hidden')) {
|
|
|
|
$otherScrollbar.show();
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(that.$container.height() - self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
if (direction == 'down') {
|
|
|
|
that.$content.css({
|
2011-11-11 10:24:49 +00:00
|
|
|
top: -self.itemHeight + 'px'
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
scrollMenu(speed);
|
|
|
|
interval = setInterval(function() {
|
|
|
|
scrollMenu(speed);
|
|
|
|
}, 100);
|
|
|
|
},
|
|
|
|
mouseleave: function() {
|
2011-11-11 10:24:49 +00:00
|
|
|
self.scrollSpeed = 1;
|
2011-04-22 22:03:10 +00:00
|
|
|
$(this).removeClass('OxSelected');
|
|
|
|
clearInterval(interval);
|
|
|
|
},
|
|
|
|
mouseup: function() {
|
2011-11-11 10:24:49 +00:00
|
|
|
self.scrollSpeed = 1;
|
2011-04-22 22:03:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2011-12-21 13:42:47 +00:00
|
|
|
function renderSpace() {
|
2011-04-22 22:03:10 +00:00
|
|
|
return $('<tr>').append(
|
2011-09-09 10:41:13 +00:00
|
|
|
$('<td>', {'class': 'OxSpace', colspan: 5})
|
2011-04-22 22:03:10 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function scrollMenu(speed) {
|
|
|
|
var containerHeight = that.$container.height(),
|
|
|
|
contentHeight = that.$content.height(),
|
2012-06-13 08:28:21 +00:00
|
|
|
top = parseInt(that.$content.css('top'), 10) || 0,
|
2011-11-11 10:24:49 +00:00
|
|
|
min = containerHeight - contentHeight + self.itemHeight,
|
2011-04-22 22:03:10 +00:00
|
|
|
max = 0;
|
2011-11-11 10:24:49 +00:00
|
|
|
top += speed * self.scrollSpeed * -self.itemHeight;
|
2011-04-22 22:03:10 +00:00
|
|
|
if (top <= min) {
|
|
|
|
top = min;
|
|
|
|
that.$scrollbars.down.hide().trigger('mouseleave');
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(containerHeight + self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
that.items[that.items.length - 1].trigger('mouseover');
|
2011-11-11 10:24:49 +00:00
|
|
|
} else if (top >= max - self.itemHeight) {
|
2011-04-22 22:03:10 +00:00
|
|
|
top = max;
|
|
|
|
that.$scrollbars.up.hide().trigger('mouseleave');
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(containerHeight + self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
that.items[0].trigger('mouseover');
|
|
|
|
}
|
|
|
|
that.$content.css({
|
|
|
|
top: top + 'px'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function scrollMenuUp() {
|
|
|
|
if (that.$scrollbars.up.is(':visible')) {
|
|
|
|
that.$content.css({
|
|
|
|
top: '0px'
|
|
|
|
});
|
|
|
|
that.$scrollbars.up.hide();
|
|
|
|
if (that.$scrollbars.down.is(':hidden')) {
|
|
|
|
that.$scrollbars.down.show();
|
|
|
|
} else {
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(that.$container.height() + self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function selectItem(position) {
|
|
|
|
var item;
|
|
|
|
if (self.options.selected > -1) {
|
2012-03-24 11:04:27 +00:00
|
|
|
item = that.items[self.options.selected];
|
|
|
|
if (item) {
|
|
|
|
item.removeClass('OxSelected');
|
|
|
|
if (item.options('file')) {
|
|
|
|
item.$button.blurButton();
|
|
|
|
that.bindEvent({key_enter: clickSelectedItem})
|
|
|
|
}
|
|
|
|
}
|
2011-04-22 22:03:10 +00:00
|
|
|
/* disabled
|
|
|
|
that.triggerEvent('deselect', {
|
|
|
|
id: item.options('id'),
|
2012-08-13 13:20:07 +00:00
|
|
|
title: Ox.parseTitle(item.options('title')[0])
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
if (position > -1) {
|
|
|
|
item = that.items[position];
|
|
|
|
Ox.forEach(that.submenus, function(submenu, id) {
|
|
|
|
if (!submenu.is(':hidden')) {
|
|
|
|
submenu.hideMenu();
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
});
|
2012-03-24 11:04:27 +00:00
|
|
|
item.options('items').length && that.submenus[item.options('id')].showMenu();
|
2011-04-22 22:03:10 +00:00
|
|
|
item.addClass('OxSelected');
|
2012-03-24 11:04:27 +00:00
|
|
|
if (item.options('file')) {
|
|
|
|
item.$button.focusButton();
|
|
|
|
that.unbindEvent('key_enter');
|
|
|
|
}
|
2011-04-22 22:03:10 +00:00
|
|
|
that.triggerEvent('select', {
|
|
|
|
id: item.options('id'),
|
2011-11-02 19:47:19 +00:00
|
|
|
title: Ox.isString(item.options('title')[0])
|
2012-08-13 13:20:07 +00:00
|
|
|
? parseTitle(item.options('title')[0]) : ''
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
self.options.selected = position;
|
|
|
|
}
|
|
|
|
|
|
|
|
function selectNextItem() {
|
|
|
|
var offset,
|
|
|
|
selected = self.options.selected;
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'sNI', selected)
|
2011-04-22 22:03:10 +00:00
|
|
|
if (!isLastEnabledItem()) {
|
|
|
|
if (selected == -1) {
|
|
|
|
scrollMenuUp();
|
|
|
|
} else {
|
|
|
|
that.items[selected].removeClass('OxSelected');
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
selected++;
|
|
|
|
} while (that.items[selected].options('disabled'))
|
|
|
|
selectItem(selected);
|
2011-11-11 10:24:49 +00:00
|
|
|
offset = that.items[selected].offset().top + self.itemHeight -
|
2011-04-22 22:03:10 +00:00
|
|
|
that.$container.offset().top - that.$container.height();
|
|
|
|
if (offset > 0) {
|
|
|
|
if (that.$scrollbars.up.is(':hidden')) {
|
|
|
|
that.$scrollbars.up.show();
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(that.$container.height() - self.itemHeight);
|
|
|
|
offset += self.itemHeight;
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
if (selected == that.items.length - 1) {
|
|
|
|
that.$scrollbars.down.hide();
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(that.$container.height() + self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
} else {
|
|
|
|
that.$content.css({
|
2012-06-13 08:28:21 +00:00
|
|
|
top: ((parseInt(that.$content.css('top'), 10) || 0) - offset) + 'px'
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function selectPreviousItem() {
|
|
|
|
var offset,
|
|
|
|
selected = self.options.selected;
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'sPI', selected)
|
2011-04-22 22:03:10 +00:00
|
|
|
if (selected > - 1) {
|
|
|
|
if (!isFirstEnabledItem()) {
|
|
|
|
that.items[selected].removeClass('OxSelected');
|
|
|
|
do {
|
|
|
|
selected--;
|
|
|
|
} while (that.items[selected].options('disabled'))
|
|
|
|
selectItem(selected);
|
|
|
|
}
|
|
|
|
offset = that.items[selected].offset().top - that.$container.offset().top;
|
|
|
|
if (offset < 0) {
|
|
|
|
if (that.$scrollbars.down.is(':hidden')) {
|
|
|
|
that.$scrollbars.down.show();
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(that.$container.height() - self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
if (selected == 0) {
|
|
|
|
that.$scrollbars.up.hide();
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(that.$container.height() + self.itemHeight);
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
that.$content.css({
|
2012-06-13 08:28:21 +00:00
|
|
|
top: ((parseInt(that.$content.css('top'), 10) || 0) - offset) + 'px'
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function selectSubmenu() {
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'selectSubmenu', self.options.selected)
|
2011-04-22 22:03:10 +00:00
|
|
|
if (self.options.selected > -1) {
|
|
|
|
var submenu = that.submenus[that.items[self.options.selected].options('id')];
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'submenu', submenu, that.submenus);
|
2011-04-22 22:03:10 +00:00
|
|
|
if (submenu && submenu.hasEnabledItems()) {
|
|
|
|
submenu.gainFocus();
|
|
|
|
submenu.selectFirstItem();
|
|
|
|
} else if (self.options.mainmenu) {
|
|
|
|
self.options.mainmenu.selectNextMenu();
|
|
|
|
}
|
|
|
|
} else if (self.options.mainmenu) {
|
|
|
|
self.options.mainmenu.selectNextMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function selectSupermenu() {
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'selectSupermenu', self.options.selected)
|
2011-04-22 22:03:10 +00:00
|
|
|
if (self.options.parent) {
|
|
|
|
self.options.selected > -1 && that.items[self.options.selected].trigger('mouseleave');
|
|
|
|
scrollMenuUp();
|
|
|
|
self.options.parent.gainFocus();
|
|
|
|
} else if (self.options.mainmenu) {
|
|
|
|
self.options.mainmenu.selectPreviousMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
|
|
|
addItem <f>
|
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.addItem = function(item, position) {
|
2011-11-03 15:42:41 +00:00
|
|
|
|
2011-04-22 22:03:10 +00:00
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
|
|
|
addItemAfter <f>
|
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.addItemAfter = function(item, id) {
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-21 10:38:18 +00:00
|
|
|
addItemBefore <f> addItemBefore
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.addItemBefore = function(item, id) {
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-21 10:38:18 +00:00
|
|
|
checkItem <f> checkItem
|
|
|
|
(id, checked) -> <u> check item, checked can be undefined/true or false
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2012-04-18 07:57:59 +00:00
|
|
|
that.checkItem = function(id, checked) {
|
2011-11-04 15:54:28 +00:00
|
|
|
Ox.Log('Menu', 'checkItem id', id)
|
2012-01-02 19:06:36 +00:00
|
|
|
var group,
|
|
|
|
ids = id.split('_'),
|
|
|
|
item,
|
|
|
|
offset,
|
|
|
|
position,
|
|
|
|
toggled;
|
2012-04-18 07:57:59 +00:00
|
|
|
checked = Ox.isUndefined(checked) ? true : checked;
|
2011-09-17 23:25:22 +00:00
|
|
|
if (ids.length == 1) {
|
|
|
|
item = that.getItem(id);
|
2012-01-02 19:06:36 +00:00
|
|
|
group = item.options('group');
|
|
|
|
if (group) {
|
|
|
|
offset = self.optionGroupOffset[group];
|
|
|
|
position = getItemPositionById(id);
|
|
|
|
toggled = self.optionGroup[item.options('group')].toggle(position - offset);
|
2011-09-17 23:25:22 +00:00
|
|
|
if (toggled.length) {
|
|
|
|
toggled.forEach(function(pos) {
|
2012-01-02 19:06:36 +00:00
|
|
|
that.items[pos + offset].toggleChecked();
|
2011-09-17 23:25:22 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
2012-04-18 07:57:59 +00:00
|
|
|
item.options({checked: checked});
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
} else {
|
2011-09-17 23:25:22 +00:00
|
|
|
that.submenus[ids.shift()].checkItem(ids.join('_'));
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-05-21 10:38:18 +00:00
|
|
|
/*@
|
|
|
|
clickItem <f> clickItem
|
2012-06-27 09:49:13 +00:00
|
|
|
(position, files) -> <o> click item at position
|
2012-05-21 10:38:18 +00:00
|
|
|
@*/
|
2012-03-24 10:13:33 +00:00
|
|
|
that.clickItem = function(position, files) {
|
|
|
|
clickItem(position, files);
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-21 10:38:18 +00:00
|
|
|
getItem <f> getItem
|
|
|
|
(id) -> <o> get item
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.getItem = function(id) {
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'getItem id', id)
|
2011-04-22 22:03:10 +00:00
|
|
|
var ids = id.split('_'),
|
|
|
|
item;
|
|
|
|
if (ids.length == 1) {
|
|
|
|
Ox.forEach(that.items, function(v) {
|
|
|
|
if (v.options('id') == id) {
|
|
|
|
item = v;
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
if (!item) {
|
|
|
|
Ox.forEach(that.submenus, function(submenu) {
|
|
|
|
item = submenu.getItem(id);
|
2012-07-05 08:58:08 +00:00
|
|
|
if (item) {
|
|
|
|
return false; // break
|
|
|
|
}
|
2011-04-22 22:03:10 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
item = that.submenus[ids.shift()].getItem(ids.join('_'));
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-21 10:38:18 +00:00
|
|
|
getSubmenu <f> getSubmenu
|
|
|
|
(id) -> <o> get submenu by id
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.getSubmenu = function(id) {
|
|
|
|
var ids = id.split('_'),
|
|
|
|
submenu;
|
|
|
|
if (ids.length == 1) {
|
|
|
|
submenu = that.submenus[id];
|
|
|
|
} else {
|
|
|
|
submenu = that.submenus[ids.shift()].getSubmenu(ids.join('_'));
|
|
|
|
}
|
2011-11-04 15:54:28 +00:00
|
|
|
//Ox.Log('Menu', 'getSubmenu', id, submenu);
|
2011-04-22 22:03:10 +00:00
|
|
|
return submenu;
|
|
|
|
}
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-21 10:38:18 +00:00
|
|
|
hasEnabledItems <f> hasEditableItems
|
|
|
|
() -> <b> menu has editable items
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.hasEnabledItems = function() {
|
|
|
|
var ret = false;
|
|
|
|
Ox.forEach(that.items, function(item) {
|
|
|
|
if (!item.options('disabled')) {
|
2012-05-22 07:11:26 +00:00
|
|
|
ret = true;
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2012-05-21 10:38:18 +00:00
|
|
|
hideMenu <f> hideMenu
|
2011-05-16 08:24:46 +00:00
|
|
|
() -> <f> Menu Object
|
|
|
|
@*/
|
2011-09-17 23:25:22 +00:00
|
|
|
that.hideMenu = function(hideParent) {
|
2011-04-22 22:03:10 +00:00
|
|
|
if (that.is(':hidden')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Ox.forEach(that.submenus, function(submenu) {
|
|
|
|
if (submenu.is(':visible')) {
|
|
|
|
submenu.hideMenu();
|
2012-07-05 08:58:08 +00:00
|
|
|
return false; // break
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
selectItem(-1);
|
|
|
|
scrollMenuUp();
|
|
|
|
that.$scrollbars.up.is(':visible') && that.$scrollbars.up.hide();
|
|
|
|
that.$scrollbars.down.is(':visible') && that.$scrollbars.down.hide();
|
|
|
|
if (self.options.parent) {
|
|
|
|
//self.options.element.removeClass('OxSelected');
|
|
|
|
self.options.parent.options({
|
|
|
|
selected: -1
|
|
|
|
});
|
2011-09-17 23:25:22 +00:00
|
|
|
hideParent && self.options.parent.hideMenu(true);
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
2011-11-01 14:16:27 +00:00
|
|
|
that.$layer && that.$layer.hide();
|
2011-09-17 23:25:22 +00:00
|
|
|
that.hide().loseFocus().triggerEvent('hide');
|
2011-04-22 22:03:10 +00:00
|
|
|
return that;
|
|
|
|
};
|
|
|
|
|
2012-05-21 10:38:18 +00:00
|
|
|
/*@
|
|
|
|
remove <f> remove
|
|
|
|
() -> <o> remove menu
|
|
|
|
@*/
|
2011-11-04 22:14:30 +00:00
|
|
|
self.superRemove = that.remove;
|
|
|
|
that.remove = function() {
|
|
|
|
Ox.forEach(that.submenus, function(submenu) {
|
|
|
|
submenu.remove();
|
|
|
|
});
|
|
|
|
self.superRemove();
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2011-05-16 10:49:48 +00:00
|
|
|
removeItem <f> removeItem
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.removeItem = function() {
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2011-05-16 10:49:48 +00:00
|
|
|
selectFirstItem <f> selectFirstItem
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.selectFirstItem = function() {
|
|
|
|
selectNextItem();
|
2012-02-15 17:29:58 +00:00
|
|
|
return that;
|
|
|
|
};
|
|
|
|
|
2014-05-12 23:47:09 +00:00
|
|
|
that.setItemKeyboard = function(id, keyboard) {
|
|
|
|
var item = that.getItem(id);
|
|
|
|
item && item.options({keyboard: keyboard});
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
|
2012-05-21 10:38:18 +00:00
|
|
|
/*@
|
|
|
|
setItemTitle <f> setItemTitle
|
|
|
|
(id, title) -> <o> set item title
|
|
|
|
@*/
|
2012-02-15 17:29:58 +00:00
|
|
|
that.setItemTitle = function(id, title) {
|
2012-08-30 20:25:32 +00:00
|
|
|
var item = that.getItem(id);
|
2012-02-15 17:29:58 +00:00
|
|
|
item && item.options({title: title});
|
|
|
|
return that;
|
2011-04-22 22:03:10 +00:00
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2011-05-16 10:49:48 +00:00
|
|
|
showMenu <f> showMenu
|
2011-05-16 08:24:46 +00:00
|
|
|
() -> <f> Menu Object
|
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.showMenu = function() {
|
|
|
|
if (!that.is(':hidden')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
that.parent().length == 0 && that.appendTo(Ox.UI.$body);
|
|
|
|
that.css({
|
|
|
|
left: '-1000px',
|
2012-05-26 15:48:19 +00:00
|
|
|
top: '-1000px'
|
2011-04-22 22:03:10 +00:00
|
|
|
}).show();
|
|
|
|
var offset = self.options.element.offset(),
|
|
|
|
width = self.options.element.outerWidth(),
|
|
|
|
height = self.options.element.outerHeight(),
|
|
|
|
left = Ox.limit(
|
2014-01-16 07:07:21 +00:00
|
|
|
offset.left + self.options.offset.left + (self.options.edge == 'bottom' ? 0 : width),
|
2011-04-22 22:03:10 +00:00
|
|
|
0, Ox.UI.$window.width() - that.width()
|
|
|
|
),
|
2014-01-16 07:07:21 +00:00
|
|
|
top = offset.top + self.options.offset.top + (self.options.edge == 'bottom' ? height : 0),
|
2011-11-05 17:27:11 +00:00
|
|
|
menuHeight = that.$content.outerHeight(), // fixme: why is outerHeight 0 when hidden?
|
2011-04-22 22:03:10 +00:00
|
|
|
menuMaxHeight = Math.floor(Ox.UI.$window.height() - top - 16);
|
|
|
|
if (self.options.parent) {
|
|
|
|
if (menuHeight > menuMaxHeight) {
|
|
|
|
top = Ox.limit(top - menuHeight + menuMaxHeight, self.options.parent.offset().top, top);
|
|
|
|
menuMaxHeight = Math.floor(Ox.UI.$window.height() - top - 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
that.css({
|
|
|
|
left: left + 'px',
|
|
|
|
top: top + 'px'
|
|
|
|
});
|
|
|
|
if (menuHeight > menuMaxHeight) {
|
2011-11-11 10:24:49 +00:00
|
|
|
that.$container.height(menuMaxHeight - self.itemHeight - 8); // margin
|
2011-04-22 22:03:10 +00:00
|
|
|
that.$scrollbars.down.show();
|
|
|
|
} else {
|
|
|
|
that.$container.height(menuHeight);
|
|
|
|
}
|
|
|
|
if (!self.options.parent) {
|
|
|
|
that.gainFocus();
|
2011-11-01 14:16:27 +00:00
|
|
|
that.$layer = Ox.Layer({type: 'menu'})
|
|
|
|
.css({top: self.options.mainmenu ? '20px' : 0})
|
|
|
|
.bindEvent({click: clickLayer})
|
|
|
|
.show();
|
2011-04-22 22:03:10 +00:00
|
|
|
}
|
|
|
|
return that;
|
|
|
|
//that.triggerEvent('show');
|
|
|
|
};
|
|
|
|
|
2011-05-16 08:24:46 +00:00
|
|
|
/*@
|
2011-09-09 10:41:13 +00:00
|
|
|
toggleMenu <f> toggleMenu
|
2011-05-16 08:24:46 +00:00
|
|
|
@*/
|
2011-04-22 22:03:10 +00:00
|
|
|
that.toggleMenu = function() {
|
2011-09-09 10:41:13 +00:00
|
|
|
return that.is(':hidden') ? that.showMenu() : that.hideMenu();
|
2011-04-22 22:03:10 +00:00
|
|
|
};
|
|
|
|
|
2012-05-21 10:38:18 +00:00
|
|
|
/*@
|
|
|
|
uncheckItem <f> uncheckItem
|
|
|
|
(id) -> <o> uncheck item
|
|
|
|
@*/
|
2012-04-18 07:57:59 +00:00
|
|
|
that.uncheckItem = function(id) {
|
|
|
|
that.checkItem(id, false);
|
|
|
|
};
|
|
|
|
|
2011-04-22 22:03:10 +00:00
|
|
|
return that;
|
|
|
|
|
|
|
|
};
|