openmedialibrary/static/js/infoView.js

721 lines
26 KiB
JavaScript
Raw Normal View History

2014-05-04 17:26:43 +00:00
'use strict';
2014-05-14 18:46:31 +00:00
oml.ui.infoView = function(identifyData) {
2014-05-04 17:26:43 +00:00
var ui = oml.user.ui,
2014-05-21 00:02:39 +00:00
iconSize = identifyData ? 256 : ui.iconSize,
2014-05-19 20:58:00 +00:00
2014-05-21 00:02:39 +00:00
css = getCSS(iconSize, oml.config.iconRatio),
2014-05-19 15:00:33 +00:00
2014-05-25 12:16:04 +00:00
ids = [
{key: 'isbn', url: 'https://google.com/search?q=ISBN+{0}'},
{key: 'asin', url: 'http://www.amazon.com/dp/{0}'},
{key: 'lccn', url: 'http://lccn.loc.gov/{0}'},
{key: 'oclc', url: 'https://www.worldcat.org/oclc/{0}'},
{key: 'olid', url: 'https://openlibrary.org/books/{0}'}
],
2014-05-04 17:26:43 +00:00
that = Ox.Element()
.addClass('OxTextPage')
.css({overflowY: 'auto'})
.bindEvent({
2014-05-21 00:02:39 +00:00
oml_icons: function() {
that.updateElement(ui.item, [$icon])
},
2014-05-04 17:26:43 +00:00
oml_item: function() {
if (ui.item) {
2014-05-17 11:45:57 +00:00
that.updateElement(ui.item);
2014-05-04 17:26:43 +00:00
}
},
oml_listselection: function(data) {
if (data.value && data.value.length) {
2014-05-17 11:45:57 +00:00
that.updateElement(data.value[0]);
2014-05-04 17:26:43 +00:00
}
}
}),
2014-05-21 00:02:39 +00:00
$icon = Ox.Element()
2014-05-04 17:26:43 +00:00
.css({
position: 'absolute',
left: '16px',
top: '16px',
2014-05-21 00:02:39 +00:00
width: css.icon.width
2014-05-04 17:26:43 +00:00
})
.appendTo(that),
$info = Ox.Element()
.addClass('OxSelectable')
.css({
position: 'absolute',
2014-05-19 15:00:33 +00:00
left: css.info.left,
2014-05-14 18:46:31 +00:00
right: !identifyData ? '176px' : 16 + Ox.UI.SCROLLBAR_SIZE + 'px',
2014-05-04 17:26:43 +00:00
top: '16px'
})
2014-05-21 00:02:39 +00:00
[iconSize == 512 ? 'hide' : 'show']()
2014-05-04 17:26:43 +00:00
.appendTo(that),
2014-05-14 23:28:49 +00:00
$data,
$image, $reflection, $reflectionImage;
2014-05-14 18:46:31 +00:00
if (!identifyData) {
2014-05-04 17:26:43 +00:00
$data = Ox.Element()
.addClass('OxSelectable')
.css({
position: 'absolute',
right: '16px',
top: '16px',
width: '128px'
})
.appendTo(that);
2014-05-14 18:46:31 +00:00
}
2014-05-04 17:26:43 +00:00
2014-05-19 15:00:33 +00:00
function getCSS(size, ratio) {
var width = Math.round(ratio >= 1 ? size : size * ratio),
height = Math.round(ratio <= 1 ? size : size / ratio),
left = size == 256 ? Math.floor((size - width) / 2) : 0;
return {
2014-05-21 00:02:39 +00:00
icon: {
2014-05-19 15:00:33 +00:00
width: size + 'px'
},
info: {
left: (size == 256 ? size + 32 : width + 48) + 'px'
},
image: {
left: left + 'px',
width: width + 'px',
height: height + 'px'
},
reflection: {
top: height + 'px'
}
};
}
2014-05-21 00:02:39 +00:00
function getIconTooltip() {
return !identifyData
? 'Click to see ' + (ui.iconSize == 256 ? 'large' : 'small')
+ ' ' + ui.icons + ', doubleclick to see '
+ (ui.icons == 'cover' ? 'preview' : 'cover')
: '';
2014-05-14 23:28:49 +00:00
}
2014-05-21 00:02:39 +00:00
function formatLight(string) {
return '<span class="OxLight">' + string + '</span>';
2014-05-14 09:57:11 +00:00
}
2014-05-16 08:06:11 +00:00
function formatKey(key) {
var item = Ox.getObjectById(oml.config.itemKeys, key);
return '<span style="font-weight: bold">'
2014-05-21 00:02:39 +00:00
+ Ox._(Ox.toTitleCase(key)) + ':</span>&nbsp;';
2014-05-16 08:06:11 +00:00
}
2014-05-21 00:02:39 +00:00
function formatValue(value, key) {
2014-05-19 22:49:47 +00:00
return value ? (Ox.isArray(value) ? value : [value]).map(function(value) {
2014-05-20 00:44:13 +00:00
return key && !identifyData ?
2014-05-21 00:02:39 +00:00
'<a href="/' + key + '==' + (
key == 'date' ? value.slice(0, 4) : value
) + '">' + value + '</a>'
2014-05-14 09:57:11 +00:00
: value;
2014-05-21 00:02:39 +00:00
}).join('; ') : '';
2014-05-14 09:57:11 +00:00
}
2014-05-04 17:26:43 +00:00
function identify(data) {
2014-05-21 00:02:39 +00:00
oml.$ui.identifyDialog = oml.ui.identifyDialog(data).open();
}
function renderIdentifyButton(data) {
return Ox.FormElementGroup({
elements: [
Ox.Button({
disabled: data.mediastate != 'available',
title: Ox._('Identify Book...'),
width: 112
})
.bindEvent({
click: function() {
identify(data);
}
}),
data.mediastate == 'available' && data.primaryid
? Ox.Select({
2014-05-25 12:16:04 +00:00
items: Ox.flatten(ids.map(function(id) {
return (data[id.key] || []).map(function(value) {
2014-05-21 00:02:39 +00:00
return {
2014-05-25 12:16:04 +00:00
id: id.key + ':' + value,
2014-05-21 00:02:39 +00:00
title: '<b>' + Ox.getObjectById(
2014-05-25 12:16:04 +00:00
oml.config.itemKeys, id.key
2014-05-21 00:02:39 +00:00
).title + ':</b> ' + value
};
});
})).concat([
{id: '', title: '<b>No ID</b>'}
]),
max: 1,
min: 1,
overlap: 'left',
title: 'select',
tooltip: Ox._('Set Primary ID'),
type: 'image',
value: data.primaryid.join(':')
})
.bindEvent({
click: function(data) {
2014-05-25 22:38:40 +00:00
// ...
2014-05-21 00:02:39 +00:00
},
change: function(data) {
oml.api.edit({
id: ui.item,
primaryid: data.value ? data.value.split(':') : ''
}, function(result) {
that.updateElement(result.data, [$data]);
});
}
})
: Ox.Button({
disabled: true,
overlap: 'left',
title: 'select',
type: 'image'
})
],
float: 'right'
})
.css({marginTop: '16px'});
2014-05-04 17:26:43 +00:00
}
function renderMediaButton(data) {
2014-05-14 23:28:49 +00:00
2014-05-14 09:57:11 +00:00
function getListItems() {
var items = [];
if (ui._lists) {
items = ui._lists.filter(function(list) {
return list.user == oml.user.preferences.username
&& list.type != 'smart';
}).map(function(list) {
return {
id: list.id,
title: Ox._('Download to {0}', [list.name])
};
});
items.splice(1, 0, [{}]);
}
return items;
}
2014-05-14 23:28:49 +00:00
2014-05-14 09:57:11 +00:00
function setListItems() {
if ($element && ui._lists) {
$element.options({
disabled: false
}).options('elements')[1].options({
items: getListItems()
});
} else {
setTimeout(setListItems, 100);
}
}
if (data.mediastate == 'unavailable' && !ui._lists) {
setListItems();
}
2015-04-20 07:53:20 +00:00
var $element = (data.mediastate == 'unavailable' || Ox.isUndefined(data.mediastate))
2014-05-12 12:57:47 +00:00
? Ox.FormElementGroup({
elements: [
Ox.Button({
title: Ox._('Download Book'),
width: 112
})
.bindEvent({
click: function() {
data.mediastate = 'transferring';
2014-05-17 11:45:57 +00:00
that.updateElement(data, $data);
2014-05-12 12:57:47 +00:00
oml.api.download({id: ui.item}, function(result) {
// ...
});
}
}),
Ox.MenuButton({
2014-05-14 09:57:11 +00:00
disabled: !ui._lists,
items: getListItems(),
2014-05-12 12:57:47 +00:00
overlap: 'left',
2014-05-21 00:02:39 +00:00
title: 'select',
2014-05-12 12:57:47 +00:00
tooltip: Ox._('Download Book to a List'),
type: 'image'
})
.bindEvent({
2014-05-14 09:57:11 +00:00
click: function(data) {
data.mediastate = 'transferring';
2014-05-17 11:45:57 +00:00
that.updateElement(data, $data);
2014-05-14 09:57:11 +00:00
oml.api.download(Ox.extend({
id: ui.item,
}, data.id == ':' ? {} : {
list: data.id.slice(1)
}), function(result) {
// ...
});
2014-05-12 12:57:47 +00:00
}
})
],
float: 'right'
})
: data.mediastate == 'transferring'
2014-05-04 17:26:43 +00:00
? Ox.FormElementGroup({
elements: [
Ox.Button({
title: Ox._('Transferring...'),
width: 112
})
.bindEvent({
click: function() {
oml.UI.set({page: 'transfers'});
}
}),
Ox.Button({
overlap: 'left',
title: 'close',
tooltip: Ox._('Cancel Transfer'),
type: 'image'
})
.bindEvent({
click: function() {
data.mediastate = 'unavailable';
2014-05-17 11:45:57 +00:00
that.updateElement(data, $data);
2014-05-25 14:05:26 +00:00
oml.api.cancelDownloads({ids: [ui.item]}, function() {
2014-05-17 11:45:57 +00:00
that.updateElement(ui.item, $data);
2014-05-04 17:26:43 +00:00
});
}
})
],
float: 'right'
})
: Ox.Button({
2014-05-12 12:57:47 +00:00
title: Ox._('Read Book'),
2014-05-04 17:26:43 +00:00
width: 128
})
.bindEvent({
click: function() {
2014-05-12 12:57:47 +00:00
oml.UI.set({itemView: 'book'});
2014-05-04 17:26:43 +00:00
}
});
2014-05-14 09:57:11 +00:00
return $element;
2014-05-04 17:26:43 +00:00
}
2014-05-19 15:00:33 +00:00
function toggleCoverSize(ratio) {
2014-05-19 20:58:00 +00:00
var css;
2014-05-21 00:02:39 +00:00
iconSize = iconSize == 256 ? 512 : 256,
css = getCSS(iconSize, ratio);
//$icon.animate(css.icon, 250);
2014-05-19 15:00:33 +00:00
$info.animate(css.info, 250);
$image.animate(css.image, 250);
$reflectionImage.animate(css.image, 250);
$reflection.animate(css.reflection, 250);
2014-05-21 00:02:39 +00:00
oml.UI.set({iconSize: iconSize});
2014-05-19 15:00:33 +00:00
}
function updateCover(ratio) {
2014-05-21 00:02:39 +00:00
var css = getCSS(iconSize, ratio);
2014-05-19 15:00:33 +00:00
$image.css(css.image).show();
$reflectionImage.css(css.image);
$reflection.css(css.reflection).show();
2014-05-14 23:28:49 +00:00
}
2014-05-17 11:45:57 +00:00
that.updateElement = function(idOrData, $elements) {
2014-05-04 17:26:43 +00:00
var data = Ox.isObject(idOrData) ? idOrData : null,
2014-05-14 09:57:11 +00:00
id = data ? null : idOrData,
2014-05-04 17:26:43 +00:00
$elements = $elements
? Ox.makeArray($elements)
2014-05-21 00:02:39 +00:00
: [$icon, $info, $data];
2014-05-04 17:26:43 +00:00
(data ? Ox.noop : oml.api.get)({
id: id,
keys: []
}, function(result) {
2014-05-17 14:42:46 +00:00
if (!identifyData && id && id != ui.item) {
2014-05-17 08:17:34 +00:00
return;
}
2014-05-04 17:26:43 +00:00
if (result) {
data = result.data;
}
2014-05-20 00:44:13 +00:00
Ox.print('BOOK DATA', data)
2014-05-21 00:02:39 +00:00
var $div,
isEditable = data.mediastate == 'available' && !identifyData,
2014-05-14 18:46:31 +00:00
src = !identifyData
2014-05-21 00:02:39 +00:00
? '/' + data.id + '/' + ui.icons + '512.jpg?' + data.modified
2014-05-14 18:46:31 +00:00
: data.cover,
2014-05-21 00:02:39 +00:00
ratio = (
ui.icons == 'cover' || identifyData
? data.coverRatio : data.previewRatio
) || oml.config.iconRatio,
size = iconSize,
2014-05-04 17:26:43 +00:00
reflectionSize = Math.round(size / 2);
$elements.forEach(function($element) {
$element.empty();
2014-05-21 00:02:39 +00:00
if ($element == $icon) {
2014-05-04 17:26:43 +00:00
2014-05-19 15:00:33 +00:00
$image = Ox.Element({
element: '<img>',
2014-05-21 00:02:39 +00:00
tooltip: getIconTooltip()
2014-05-19 15:00:33 +00:00
})
2014-05-14 23:28:49 +00:00
.on({
2014-05-19 18:12:02 +00:00
error: function() {
if (size == 512) {
$info.show();
}
},
2014-05-14 23:28:49 +00:00
load: function() {
2014-05-19 15:00:33 +00:00
ratio = $image[0].width / $image[0].height;
updateCover(ratio);
2014-05-19 18:12:02 +00:00
if (size == 512) {
$info.css({
left: getCSS(512, ratio).info.left
}).show();
}
2014-05-14 23:28:49 +00:00
}
})
2014-05-04 17:26:43 +00:00
.attr({src: src})
.css({
2014-05-14 23:28:49 +00:00
position: 'absolute'
2014-05-04 17:26:43 +00:00
})
2014-05-14 23:28:49 +00:00
.hide()
2014-05-19 15:00:33 +00:00
.bindEvent({
2014-05-21 00:02:39 +00:00
doubleclick: function() {
if (!identifyData) {
oml.UI.set({
icons: ui.icons == 'cover'
? 'preview' : 'cover'
});
}
},
2014-05-19 15:00:33 +00:00
singleclick: function() {
2014-05-19 20:58:00 +00:00
if (!identifyData) {
toggleCoverSize(ratio);
}
2014-05-19 15:00:33 +00:00
}
})
2014-05-21 00:02:39 +00:00
.appendTo($icon);
2014-05-04 17:26:43 +00:00
$reflection = $('<div>')
.addClass('OxReflection')
.css({
position: 'absolute',
width: size + 'px',
height: reflectionSize + 'px',
overflow: 'hidden'
})
2014-05-14 23:28:49 +00:00
.hide()
2014-05-21 00:02:39 +00:00
.appendTo($icon);
2014-05-04 17:26:43 +00:00
2014-05-14 23:28:49 +00:00
$reflectionImage = $('<img>')
2014-05-04 17:26:43 +00:00
.attr({src: src})
.css({
2014-05-14 23:28:49 +00:00
position: 'absolute'
2014-05-04 17:26:43 +00:00
})
.appendTo($reflection);
$('<div>')
.css({
position: 'absolute',
width: size + 'px',
height: reflectionSize + 'px'
})
.appendTo($reflection);
} else if ($element == $info) {
2014-05-21 00:02:39 +00:00
// -------- Title
2014-05-04 17:26:43 +00:00
$('<div>')
2014-05-19 20:58:00 +00:00
.css({
2014-05-21 00:02:39 +00:00
marginTop: '-2px'
2014-05-19 20:58:00 +00:00
})
2014-05-21 00:02:39 +00:00
.append(
Ox.EditableContent({
clickLink: oml.clickLink,
editable: isEditable,
tooltip: isEditable ? oml.getEditTooltip() : '',
value: data.title || 'No Title'
})
.css({
fontWeight: 'bold',
fontSize: '13px'
})
.bindEvent({
submit: function(event) {
editMetadata('title', event.value);
}
})
2014-05-14 09:57:11 +00:00
)
2014-05-04 17:26:43 +00:00
.appendTo($info);
2014-05-21 00:02:39 +00:00
// -------- Author
2014-05-14 09:57:11 +00:00
2014-05-21 00:02:39 +00:00
$('<div>')
.css({
marginTop: '2px'
})
.append(
Ox.EditableContent({
clickLink: oml.clickLink,
editable: isEditable,
format: function(value) {
return formatValue(value.split('; '), 'author');
},
placeholder: formatLight(Ox._('Unknown Author')),
tooltip: isEditable ? oml.getEditTooltip() : '',
value: data.author ? data.author.join('; ') : ''
})
.css({
marginBottom: '-3px',
fontWeight: 'bold',
fontSize: '13px'
})
.bindEvent({
submit: function(event) {
editMetadata('author', event.value);
}
})
)
.appendTo($info);
// -------- Place, Publisher, Date
$div = $('<div>')
.css({
marginTop: '4px',
})
.appendTo($info);
['place', 'publisher', 'date'].forEach(function(key, index) {
if (index) {
$('<span>').html(', ').appendTo($div);
}
$('<span>')
.html(formatKey(key))
.appendTo($div);
Ox.EditableContent({
clickLink: oml.clickLink,
editable: isEditable,
format: function(value) {
return formatValue(value.split('; '), key)
},
placeholder: formatLight('unknown'),
tooltip: isEditable ? oml.getEditTooltip() : '',
value: key == 'place'
? (data[key] ? data[key].join('; ') : [''])
: data[key] || ''
2014-05-16 08:06:11 +00:00
})
2014-05-21 00:02:39 +00:00
.bindEvent({
submit: function(event) {
editMetadata(key, event.value);
}
})
.appendTo($div);
});
2014-05-19 20:58:00 +00:00
2014-05-21 00:02:39 +00:00
// -------- Edition, Language, Pages
$div = $('<div>')
.css({
marginTop: '4px',
})
.appendTo($info);
['edition', 'language', 'pages'].forEach(function(key, index) {
if (index) {
$('<span>').html(', ').appendTo($div);
}
$('<span>')
.html(formatKey(key))
.appendTo($div);
2015-05-14 10:04:39 +00:00
Ox.print('colors', key, data[key])
2014-05-21 00:02:39 +00:00
Ox.EditableContent({
clickLink: oml.clickLink,
editable: isEditable,
format: function(value) {
return key == 'language'
2015-04-20 07:53:20 +00:00
? formatValue(value.split('; '), key)
2014-05-21 00:02:39 +00:00
: value;
},
placeholder: formatLight('unknown'),
tooltip: isEditable ? oml.getEditTooltip() : '',
2015-05-14 10:04:39 +00:00
value: data[key]
? key == 'language' && Ox.isArray(data[key])
? data[key].join('; ') : data[key] : ''
2014-05-19 20:58:00 +00:00
})
2014-05-21 00:02:39 +00:00
.bindEvent({
submit: function(event) {
editMetadata(key, event.value);
}
})
.appendTo($div);
});
2014-05-21 11:07:41 +00:00
// -------- Primary ID
if (data.primaryid) {
$('<div>')
.css({
marginTop: '4px',
})
.html(
'<b>' + Ox.getObjectById(oml.config.itemKeys, data.primaryid[0]).title
+ ':</b> ' + data.primaryid[1]
)
.appendTo($info);
}
2014-05-21 00:02:39 +00:00
// -------- Classification
2014-05-04 17:26:43 +00:00
2014-05-16 08:06:11 +00:00
if (data.classification) {
$('<div>')
.css({
marginTop: '8px',
})
2014-05-19 22:49:47 +00:00
.html(
2014-05-20 00:44:13 +00:00
formatValue(data.classification, 'classification')
2014-05-19 22:49:47 +00:00
)
2014-05-16 08:06:11 +00:00
.appendTo($info);
}
2014-05-04 17:26:43 +00:00
2014-05-21 00:02:39 +00:00
// -------- Description
2014-05-14 09:57:11 +00:00
if (data.description) {
$('<div>')
.css({
marginTop: '8px',
textAlign: 'justify'
})
2014-05-19 22:49:47 +00:00
.html(
Ox.encodeHTMLEntities(data.description)
)
2014-05-14 09:57:11 +00:00
.appendTo($info);
}
2014-05-14 18:46:31 +00:00
$('<div>').css({height: '16px'}).appendTo($info);
2014-05-04 17:26:43 +00:00
2014-05-21 00:02:39 +00:00
oml.createLinks($info);
2014-05-04 17:26:43 +00:00
} else if ($element == $data) {
2014-05-21 00:02:39 +00:00
renderMediaButton(data).appendTo($data);
2014-05-12 12:57:47 +00:00
$('<div>')
.addClass('OxSelectable')
.css({
2014-05-14 09:57:11 +00:00
marginTop: '10px',
2014-05-12 12:57:47 +00:00
})
.text(
[
2014-05-21 00:02:39 +00:00
data.extension.toUpperCase(),
2014-05-12 12:57:47 +00:00
Ox.formatValue(data.size, 'B')
].join(', ')
)
.appendTo($data);
['accessed', 'modified', 'added', 'created'].forEach(function(id) {
var title;
if (data[id]) {
title = Ox.getObjectById(oml.config.itemKeys, id).title;
$('<div>')
.css({
marginTop: '8px',
fontWeight: 'bold'
})
.text(title)
.appendTo($data);
2014-05-14 09:57:11 +00:00
$('<div>')
.text(Ox.formatDate(data[id], '%b %e, %Y'))
2014-05-12 12:57:47 +00:00
.appendTo($data);
}
});
2014-05-21 00:02:39 +00:00
renderIdentifyButton(data).appendTo($data);
2014-05-12 23:43:27 +00:00
2014-05-25 12:16:04 +00:00
ids.forEach(function(id, index) {
2014-05-14 09:57:11 +00:00
var title;
2014-05-25 12:16:04 +00:00
if (data[id.key] && !Ox.isEmpty(data[id.key])) {
title = Ox.getObjectById(oml.config.itemKeys, id.key).title;
2014-05-12 23:43:27 +00:00
$('<div>')
.css({
marginTop: (index == 0 ? 10 : 6) + 'px',
fontWeight: 'bold'
})
.text(title)
.appendTo($data);
2014-05-25 12:16:04 +00:00
Ox.makeArray(data[id.key]/*FIXME!*/).forEach(function(value) {
2015-04-20 07:53:20 +00:00
var isPrimary = data.primaryid && data.primaryid[0] == id.key
2014-05-21 00:02:39 +00:00
&& data.primaryid[1] == value;
2014-05-25 12:16:04 +00:00
value = Ox.encodeHTMLEntities(value);
2014-05-21 00:02:39 +00:00
Ox.Element({
tooltip: isPrimary ? 'Primary ID' : ''
})
.html(
2014-05-25 12:16:04 +00:00
'<a href="' + Ox.formatString(id.url, [value])
+ '" target="_blank">' + value + '</a>'
+ (isPrimary ? ' (*)' : '')
2014-05-21 00:02:39 +00:00
)
.appendTo($data);
});
2014-05-14 09:57:11 +00:00
}
});
2014-05-04 17:26:43 +00:00
$('<div>').css({height: '16px'}).appendTo($data);
}
});
function editMetadata(key, value) {
if (value != data[key]) {
2014-05-21 00:02:39 +00:00
var edit = {id: data.id};
if (Ox.contains(['author', 'place'], key)) {
edit[key] = value ? value.split('; ') : [];
} else {
edit[key] = value;
}
2014-05-04 17:26:43 +00:00
oml.api.edit(edit, function(result) {
2014-05-21 00:02:39 +00:00
oml.$ui.browser.value(
result.data.id, key, result.data[key]
);
2014-05-04 17:26:43 +00:00
});
}
}
});
};
2014-05-14 18:46:31 +00:00
if (!identifyData) {
2014-05-17 11:45:57 +00:00
ui.item && that.updateElement(ui.item);
2014-05-14 18:46:31 +00:00
} else {
2014-05-21 00:02:39 +00:00
that.updateElement(identifyData, [$icon, $info]);
2014-05-14 18:46:31 +00:00
}
2014-05-04 17:26:43 +00:00
oml.bindEvent({
transfer: function(data) {
if (data.id == ui.item && data.progress == 1) {
Ox.Request.clearCache(); // FIXME: too much
2014-05-17 11:45:57 +00:00
that.updateElement(ui.item, [$info, $data]);
2014-05-04 17:26:43 +00:00
}
}
});
that.bindEvent({
mousedown: function() {
2015-03-07 12:39:59 +00:00
setTimeout(function() {
!Ox.Focus.focusedElementIsInput() && that.gainFocus();
});
}
})
2014-05-04 17:26:43 +00:00
return that;
};