more...
This commit is contained in:
parent
2ee2bc178a
commit
10d2f35b7b
20 changed files with 1334 additions and 629 deletions
|
@ -20,8 +20,8 @@ class Changelog(db.Model):
|
|||
editlist name {name: newname}
|
||||
orderlists [name, name, name]
|
||||
removelist name
|
||||
additemtolist listname itemid
|
||||
removeitemfromlist listname itemid
|
||||
addlistitems listname [ids]
|
||||
removelistitems listname [ids]
|
||||
editusername username
|
||||
editcontact string
|
||||
addpeer peerid peername
|
||||
|
@ -182,24 +182,17 @@ class Changelog(db.Model):
|
|||
l.remove()
|
||||
return True
|
||||
|
||||
def action_addlistitem(self, user, timestamp, name, itemid):
|
||||
from item.models import Item
|
||||
def action_addlistitems(self, user, timestamp, name, ids):
|
||||
from user.models import List
|
||||
l = List.get(user.id, name)
|
||||
i = Item.get(itemid)
|
||||
if l and i:
|
||||
i.lists.append(l)
|
||||
i.update()
|
||||
l = List.get_or_create(user.id, name)
|
||||
l.add_items(ids)
|
||||
return True
|
||||
|
||||
def action_removelistitem(self, user, timestamp, name, itemid):
|
||||
from item.models import Item
|
||||
def action_removelistitem(self, user, timestamp, name, ids):
|
||||
from user.models import List
|
||||
l = List.get(user.id, name)
|
||||
i = Item.get(itemid)
|
||||
if l and i:
|
||||
i.lists.remove(l)
|
||||
i.update()
|
||||
if l:
|
||||
l.remove_items(ids)
|
||||
return True
|
||||
|
||||
def action_editusername(self, user, timestamp, username):
|
||||
|
|
|
@ -6,7 +6,6 @@ from flask import json
|
|||
from oxflask.api import actions
|
||||
from oxflask.shortcuts import returns_json
|
||||
|
||||
from oml import utils
|
||||
import query
|
||||
|
||||
import models
|
||||
|
@ -118,6 +117,8 @@ def edit(request):
|
|||
if item and keys and item.json()['mediastate'] == 'available':
|
||||
key = keys[0]
|
||||
print 'update mainid', key, data[key]
|
||||
if key in ('isbn10', 'isbn13'):
|
||||
data[key] = utils.normalize_isbn(data[key])
|
||||
item.update_mainid(key, data[key])
|
||||
response = item.json()
|
||||
else:
|
||||
|
|
|
@ -233,6 +233,12 @@ class Item(db.Model):
|
|||
else:
|
||||
f.value = '%s:' % p.id
|
||||
db.session.add(f)
|
||||
for l in self.lists:
|
||||
f = Find()
|
||||
f.item_id = self.id
|
||||
f.key = 'list'
|
||||
f.value = l.find_id
|
||||
db.session.add(f)
|
||||
|
||||
def update(self):
|
||||
users = map(str, list(self.users))
|
||||
|
|
104
oml/localnodes.py
Normal file
104
oml/localnodes.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
|
||||
import socket
|
||||
import thread
|
||||
import json
|
||||
import struct
|
||||
from threading import Thread
|
||||
|
||||
from settings import preferences, server, USER_ID
|
||||
from node.utils import get_public_ipv6
|
||||
|
||||
def can_connect(data):
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
s.settimeout(1)
|
||||
s.connect((data['host'], data['port']))
|
||||
s.close()
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
class LocalNodes(Thread):
|
||||
_active = True
|
||||
_nodes = {}
|
||||
|
||||
_BROADCAST = "ff02::1"
|
||||
_PORT = 9851
|
||||
TTL = 2
|
||||
|
||||
def __init__(self, app):
|
||||
self._app = app
|
||||
Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.start()
|
||||
self.host = get_public_ipv6()
|
||||
self.send()
|
||||
|
||||
def send(self):
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
|
||||
ttl = struct.pack('@i', self.TTL)
|
||||
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, ttl)
|
||||
message = json.dumps({
|
||||
'id': USER_ID,
|
||||
'username': preferences.get('username', 'anonymous'),
|
||||
'host': self.host,
|
||||
'port': server['node_port'],
|
||||
})
|
||||
s.sendto(message + '\0', (self._BROADCAST, self._PORT))
|
||||
|
||||
def receive(self):
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind(('', self._PORT))
|
||||
group_bin = socket.inet_pton(socket.AF_INET6, self._BROADCAST) + '\0'*4
|
||||
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group_bin)
|
||||
|
||||
while self._active:
|
||||
data, addr = s.recvfrom(1024)
|
||||
while data[-1] == '\0':
|
||||
data = data[:-1] # Strip trailing \0's
|
||||
data = self.validate(data)
|
||||
if data:
|
||||
if data['id'] not in self._nodes:
|
||||
thread.start_new_thread(self.new_node, (data, ))
|
||||
else:
|
||||
print 'UPDATE NODE', data
|
||||
self._nodes[data['id']] = data
|
||||
|
||||
def validate(self, data):
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except:
|
||||
return None
|
||||
for key in ['id', 'username', 'host', 'port']:
|
||||
if key not in data:
|
||||
return None
|
||||
return data
|
||||
|
||||
def get(self, user_id):
|
||||
if user_id in self._nodes:
|
||||
if can_connect(self._nodes[user_id]):
|
||||
return self._nodes[user_id]
|
||||
|
||||
def new_node(self, data):
|
||||
print 'NEW NODE', data
|
||||
if can_connect(data):
|
||||
self._nodes[data['id']] = data
|
||||
with self._app.app_context():
|
||||
import user.models
|
||||
u = user.models.User.get_or_create(data['id'])
|
||||
u.info['username'] = data['username']
|
||||
u.info['local'] = data
|
||||
u.save()
|
||||
self.send()
|
||||
|
||||
def run(self):
|
||||
self.receive()
|
||||
|
||||
def join(self):
|
||||
self._active = False
|
||||
return Thread.join(self)
|
|
@ -107,11 +107,8 @@ def start(app):
|
|||
(r"/get/(.*)", ShareHandler, dict(app=app)),
|
||||
(r".*", NodeHandler, dict(app=app)),
|
||||
])
|
||||
|
||||
#tr = WSGIContainer(node_app)
|
||||
#http_server= HTTPServer(tr)
|
||||
http_server.listen(settings.server['node_port'], settings.server['node_address'])
|
||||
host = utils.get_public_ipv4()
|
||||
host = utils.get_public_ipv6()
|
||||
state.online = directory.put(settings.sk, {
|
||||
'host': host,
|
||||
'port': settings.server['node_port']
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
|
||||
import socket
|
||||
import requests
|
||||
from urlparse import urlparse
|
||||
|
||||
def get_public_ipv6():
|
||||
host = ('2a01:4f8:120:3201::3', 25519)
|
||||
|
|
25
oml/nodes.py
25
oml/nodes.py
|
@ -19,6 +19,7 @@ from changelog import Changelog
|
|||
|
||||
import directory
|
||||
from websocket import trigger_event
|
||||
from localnodes import LocalNodes
|
||||
|
||||
ENCODING='base64'
|
||||
|
||||
|
@ -26,8 +27,9 @@ class Node(object):
|
|||
online = False
|
||||
download_speed = 0
|
||||
|
||||
def __init__(self, app, user):
|
||||
self._app = app
|
||||
def __init__(self, nodes, user):
|
||||
self._nodes = nodes
|
||||
self._app = nodes._app
|
||||
self.user_id = user.id
|
||||
key = str(user.id)
|
||||
self.vk = ed25519.VerifyingKey(key, encoding=ENCODING)
|
||||
|
@ -35,10 +37,15 @@ class Node(object):
|
|||
|
||||
@property
|
||||
def url(self):
|
||||
if ':' in self.host:
|
||||
url = 'http://[%s]:%s' % (self.host, self.port)
|
||||
local = self.get_local()
|
||||
if local:
|
||||
url = 'http://[%s]:%s' % (local['host'], local['port'])
|
||||
print 'using local peer discovery to access node', url
|
||||
else:
|
||||
url = 'http://%s:%s' % (self.host, self.port)
|
||||
if ':' in self.host:
|
||||
url = 'http://[%s]:%s' % (self.host, self.port)
|
||||
else:
|
||||
url = 'http://%s:%s' % (self.host, self.port)
|
||||
return url
|
||||
|
||||
def resolve_host(self):
|
||||
|
@ -51,6 +58,11 @@ class Node(object):
|
|||
self.host = None
|
||||
self.port = 9851
|
||||
|
||||
def get_local(self):
|
||||
if self._nodes and self._nodes._local:
|
||||
return self._nodes._local.get(self.user_id)
|
||||
return None
|
||||
|
||||
def request(self, action, *args):
|
||||
if not self.host:
|
||||
self.resolve_host()
|
||||
|
@ -211,6 +223,7 @@ class Nodes(Thread):
|
|||
self._app = app
|
||||
self._q = Queue()
|
||||
self._running = True
|
||||
self._local = LocalNodes(app)
|
||||
Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.start()
|
||||
|
@ -238,7 +251,7 @@ class Nodes(Thread):
|
|||
def _add_node(self, user_id):
|
||||
if user_id not in self._nodes:
|
||||
from user.models import User
|
||||
self._nodes[user_id] = Node(self._app, User.get_or_create(user_id))
|
||||
self._nodes[user_id] = Node(self, User.get_or_create(user_id))
|
||||
else:
|
||||
self._nodes[user_id].online = True
|
||||
trigger_event('status', {
|
||||
|
|
|
@ -144,6 +144,8 @@ class Parser(object):
|
|||
l = self._list.query.filter_by(user_id=p.id, name=name).first()
|
||||
else:
|
||||
l = None
|
||||
if l:
|
||||
v = l.find_id
|
||||
if l and l._query:
|
||||
data = l._query
|
||||
q = self.parse_conditions(data.get('conditions', []),
|
||||
|
|
|
@ -38,7 +38,7 @@ server_defaults = {
|
|||
'port': 9842,
|
||||
'address': '127.0.0.1',
|
||||
'node_port': 9851,
|
||||
'node_address': '::',
|
||||
'node_address': '',
|
||||
'extract_text': True,
|
||||
'directory_service': 'http://[2a01:4f8:120:3201::3]:25519',
|
||||
'lookup_service': 'http://data.openmedialibrary.com',
|
||||
|
|
|
@ -84,9 +84,9 @@ actions.register(getUsers)
|
|||
|
||||
@returns_json
|
||||
def getLists(request):
|
||||
lists = {}
|
||||
lists = []
|
||||
for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)):
|
||||
lists[u.id] = u.lists_json()
|
||||
lists += u.lists_json()
|
||||
return {
|
||||
'lists': lists
|
||||
}
|
||||
|
@ -132,28 +132,24 @@ def editList(request):
|
|||
actions.register(editList, cache=False)
|
||||
|
||||
@returns_json
|
||||
def addListItem(request):
|
||||
def addListItems(request):
|
||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||
l = models.List.get_or_create(data['id'])
|
||||
i = Item.get(data['item'])
|
||||
if l and i:
|
||||
l.items.append(i)
|
||||
models.db.session.add(l)
|
||||
i.update()
|
||||
l = models.List.get_or_create(data['list'])
|
||||
if l:
|
||||
l.add_items(data['items'])
|
||||
return l.json()
|
||||
return {}
|
||||
actions.register(addListItem, cache=False)
|
||||
actions.register(addListItems, cache=False)
|
||||
|
||||
@returns_json
|
||||
def removeListItem(request):
|
||||
def removeListItems(request):
|
||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||
l = models.List.get(data['id'])
|
||||
i = Item.get(data['item'])
|
||||
if l and i:
|
||||
l.items.remove(i)
|
||||
models.db.session.add(l)
|
||||
i.update()
|
||||
l = models.List.get(data['list'])
|
||||
if l:
|
||||
l.remove_items(data['items'])
|
||||
return l.json()
|
||||
return {}
|
||||
actions.register(removeListItem, cache=False)
|
||||
actions.register(removeListItems, cache=False)
|
||||
|
||||
@returns_json
|
||||
def sortLists(request):
|
||||
|
|
|
@ -59,7 +59,7 @@ class User(db.Model):
|
|||
return j
|
||||
|
||||
def check_online(self):
|
||||
return state.nodes.check_online(self.id)
|
||||
return state.nodes and state.nodes.check_online(self.id)
|
||||
|
||||
def lists_json(self):
|
||||
return [l.json() for l in self.lists.order_by('position')]
|
||||
|
@ -158,25 +158,39 @@ class List(db.Model):
|
|||
from item.models import Item
|
||||
for item_id in items:
|
||||
i = Item.get(item_id)
|
||||
self.items.add(i)
|
||||
self.items.append(i)
|
||||
i.update()
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
for item_id in items:
|
||||
i = Item.get(item_id)
|
||||
i.update_lists()
|
||||
db.session.commit()
|
||||
if self.user_id == settings.USER_ID:
|
||||
Changelog.record(self.user, 'addlistitems', self.name, items)
|
||||
|
||||
def remove_items(self, items):
|
||||
from item.models import Item
|
||||
for item_id in items:
|
||||
i = Item.get(item_id)
|
||||
self.items.remove(i)
|
||||
i.update()
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
for item_id in items:
|
||||
i = Item.get(item_id)
|
||||
i.update_lists()
|
||||
db.session.commit()
|
||||
if self.user_id == settings.USER_ID:
|
||||
Changelog.record(self.user, 'removelistitems', self.name, items)
|
||||
|
||||
def remove(self):
|
||||
if not self._query:
|
||||
for i in self.items:
|
||||
self.items.remove(i)
|
||||
if not self._query:
|
||||
print 'record change: removelist', self.user, self.name
|
||||
Changelog.record(self.user, 'removelist', self.name)
|
||||
if self.user_id == settings.USER_ID:
|
||||
Changelog.record(self.user, 'removelist', self.name)
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
|
||||
|
@ -184,10 +198,21 @@ class List(db.Model):
|
|||
def public_id(self):
|
||||
id = ''
|
||||
if self.user_id != settings.USER_ID:
|
||||
id += self.user_id
|
||||
id = '%s:%s' % (id, self.name)
|
||||
id += self.user.nickname
|
||||
id = u'%s:%s' % (id, self.name)
|
||||
return id
|
||||
|
||||
@property
|
||||
def find_id(self):
|
||||
id = ''
|
||||
if self.user_id != settings.USER_ID:
|
||||
id += self.user_id
|
||||
id = u'%s:%s' % (id, self.id)
|
||||
return id
|
||||
|
||||
def __repr__(self):
|
||||
return self.public_id.encode('utf-8')
|
||||
|
||||
def items_count(self):
|
||||
from item.models import Item
|
||||
if self._query:
|
||||
|
@ -199,6 +224,7 @@ class List(db.Model):
|
|||
def json(self):
|
||||
r = {
|
||||
'id': self.public_id,
|
||||
'user': self.user.nickname if self.user_id != settings.USER_ID else settings.preferences['username'],
|
||||
'name': self.name,
|
||||
'index': self.position,
|
||||
'items': self.items_count(),
|
||||
|
|
|
@ -9,8 +9,8 @@ oml.ui.browser = function() {
|
|||
defaultRatio: oml.config.coverRatio,
|
||||
draggable: true,
|
||||
item: function(data, sort, size) {
|
||||
var color = oml.getFileTypeColor(data).map(function(rgb) {
|
||||
return rgb.concat(0.8)
|
||||
var color = oml.getFileInfoColor(ui.fileInfo, data).map(function(rgb) {
|
||||
return rgb.concat(0.8);
|
||||
}),
|
||||
ratio = data.coverRatio || oml.config.coverRatio,
|
||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||
|
@ -28,12 +28,14 @@ oml.ui.browser = function() {
|
|||
height: Math.round(size / 12.8) + 'px',
|
||||
borderWidth: Math.round(size / 64) + 'px 0',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'rgba(' + color[1].join(', ') + ')',
|
||||
borderColor: 'rgba(' + color[2].join(', ') + ')',
|
||||
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
||||
fontSize: Math.round(size / 16) + 'px',
|
||||
textAlign: 'center',
|
||||
color: 'rgba(' + color[1].join(', ') + ')',
|
||||
backgroundColor: 'rgba(' + color[0].join(', ') + ')',
|
||||
color: 'rgba(' + color[2].join(', ') + ')',
|
||||
backgroundImage: '-webkit-linear-gradient(top, ' + color.slice(0, 2).map(function(rgba) {
|
||||
return 'rgba(' + rgba.join(', ') + ')';
|
||||
}).join(', ') + ')',
|
||||
WebkitTransform: 'rotate(45deg)'
|
||||
})
|
||||
.html(
|
||||
|
@ -55,8 +57,8 @@ oml.ui.browser = function() {
|
|||
}), callback);
|
||||
},
|
||||
keys: [
|
||||
'author', 'coverRatio', 'extension',
|
||||
'id', 'size', 'textsize', 'title'
|
||||
'author', 'coverRatio', 'extension', 'id',
|
||||
'mediastate', 'size', 'textsize', 'title'
|
||||
],
|
||||
max: 1,
|
||||
min: 1,
|
||||
|
@ -101,6 +103,8 @@ oml.ui.browser = function() {
|
|||
}
|
||||
});
|
||||
|
||||
oml.enableDragAndDrop(that);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
|
@ -58,200 +58,187 @@ oml.ui.folders = function() {
|
|||
oml.$ui.libraryList = [];
|
||||
oml.$ui.folderList = [];
|
||||
|
||||
oml.api.getUsers(function(result) {
|
||||
oml.getUsersAndLists(function(users, lists) {
|
||||
|
||||
var peers = result.data.users.filter(function(user) {
|
||||
return user.peered;
|
||||
});
|
||||
Ox.print('GOT USERS AND LISTS', users, lists);
|
||||
|
||||
oml.api.getLists(function(result) {
|
||||
users.forEach(function(user, index) {
|
||||
|
||||
Ox.print('GOT LISTS', result.data);
|
||||
var $content,
|
||||
items = lists.filter(function(list) {
|
||||
return list.user == user.nickname
|
||||
&& list.type != 'library';
|
||||
}),
|
||||
libraryId = (!index ? '' : user.nickname) + ':'
|
||||
|
||||
var users = [
|
||||
{
|
||||
id: oml.user.id,
|
||||
nickname: oml.user.preferences.username,
|
||||
online: oml.user.online
|
||||
}
|
||||
].concat(peers),
|
||||
userIndex[user.nickname] = index;
|
||||
|
||||
lists = result.data.lists;
|
||||
|
||||
users.forEach(function(user, index) {
|
||||
|
||||
var $content,
|
||||
libraryId = (!index ? '' : user.nickname) + ':';
|
||||
|
||||
userIndex[user.nickname] = index;
|
||||
|
||||
oml.$ui.folder[index] = Ox.CollapsePanel({
|
||||
collapsed: false,
|
||||
extras: [
|
||||
oml.ui.statusIcon(
|
||||
!oml.user.online ? 'unknown'
|
||||
: user.online ? 'connected'
|
||||
: 'disconnected'
|
||||
),
|
||||
{},
|
||||
Ox.Button({
|
||||
style: 'symbol',
|
||||
title: 'info',
|
||||
tooltip: Ox._(!index ? 'Preferences' : 'Profile'),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
if (!index) {
|
||||
oml.UI.set({
|
||||
page: 'preferences',
|
||||
'part.preferences': 'account'
|
||||
});
|
||||
} else {
|
||||
oml.UI.set({page: 'users'})
|
||||
}
|
||||
oml.$ui.folder[index] = Ox.CollapsePanel({
|
||||
collapsed: false,
|
||||
extras: [
|
||||
oml.ui.statusIcon(
|
||||
!oml.user.online ? 'unknown'
|
||||
: user.online ? 'connected'
|
||||
: 'disconnected'
|
||||
),
|
||||
{},
|
||||
Ox.Button({
|
||||
style: 'symbol',
|
||||
title: 'info',
|
||||
tooltip: Ox._(!index ? 'Preferences' : 'Profile'),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
if (!index) {
|
||||
oml.UI.set({
|
||||
page: 'preferences',
|
||||
'part.preferences': 'account'
|
||||
});
|
||||
} else {
|
||||
oml.UI.set({page: 'users'})
|
||||
}
|
||||
})
|
||||
],
|
||||
title: Ox.encodeHTMLEntities(user.nickname)
|
||||
})
|
||||
.css({
|
||||
width: ui.sidebarSize
|
||||
}
|
||||
})
|
||||
],
|
||||
title: Ox.encodeHTMLEntities(user.nickname)
|
||||
})
|
||||
.css({
|
||||
width: ui.sidebarSize
|
||||
})
|
||||
.bindEvent({
|
||||
toggle: function(data) {
|
||||
oml.UI.set('showFolder.' + user.nickname, !data.collapsed);
|
||||
}
|
||||
})
|
||||
.bindEvent(
|
||||
'oml_showfolder.' + user.nickname.toLowerCase(),
|
||||
function(data) {
|
||||
oml.$ui.folder[index].options({collapsed: !data.value});
|
||||
}
|
||||
)
|
||||
.appendTo(that);
|
||||
|
||||
$content = oml.$ui.folder[index].$content
|
||||
.css({
|
||||
height: (1 + items.length) * 16 + 'px'
|
||||
});
|
||||
|
||||
$lists.push(
|
||||
oml.$ui.libraryList[index] = oml.ui.folderList({
|
||||
items: [
|
||||
{
|
||||
id: libraryId,
|
||||
name: Ox._('Library'),
|
||||
type: 'library',
|
||||
items: -1
|
||||
}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
toggle: function(data) {
|
||||
oml.UI.set('showFolder.' + user.nickname, !data.collapsed);
|
||||
add: function() {
|
||||
!index && oml.addList();
|
||||
},
|
||||
load: function() {
|
||||
oml.api.find({
|
||||
query: getFind(libraryId)
|
||||
}, function(result) {
|
||||
oml.$ui.libraryList[index].value(
|
||||
libraryId, 'items', result.data.items
|
||||
);
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({find: getFind(data.ids[0])});
|
||||
},
|
||||
selectnext: function() {
|
||||
oml.UI.set({find: getFind(lists[user.id][0].id)});
|
||||
},
|
||||
selectprevious: function() {
|
||||
var userId = !index ? null : users[index - 1].id,
|
||||
set = {
|
||||
find: getFind(
|
||||
!index
|
||||
? ''
|
||||
: Ox.last(lists[userId]).id
|
||||
)
|
||||
};
|
||||
if (userId) {
|
||||
Ox.extend(set, 'showFolder.' + userId, true);
|
||||
}
|
||||
oml.UI.set(set);
|
||||
}
|
||||
})
|
||||
.bindEvent(
|
||||
'oml_showfolder.' + user.nickname.toLowerCase(),
|
||||
function(data) {
|
||||
oml.$ui.folder[index].options({collapsed: !data.value});
|
||||
}
|
||||
)
|
||||
.appendTo(that);
|
||||
.appendTo($content)
|
||||
);
|
||||
|
||||
$content = oml.$ui.folder[index].$content
|
||||
.css({
|
||||
height: (1 + lists[user.id].length) * 16 + 'px'
|
||||
});
|
||||
|
||||
$lists.push(
|
||||
oml.$ui.libraryList[index] = oml.ui.folderList({
|
||||
items: [
|
||||
{
|
||||
id: libraryId,
|
||||
name: Ox._('Library'),
|
||||
type: 'library',
|
||||
items: -1
|
||||
}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
add: function() {
|
||||
!index && oml.addList();
|
||||
},
|
||||
load: function() {
|
||||
oml.api.find({
|
||||
query: getFind(libraryId)
|
||||
}, function(result) {
|
||||
oml.$ui.libraryList[index].value(
|
||||
libraryId, 'items', result.data.items
|
||||
);
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({find: getFind(data.ids[0])});
|
||||
},
|
||||
selectnext: function() {
|
||||
oml.UI.set({find: getFind(lists[user.id][0].id)});
|
||||
},
|
||||
selectprevious: function() {
|
||||
var userId = !index ? null : users[index - 1].id,
|
||||
set = {
|
||||
find: getFind(
|
||||
!index
|
||||
? ''
|
||||
: Ox.last(lists[userId]).id
|
||||
)
|
||||
};
|
||||
if (userId) {
|
||||
Ox.extend(set, 'showFolder.' + userId, true);
|
||||
}
|
||||
oml.UI.set(set);
|
||||
}
|
||||
})
|
||||
.appendTo($content)
|
||||
);
|
||||
|
||||
$lists.push(
|
||||
oml.$ui.folderList[index] = oml.ui.folderList({
|
||||
draggable: !!index,
|
||||
items: lists[user.id],
|
||||
sortable: true
|
||||
})
|
||||
.bindEvent({
|
||||
add: function() {
|
||||
!index && oml.addList();
|
||||
},
|
||||
'delete': function() {
|
||||
!index && oml.deleteList();
|
||||
},
|
||||
key_control_d: function() {
|
||||
oml.addList(ui._list);
|
||||
},
|
||||
load: function() {
|
||||
$lists.push(
|
||||
oml.$ui.folderList[index] = oml.ui.folderList({
|
||||
draggable: !!index,
|
||||
items: items,
|
||||
sortable: true
|
||||
})
|
||||
.bindEvent({
|
||||
add: function() {
|
||||
!index && oml.addList();
|
||||
},
|
||||
'delete': function() {
|
||||
!index && oml.deleteList();
|
||||
},
|
||||
key_control_d: function() {
|
||||
oml.addList(ui._list);
|
||||
},
|
||||
load: function() {
|
||||
// ...
|
||||
},
|
||||
move: function(data) {
|
||||
lists[user.id] = data.ids.map(function(listId) {
|
||||
return Ox.getObjectById(lists[user.id], listId);
|
||||
});
|
||||
oml.api.sortLists({
|
||||
ids: data.ids,
|
||||
user: user.id
|
||||
}, function(result) {
|
||||
// ...
|
||||
},
|
||||
move: function(data) {
|
||||
lists[user.id] = data.ids.map(function(listId) {
|
||||
return Ox.getObjectById(lists[user.id], listId);
|
||||
});
|
||||
oml.api.sortLists({
|
||||
ids: data.ids,
|
||||
user: user.id
|
||||
}, function(result) {
|
||||
// ...
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
oml.ui.listDialog().open();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({find: getFind(data.ids[0])});
|
||||
},
|
||||
selectnext: function() {
|
||||
if (index < users.length - 1) {
|
||||
oml.UI.set(Ox.extend(
|
||||
{find: getFind(users[index + 1].nickname + ':')},
|
||||
'showFolder.' + users[index + 1].nickname,
|
||||
true
|
||||
));
|
||||
}
|
||||
},
|
||||
selectprevious: function() {
|
||||
oml.UI.set({find: getFind(libraryId)});
|
||||
});
|
||||
},
|
||||
open: function() {
|
||||
oml.ui.listDialog().open();
|
||||
},
|
||||
select: function(data) {
|
||||
oml.UI.set({find: getFind(data.ids[0])});
|
||||
},
|
||||
selectnext: function() {
|
||||
if (index < users.length - 1) {
|
||||
oml.UI.set(Ox.extend(
|
||||
{find: getFind(users[index + 1].nickname + ':')},
|
||||
'showFolder.' + users[index + 1].nickname,
|
||||
true
|
||||
));
|
||||
}
|
||||
})
|
||||
.bindEvent(function(data, event) {
|
||||
if (!index) {
|
||||
Ox.print('LIST EVENT', event, data);
|
||||
}
|
||||
})
|
||||
.css({height: lists[user.id].length * 16 + 'px'})
|
||||
.appendTo($content)
|
||||
);
|
||||
},
|
||||
selectprevious: function() {
|
||||
oml.UI.set({find: getFind(libraryId)});
|
||||
}
|
||||
})
|
||||
.bindEvent(function(data, event) {
|
||||
if (!index) {
|
||||
Ox.print('LIST EVENT', event, data);
|
||||
}
|
||||
})
|
||||
.css({height: items.length * 16 + 'px'})
|
||||
.appendTo($content)
|
||||
);
|
||||
|
||||
oml.$ui.folderList[index].$body.css({top: '16px'});
|
||||
|
||||
});
|
||||
|
||||
selectList();
|
||||
oml.$ui.folderList[index].$body.css({top: '16px'});
|
||||
|
||||
});
|
||||
|
||||
selectList();
|
||||
|
||||
});
|
||||
|
||||
function getFind(list) {
|
||||
|
|
|
@ -8,8 +8,8 @@ oml.ui.gridView = function() {
|
|||
defaultRatio: oml.config.coverRatio,
|
||||
draggable: true,
|
||||
item: function(data, sort, size) {
|
||||
var color = oml.getFileTypeColor(data).map(function(rgb) {
|
||||
return rgb.concat(0.8)
|
||||
var color = oml.getFileInfoColor(ui.fileInfo, data).map(function(rgb) {
|
||||
return rgb.concat(0.8);
|
||||
}),
|
||||
ratio = data.coverRatio || oml.config.coverRatio,
|
||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||
|
@ -20,6 +20,9 @@ oml.ui.gridView = function() {
|
|||
? (data.author || '') : data[sortKey]
|
||||
);
|
||||
size = size || 128;
|
||||
Ox.print('WTF', '-webkit-linear-gradient(top, ' + color.slice(2).map(function(rgba) {
|
||||
return 'rgba(' + rgba.join(', ') + ')';
|
||||
}).join(', ') + ')');
|
||||
return {
|
||||
extra: ui.showFileInfo ? $('<div>')
|
||||
.css({
|
||||
|
@ -27,12 +30,14 @@ oml.ui.gridView = function() {
|
|||
height: Math.round(size / 12.8) + 'px',
|
||||
borderWidth: Math.round(size / 64) + 'px 0',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'rgba(' + color[1].join(', ') + ')',
|
||||
borderColor: 'rgba(' + color[2].join(', ') + ')',
|
||||
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
||||
fontSize: Math.round(size / 16) + 'px',
|
||||
textAlign: 'center',
|
||||
color: 'rgba(' + color[1].join(', ') + ')',
|
||||
backgroundColor: 'rgba(' + color[0].join(', ') + ')',
|
||||
color: 'rgba(' + color[2].join(', ') + ')',
|
||||
backgroundImage: '-webkit-linear-gradient(top, ' + color.slice(0, 2).map(function(rgba) {
|
||||
return 'rgba(' + rgba.join(', ') + ')';
|
||||
}).join(', ') + ')',
|
||||
WebkitTransform: 'rotate(45deg)'
|
||||
})
|
||||
.html(
|
||||
|
@ -54,8 +59,8 @@ oml.ui.gridView = function() {
|
|||
}), callback);
|
||||
},
|
||||
keys: [
|
||||
'author', 'coverRatio', 'extension',
|
||||
'id', 'size', 'textsize', 'title'
|
||||
'author', 'coverRatio', 'extension', 'id',
|
||||
'mediastate', 'size', 'textsize', 'title'
|
||||
],
|
||||
selected: ui.listSelection,
|
||||
size: 128,
|
||||
|
|
|
@ -162,7 +162,40 @@ oml.ui.infoView = function() {
|
|||
}
|
||||
|
||||
function renderMediaButton(data) {
|
||||
return data.mediastate == 'transferring'
|
||||
return data.mediastate == 'unavailable'
|
||||
? Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Button({
|
||||
title: Ox._('Download Book'),
|
||||
width: 112
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
data.mediastate = 'transferring';
|
||||
that.update(data, $data);
|
||||
oml.api.download({id: ui.item}, function(result) {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
}),
|
||||
Ox.MenuButton({
|
||||
items: [
|
||||
{id: '', title: Ox._('Library')}
|
||||
],
|
||||
overlap: 'left',
|
||||
title: 'list',
|
||||
tooltip: Ox._('Download Book to a List'),
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
],
|
||||
float: 'right'
|
||||
})
|
||||
: data.mediastate == 'transferring'
|
||||
? Ox.FormElementGroup({
|
||||
elements: [
|
||||
Ox.Button({
|
||||
|
@ -192,29 +225,13 @@ oml.ui.infoView = function() {
|
|||
],
|
||||
float: 'right'
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
: Ox.Button({
|
||||
title: Ox._(
|
||||
data.mediastate == 'available' ? 'Read Book' : 'Download Book'
|
||||
),
|
||||
title: Ox._('Read Book'),
|
||||
width: 128
|
||||
})
|
||||
.css({
|
||||
marginTop: '8px'
|
||||
})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
if (data.mediastate == 'available') {
|
||||
oml.UI.set({itemView: 'book'});
|
||||
} else {
|
||||
data.mediastate = 'transferring';
|
||||
that.update(data, $data);
|
||||
oml.api.download({id: ui.item}, function(result) {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
oml.UI.set({itemView: 'book'});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -339,10 +356,50 @@ oml.ui.infoView = function() {
|
|||
|
||||
} else if ($element == $data) {
|
||||
|
||||
$mediaButton = renderMediaButton(data)
|
||||
.appendTo($data);
|
||||
|
||||
$('<div>')
|
||||
.addClass('OxSelectable')
|
||||
.css({
|
||||
marginTop: '8px',
|
||||
})
|
||||
.text(
|
||||
[
|
||||
data.extension.toUpperCase(),
|
||||
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);
|
||||
Ox.EditableContent({
|
||||
editable: false,
|
||||
format: function(value) {
|
||||
return value ? Ox.formatDate(value, '%b %e, %Y') : '';
|
||||
},
|
||||
placeholder: Ox._('unknown'),
|
||||
value: data[id] || ''
|
||||
})
|
||||
.appendTo($data);
|
||||
}
|
||||
});
|
||||
|
||||
Ox.Button({
|
||||
title: Ox._('Identify Book...'),
|
||||
width: 128
|
||||
})
|
||||
.css({marginTop: '16px'})
|
||||
.bindEvent({
|
||||
click: function() {
|
||||
identify(data);
|
||||
|
@ -385,42 +442,6 @@ oml.ui.infoView = function() {
|
|||
|
||||
});
|
||||
|
||||
$mediaButton = renderMediaButton(data)
|
||||
.appendTo($data);
|
||||
|
||||
$('<div>')
|
||||
.addClass('OxSelectable')
|
||||
.css({
|
||||
marginTop: '8px',
|
||||
})
|
||||
.text(
|
||||
[
|
||||
data.extension.toUpperCase(),
|
||||
Ox.formatValue(data.size, 'B')
|
||||
].join(', ')
|
||||
)
|
||||
.appendTo($data);
|
||||
|
||||
['accessed', 'modified', 'added', 'created'].forEach(function(id) {
|
||||
var title = Ox.getObjectById(oml.config.itemKeys, id).title;
|
||||
$('<div>')
|
||||
.css({
|
||||
marginTop: '8px',
|
||||
fontWeight: 'bold'
|
||||
})
|
||||
.text(title)
|
||||
.appendTo($data);
|
||||
Ox.EditableContent({
|
||||
editable: false,
|
||||
format: function(value) {
|
||||
return value ? Ox.formatDate(value, '%b %e, %Y') : '';
|
||||
},
|
||||
placeholder: Ox._('unknown'),
|
||||
value: data[id] || ''
|
||||
})
|
||||
.appendTo($data);
|
||||
});
|
||||
|
||||
$('<div>').css({height: '16px'}).appendTo($data);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ oml.ui.leftPanel = function() {
|
|||
resize: function(data) {
|
||||
ui.sidebarSize = data.size;
|
||||
oml.resizeListFolders();
|
||||
that.size(2, data.size);
|
||||
that.size(2, oml.getInfoHeight());
|
||||
if (!ui.showInfo) {
|
||||
that.css({bottom: -data.size + 'px'});
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ oml.ui.list = function() {
|
|||
}
|
||||
});
|
||||
|
||||
oml.enableDragAndDrop(that);
|
||||
|
||||
return that;
|
||||
|
||||
};
|
|
@ -46,151 +46,8 @@ oml.ui.mainMenu = function() {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'listMenu',
|
||||
title: Ox._('List'),
|
||||
items: [
|
||||
{
|
||||
id: 'newlist',
|
||||
title: Ox._('New List'),
|
||||
keyboard: 'control n'
|
||||
},
|
||||
{
|
||||
id: 'newlistfromselection',
|
||||
title: Ox._('New List from Selection'),
|
||||
keyboard: 'shift control n',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'newsmartlist',
|
||||
title: Ox._('New Smart List'),
|
||||
keyboard: 'alt control n'
|
||||
},
|
||||
{
|
||||
id: 'newsmartlistfromresults',
|
||||
title: Ox._('New Smart List from Results'),
|
||||
keyboard: 'shift alt control n',
|
||||
disabled: true
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'duplicatelist',
|
||||
title: Ox._('Duplicate List'),
|
||||
keyboard: 'control d',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'editlist',
|
||||
title: Ox._('Edit List...'),
|
||||
keyboard: 'return',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
id: 'deletelist',
|
||||
title: Ox._('Delete List...'),
|
||||
keyboard: 'delete',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'editMenu',
|
||||
title: Ox._('Edit'),
|
||||
items: [
|
||||
{
|
||||
id: 'importitems',
|
||||
title: Ox._('Import Books...')
|
||||
},
|
||||
{
|
||||
id: 'exportitems',
|
||||
title: Ox._('Export Books...')
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'download',
|
||||
title: Ox._('Download Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control d'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'selectall',
|
||||
title: Ox._('Select All'),
|
||||
keyboard: 'control a'
|
||||
},
|
||||
{
|
||||
id: 'selectnone',
|
||||
title: Ox._('Select None'),
|
||||
keyboard: 'shift control a'
|
||||
},
|
||||
{
|
||||
id: 'invertselection',
|
||||
title: Ox._('Invert Selection'),
|
||||
keyboard: 'alt control a'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'cut',
|
||||
title: Ox._('Cut Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control x'
|
||||
},
|
||||
{
|
||||
id: 'cutadd',
|
||||
title: Ox._('Cut and Add to Clipboard'),
|
||||
disabled: true,
|
||||
keyboard: 'shift control x'
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
title: Ox._('Copy Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control c'
|
||||
},
|
||||
{
|
||||
id: 'copyadd',
|
||||
title: Ox._('Copy and Add to Clipboard'),
|
||||
disabled: true,
|
||||
keyboard: 'shift control c'
|
||||
},
|
||||
{
|
||||
id: 'paste',
|
||||
title: Ox._('Paste Items'),
|
||||
disabled: true,
|
||||
keyboard: 'control v'
|
||||
},
|
||||
{
|
||||
id: 'clearclipboard',
|
||||
title: Ox._('Clear Clipboard'),
|
||||
disabled: true
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'delete',
|
||||
title: Ox._('Delete Items from List'),
|
||||
disabled: true,
|
||||
keyboard: 'delete'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'undo',
|
||||
title: Ox._('Undo'),
|
||||
disabled: true,
|
||||
keyboard: 'control z'
|
||||
},
|
||||
{
|
||||
id: 'redo',
|
||||
title: Ox._('Redo'),
|
||||
disabled: true,
|
||||
keyboard: 'shift control z'
|
||||
},
|
||||
{
|
||||
id: 'clearhistory',
|
||||
title: Ox._('Clear History'),
|
||||
disabled: true,
|
||||
}
|
||||
]
|
||||
},
|
||||
getListMenu(),
|
||||
getEditMenu(),
|
||||
{
|
||||
id: 'viewMenu',
|
||||
title: Ox._('View'),
|
||||
|
@ -529,6 +386,9 @@ oml.ui.mainMenu = function() {
|
|||
oml.UI.set({showSidebar: !ui.showSidebar});
|
||||
},
|
||||
oml_find: function() {
|
||||
that.replaceMenu('listMenu', getListMenu());
|
||||
that.replaceMenu('editMenu', getEditMenu());
|
||||
/*
|
||||
var action = Ox.startsWith(ui._list, ':') && ui._list != ':'
|
||||
? 'enableItem' : 'disableItem';
|
||||
that[
|
||||
|
@ -537,6 +397,7 @@ oml.ui.mainMenu = function() {
|
|||
]('duplicatelist');
|
||||
that[action]('editlist');
|
||||
that[action]('deletelist');
|
||||
*/
|
||||
},
|
||||
oml_item: function(data) {
|
||||
if (!!data.value != !!data.previousValue) {
|
||||
|
@ -544,6 +405,9 @@ oml.ui.mainMenu = function() {
|
|||
that[data.value ? 'enableItem' : 'disableItem']('showbrowser');
|
||||
}
|
||||
},
|
||||
oml_listselection: function(data) {
|
||||
that.replaceMenu('editMenu', getEditMenu());
|
||||
},
|
||||
oml_showbrowser: function(data) {
|
||||
that.setItemTitle('showbrowser', Ox._((data.value ? 'Hide' : 'Show') + ' Browser'));
|
||||
},
|
||||
|
@ -559,10 +423,206 @@ oml.ui.mainMenu = function() {
|
|||
},
|
||||
});
|
||||
|
||||
function getItemMenu() {
|
||||
// ...
|
||||
function getEditMenu() {
|
||||
var listData = oml.getListData(),
|
||||
username = oml.user.preferences.username,
|
||||
selectionItems = ui.listSelection.length,
|
||||
selectionItemName = (
|
||||
selectionItems > 1 ? Ox.formatNumber(selectionItems) + ' ' : ''
|
||||
) + Ox._(clipboardItems == 1 ? 'Book' : 'Books'),
|
||||
clipboardItems = oml.clipboard.items(),
|
||||
clipboardType = oml.clipboard.type(),
|
||||
clipboardItemName = !clipboardItems ? ''
|
||||
: (
|
||||
clipboardItems > 1 ? Ox.formatNumber(clipboardItems) + ' ' : ''
|
||||
) + Ox._(clipboardItems == 1 ? 'Book' : 'Books'),
|
||||
canSelect = !ui.item,
|
||||
canCopy = canSelect && selectionItems,
|
||||
canCut = canCopy && listData.editable,
|
||||
canPaste = listData.editable && clipboardItems,
|
||||
canAdd = canCopy && clipboardItems && clipboardItemType == ui.section,
|
||||
canDownload = listData.user != username && selectionItems,
|
||||
historyItems = oml.history.items(),
|
||||
undoText = oml.history.undoText(),
|
||||
redoText = oml.history.redoText();
|
||||
return {
|
||||
id: 'editMenu',
|
||||
title: Ox._('Edit'),
|
||||
items: [
|
||||
{
|
||||
id: 'importitems',
|
||||
title: Ox._('Import Books...')
|
||||
},
|
||||
{
|
||||
id: 'exportitems',
|
||||
title: Ox._('Export Books...')
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'download',
|
||||
title: Ox._('Download {0}', [selectionItemName]),
|
||||
disabled: !canDownload,
|
||||
keyboard: 'control d'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'selectall',
|
||||
title: Ox._('Select All'),
|
||||
disabled: !canSelect,
|
||||
keyboard: 'control a'
|
||||
},
|
||||
{
|
||||
id: 'selectnone',
|
||||
title: Ox._('Select None'),
|
||||
disabled: !canSelect,
|
||||
keyboard: 'shift control a'
|
||||
},
|
||||
{
|
||||
id: 'invertselection',
|
||||
title: Ox._('Invert Selection'),
|
||||
disabled: !canSelect,
|
||||
keyboard: 'alt control a'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'cut',
|
||||
title: Ox._('Cut {0}', [selectionItemName]),
|
||||
disabled: !canCut,
|
||||
keyboard: 'control x'
|
||||
},
|
||||
{
|
||||
id: 'cutadd',
|
||||
title: Ox._('Cut and Add to Clipboard'),
|
||||
disabled: !canCut || !canAdd,
|
||||
keyboard: 'shift control x'
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
title: Ox._('Copy {0}', [selectionItemName]),
|
||||
disabled: !canCopy,
|
||||
keyboard: 'control c'
|
||||
},
|
||||
{
|
||||
id: 'copyadd',
|
||||
title: Ox._('Copy and Add to Clipboard'),
|
||||
disabled: !canCopy || !canAdd,
|
||||
keyboard: 'shift control c'
|
||||
},
|
||||
{
|
||||
id: 'paste',
|
||||
title: !clipboardItems ? Ox._('Paste') : Ox._('Paste {0}', [clipboardItemName]),
|
||||
disabled: !canPaste,
|
||||
keyboard: 'control v'
|
||||
},
|
||||
{
|
||||
id: 'clearclipboard',
|
||||
title: Ox._('Clear Clipboard'),
|
||||
disabled: !clipboardItems
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'delete',
|
||||
title: Ox._('Delete {0} from List', [selectionItemName]),
|
||||
disabled: !canCut,
|
||||
keyboard: 'delete'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'undo',
|
||||
title: undoText ? Ox._('Undo {0}', [undoText]) : Ox._('Undo'),
|
||||
disabled: !undoText,
|
||||
keyboard: 'control z'
|
||||
},
|
||||
{
|
||||
id: 'redo',
|
||||
title: redoText ? Ox._('Redo {0}', [redoText]) : Ox._('Redo'),
|
||||
disabled: !redoText,
|
||||
keyboard: 'shift control z'
|
||||
},
|
||||
{
|
||||
id: 'clearhistory',
|
||||
title: Ox._('Clear History'),
|
||||
disabled: !historyItems,
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function getListMenu() {
|
||||
var isLibraries = !ui._list,
|
||||
isLibrary = Ox.endsWith(ui._list, ':'),
|
||||
isList = !isLibraries && !isLibrary,
|
||||
isOwnList = ui._list[0] == ':';
|
||||
return {
|
||||
id: 'listMenu',
|
||||
title: Ox._('List'),
|
||||
items: [
|
||||
{
|
||||
id: 'libraries',
|
||||
title: Ox._('All Libraries'),
|
||||
keyboard: 'shift control w'
|
||||
},
|
||||
{
|
||||
id: 'library',
|
||||
title: Ox._('This Library'),
|
||||
disabled: isLibraries,
|
||||
keyboard: isLibrary ? 'control w' : ''
|
||||
},
|
||||
{
|
||||
id: 'list',
|
||||
title: Ox._('This List'),
|
||||
disabled: isLibrary,
|
||||
keyboard: isLibrary ? '' : 'control w'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'newlist',
|
||||
title: Ox._('New List'),
|
||||
keyboard: 'control n'
|
||||
},
|
||||
{
|
||||
id: 'newlistfromselection',
|
||||
title: Ox._('New List from Selection'),
|
||||
keyboard: 'shift control n',
|
||||
disabled: !ui.listSelection.length
|
||||
},
|
||||
{
|
||||
id: 'newsmartlist',
|
||||
title: Ox._('New Smart List'),
|
||||
keyboard: 'alt control n'
|
||||
},
|
||||
{
|
||||
id: 'newsmartlistfromresults',
|
||||
title: Ox._('New Smart List from Results'),
|
||||
keyboard: 'shift alt control n'
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: 'duplicatelist',
|
||||
title: Ox._('Duplicate List'),
|
||||
disabled: !isList
|
||||
},
|
||||
{
|
||||
id: 'editlist',
|
||||
title: Ox._('Edit List...'),
|
||||
keyboard: 'return',
|
||||
disabled: !isOwnList
|
||||
},
|
||||
{
|
||||
id: 'deletelist',
|
||||
title: Ox._('Delete List...'),
|
||||
keyboard: 'delete',
|
||||
disabled: !isOwnList
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
that.update = function() {
|
||||
return that.updateMenu('listMenu', getListMenu())
|
||||
.updateMenu('editMenu', getEditMenu());
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
|
|
|
@ -453,12 +453,12 @@ oml.ui.usersDialog = function() {
|
|||
})
|
||||
.bindEvent({
|
||||
select: function(data) {
|
||||
$lists.forEach(function($element) {
|
||||
if ($element != $list) {
|
||||
$element.options({selected: []});
|
||||
}
|
||||
});
|
||||
renderUser(Ox.getObjectById(users, data.ids[0]));
|
||||
if (data.ids.length) {
|
||||
selectItem($list);
|
||||
renderUser(Ox.getObjectById(users, data.ids[0]));
|
||||
} else {
|
||||
renderUser();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,3 +1,564 @@
|
|||
oml.addList = function() {
|
||||
// addList(isSmart, isFrom) or addList(list) [=dupicate]
|
||||
var args = arguments,
|
||||
isDuplicate = args.length == 1,
|
||||
isSmart, isFrom, list, listData, data;
|
||||
oml.api.getLists(function(result) {
|
||||
var lists = result.data.lists,
|
||||
listNames = lists[oml.user.id].map(function(list) {
|
||||
return list.name;
|
||||
}),
|
||||
query;
|
||||
if (!isDuplicate) {
|
||||
isSmart = args[0];
|
||||
isFrom = args[1];
|
||||
data = {
|
||||
name: oml.validateName(Ox._('Untitled'), listNames),
|
||||
type: !isSmart ? 'static' : 'smart'
|
||||
};
|
||||
if (isFrom) {
|
||||
if (!isSmart) {
|
||||
data.items = ui.listSelection;
|
||||
} else {
|
||||
data.query = ui.find;
|
||||
}
|
||||
}
|
||||
addList();
|
||||
} else {
|
||||
list = args[0];
|
||||
listData = Ox.getObjectById(Ox.flatten(Ox.values(lists)), list);
|
||||
data = Ox.extend({
|
||||
name: oml.validateName(listData.name, listNames),
|
||||
type: listData.type
|
||||
}, listData.query ? {
|
||||
query: listData.query
|
||||
} : {});
|
||||
if (!data.query) {
|
||||
var query = {
|
||||
conditions: [{key: 'list', operator: '==', value: list}],
|
||||
operator: '&'
|
||||
};
|
||||
oml.api.find({query: query}, function(result) {
|
||||
if (result.data.items) {
|
||||
oml.api.find({
|
||||
query: query,
|
||||
keys: ['id'],
|
||||
sort: [{key: 'id', operator: '+'}],
|
||||
range: [0, result.data.items]
|
||||
}, function(result) {
|
||||
data.items = result.data.items.map(function(item) {
|
||||
return item.id;
|
||||
});
|
||||
addList();
|
||||
});
|
||||
} else {
|
||||
addList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addList();
|
||||
}
|
||||
}
|
||||
});
|
||||
function addList() {
|
||||
oml.api.addList(data, function(result) {
|
||||
var list = result.data.id,
|
||||
$folderList = oml.$ui.folderList[0];
|
||||
oml.$ui.folder[0].options({collapsed: false}); // FIXME: SET UI!
|
||||
// FIXME: DOESN'T WORK
|
||||
$folderList
|
||||
.bindEventOnce({
|
||||
load: function() {
|
||||
$folderList
|
||||
.gainFocus()
|
||||
.options({selected: [list]});
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: list
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||
}
|
||||
});
|
||||
oml.updateLists();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
oml.clearFilters = function() {
|
||||
var ui = oml.user.ui,
|
||||
find = Ox.clone(ui.find, true),
|
||||
indices = ui._filterState.map(function(filterState) {
|
||||
return filterState.index;
|
||||
}).filter(function(index) {
|
||||
return index > -1;
|
||||
});
|
||||
find.conditions = find.conditions.filter(function(condition, index) {
|
||||
return !Ox.contains(indices, index);
|
||||
});
|
||||
oml.UI.set({find: find});
|
||||
};
|
||||
|
||||
oml.deleteList = function() {
|
||||
var ui = oml.user.ui;
|
||||
oml.ui.confirmDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
title: Ox._('No, Keep List')
|
||||
}),
|
||||
Ox.Button({
|
||||
title: Ox._('Yes, Delete List')
|
||||
})
|
||||
],
|
||||
content: Ox._('Are you sure you want to delete this list?'),
|
||||
title: Ox._('Delete List')
|
||||
}, function() {
|
||||
oml.api.removeList({
|
||||
id: ui._list
|
||||
}, function() {
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: ':'
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
oml.updateLists();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
oml.doHistory = function(action, items, targets, callback) {
|
||||
items = Ox.makeArray(items);
|
||||
targets = Ox.makeArray(targets);
|
||||
if (action == 'copy' || action == 'paste') {
|
||||
addItems(items, targets[0], addToHistory);
|
||||
} else if (action == 'cut' || action == 'delete') {
|
||||
removeItems(items, targets[0], addToHistory);
|
||||
} else if (action == 'move') {
|
||||
removeItems(items, targets[0], function() {
|
||||
addItems(items, targets[1], addToHistory);
|
||||
});
|
||||
}
|
||||
function addToHistory(result, addedItems) {
|
||||
var actions = {
|
||||
copy: 'Copying',
|
||||
cut: 'Cutting',
|
||||
'delete': 'Deleting',
|
||||
move: 'Moving',
|
||||
paste: 'Pasting'
|
||||
},
|
||||
length = items.length,
|
||||
text = Ox._(actions[action]) + ' ' + (
|
||||
length == 1 ? 'Book' : 'Books'
|
||||
);
|
||||
oml.history.add({
|
||||
action: action,
|
||||
items: action == 'cut' || action == 'delete' ? [items]
|
||||
: action == 'copy' || action == 'paste' ? [addedItems]
|
||||
: [items, addedItems], // move
|
||||
positions: [],
|
||||
targets: targets,
|
||||
text: text
|
||||
});
|
||||
callback(result);
|
||||
}
|
||||
};
|
||||
|
||||
oml.redoHistory = function(callback) {
|
||||
var object = oml.history.redo();
|
||||
if (object) {
|
||||
if (object.action == 'copy' || object.action == 'paste') {
|
||||
addItems(object.items[0], object.targets[0], done);
|
||||
} else if (object.action == 'cut' || object.action == 'delete') {
|
||||
removeItems(object.items[0], object.targets[0], done);
|
||||
} else if (object.action == 'move') {
|
||||
removeItems(object.items[0], object.targets[0], function() {
|
||||
addItems(object.items[1], object.targets[1], done);
|
||||
});
|
||||
}
|
||||
}
|
||||
function done() {
|
||||
doneHistory(object, callback);
|
||||
}
|
||||
};
|
||||
|
||||
oml.undoHistory = function(callback) {
|
||||
var object = oml.history.undo();
|
||||
if (object) {
|
||||
if (object.action == 'copy' || object.action == 'paste') {
|
||||
removeItems(object.items[0], object.targets[0], done);
|
||||
} else if (object.action == 'cut' || object.action == 'delete') {
|
||||
addItems(object.items[0], object.targets[0], done);
|
||||
} else if (object.action == 'move') {
|
||||
removeItems(object.items[1], object.targets[1], function() {
|
||||
addItems(object.items[0], object.targets[0], done);
|
||||
});
|
||||
}
|
||||
}
|
||||
function done() {
|
||||
doneHistory(object, callback);
|
||||
}
|
||||
};
|
||||
|
||||
function addItems(items, target, callback) {
|
||||
oml.api.find({
|
||||
query: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: target
|
||||
}],
|
||||
operator: '&'
|
||||
},
|
||||
positions: items
|
||||
}, function(result) {
|
||||
var existingItems = Object.keys(result.data.positions),
|
||||
addedItems = items.filter(function(item) {
|
||||
return !Ox.contains(existingItems, item);
|
||||
});
|
||||
if (addedItems.length) {
|
||||
oml.api.addListItems({
|
||||
items: addedItems,
|
||||
list: target
|
||||
}, function(result) {
|
||||
callback(result, addedItems);
|
||||
});
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function doneHistory(object, callback) {
|
||||
var list, listData, ui = oml.user.ui;
|
||||
Ox.Request.clearCache('find');
|
||||
object.targets.filter(function(list) {
|
||||
return list != ui._list;
|
||||
}).forEach(function(list) {
|
||||
listData = oml.getListData(list);
|
||||
oml.api.find({
|
||||
query: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: list
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
}, function(result) {
|
||||
oml.$ui.folderList[listData.folder].value(
|
||||
list, 'items', result.data.items
|
||||
);
|
||||
});
|
||||
});
|
||||
if (Ox.contains(object.targets, ui._list)) {
|
||||
// FIXME: Why is this timeout needed?
|
||||
setTimeout(oml.reloadList, 250);
|
||||
}
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
function removeItems(items, target, callback) {
|
||||
oml.api.removeListItems({
|
||||
items: items,
|
||||
list: target
|
||||
}, callback);
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
oml.enableDragAndDrop = function($list, canMove) {
|
||||
|
||||
var $tooltip = Ox.Tooltip({
|
||||
animate: false
|
||||
}),
|
||||
drag = {},
|
||||
scrollInterval,
|
||||
ui = oml.user.ui,
|
||||
username = oml.user.preferences.username;
|
||||
|
||||
$list.bindEvent({
|
||||
draganddropstart: function(data) {
|
||||
Ox.print('DND START', data);
|
||||
var $lists = oml.$ui.libraryList.concat(oml.$ui.folderList);
|
||||
drag.action = 'copy';
|
||||
drag.ids = $list.options('selected');
|
||||
drag.item = drag.ids.length == 1
|
||||
? $list.value(drag.ids[0], 'title')
|
||||
: drag.ids.length;
|
||||
drag.source = oml.getListData();
|
||||
drag.targets = {};
|
||||
$lists.forEach(function($list) {
|
||||
$list.addClass('OxDroppable').find('.OxItem').each(function() {
|
||||
var $item = $(this),
|
||||
id = $item.data('id'),
|
||||
data = oml.getListData(id);
|
||||
drag.targets[id] = Ox.extend(data, {
|
||||
editable: data.editable || (
|
||||
data.type == 'library'
|
||||
&& drag.source.user != username
|
||||
&& data.user == 'username'
|
||||
),
|
||||
selected: data.id == ui._list
|
||||
}, data);
|
||||
if (!drag.targets[id].selected && drag.targets[id].editable) {
|
||||
$item.addClass('OxDroppable');
|
||||
}
|
||||
});
|
||||
});
|
||||
$tooltip.options({title: getTitle()}).show(data.event);
|
||||
Ox.UI.$window.on({
|
||||
keydown: keydown,
|
||||
keyup: keyup
|
||||
});
|
||||
},
|
||||
draganddrop: function(data) {
|
||||
var event = data.event;
|
||||
$tooltip.options({
|
||||
title: getTitle(event)
|
||||
}).show(event);
|
||||
if (scrollInterval && !isAtListsTop(event) && !isAtListsBottom(event)) {
|
||||
clearInterval(scrollInterval);
|
||||
scrollInterval = 0;
|
||||
}
|
||||
},
|
||||
draganddroppause: function(data) {
|
||||
var event = data.event, scroll, title,
|
||||
ui = oml.user.ui,
|
||||
$parent, $grandparent, $panel;
|
||||
if (!ui.showSidebar) {
|
||||
if (event.clientX < 16 && event.clientY >= 44
|
||||
&& event.clientY < window.innerHeight - 16
|
||||
) {
|
||||
oml.$ui.mainPanel.toggle(0);
|
||||
}
|
||||
} else {
|
||||
$parent = $(event.target).parent();
|
||||
$grandparent = $parent.parent();
|
||||
$panel = $parent.is('.OxCollapsePanel') ? $parent
|
||||
: $grandparent.is('.OxCollapsePanel') ? $grandparent
|
||||
: null;
|
||||
if ($panel) {
|
||||
title = $panel.children('.OxBar').children('.OxTitle')
|
||||
.html().split(' ')[0].toLowerCase();
|
||||
if (!ui.showFolder[title]) {
|
||||
Ox.UI.elements[$panel.data('oxid')].options({
|
||||
collapsed: false
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!scrollInterval) {
|
||||
scroll = isAtListsTop(event) ? -16
|
||||
: isAtListsBottom(event) ? 16
|
||||
: 0
|
||||
if (scroll) {
|
||||
scrollInterval = setInterval(function() {
|
||||
oml.$ui.folders.scrollTop(
|
||||
oml.$ui.folders.scrollTop() + scroll
|
||||
);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
draganddropenter: function(data) {
|
||||
var $parent = $(data.event.target).parent(),
|
||||
$item = $parent.is('.OxItem') ? $parent : $parent.parent(),
|
||||
$list = $item.parent().parent().parent().parent();
|
||||
if ($list.is('.OxDroppable')) {
|
||||
$item.addClass('OxDrop');
|
||||
drag.target = drag.targets[$item.data('id')];
|
||||
} else {
|
||||
drag.target = null;
|
||||
}
|
||||
},
|
||||
draganddropleave: function(data) {
|
||||
var $parent = $(data.event.target).parent(),
|
||||
$item = $parent.is('.OxItem') ? $parent : $parent.parent();
|
||||
if ($item.is('.OxDroppable')) {
|
||||
$item.removeClass('OxDrop');
|
||||
drag.target = null;
|
||||
}
|
||||
},
|
||||
draganddropend: function(data) {
|
||||
var targets;
|
||||
Ox.UI.$window.off({
|
||||
keydown: keydown,
|
||||
keyup: keyup
|
||||
});
|
||||
if (
|
||||
drag.target && drag.target.editable && !drag.target.selected
|
||||
&& (drag.action == 'copy' || drag.source.editable)
|
||||
) {
|
||||
var targets = drag.action == 'copy' ? drag.target.id
|
||||
: [oml.user.ui._list, drag.target.id];
|
||||
oml.doHistory(drag.action, data.ids, targets, function() {
|
||||
Ox.Request.clearCache('find');
|
||||
oml.api.find({
|
||||
query: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: drag.target.id
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
}, function(result) {
|
||||
oml.$ui.folderList[drag.target.folder].value(
|
||||
drag.target.id, 'items', result.data.items
|
||||
);
|
||||
cleanup(250);
|
||||
});
|
||||
drag.action == 'move' && oml.reloadList();
|
||||
});
|
||||
} else {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function cleanup(ms) {
|
||||
ms = ms || 0;
|
||||
drag = {};
|
||||
clearInterval(scrollInterval);
|
||||
scrollInterval = 0;
|
||||
setTimeout(function() {
|
||||
$('.OxDroppable').removeClass('OxDroppable');
|
||||
$('.OxDrop').removeClass('OxDrop');
|
||||
$tooltip.hide();
|
||||
}, ms);
|
||||
}
|
||||
|
||||
function getTitle() {
|
||||
var image, text,
|
||||
actionText = drag.action == 'copy' ? (
|
||||
drag.source.user == username ? 'copy' : 'download'
|
||||
) : 'move',
|
||||
itemText = Ox.isString(drag.item)
|
||||
? '"' + Ox.encodeHTMLEntities(Ox.truncate(drag.item, 32)) + '"'
|
||||
: Ox._('{0} books', [drag.item]),
|
||||
targetText;
|
||||
if (drag.action == 'move') {
|
||||
if (drag.source.user != username) {
|
||||
text = Ox._('You can only remove books<br>from your own lists.');
|
||||
} else if (drag.source.type == 'library') {
|
||||
text = Ox._('You cannot move books<br>out of your library.');
|
||||
} else if (drag.source.type == 'smart') {
|
||||
text = Ox._('You cannot move books<br>out of a smart list.');
|
||||
}
|
||||
} else if (drag.target) {
|
||||
targetText = drag.target.type == 'libraries' ? Ox._('a library')
|
||||
: drag.target.type == 'library' ? Ox._('your library')
|
||||
: Ox._('the list "{0}"', [Ox.encodeHTMLEntities(Ox.truncate(drag.target.name, 32))]);
|
||||
if (
|
||||
(
|
||||
drag.target.type == 'library'
|
||||
&& drag.source.user == username
|
||||
&& drag.target.user == username
|
||||
)
|
||||
|| drag.target.selected
|
||||
) {
|
||||
text = Ox._('{0}<br>is already in {1}.', [
|
||||
Ox._(itemText[0] == '"' ? '' : 'These ') + itemText,
|
||||
targetText
|
||||
]);
|
||||
} else if (drag.target.user != username) {
|
||||
text = Ox._(
|
||||
'You can only {0} books<br>to your own {1}.',
|
||||
[actionText, drag.target.type == 'library' ? 'library' : 'lists']
|
||||
);
|
||||
} else if (drag.target.type == 'smart') {
|
||||
text = Ox._('You cannot {0} books<br>to a smart list.', [actionText]);
|
||||
}
|
||||
}
|
||||
if (text) {
|
||||
image = 'symbolClose'
|
||||
} else {
|
||||
image = drag.action == 'copy' ? (
|
||||
drag.source.user == username ? 'symbolAdd' : 'symbolDownload'
|
||||
) : 'symbolRemove',
|
||||
text = Ox._(Ox.toTitleCase(actionText)) + ' ' + (
|
||||
Ox.isString(drag.item)
|
||||
? '"' + Ox.encodeHTMLEntities(Ox.truncate(drag.item, 32)) + '"'
|
||||
: drag.item + ' ' + 'books'
|
||||
) + '<br>' + (
|
||||
drag.target && drag.target.editable && !drag.target.selected
|
||||
? Ox._('to {0}.', [targetText])
|
||||
: drag.source.user == username
|
||||
? Ox._('to {0} list.', [ui._list == ':' ? 'a' : 'another'])
|
||||
: Ox._('to your library or to one of your lists.')
|
||||
);
|
||||
}
|
||||
return $('<div>')
|
||||
.append(
|
||||
$('<div>')
|
||||
.css({
|
||||
float: 'left',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
padding: '1px',
|
||||
border: '3px solid rgb(' + Ox.Theme.getThemeData().symbolDefaultColor.join(', ') + ')',
|
||||
borderRadius: '12px',
|
||||
margin: '3px 2px 2px 2px'
|
||||
})
|
||||
.append(
|
||||
$('<img>')
|
||||
.attr({src: Ox.UI.getImageURL(image)})
|
||||
.css({width: '16px', height: '16px'})
|
||||
)
|
||||
)
|
||||
.append(
|
||||
$('<div>')
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '1px 2px 2px 2px',
|
||||
fontSize: '11px',
|
||||
whiteSpace: 'nowrap'
|
||||
})
|
||||
.html(text)
|
||||
);
|
||||
}
|
||||
|
||||
function isAtListsTop(e) {
|
||||
return ui.showSidebar
|
||||
&& e.clientX < ui.sidebarSize
|
||||
&& e.clientY >= 44 && e.clientY < 60;
|
||||
}
|
||||
|
||||
function isAtListsBottom(e) {
|
||||
var listsBottom = window.innerHeight - oml.getInfoHeight();
|
||||
return ui.showSidebar
|
||||
&& e.clientX < ui.sidebarSize
|
||||
&& e.clientY >= listsBottom - 16 && e.clientY < listsBottom;
|
||||
}
|
||||
|
||||
function keydown(e) {
|
||||
if (e.metaKey) {
|
||||
drag.action = 'move';
|
||||
$tooltip.options({title: getTitle()}).show();
|
||||
}
|
||||
}
|
||||
|
||||
function keyup(e) {
|
||||
if (drag.action == 'move') {
|
||||
drag.action = 'copy';
|
||||
$tooltip.options({title: getTitle()}).show();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
// Note: getFindState has to run after getListState and getFilterState
|
||||
|
@ -140,146 +701,64 @@
|
|||
|
||||
}());
|
||||
|
||||
oml.addList = function() {
|
||||
// addList(isSmart, isFrom) or addList(list) [=dupicate]
|
||||
var args = arguments,
|
||||
isDuplicate = args.length == 1,
|
||||
isSmart, isFrom, list, listData, data;
|
||||
oml.api.getLists(function(result) {
|
||||
var lists = result.data.lists,
|
||||
listNames = lists[oml.user.id].map(function(list) {
|
||||
return list.name;
|
||||
}),
|
||||
query;
|
||||
if (!isDuplicate) {
|
||||
isSmart = args[0];
|
||||
isFrom = args[1];
|
||||
data = {
|
||||
name: oml.validateName(Ox._('Untitled'), listNames),
|
||||
type: !isSmart ? 'static' : 'smart'
|
||||
};
|
||||
if (isFrom) {
|
||||
if (!isSmart) {
|
||||
data.items = ui.listSelection;
|
||||
} else {
|
||||
data.query = ui.find;
|
||||
}
|
||||
}
|
||||
addList();
|
||||
} else {
|
||||
list = args[0];
|
||||
listData = Ox.getObjectById(Ox.flatten(Ox.values(lists)), list);
|
||||
Ox.print('LISTDATA,', listData)
|
||||
data = Ox.extend({
|
||||
name: oml.validateName(listData.name, listNames),
|
||||
type: listData.type
|
||||
}, listData.query ? {
|
||||
query: listData.query
|
||||
} : {});
|
||||
if (!data.query) {
|
||||
var query = {
|
||||
conditions: [{key: 'list', operator: '==', value: list}],
|
||||
operator: '&'
|
||||
};
|
||||
oml.api.find({query: query}, function(result) {
|
||||
if (result.data.items) {
|
||||
oml.api.find({
|
||||
query: query,
|
||||
keys: ['id'],
|
||||
sort: [{key: 'id', operator: '+'}],
|
||||
range: [0, result.data.items]
|
||||
}, function(result) {
|
||||
data.items = result.data.items.map(function(item) {
|
||||
return item.id;
|
||||
});
|
||||
addList();
|
||||
});
|
||||
} else {
|
||||
addList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addList();
|
||||
}
|
||||
}
|
||||
});
|
||||
function addList() {
|
||||
Ox.print('DATA, ', data);
|
||||
oml.api.addList(data, function(result) {
|
||||
Ox.print('LIST ADDED', result.data);
|
||||
var list = result.data.id,
|
||||
$folderList = oml.$ui.folderList[0];
|
||||
oml.$ui.folder[0].options({collapsed: false}); // FIXME: SET UI!
|
||||
// FIXME: DOESN'T WORK
|
||||
$folderList
|
||||
.bindEventOnce({
|
||||
load: function() {
|
||||
$folderList
|
||||
.gainFocus()
|
||||
.options({selected: [list]});
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: list
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||
}
|
||||
});
|
||||
oml.updateLists();
|
||||
});
|
||||
}
|
||||
oml.getFileInfoColor = function(type, data) {
|
||||
return type == 'extension' ? (
|
||||
data.extension == 'epub' ? [[32, 160, 32], [0, 128, 0], [128, 255, 128]]
|
||||
: data.extension == 'pdf' ? (
|
||||
data.textsize
|
||||
? [[224, 32, 32], [192, 0, 0], [255, 192, 192]]
|
||||
: [[224, 128, 32], [192, 96, 0], [255, 192, 128]]
|
||||
)
|
||||
: data.extension == 'txt' ? [[255, 255, 255], [224, 224, 224], [0, 0, 0]]
|
||||
: [[96, 96, 96], [64, 64, 64], [192, 192, 192]]
|
||||
) : data.mediastate == 'available' ? [[32, 160, 32], [0, 128, 0], [128, 255, 128]]
|
||||
: data.mediastate == 'transferring' ? [[160, 160, 32], [128, 128, 0], [255, 255, 128]]
|
||||
: [[224, 32, 32], [192, 0, 0], [255, 192, 192]];
|
||||
};
|
||||
|
||||
oml.clearFilters = function() {
|
||||
var ui = oml.user.ui,
|
||||
find = Ox.clone(ui.find, true),
|
||||
indices = ui._filterState.map(function(filterState) {
|
||||
return filterState.index;
|
||||
}).filter(function(index) {
|
||||
return index > -1;
|
||||
});
|
||||
find.conditions = find.conditions.filter(function(condition, index) {
|
||||
return !Ox.contains(indices, index);
|
||||
});
|
||||
oml.UI.set({find: find});
|
||||
};
|
||||
|
||||
oml.deleteList = function() {
|
||||
oml.getFilterSizes = function() {
|
||||
var ui = oml.user.ui;
|
||||
oml.ui.confirmDialog({
|
||||
buttons: [
|
||||
Ox.Button({
|
||||
title: Ox._('No, Keep List')
|
||||
}),
|
||||
Ox.Button({
|
||||
title: Ox._('Yes, Delete List')
|
||||
})
|
||||
],
|
||||
content: Ox._('Are you sure you want to delete this list?'),
|
||||
title: Ox._('Delete List')
|
||||
}, function() {
|
||||
oml.api.removeList({
|
||||
id: ui._list
|
||||
}, function() {
|
||||
oml.UI.set({
|
||||
find: {
|
||||
conditions: [{
|
||||
key: 'list',
|
||||
operator: '==',
|
||||
value: ':'
|
||||
}],
|
||||
operator: '&'
|
||||
}
|
||||
});
|
||||
oml.updateLists();
|
||||
});
|
||||
});
|
||||
}
|
||||
return Ox.splitInt(
|
||||
window.innerWidth - ui.showSidebar * ui.sidebarSize - 1,
|
||||
5
|
||||
);
|
||||
};
|
||||
|
||||
oml.getInfoHeight = function() {
|
||||
return Math.min(
|
||||
oml.user.ui.sidebarSize,
|
||||
window.innerHeight - 20 - 24 - 16 - 1
|
||||
);
|
||||
};
|
||||
|
||||
oml.getListData = function(list) {
|
||||
var data = {}, ui = oml.user.ui;
|
||||
if (Ox.isUndefined(list)) {
|
||||
list = ui._list;
|
||||
}
|
||||
if (ui._lists) {
|
||||
data = ui._lists[list];
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
oml.getListFoldersHeight = function() {
|
||||
var ui = oml.user.ui;
|
||||
return Object.keys(ui.showFolder).reduce(function(value, id, index) {
|
||||
var items = oml.$ui.folderList[index].options('items').length;
|
||||
return value + 16 + ui.showFolder[id] * (1 + items) * 16;
|
||||
}, 16);
|
||||
};
|
||||
|
||||
oml.getListFoldersWidth = function() {
|
||||
var ui = oml.user.ui;
|
||||
return ui.sidebarSize - (
|
||||
oml.$ui.appPanel
|
||||
&& oml.getListFoldersHeight()
|
||||
> window.innerHeight - 20 - 24 - 1 - ui.showInfo * oml.getInfoHeight()
|
||||
? Ox.UI.SCROLLBAR_SIZE : 0
|
||||
);
|
||||
};
|
||||
|
||||
oml.getPageTitle = function(stateOrURL) {
|
||||
var page = Ox.getObjectById(
|
||||
|
@ -298,44 +777,47 @@ oml.getSortOperator = function(key) {
|
|||
) ? '+' : '-';
|
||||
};
|
||||
|
||||
oml.getFileTypeColor = function(data) {
|
||||
return data.extension == 'epub' ? [[0, 128, 0], [128, 255, 128]]
|
||||
: data.extension == 'pdf' ? (
|
||||
data.textsize ? [[192, 0, 0], [255, 192, 192]]
|
||||
: [[192, 96, 0], [255, 192, 128]]
|
||||
)
|
||||
: data.extension == 'txt' ? [[255, 255, 255], [0, 0, 0]]
|
||||
: [[64, 64, 64], [192, 192, 192]];
|
||||
};
|
||||
|
||||
oml.getFilterSizes = function() {
|
||||
var ui = oml.user.ui;
|
||||
return Ox.splitInt(
|
||||
window.innerWidth - ui.showSidebar * ui.sidebarSize - 1,
|
||||
5
|
||||
);
|
||||
};
|
||||
|
||||
oml.getListFoldersHeight = function() {
|
||||
var ui = oml.user.ui;
|
||||
return Object.keys(ui.showFolder).reduce(function(value, id, index) {
|
||||
var items = oml.$ui.folderList[index].options('items').length;
|
||||
Ox.print('REDUCE', value, id, index, '...', items)
|
||||
return value + 16 + ui.showFolder[id] * (1 + items) * 16;
|
||||
}, 16);
|
||||
};
|
||||
|
||||
oml.getListFoldersWidth = function() {
|
||||
var ui = oml.user.ui;
|
||||
Ox.print('HEIGHT::::', oml.getListFoldersHeight(), 'SCROLLBAR????', oml.$ui.appPanel
|
||||
&& oml.getListFoldersHeight()
|
||||
> window.innerHeight - 20 - 24 - 1 - ui.showInfo * ui.sidebarSize)
|
||||
return ui.sidebarSize - (
|
||||
oml.$ui.appPanel
|
||||
&& oml.getListFoldersHeight()
|
||||
> window.innerHeight - 20 - 24 - 1 - ui.showInfo * ui.sidebarSize
|
||||
? Ox.UI.SCROLLBAR_SIZE : 0
|
||||
);
|
||||
oml.getUsersAndLists = function(callback) {
|
||||
var lists = [{
|
||||
id: '',
|
||||
name: Ox._('All Libraries'),
|
||||
type: 'libraries'
|
||||
}],
|
||||
ui = oml.user.ui,
|
||||
username = oml.user.preferences.username,
|
||||
users = [{
|
||||
id: oml.user.id,
|
||||
nickname: username,
|
||||
online: oml.user.online
|
||||
}];
|
||||
oml.api.getUsers(function(result) {
|
||||
users = users.concat(
|
||||
result.data.users.filter(function(user) {
|
||||
return user.peered;
|
||||
})
|
||||
);
|
||||
users.forEach(function(user) {
|
||||
lists.push({
|
||||
id: (user.nickname == username ? '' : user.nickname) + ':',
|
||||
name: Ox._('Library'),
|
||||
type: 'library',
|
||||
user: user.nickname
|
||||
});
|
||||
});
|
||||
oml.api.getLists(function(result) {
|
||||
lists = lists.concat(result.data.lists);
|
||||
if (!ui.lists) {
|
||||
oml.$ui.mainMenu.update();
|
||||
}
|
||||
ui._lists = {};
|
||||
Ox.forEach(lists, function(list) {
|
||||
ui._lists[list.id] = Ox.extend(list, {
|
||||
editable: list.user == username && list.type == 'static',
|
||||
});
|
||||
});
|
||||
callback(users, lists);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
oml.hasDialogOrScreen = function() {
|
||||
|
@ -344,6 +826,11 @@ oml.hasDialogOrScreen = function() {
|
|||
|| !!$('.OxScreen').length;
|
||||
};
|
||||
|
||||
oml.reloadList = function() {
|
||||
Ox.print('RELOAD LIST NOT IMPLEMENTED')
|
||||
// ...
|
||||
};
|
||||
|
||||
oml.resizeFilters = function() {
|
||||
// ...
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue