diff --git a/static/js/documentDialog.js b/static/js/documentDialog.js new file mode 100644 index 00000000..2a364473 --- /dev/null +++ b/static/js/documentDialog.js @@ -0,0 +1,99 @@ +// vim: et:ts=4:sw=4:sts=4:ft=javascript + +'use strict'; + +pandora.ui.documentDialog = function(items, index) { + + var dialogHeight = Math.round((window.innerHeight - 48) * 0.9), + dialogWidth = Math.round(window.innerWidth * 0.9), + documentHeight = dialogHeight - 24, + documentWidth = dialogWidth, + item = items[index], + + $content = Ox.Element(), + + that = Ox.Dialog({ + closeButton: true, + content: $content, + height: dialogHeight, + maximizeButton: true, + minHeight: 256, + minWidth: 512, + padding: 0, + removeOnClose: true, + title: item.name + '.' + item.extension, + width: dialogWidth + }) + .bindEvent({ + close: function() { + pandora.UI.set({document: ''}); + }, + resize: function(data) { + $content.options({ + height: data.height - 24, + width: data.width + }); + }, + pandora_document: function(data) { + if (Ox.getObjectById(items, data.value)) { + + } else { + + } + } + }), + + $selectButton = Ox.ButtonGroup({ + buttons: [ + {id: 'previous', title: 'left'}, + {id: 'next', title: 'right'} + ], + type: 'image' + }) + .css({float: 'right', margin: '4px'}) + .bindEvent({ + click: function(data) { + index = Ox.mod( + index + (data.id == 'previous' ? -1 : 1), + items.length + ); + pandora.UI.set({document: items[index].id}); + } + }); + + $(that.find('.OxBar')[0]).append($selectButton); + + setContent(); + + function setContent() { + $content.replaceWith( + $content = ( + item.extension == 'pdf' + ? Ox.Element() + : Ox.ImageViewer({ + height: documentHeight, + imageHeight: item.dimensions[1], + imagePreviewURL: '/documents/' + item.id + '/256p.jpg', + imageURL: '/documents/' + item.id + '/' + + item.name + '.' + item.extension, + imageWidth: item.dimensions[0], + width: documentWidth + }) + ) + .bindEvent({ + center: function() { + pandora.UI.set('document.' + item.id + '.center', data.center); + }, + page: function() { + pandora.UI.set('document.' + item.id + '.page', data.page); + }, + zoom: function(data) { + pandora.UI.set('document.' + item.id + '.zoom', data.zoom); + } + }) + ); + } + + return that; + +}; diff --git a/static/js/documentsPanel.js b/static/js/documentsPanel.js new file mode 100644 index 00000000..00e6a58c --- /dev/null +++ b/static/js/documentsPanel.js @@ -0,0 +1,654 @@ +// vim: et:ts=4:sw=4:sts=4:ft=javascript + +'use strict'; + +pandora.ui.documentsPanel = function(options) { + + var ui = pandora.user.ui, + + hasItemView = false, + isItemView = false, + + columns = [ + { + id: 'type', + operator: '+', + title: Ox._('Type'), + visible: true, + width: 128 + }, + { + id: 'name', + operator: '+', + title: Ox._('Name'), + visible: true, + width: 256 + }, + { + format: function(value) { + return value.toUpperCase(); + }, + id: 'extension', + operator: '+', + title: Ox._('Extension'), + visible: true, + width: 64 + }, + { + align: 'right', + format: function(value) { + return Ox.isArray(value) + ? Ox.formatDimensions(value, 'px') + : Ox.formatCount(value, 'page'); + }, + id: 'dimensions', + operator: '-', + title: Ox._('Dimensions'), + visible: true, + width: 128 + }, + { + align: 'right', + format: function(value) { + return Ox.formatValue(value, 'B'); + }, + id: 'size', + operator: '-', + title: Ox._('Size'), + visible: true, + width: 64 + }, + { + id: 'description', + operator: '+', + title: Ox._('Description'), + visible: true, + width: 256 + }, + { + align: 'right', + id: 'matches', + operator: '-', + title: Ox._('Matches'), + visible: true, + width: 64 + }, + { + id: 'user', + operator: '+', + title: Ox._('User'), + visible: true, + width: 128 + }, + { + align: 'right', + format: function(value) { + return Ox.formatDate(value, '%F %T'); + }, + id: 'created', + operator: '-', + title: Ox._('Created'), + visible: true, + width: 144 + }, + { + align: 'right', + format: function(value) { + return Ox.formatDate(value, '%F %T'); + }, + id: 'modified', + operator: '-', + title: Ox._('Modified'), + visible: true, + width: 144 + } + ], + + $listbar = Ox.Bar({size: 24}), + + $addButton = Ox.Button({ + title: isItemView ? 'add' : 'upload', + tooltip: Ox._(isItemView ? 'Add Documents...' : 'Upload Documents...'), + type: 'image' + }) + .css({float: 'left', margin: '4px 2px 4px 4px'}) + .bindEvent({ + click: function() { + + } + }) + .appendTo($listbar), + + $viewSelect = Ox.Select({ + items: [ + {id: 'list', title: Ox._('List')}, + {id: 'grid', title: Ox._('Grid')} + ], + width: 192 + }) + .css({float: 'left', margin: '4px 2px'}) + .bindEvent({ + change: function(data) { + pandora.UI.set({documentsView: data.id}); + } + }) + .appendTo($listbar), + + $sortSelect = Ox.Select({ + items: columns.map(function(column) { + return { + id: column.id, + title: Ox._('Sort by ' + column.title) + }; + }), + width: 192 + }) + .bindEvent({ + change: function(data) { + var key = data.value; + pandora.UI.set({documentsSort: [{ + key: key; + operator: Ox.getObjectById(columns, key).operator + }]}); + } + }), + + $orderButton = Ox.Button({ + overlap: 'left', + title: getOrderButtonTitle(), + tooltip: getOrderButtonTooltip(), + type: 'image' + }) + .bindEvent({ + click: function() { + pandora.UI.set({documentsSort: [{ + key: ui.documentsSort[0].key, + operator: ui.documentsSort[0].operator == '+' ? '-' : '+' + }]}); + } + }), + + $sortElement = Ox.FormElementGroup({ + elements: [$sortSelect, $orderButton], + float: 'right' + }) + .css({float: 'left', margin: '4px 2px'}) + .appendTo($listbar), + + $findSelect = Ox.Select({ + items: isItemView ? [ + {id: 'all', title: Ox._('Find: All')}, + {id: 'name', title: Ox._('Find: Name')} + ] : [ + {id: 'all', title: Ox._('Find: All')}, + {id: 'name', title: Ox._('Find: Name')}, + {id: 'user', title: Ox._('Find: User')} + ], + overlap: 'right', + type: 'image' + }) + .bindEvent({ + change: function(data) { + + } + }), + + $findInput = Ox.Input({ + changeOnKeypress: true, + clear: true, + placeholder: Ox._('Find: All'), + width: 192 + }) + .bindEvent({ + change: updateList + }), + + $findElement = Ox.FormElementGroup({ + elements: [ + $findSelect, + $findInput + ] + }) + .css({float: 'right', margin: '4px 4px 4px 2px'}) + .appendTo($listbar), + + $list = renderList(), + + $statusbar = Ox.Bar({size: 16}), + + $status = Ox.Element() + .css({ + margin: '2px 4px', + fontSize: '9px', + overflow: 'hidden', + textOverflow: 'ellipsis' + }) + .html(Ox._('Loading...')) + .appendTo($statusbar), + + $listPanel = Ox.SplitPanel({ + elements: [ + {element: $listbar, size: 24}, + {element: $list}, + {element: $statusbar, size: 16} + ], + orientation: 'vertical' + }), + + $itembar = Ox.Bar({size: 24}), + + $itemMenu = Ox.MenuButton({ + items: isItemView ? [ + {id: 'open', title: '', keyboard: 'enter'}, + {id: 'edit', title: ''}, + {id: 'remove', title: ''} + ] : [ + {id: 'open', title: '', keyboard: 'enter'}, + {id: 'addtoitem', title: ''}, + {id: 'addtolist', title: ''}, + {id: 'replace', title: Ox._('Replace Document...')}, + {id: 'delete', title: '', keyboard: 'delete'} + ], + title: 'set', + tooltip: 'Options' + }) + .bindEvent({ + click: function() { + + } + }), + + $selectButton = Ox.ButtonGroup({ + buttons: [ + {id: 'previous', title: 'left'}, + {id: 'next', title: 'right'} + ], + type: 'image' + }) + .css({float: 'right', margin: '4px 2px'}) + .appendTo($itembar) + .bindEvent({ + click: function(data) { + + } + }), + + $deselectButton = Ox.Button({ + title: 'close', + tooltip: Ox._('Done'), + type: 'image' + }) + .css({float: 'right', margin: '4px 4px 4px 2px'}) + .bindEvent({ + click: function() { + pandora.UI.set( + 'documentsSelection.' + (isItemView ? ui.item : ''), + [] + ) + } + }) + .appendTo($itembar), + + $item = Ox.Element().css({overflowY: 'scroll'}), + + $preview, + + $form, + + $itemPanel = Ox.SplitPanel({ + elements: [ + {element: $itembar, size: 24}, + {element: $item} + ], + orientation: 'vertical' + }) + .bindEvent({ + resize: function(data) { + ui.sidebarSize = data.size; + }, + resizeend: function(data) { + // set to 0 so that UI.set registers a change of the value + ui.documentSize = 0; + pandora.UI.set({documentSize: data.size}); + }, + toggle: function() { + pandora.UI.set({showDocument: !data.collapsed}); + } + }), + + that = Ox.SplitPanel({ + elements: [ + { + element: $listPanel + }, + { + collapsible: isItemView, + collapsed: !ui.showDocument, + element: $itemPanel, + size: ui.documentSize + resizable: isItemView, + resize: isItemView ? [192, 256, 320, 384] : [] + } + ], + orientation: 'horizontal' + }) + .bindEvent({ + pandora_documentssort: function(data) { + updateSortElement(); + $list.options({sort: data.value}); + } + }); + + that.bindEvent( + 'documentsSelection.' + (isItemView ? ui.item : ''), + selectDocuments + }); + + function addDocuments() { + pandora.api.addDocument({ + item: ui.item, + ids: $list.options('selected') + }, function() { + Ox.Request.clearCache(); + // ... + $list.reloadList(); + }); + } + + function deleteDocuments() { + pandora.ui.deleteDocumentDialog($list.options('selected'), function() { + Ox.Request.clearCache(); + // ... + $list.reloadList(); + }).open(); + } + + function getOrderButtonTitle() { + return ui.documentsSort[0].operator == '+' ? 'up' : 'down'; + } + + function getOrderButtonTooltip() { + return Ox._(ui.documentsSort[0].operator == '+' ? 'ascending' : 'descending'); + } + + function getPreviewSize() { + var ratio = $list.value(selected[0], 'ratio'), + size = ui.documentSize - 16 - Ox.UI.SCROLLBAR_SIZE, + height = ratio > 1 ? size / ratio : size, + width = ratio > 1 ? size : size * ratio, + left = Math.floor((size - width) / 2); + return { + height: height, + // fixme: CSS gets applied twice, to image and enclosing element + margin: [8, 8, 8, 8 + left].map(function(px) { + return Math.round(px / 2) + 'px'; + }).join(' '), + width: width + }; + } + + function removeDocuments() { + pandora.api.removeDocument({ + item: ui.item, + ids: $list.options('selected') + }, function() { + Ox.Request.clearCache(); + // ... + $list.reloadList(); + }); + } + + function renderForm() { + var item = $list.value(selected), + editable = item.user == pandora.user.username + || pandora.site.capabilities.canEditDocuments[pandora.user.level], + inputWidth = ui.documentSize - 16 - Ox.UI.SCROLLBAR_SIZE, + labelWidth = 80; + return Ox.Form({ + items: [ + Ox.Input({ + disabled: !editable, + id: 'name', + label: Ox._('Name'), + labelWidth: labelWidth, + value: item.name, + width: inputWidth + }), + Ox.Input({ + disabled: true, + id: 'extension', + label: Ox._('Extension'), + labelWidth: labelWidth, + value: item.extension, + width: inputWidth + }), + Ox.Input({ + disabled: true, + id: 'dimensions', + label: Ox._('Dimensions'), + labelWidth: labelWidth, + value: Ox.isArray(value) + ? Ox.formatDimensions(value, 'px') + : Ox.formatCount(value, 'page'), + width: inputWidth + }), + Ox.Input({ + disabled: true, + id: 'size', + label: Ox._('Size'), + labelWidth: labelWidth, + value: Ox.formatValue(item.size, 'B'), + width: inputWidth + }), + Ox.Input({ + disabled: true, + id: 'matches', + label: Ox._('Matches'), + labelWidth: labelWidth, + value: item.matches, + width: inputWidth + }), + Ox.Input({ + disabled: true, + id: 'username', + label: Ox._('Username'), + labelWidth: labelWidth, + value: item.user, + width: inputWidth + }), + Ox.Input({ + disabled: !editable, + id: 'type', + label: Ox._('Type'), + labelWidth: labelWidth, + value: item.type, + width: inputWidth + }), + Ox.Input({ + disabled: !editable, + height: 256, + id: 'description', + placeholder: Ox._('Description'), + type: 'textarea', + value: item.description, + width: inputWidth + }) + ], + width: 240 + }) + .css({margin: '12px 8px 8px 8px'}) + .bindEvent({ + change: function(event) { + var data = Ox.extend({id: item.id}, event.id, event.data.value); + pandora.api.editDocument(data, function(result) { + $list.value(result.data.id, event.id, result.data[event.id]); + if (event.id == 'name') { + $list.value(item.id, 'id', result.data.id); + } + Ox.Request.clearCache('findDocuments'); + $list.reloadList(); + }); + } + }); + } + + function renderList() { + var options = { + items: pandora.api.findDocuments, + keys: ['ratio'], + query: {conditions: [], operator: '&'}, + sort: ui.documentsSort.concat([ + {key: 'extension', operator: '+'}, + {key: 'filename', operator: '+'} + ]), + sums: ['size'], + unique: 'id' + }; + return (ui.documentsView == 'list' ? Ox.TableList(Ox.extend(options, { + columns: columns, + columnsVisible: true, + scrollbarVisible: true, + })) : Ox.IconList(Ox.extend(options, { + item: function(data, sort, size) { + var sortKey = sort[0].key, + infoKey = sortKey == 'name' ? 'extension' : sortKey, + info = ( + Ox.getObjectById(columns, infoKey).format || Ox.identity + )(data[infoKey]), + size = size || 128; + return { + height: Math.round(data.ratio > 1 ? size / ratio : size), + id: data.id, + info: info, + title: data.name, + url: '/documents/' + data.id + '/256p.jpg', + width: Math.round(data.ratio > 1 ? size : size * ratio) + }; + }, + size: 128 + }))) + .bindEvent({ + add: uploadDocuments, + 'delete': deleteDocuments, + init: function(data) { + $status.html( + Ox.toTitleCase(Ox.formatCount(data.items, 'document')) + + ', ' + Ox.formatValue(data.size, 'B') + ); + }, + select: function() { + pandora.UI.set( + 'documentsSelection.' + (isItemView ? ui.item : ''), + data.ids + ); + }, + sort: function() { + pandora.UI.set({documentsSort: data.sort}); + } + }); + } + + function renderPreview() { + var size = getPreviewSize(), + src = '/documents/' + selected + '/256p.jpg'; + return Ox.ImageElement({ + height: size.height, + src: src, + width: size.width + }) + .css({ + margin: size.margin, + borderRadius: '8px' + }) + .on({ + click: function() { + var item = $list.value(selected); + window.open( + '/documents/' + selected + '/' + item.name + '.' + item.extension, + '_blank' + ); + } + }); + } + + function selectDocuments() { + var items = ui.documentSelection[isItemView ? ui.item : ''], + string = items.length < 2 ? 'Document' : items.length + ' Documents'; + ['open', 'edit', 'delete'].forEach(function(id) { + $itemMenu[items.length ? 'enableItem' : 'disableItem'] + }); + $itemMenu.setItemTitle('open', Ox._('Open ' + string)); + if (isItemView) { + $itemMenu.setItemTitle( + 'edit', Ox._('Edit ' + string + '...') + ) + .setItemTitle('remove', Ox._( + 'Remove ' + string + ' from this ' + + pandora.site.itemName.singular + )); + } else { + $itemMenu.setItemTitle('addtoitem', Ox._( + : 'Add ' + string + ' to Current ' + + pandora.site.itemName.singular + )) + .setItemTitle('addtolist', Ox._( + 'Add ' + string + ' to All Items in Current List' + )) + .setItemTitle('delete', Ox._('Delete ' + string + '...')); + } + $selectButton(items.length ? 'show' : 'hide'); + $deselectButton(items.length ? 'show' : 'hide'); + $item.empty(); + if (items.length) { + $preview = renderPreview().appendTo($item); + $form = renderForm().appendTo($item); + } + } + + function updateList() { + var key = $findSelect.value(), + value = $findInput.value(), + query = { + conditions: value + ? [].concat( + key != 'user' + ? [{key: 'name', value: value, operator: '='}] + : [], + key == 'all' + ? [key: 'description', value: value, operator: '='] + : [], + key != 'name' + ? [{key: 'user', value: value, operator: '='}] + : [] + ) + : [], + operator: '|' + }; + $list.options({query: query}); + } + + function updateSortElement() { + $sortSelect.value(ui.documentsSort[0].key); + $orderButton.options({ + title: getOrderButtonTitle(), + tooltip: getOrderButtonTooltip() + }); + } + + function uploadDocuments(data) { + pandora.ui.uploadDocumentDialog(data.files, function(files) { + if (files) { + Ox.Request.clearCache('findDocuments'); + $list.bindEventOnce({ + load: function() { + $list.options({selected: [files.ids]}); + } + }) + .reloadList(); + } + }).open(); + } + + return that; + +} \ No newline at end of file