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}
|
editlist name {name: newname}
|
||||||
orderlists [name, name, name]
|
orderlists [name, name, name]
|
||||||
removelist name
|
removelist name
|
||||||
additemtolist listname itemid
|
addlistitems listname [ids]
|
||||||
removeitemfromlist listname itemid
|
removelistitems listname [ids]
|
||||||
editusername username
|
editusername username
|
||||||
editcontact string
|
editcontact string
|
||||||
addpeer peerid peername
|
addpeer peerid peername
|
||||||
|
@ -182,24 +182,17 @@ class Changelog(db.Model):
|
||||||
l.remove()
|
l.remove()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_addlistitem(self, user, timestamp, name, itemid):
|
def action_addlistitems(self, user, timestamp, name, ids):
|
||||||
from item.models import Item
|
|
||||||
from user.models import List
|
from user.models import List
|
||||||
l = List.get(user.id, name)
|
l = List.get_or_create(user.id, name)
|
||||||
i = Item.get(itemid)
|
l.add_items(ids)
|
||||||
if l and i:
|
|
||||||
i.lists.append(l)
|
|
||||||
i.update()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_removelistitem(self, user, timestamp, name, itemid):
|
def action_removelistitem(self, user, timestamp, name, ids):
|
||||||
from item.models import Item
|
|
||||||
from user.models import List
|
from user.models import List
|
||||||
l = List.get(user.id, name)
|
l = List.get(user.id, name)
|
||||||
i = Item.get(itemid)
|
if l:
|
||||||
if l and i:
|
l.remove_items(ids)
|
||||||
i.lists.remove(l)
|
|
||||||
i.update()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_editusername(self, user, timestamp, username):
|
def action_editusername(self, user, timestamp, username):
|
||||||
|
|
|
@ -6,7 +6,6 @@ from flask import json
|
||||||
from oxflask.api import actions
|
from oxflask.api import actions
|
||||||
from oxflask.shortcuts import returns_json
|
from oxflask.shortcuts import returns_json
|
||||||
|
|
||||||
from oml import utils
|
|
||||||
import query
|
import query
|
||||||
|
|
||||||
import models
|
import models
|
||||||
|
@ -118,6 +117,8 @@ def edit(request):
|
||||||
if item and keys and item.json()['mediastate'] == 'available':
|
if item and keys and item.json()['mediastate'] == 'available':
|
||||||
key = keys[0]
|
key = keys[0]
|
||||||
print 'update mainid', key, data[key]
|
print 'update mainid', key, data[key]
|
||||||
|
if key in ('isbn10', 'isbn13'):
|
||||||
|
data[key] = utils.normalize_isbn(data[key])
|
||||||
item.update_mainid(key, data[key])
|
item.update_mainid(key, data[key])
|
||||||
response = item.json()
|
response = item.json()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -233,6 +233,12 @@ class Item(db.Model):
|
||||||
else:
|
else:
|
||||||
f.value = '%s:' % p.id
|
f.value = '%s:' % p.id
|
||||||
db.session.add(f)
|
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):
|
def update(self):
|
||||||
users = map(str, list(self.users))
|
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"/get/(.*)", ShareHandler, dict(app=app)),
|
||||||
(r".*", NodeHandler, 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'])
|
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, {
|
state.online = directory.put(settings.sk, {
|
||||||
'host': host,
|
'host': host,
|
||||||
'port': settings.server['node_port']
|
'port': settings.server['node_port']
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
import requests
|
|
||||||
from urlparse import urlparse
|
|
||||||
|
|
||||||
def get_public_ipv6():
|
def get_public_ipv6():
|
||||||
host = ('2a01:4f8:120:3201::3', 25519)
|
host = ('2a01:4f8:120:3201::3', 25519)
|
||||||
|
|
19
oml/nodes.py
19
oml/nodes.py
|
@ -19,6 +19,7 @@ from changelog import Changelog
|
||||||
|
|
||||||
import directory
|
import directory
|
||||||
from websocket import trigger_event
|
from websocket import trigger_event
|
||||||
|
from localnodes import LocalNodes
|
||||||
|
|
||||||
ENCODING='base64'
|
ENCODING='base64'
|
||||||
|
|
||||||
|
@ -26,8 +27,9 @@ class Node(object):
|
||||||
online = False
|
online = False
|
||||||
download_speed = 0
|
download_speed = 0
|
||||||
|
|
||||||
def __init__(self, app, user):
|
def __init__(self, nodes, user):
|
||||||
self._app = app
|
self._nodes = nodes
|
||||||
|
self._app = nodes._app
|
||||||
self.user_id = user.id
|
self.user_id = user.id
|
||||||
key = str(user.id)
|
key = str(user.id)
|
||||||
self.vk = ed25519.VerifyingKey(key, encoding=ENCODING)
|
self.vk = ed25519.VerifyingKey(key, encoding=ENCODING)
|
||||||
|
@ -35,6 +37,11 @@ class Node(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
local = self.get_local()
|
||||||
|
if local:
|
||||||
|
url = 'http://[%s]:%s' % (local['host'], local['port'])
|
||||||
|
print 'using local peer discovery to access node', url
|
||||||
|
else:
|
||||||
if ':' in self.host:
|
if ':' in self.host:
|
||||||
url = 'http://[%s]:%s' % (self.host, self.port)
|
url = 'http://[%s]:%s' % (self.host, self.port)
|
||||||
else:
|
else:
|
||||||
|
@ -51,6 +58,11 @@ class Node(object):
|
||||||
self.host = None
|
self.host = None
|
||||||
self.port = 9851
|
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):
|
def request(self, action, *args):
|
||||||
if not self.host:
|
if not self.host:
|
||||||
self.resolve_host()
|
self.resolve_host()
|
||||||
|
@ -211,6 +223,7 @@ class Nodes(Thread):
|
||||||
self._app = app
|
self._app = app
|
||||||
self._q = Queue()
|
self._q = Queue()
|
||||||
self._running = True
|
self._running = True
|
||||||
|
self._local = LocalNodes(app)
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
self.start()
|
self.start()
|
||||||
|
@ -238,7 +251,7 @@ class Nodes(Thread):
|
||||||
def _add_node(self, user_id):
|
def _add_node(self, user_id):
|
||||||
if user_id not in self._nodes:
|
if user_id not in self._nodes:
|
||||||
from user.models import User
|
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:
|
else:
|
||||||
self._nodes[user_id].online = True
|
self._nodes[user_id].online = True
|
||||||
trigger_event('status', {
|
trigger_event('status', {
|
||||||
|
|
|
@ -144,6 +144,8 @@ class Parser(object):
|
||||||
l = self._list.query.filter_by(user_id=p.id, name=name).first()
|
l = self._list.query.filter_by(user_id=p.id, name=name).first()
|
||||||
else:
|
else:
|
||||||
l = None
|
l = None
|
||||||
|
if l:
|
||||||
|
v = l.find_id
|
||||||
if l and l._query:
|
if l and l._query:
|
||||||
data = l._query
|
data = l._query
|
||||||
q = self.parse_conditions(data.get('conditions', []),
|
q = self.parse_conditions(data.get('conditions', []),
|
||||||
|
|
|
@ -38,7 +38,7 @@ server_defaults = {
|
||||||
'port': 9842,
|
'port': 9842,
|
||||||
'address': '127.0.0.1',
|
'address': '127.0.0.1',
|
||||||
'node_port': 9851,
|
'node_port': 9851,
|
||||||
'node_address': '::',
|
'node_address': '',
|
||||||
'extract_text': True,
|
'extract_text': True,
|
||||||
'directory_service': 'http://[2a01:4f8:120:3201::3]:25519',
|
'directory_service': 'http://[2a01:4f8:120:3201::3]:25519',
|
||||||
'lookup_service': 'http://data.openmedialibrary.com',
|
'lookup_service': 'http://data.openmedialibrary.com',
|
||||||
|
|
|
@ -84,9 +84,9 @@ actions.register(getUsers)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def getLists(request):
|
def getLists(request):
|
||||||
lists = {}
|
lists = []
|
||||||
for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)):
|
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 {
|
return {
|
||||||
'lists': lists
|
'lists': lists
|
||||||
}
|
}
|
||||||
|
@ -132,28 +132,24 @@ def editList(request):
|
||||||
actions.register(editList, cache=False)
|
actions.register(editList, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def addListItem(request):
|
def addListItems(request):
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
l = models.List.get_or_create(data['id'])
|
l = models.List.get_or_create(data['list'])
|
||||||
i = Item.get(data['item'])
|
if l:
|
||||||
if l and i:
|
l.add_items(data['items'])
|
||||||
l.items.append(i)
|
return l.json()
|
||||||
models.db.session.add(l)
|
|
||||||
i.update()
|
|
||||||
return {}
|
return {}
|
||||||
actions.register(addListItem, cache=False)
|
actions.register(addListItems, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def removeListItem(request):
|
def removeListItems(request):
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
l = models.List.get(data['id'])
|
l = models.List.get(data['list'])
|
||||||
i = Item.get(data['item'])
|
if l:
|
||||||
if l and i:
|
l.remove_items(data['items'])
|
||||||
l.items.remove(i)
|
return l.json()
|
||||||
models.db.session.add(l)
|
|
||||||
i.update()
|
|
||||||
return {}
|
return {}
|
||||||
actions.register(removeListItem, cache=False)
|
actions.register(removeListItems, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def sortLists(request):
|
def sortLists(request):
|
||||||
|
|
|
@ -59,7 +59,7 @@ class User(db.Model):
|
||||||
return j
|
return j
|
||||||
|
|
||||||
def check_online(self):
|
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):
|
def lists_json(self):
|
||||||
return [l.json() for l in self.lists.order_by('position')]
|
return [l.json() for l in self.lists.order_by('position')]
|
||||||
|
@ -158,24 +158,38 @@ class List(db.Model):
|
||||||
from item.models import Item
|
from item.models import Item
|
||||||
for item_id in items:
|
for item_id in items:
|
||||||
i = Item.get(item_id)
|
i = Item.get(item_id)
|
||||||
self.items.add(i)
|
self.items.append(i)
|
||||||
|
i.update()
|
||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
db.session.commit()
|
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):
|
def remove_items(self, items):
|
||||||
from item.models import Item
|
from item.models import Item
|
||||||
for item_id in items:
|
for item_id in items:
|
||||||
i = Item.get(item_id)
|
i = Item.get(item_id)
|
||||||
self.items.remove(i)
|
self.items.remove(i)
|
||||||
|
i.update()
|
||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
db.session.commit()
|
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):
|
def remove(self):
|
||||||
if not self._query:
|
if not self._query:
|
||||||
for i in self.items:
|
for i in self.items:
|
||||||
self.items.remove(i)
|
self.items.remove(i)
|
||||||
if not self._query:
|
if not self._query:
|
||||||
print 'record change: removelist', self.user, self.name
|
if self.user_id == settings.USER_ID:
|
||||||
Changelog.record(self.user, 'removelist', self.name)
|
Changelog.record(self.user, 'removelist', self.name)
|
||||||
db.session.delete(self)
|
db.session.delete(self)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -184,10 +198,21 @@ class List(db.Model):
|
||||||
def public_id(self):
|
def public_id(self):
|
||||||
id = ''
|
id = ''
|
||||||
if self.user_id != settings.USER_ID:
|
if self.user_id != settings.USER_ID:
|
||||||
id += self.user_id
|
id += self.user.nickname
|
||||||
id = '%s:%s' % (id, self.name)
|
id = u'%s:%s' % (id, self.name)
|
||||||
return id
|
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):
|
def items_count(self):
|
||||||
from item.models import Item
|
from item.models import Item
|
||||||
if self._query:
|
if self._query:
|
||||||
|
@ -199,6 +224,7 @@ class List(db.Model):
|
||||||
def json(self):
|
def json(self):
|
||||||
r = {
|
r = {
|
||||||
'id': self.public_id,
|
'id': self.public_id,
|
||||||
|
'user': self.user.nickname if self.user_id != settings.USER_ID else settings.preferences['username'],
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
'index': self.position,
|
'index': self.position,
|
||||||
'items': self.items_count(),
|
'items': self.items_count(),
|
||||||
|
|
|
@ -9,8 +9,8 @@ oml.ui.browser = function() {
|
||||||
defaultRatio: oml.config.coverRatio,
|
defaultRatio: oml.config.coverRatio,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
item: function(data, sort, size) {
|
item: function(data, sort, size) {
|
||||||
var color = oml.getFileTypeColor(data).map(function(rgb) {
|
var color = oml.getFileInfoColor(ui.fileInfo, data).map(function(rgb) {
|
||||||
return rgb.concat(0.8)
|
return rgb.concat(0.8);
|
||||||
}),
|
}),
|
||||||
ratio = data.coverRatio || oml.config.coverRatio,
|
ratio = data.coverRatio || oml.config.coverRatio,
|
||||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||||
|
@ -28,12 +28,14 @@ oml.ui.browser = function() {
|
||||||
height: Math.round(size / 12.8) + 'px',
|
height: Math.round(size / 12.8) + 'px',
|
||||||
borderWidth: Math.round(size / 64) + 'px 0',
|
borderWidth: Math.round(size / 64) + 'px 0',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderColor: 'rgba(' + color[1].join(', ') + ')',
|
borderColor: 'rgba(' + color[2].join(', ') + ')',
|
||||||
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
||||||
fontSize: Math.round(size / 16) + 'px',
|
fontSize: Math.round(size / 16) + 'px',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: 'rgba(' + color[1].join(', ') + ')',
|
color: 'rgba(' + color[2].join(', ') + ')',
|
||||||
backgroundColor: 'rgba(' + color[0].join(', ') + ')',
|
backgroundImage: '-webkit-linear-gradient(top, ' + color.slice(0, 2).map(function(rgba) {
|
||||||
|
return 'rgba(' + rgba.join(', ') + ')';
|
||||||
|
}).join(', ') + ')',
|
||||||
WebkitTransform: 'rotate(45deg)'
|
WebkitTransform: 'rotate(45deg)'
|
||||||
})
|
})
|
||||||
.html(
|
.html(
|
||||||
|
@ -55,8 +57,8 @@ oml.ui.browser = function() {
|
||||||
}), callback);
|
}), callback);
|
||||||
},
|
},
|
||||||
keys: [
|
keys: [
|
||||||
'author', 'coverRatio', 'extension',
|
'author', 'coverRatio', 'extension', 'id',
|
||||||
'id', 'size', 'textsize', 'title'
|
'mediastate', 'size', 'textsize', 'title'
|
||||||
],
|
],
|
||||||
max: 1,
|
max: 1,
|
||||||
min: 1,
|
min: 1,
|
||||||
|
@ -101,6 +103,8 @@ oml.ui.browser = function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
oml.enableDragAndDrop(that);
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
||||||
};
|
};
|
|
@ -58,30 +58,18 @@ oml.ui.folders = function() {
|
||||||
oml.$ui.libraryList = [];
|
oml.$ui.libraryList = [];
|
||||||
oml.$ui.folderList = [];
|
oml.$ui.folderList = [];
|
||||||
|
|
||||||
oml.api.getUsers(function(result) {
|
oml.getUsersAndLists(function(users, lists) {
|
||||||
|
|
||||||
var peers = result.data.users.filter(function(user) {
|
Ox.print('GOT USERS AND LISTS', users, lists);
|
||||||
return user.peered;
|
|
||||||
});
|
|
||||||
|
|
||||||
oml.api.getLists(function(result) {
|
|
||||||
|
|
||||||
Ox.print('GOT LISTS', result.data);
|
|
||||||
|
|
||||||
var users = [
|
|
||||||
{
|
|
||||||
id: oml.user.id,
|
|
||||||
nickname: oml.user.preferences.username,
|
|
||||||
online: oml.user.online
|
|
||||||
}
|
|
||||||
].concat(peers),
|
|
||||||
|
|
||||||
lists = result.data.lists;
|
|
||||||
|
|
||||||
users.forEach(function(user, index) {
|
users.forEach(function(user, index) {
|
||||||
|
|
||||||
var $content,
|
var $content,
|
||||||
libraryId = (!index ? '' : user.nickname) + ':';
|
items = lists.filter(function(list) {
|
||||||
|
return list.user == user.nickname
|
||||||
|
&& list.type != 'library';
|
||||||
|
}),
|
||||||
|
libraryId = (!index ? '' : user.nickname) + ':'
|
||||||
|
|
||||||
userIndex[user.nickname] = index;
|
userIndex[user.nickname] = index;
|
||||||
|
|
||||||
|
@ -133,7 +121,7 @@ oml.ui.folders = function() {
|
||||||
|
|
||||||
$content = oml.$ui.folder[index].$content
|
$content = oml.$ui.folder[index].$content
|
||||||
.css({
|
.css({
|
||||||
height: (1 + lists[user.id].length) * 16 + 'px'
|
height: (1 + items.length) * 16 + 'px'
|
||||||
});
|
});
|
||||||
|
|
||||||
$lists.push(
|
$lists.push(
|
||||||
|
@ -190,7 +178,7 @@ oml.ui.folders = function() {
|
||||||
$lists.push(
|
$lists.push(
|
||||||
oml.$ui.folderList[index] = oml.ui.folderList({
|
oml.$ui.folderList[index] = oml.ui.folderList({
|
||||||
draggable: !!index,
|
draggable: !!index,
|
||||||
items: lists[user.id],
|
items: items,
|
||||||
sortable: true
|
sortable: true
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
|
@ -241,7 +229,7 @@ oml.ui.folders = function() {
|
||||||
Ox.print('LIST EVENT', event, data);
|
Ox.print('LIST EVENT', event, data);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.css({height: lists[user.id].length * 16 + 'px'})
|
.css({height: items.length * 16 + 'px'})
|
||||||
.appendTo($content)
|
.appendTo($content)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -252,7 +240,6 @@ oml.ui.folders = function() {
|
||||||
selectList();
|
selectList();
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
function getFind(list) {
|
function getFind(list) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -8,8 +8,8 @@ oml.ui.gridView = function() {
|
||||||
defaultRatio: oml.config.coverRatio,
|
defaultRatio: oml.config.coverRatio,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
item: function(data, sort, size) {
|
item: function(data, sort, size) {
|
||||||
var color = oml.getFileTypeColor(data).map(function(rgb) {
|
var color = oml.getFileInfoColor(ui.fileInfo, data).map(function(rgb) {
|
||||||
return rgb.concat(0.8)
|
return rgb.concat(0.8);
|
||||||
}),
|
}),
|
||||||
ratio = data.coverRatio || oml.config.coverRatio,
|
ratio = data.coverRatio || oml.config.coverRatio,
|
||||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||||
|
@ -20,6 +20,9 @@ oml.ui.gridView = function() {
|
||||||
? (data.author || '') : data[sortKey]
|
? (data.author || '') : data[sortKey]
|
||||||
);
|
);
|
||||||
size = size || 128;
|
size = size || 128;
|
||||||
|
Ox.print('WTF', '-webkit-linear-gradient(top, ' + color.slice(2).map(function(rgba) {
|
||||||
|
return 'rgba(' + rgba.join(', ') + ')';
|
||||||
|
}).join(', ') + ')');
|
||||||
return {
|
return {
|
||||||
extra: ui.showFileInfo ? $('<div>')
|
extra: ui.showFileInfo ? $('<div>')
|
||||||
.css({
|
.css({
|
||||||
|
@ -27,12 +30,14 @@ oml.ui.gridView = function() {
|
||||||
height: Math.round(size / 12.8) + 'px',
|
height: Math.round(size / 12.8) + 'px',
|
||||||
borderWidth: Math.round(size / 64) + 'px 0',
|
borderWidth: Math.round(size / 64) + 'px 0',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderColor: 'rgba(' + color[1].join(', ') + ')',
|
borderColor: 'rgba(' + color[2].join(', ') + ')',
|
||||||
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
margin: Math.round(size / 18) + 'px ' + Math.round(width / 3) + 'px',
|
||||||
fontSize: Math.round(size / 16) + 'px',
|
fontSize: Math.round(size / 16) + 'px',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: 'rgba(' + color[1].join(', ') + ')',
|
color: 'rgba(' + color[2].join(', ') + ')',
|
||||||
backgroundColor: 'rgba(' + color[0].join(', ') + ')',
|
backgroundImage: '-webkit-linear-gradient(top, ' + color.slice(0, 2).map(function(rgba) {
|
||||||
|
return 'rgba(' + rgba.join(', ') + ')';
|
||||||
|
}).join(', ') + ')',
|
||||||
WebkitTransform: 'rotate(45deg)'
|
WebkitTransform: 'rotate(45deg)'
|
||||||
})
|
})
|
||||||
.html(
|
.html(
|
||||||
|
@ -54,8 +59,8 @@ oml.ui.gridView = function() {
|
||||||
}), callback);
|
}), callback);
|
||||||
},
|
},
|
||||||
keys: [
|
keys: [
|
||||||
'author', 'coverRatio', 'extension',
|
'author', 'coverRatio', 'extension', 'id',
|
||||||
'id', 'size', 'textsize', 'title'
|
'mediastate', 'size', 'textsize', 'title'
|
||||||
],
|
],
|
||||||
selected: ui.listSelection,
|
selected: ui.listSelection,
|
||||||
size: 128,
|
size: 128,
|
||||||
|
|
|
@ -162,7 +162,40 @@ oml.ui.infoView = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMediaButton(data) {
|
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({
|
? Ox.FormElementGroup({
|
||||||
elements: [
|
elements: [
|
||||||
Ox.Button({
|
Ox.Button({
|
||||||
|
@ -192,29 +225,13 @@ oml.ui.infoView = function() {
|
||||||
],
|
],
|
||||||
float: 'right'
|
float: 'right'
|
||||||
})
|
})
|
||||||
.css({
|
|
||||||
marginTop: '8px'
|
|
||||||
})
|
|
||||||
: Ox.Button({
|
: Ox.Button({
|
||||||
title: Ox._(
|
title: Ox._('Read Book'),
|
||||||
data.mediastate == 'available' ? 'Read Book' : 'Download Book'
|
|
||||||
),
|
|
||||||
width: 128
|
width: 128
|
||||||
})
|
})
|
||||||
.css({
|
|
||||||
marginTop: '8px'
|
|
||||||
})
|
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
if (data.mediastate == 'available') {
|
|
||||||
oml.UI.set({itemView: 'book'});
|
oml.UI.set({itemView: 'book'});
|
||||||
} else {
|
|
||||||
data.mediastate = 'transferring';
|
|
||||||
that.update(data, $data);
|
|
||||||
oml.api.download({id: ui.item}, function(result) {
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -339,10 +356,50 @@ oml.ui.infoView = function() {
|
||||||
|
|
||||||
} else if ($element == $data) {
|
} 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({
|
Ox.Button({
|
||||||
title: Ox._('Identify Book...'),
|
title: Ox._('Identify Book...'),
|
||||||
width: 128
|
width: 128
|
||||||
})
|
})
|
||||||
|
.css({marginTop: '16px'})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
identify(data);
|
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);
|
$('<div>').css({height: '16px'}).appendTo($data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ oml.ui.leftPanel = function() {
|
||||||
resize: function(data) {
|
resize: function(data) {
|
||||||
ui.sidebarSize = data.size;
|
ui.sidebarSize = data.size;
|
||||||
oml.resizeListFolders();
|
oml.resizeListFolders();
|
||||||
that.size(2, data.size);
|
that.size(2, oml.getInfoHeight());
|
||||||
if (!ui.showInfo) {
|
if (!ui.showInfo) {
|
||||||
that.css({bottom: -data.size + 'px'});
|
that.css({bottom: -data.size + 'px'});
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ oml.ui.list = function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
oml.enableDragAndDrop(that);
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
||||||
};
|
};
|
|
@ -46,151 +46,8 @@ oml.ui.mainMenu = function() {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
getListMenu(),
|
||||||
id: 'listMenu',
|
getEditMenu(),
|
||||||
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,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'viewMenu',
|
id: 'viewMenu',
|
||||||
title: Ox._('View'),
|
title: Ox._('View'),
|
||||||
|
@ -529,6 +386,9 @@ oml.ui.mainMenu = function() {
|
||||||
oml.UI.set({showSidebar: !ui.showSidebar});
|
oml.UI.set({showSidebar: !ui.showSidebar});
|
||||||
},
|
},
|
||||||
oml_find: function() {
|
oml_find: function() {
|
||||||
|
that.replaceMenu('listMenu', getListMenu());
|
||||||
|
that.replaceMenu('editMenu', getEditMenu());
|
||||||
|
/*
|
||||||
var action = Ox.startsWith(ui._list, ':') && ui._list != ':'
|
var action = Ox.startsWith(ui._list, ':') && ui._list != ':'
|
||||||
? 'enableItem' : 'disableItem';
|
? 'enableItem' : 'disableItem';
|
||||||
that[
|
that[
|
||||||
|
@ -537,6 +397,7 @@ oml.ui.mainMenu = function() {
|
||||||
]('duplicatelist');
|
]('duplicatelist');
|
||||||
that[action]('editlist');
|
that[action]('editlist');
|
||||||
that[action]('deletelist');
|
that[action]('deletelist');
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
oml_item: function(data) {
|
oml_item: function(data) {
|
||||||
if (!!data.value != !!data.previousValue) {
|
if (!!data.value != !!data.previousValue) {
|
||||||
|
@ -544,6 +405,9 @@ oml.ui.mainMenu = function() {
|
||||||
that[data.value ? 'enableItem' : 'disableItem']('showbrowser');
|
that[data.value ? 'enableItem' : 'disableItem']('showbrowser');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
oml_listselection: function(data) {
|
||||||
|
that.replaceMenu('editMenu', getEditMenu());
|
||||||
|
},
|
||||||
oml_showbrowser: function(data) {
|
oml_showbrowser: function(data) {
|
||||||
that.setItemTitle('showbrowser', Ox._((data.value ? 'Hide' : 'Show') + ' Browser'));
|
that.setItemTitle('showbrowser', Ox._((data.value ? 'Hide' : 'Show') + ' Browser'));
|
||||||
},
|
},
|
||||||
|
@ -559,9 +423,205 @@ 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;
|
return that;
|
||||||
|
|
||||||
|
|
|
@ -453,12 +453,12 @@ oml.ui.usersDialog = function() {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
$lists.forEach(function($element) {
|
if (data.ids.length) {
|
||||||
if ($element != $list) {
|
selectItem($list);
|
||||||
$element.options({selected: []});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
renderUser(Ox.getObjectById(users, data.ids[0]));
|
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() {
|
(function() {
|
||||||
|
|
||||||
// Note: getFindState has to run after getListState and getFilterState
|
// Note: getFindState has to run after getListState and getFilterState
|
||||||
|
@ -140,146 +701,64 @@
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|
||||||
oml.addList = function() {
|
oml.getFileInfoColor = function(type, data) {
|
||||||
// addList(isSmart, isFrom) or addList(list) [=dupicate]
|
return type == 'extension' ? (
|
||||||
var args = arguments,
|
data.extension == 'epub' ? [[32, 160, 32], [0, 128, 0], [128, 255, 128]]
|
||||||
isDuplicate = args.length == 1,
|
: data.extension == 'pdf' ? (
|
||||||
isSmart, isFrom, list, listData, data;
|
data.textsize
|
||||||
oml.api.getLists(function(result) {
|
? [[224, 32, 32], [192, 0, 0], [255, 192, 192]]
|
||||||
var lists = result.data.lists,
|
: [[224, 128, 32], [192, 96, 0], [255, 192, 128]]
|
||||||
listNames = lists[oml.user.id].map(function(list) {
|
)
|
||||||
return list.name;
|
: data.extension == 'txt' ? [[255, 255, 255], [224, 224, 224], [0, 0, 0]]
|
||||||
}),
|
: [[96, 96, 96], [64, 64, 64], [192, 192, 192]]
|
||||||
query;
|
) : data.mediastate == 'available' ? [[32, 160, 32], [0, 128, 0], [128, 255, 128]]
|
||||||
if (!isDuplicate) {
|
: data.mediastate == 'transferring' ? [[160, 160, 32], [128, 128, 0], [255, 255, 128]]
|
||||||
isSmart = args[0];
|
: [[224, 32, 32], [192, 0, 0], [255, 192, 192]];
|
||||||
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.clearFilters = function() {
|
oml.getFilterSizes = 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;
|
var ui = oml.user.ui;
|
||||||
oml.ui.confirmDialog({
|
return Ox.splitInt(
|
||||||
buttons: [
|
window.innerWidth - ui.showSidebar * ui.sidebarSize - 1,
|
||||||
Ox.Button({
|
5
|
||||||
title: Ox._('No, Keep List')
|
);
|
||||||
}),
|
};
|
||||||
Ox.Button({
|
|
||||||
title: Ox._('Yes, Delete List')
|
oml.getInfoHeight = function() {
|
||||||
})
|
return Math.min(
|
||||||
],
|
oml.user.ui.sidebarSize,
|
||||||
content: Ox._('Are you sure you want to delete this list?'),
|
window.innerHeight - 20 - 24 - 16 - 1
|
||||||
title: Ox._('Delete List')
|
);
|
||||||
}, function() {
|
};
|
||||||
oml.api.removeList({
|
|
||||||
id: ui._list
|
oml.getListData = function(list) {
|
||||||
}, function() {
|
var data = {}, ui = oml.user.ui;
|
||||||
oml.UI.set({
|
if (Ox.isUndefined(list)) {
|
||||||
find: {
|
list = ui._list;
|
||||||
conditions: [{
|
|
||||||
key: 'list',
|
|
||||||
operator: '==',
|
|
||||||
value: ':'
|
|
||||||
}],
|
|
||||||
operator: '&'
|
|
||||||
}
|
}
|
||||||
});
|
if (ui._lists) {
|
||||||
oml.updateLists();
|
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) {
|
oml.getPageTitle = function(stateOrURL) {
|
||||||
var page = Ox.getObjectById(
|
var page = Ox.getObjectById(
|
||||||
|
@ -298,44 +777,47 @@ oml.getSortOperator = function(key) {
|
||||||
) ? '+' : '-';
|
) ? '+' : '-';
|
||||||
};
|
};
|
||||||
|
|
||||||
oml.getFileTypeColor = function(data) {
|
oml.getUsersAndLists = function(callback) {
|
||||||
return data.extension == 'epub' ? [[0, 128, 0], [128, 255, 128]]
|
var lists = [{
|
||||||
: data.extension == 'pdf' ? (
|
id: '',
|
||||||
data.textsize ? [[192, 0, 0], [255, 192, 192]]
|
name: Ox._('All Libraries'),
|
||||||
: [[192, 96, 0], [255, 192, 128]]
|
type: 'libraries'
|
||||||
)
|
}],
|
||||||
: data.extension == 'txt' ? [[255, 255, 255], [0, 0, 0]]
|
ui = oml.user.ui,
|
||||||
: [[64, 64, 64], [192, 192, 192]];
|
username = oml.user.preferences.username,
|
||||||
};
|
users = [{
|
||||||
|
id: oml.user.id,
|
||||||
oml.getFilterSizes = function() {
|
nickname: username,
|
||||||
var ui = oml.user.ui;
|
online: oml.user.online
|
||||||
return Ox.splitInt(
|
}];
|
||||||
window.innerWidth - ui.showSidebar * ui.sidebarSize - 1,
|
oml.api.getUsers(function(result) {
|
||||||
5
|
users = users.concat(
|
||||||
);
|
result.data.users.filter(function(user) {
|
||||||
};
|
return user.peered;
|
||||||
|
})
|
||||||
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
|
|
||||||
);
|
);
|
||||||
|
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() {
|
oml.hasDialogOrScreen = function() {
|
||||||
|
@ -344,6 +826,11 @@ oml.hasDialogOrScreen = function() {
|
||||||
|| !!$('.OxScreen').length;
|
|| !!$('.OxScreen').length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oml.reloadList = function() {
|
||||||
|
Ox.print('RELOAD LIST NOT IMPLEMENTED')
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
oml.resizeFilters = function() {
|
oml.resizeFilters = function() {
|
||||||
// ...
|
// ...
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue