diff --git a/pandora/config.0xdb.jsonc b/pandora/config.0xdb.jsonc index 1bc93447..45638426 100644 --- a/pandora/config.0xdb.jsonc +++ b/pandora/config.0xdb.jsonc @@ -31,6 +31,8 @@ "canEditAnnotations": {"staff": true, "admin": true}, "canEditEvents": {"staff": true, "admin": true}, "canEditFeaturedLists": {"staff": true, "admin": true}, + "canEditFeaturedTexts": {"staff": true, "admin": true}, + "canEditFeaturedEdits": {"staff": true, "admin": true}, "canEditMetadata": {"staff": true, "admin": true}, "canEditPlaces": {"staff": true, "admin": true}, "canEditSitePages": {"staff": true, "admin": true}, @@ -712,6 +714,8 @@ "icons": "posters", "infoIconSize": 256, "item": "", + "text": "", + "edit": "", "itemFind": "", "itemSort": [{"key": "position", "operator": "+"}], "itemView": "info", @@ -749,6 +753,11 @@ "featured": true, "volumes": true } + "texts": { + "personal": true, + "favorite": true, + "featured": true + } }, "showSidebar": true, "showSitePosters": false, diff --git a/pandora/config.padma.jsonc b/pandora/config.padma.jsonc index 699c7fbd..7daf8ed6 100644 --- a/pandora/config.padma.jsonc +++ b/pandora/config.padma.jsonc @@ -29,6 +29,8 @@ "canEditAnnotations": {"staff": true, "admin": true}, "canEditEvents": {"staff": true, "admin": true}, "canEditFeaturedLists": {"staff": true, "admin": true}, + "canEditFeaturedTexts": {"staff": true, "admin": true}, + "canEditFeaturedEdits": {"staff": true, "admin": true}, "canEditMetadata": {"staff": true, "admin": true}, "canEditPlaces": {"staff": true, "admin": true}, "canEditSitePages": {"staff": true, "admin": true}, @@ -630,6 +632,8 @@ "icons": "frames", "infoIconSize": 256, "item": "", + "text": "", + "edit": "", "itemFind": "", "itemSort": [{"key": "position", "operator": "+"}], "itemView": "info", @@ -670,6 +674,11 @@ "favorite": true, "featured": true, "volumes": true + }, + "texts": { + "personal": true, + "favorite": true, + "featured": true } }, "showSidebar": true, diff --git a/pandora/config.pandora.jsonc b/pandora/config.pandora.jsonc index e3f430c9..ff363739 100644 --- a/pandora/config.pandora.jsonc +++ b/pandora/config.pandora.jsonc @@ -29,6 +29,8 @@ "canEditAnnotations": {"staff": true, "admin": true}, "canEditEvents": {"staff": true, "admin": true}, "canEditFeaturedLists": {"staff": true, "admin": true}, + "canEditFeaturedTexts": {"staff": true, "admin": true}, + "canEditFeaturedEdits": {"staff": true, "admin": true}, "canEditMetadata": {"staff": true, "admin": true}, "canEditPlaces": {"staff": true, "admin": true}, "canEditSitePages": {"staff": true, "admin": true}, @@ -549,6 +551,8 @@ "icons": "posters", "infoIconSize": 256, "item": "", + "text": "", + "edit": "", "itemFind": "", "itemSort": [{"key": "position", "operator": "+"}], "itemView": "info", @@ -589,6 +593,11 @@ "featured": true, "volumes": true } + "texts": { + "personal": true, + "favorite": true, + "featured": true + } }, "showSidebar": true, "showSitePosters": false, diff --git a/pandora/itemlist/views.py b/pandora/itemlist/views.py index 54871f3d..5b4a1944 100644 --- a/pandora/itemlist/views.py +++ b/pandora/itemlist/views.py @@ -207,7 +207,9 @@ def addList(request): return { status: {'code': int, 'text': string}, data: { - list: + id: + name: + ... } } ''' diff --git a/pandora/text/managers.py b/pandora/text/managers.py new file mode 100644 index 00000000..b3dd2647 --- /dev/null +++ b/pandora/text/managers.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from datetime import datetime + +from django.db.models import Q, Manager + +import models + + +def parseCondition(condition, user): + ''' + ''' + k = condition.get('key', 'name') + k = { + 'user': 'user__username', + 'position': 'position__position', + 'posterFrames': 'poster_frames', + }.get(k, k) + if not k: + k = 'name' + v = condition['value'] + op = condition.get('operator') + if not op: + op = '=' + if op.startswith('!'): + op = op[1:] + exclude = True + else: + exclude = False + if k == 'id': + v = v.split(":") + if len(v) >= 2: + v = (v[0], ":".join(v[1:])) + q = Q(user__username=v[0], name=v[1]) + else: + q = Q(id__in=[]) + return q + if k == 'subscribed': + key = 'subscribed_users__username' + v = user.username + elif isinstance(v, bool): #featured and public flag + key = k + else: + key = "%s%s" % (k, { + '==': '__iexact', + '^': '__istartswith', + '$': '__iendswith', + }.get(op, '__icontains')) + key = str(key) + if exclude: + q = ~Q(**{key: v}) + else: + q = Q(**{key: v}) + return q + +def parseConditions(conditions, operator, user): + ''' + conditions: [ + { + value: "war" + } + { + key: "year", + value: "1970-1980, + operator: "!=" + }, + { + key: "country", + value: "f", + operator: "^" + } + ], + operator: "&" + ''' + conn = [] + for condition in conditions: + if 'conditions' in condition: + q = parseConditions(condition['conditions'], + condition.get('operator', '&'), user) + if q: + conn.append(q) + pass + else: + conn.append(parseCondition(condition, user)) + if conn: + q = conn[0] + for c in conn[1:]: + if operator == '|': + q = q | c + else: + q = q & c + return q + return None + + +class TextManager(Manager): + + def get_query_set(self): + return super(TextManager, self).get_query_set() + + def find(self, data, user): + ''' + query: { + conditions: [ + { + value: "war" + } + { + key: "year", + value: "1970-1980, + operator: "!=" + }, + { + key: "country", + value: "f", + operator: "^" + } + ], + operator: "&" + } + ''' + + #join query with operator + qs = self.get_query_set() + conditions = parseConditions(data['query'].get('conditions', []), + data['query'].get('operator', '&'), + user) + if conditions: + qs = qs.filter(conditions) + + if user.is_anonymous(): + qs = qs.filter(Q(status='public') | Q(status='featured')) + else: + qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user)) + return qs + diff --git a/pandora/text/migrations/0002_auto__del_image__del_attachment__add_position__add_unique_position_use.py b/pandora/text/migrations/0002_auto__del_image__del_attachment__add_position__add_unique_position_use.py new file mode 100644 index 00000000..3064cceb --- /dev/null +++ b/pandora/text/migrations/0002_auto__del_image__del_attachment__add_position__add_unique_position_use.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting model 'Image' + db.delete_table('text_image') + + # Deleting model 'Attachment' + db.delete_table('text_attachment') + + # Adding model 'Position' + db.create_table('text_position', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('text', self.gf('django.db.models.fields.related.ForeignKey')(related_name='position', to=orm['text.Text'])), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='text_position', to=orm['auth.User'])), + ('section', self.gf('django.db.models.fields.CharField')(max_length='255')), + ('position', self.gf('django.db.models.fields.IntegerField')(default=0)), + )) + db.send_create_signal('text', ['Position']) + + # Adding unique constraint on 'Position', fields ['user', 'text', 'section'] + db.create_unique('text_position', ['user_id', 'text_id', 'section']) + + # Deleting field 'Text.public' + db.delete_column('text_text', 'public') + + # Deleting field 'Text.slug' + db.delete_column('text_text', 'slug') + + # Deleting field 'Text.title' + db.delete_column('text_text', 'title') + + # Deleting field 'Text.published' + db.delete_column('text_text', 'published') + + # Adding field 'Text.name' + db.add_column('text_text', 'name', + self.gf('django.db.models.fields.CharField')(default=datetime.datetime(2013, 2, 15, 0, 0), max_length=255), + keep_default=False) + + # Adding field 'Text.status' + db.add_column('text_text', 'status', + self.gf('django.db.models.fields.CharField')(default='private', max_length=20), + keep_default=False) + + # Adding field 'Text.description' + db.add_column('text_text', 'description', + self.gf('django.db.models.fields.TextField')(default=''), + keep_default=False) + + # Adding field 'Text.icon' + db.add_column('text_text', 'icon', + self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True), + keep_default=False) + + # Adding field 'Text.poster_frames' + db.add_column('text_text', 'poster_frames', + self.gf('ox.django.fields.TupleField')(default=[]), + keep_default=False) + + # Adding M2M table for field subscribed_users on 'Text' + db.create_table('text_text_subscribed_users', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('text', models.ForeignKey(orm['text.text'], null=False)), + ('user', models.ForeignKey(orm['auth.user'], null=False)) + )) + db.create_unique('text_text_subscribed_users', ['text_id', 'user_id']) + + # Adding unique constraint on 'Text', fields ['user', 'name'] + db.create_unique('text_text', ['user_id', 'name']) + + + def backwards(self, orm): + # Removing unique constraint on 'Text', fields ['user', 'name'] + db.delete_unique('text_text', ['user_id', 'name']) + + # Removing unique constraint on 'Position', fields ['user', 'text', 'section'] + db.delete_unique('text_position', ['user_id', 'text_id', 'section']) + + # Adding model 'Image' + db.create_table('text_image', ( + ('caption', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), + ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('text', ['Image']) + + # Adding model 'Attachment' + db.create_table('text_attachment', ( + ('caption', self.gf('django.db.models.fields.CharField')(default='', max_length=255)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + )) + db.send_create_signal('text', ['Attachment']) + + # Deleting model 'Position' + db.delete_table('text_position') + + # Adding field 'Text.public' + db.add_column('text_text', 'public', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + # Adding field 'Text.slug' + db.add_column('text_text', 'slug', + self.gf('django.db.models.fields.SlugField')(default='', max_length=50), + keep_default=False) + + # Adding field 'Text.title' + db.add_column('text_text', 'title', + self.gf('django.db.models.fields.CharField')(max_length=1000, null=True), + keep_default=False) + + # Adding field 'Text.published' + db.add_column('text_text', 'published', + self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now), + keep_default=False) + + # Deleting field 'Text.name' + db.delete_column('text_text', 'name') + + # Deleting field 'Text.status' + db.delete_column('text_text', 'status') + + # Deleting field 'Text.description' + db.delete_column('text_text', 'description') + + # Deleting field 'Text.icon' + db.delete_column('text_text', 'icon') + + # Deleting field 'Text.poster_frames' + db.delete_column('text_text', 'poster_frames') + + # Removing M2M table for field subscribed_users on 'Text' + db.delete_table('text_text_subscribed_users') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'text.position': { + 'Meta': {'unique_together': "(('user', 'text', 'section'),)", 'object_name': 'Position'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': "'255'"}), + 'text': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['text.Text']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'text_position'", 'to': "orm['auth.User']"}) + }, + 'text.text': { + 'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Text'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'icon': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'poster_frames': ('ox.django.fields.TupleField', [], {'default': '[]'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}), + 'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_texts'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'text': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'texts'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['text'] diff --git a/pandora/text/migrations/0003_auto__add_field_text_type__add_field_text_links.py b/pandora/text/migrations/0003_auto__add_field_text_type__add_field_text_links.py new file mode 100644 index 00000000..2d90dea9 --- /dev/null +++ b/pandora/text/migrations/0003_auto__add_field_text_type__add_field_text_links.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Text.type' + db.add_column('text_text', 'type', + self.gf('django.db.models.fields.CharField')(default='html', max_length=255), + keep_default=False) + + # Adding field 'Text.links' + db.add_column('text_text', 'links', + self.gf('ox.django.fields.DictField')(default={}), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Text.type' + db.delete_column('text_text', 'type') + + # Deleting field 'Text.links' + db.delete_column('text_text', 'links') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'text.position': { + 'Meta': {'unique_together': "(('user', 'text', 'section'),)", 'object_name': 'Position'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'section': ('django.db.models.fields.CharField', [], {'max_length': "'255'"}), + 'text': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['text.Text']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'text_position'", 'to': "orm['auth.User']"}) + }, + 'text.text': { + 'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Text'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'icon': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'links': ('ox.django.fields.DictField', [], {'default': '{}'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'poster_frames': ('ox.django.fields.TupleField', [], {'default': '[]'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}), + 'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_texts'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'text': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'type': ('django.db.models.fields.CharField', [], {'default': "'html'", 'max_length': '255'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'texts'", 'to': "orm['auth.User']"}) + } + } + + complete_apps = ['text'] \ No newline at end of file diff --git a/pandora/text/models.py b/pandora/text/models.py index 57d3a43d..00e23d70 100644 --- a/pandora/text/models.py +++ b/pandora/text/models.py @@ -2,40 +2,158 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement from datetime import datetime +import os +import subprocess +from glob import glob from django.db import models from django.contrib.auth.models import User +from django.conf import settings +import ox +from ox.django.fields import DictField, TupleField + +from archive import extract + +import managers class Text(models.Model): + + class Meta: + unique_together = ("user", "name") + created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) - published = models.DateTimeField(default=datetime.now, editable=False) - public = models.BooleanField(default=False) + user = models.ForeignKey(User, related_name='texts') + name = models.CharField(max_length=255) + status = models.CharField(max_length=20, default='private') + _status = ['private', 'public', 'featured'] + type = models.CharField(max_length=255, default='html') + description = models.TextField(default='') - user = models.ForeignKey(User) - slug = models.SlugField() - title = models.CharField(null=True, max_length=1000) - text = models.TextField(default='') + icon = models.ImageField(default=None, blank=True, + upload_to=lambda i, x: i.path("icon.jpg")) + + text = models.TextField(default="") + links = DictField(default={}, editable=True) + + poster_frames = TupleField(default=[], editable=False) + subscribed_users = models.ManyToManyField(User, related_name='subscribed_texts') + + objects = managers.TextManager() + + def save(self, *args, **kwargs): + super(Text, self).save(*args, **kwargs) def __unicode__(self): - return u"%s <%s>" % (self.title, self.slug) + return self.get_id() - def get_absolute_url(self): - return '/text/%s' % self.slug + def get_id(self): + return u'%s:%s' % (self.user.username, self.name) + def accessible(self, user): + return self.user == user or self.status in ('public', 'featured') -class Image(models.Model): - image = models.ImageField(upload_to='text/image') - caption = models.CharField(max_length=255, default="") + def editable(self, user): + if user.is_anonymous(): + return False + if self.user == user or \ + user.is_staff or \ + user.get_profile().capability('canEditFeaturedTexts') == True: + return True + return False - def get_absolute_url(self): - return self.image.url + def json(self, keys=None, user=None): + if not keys: + keys=['id', 'name', 'user', 'status', 'subscribed', 'posterFrames', 'description', 'text', 'type', 'links'] + response = {} + _map = { + 'posterFrames': 'poster_frames' + } + for key in keys: + if key == 'id': + response[key] = self.get_id() + elif key == 'user': + response[key] = self.user.username + elif key == 'subscribers': + response[key] = self.subscribed_users.all().count() + elif key == 'subscribed': + if user and not user.is_anonymous(): + response[key] = self.subscribed_users.filter(id=user.id).exists() + elif hasattr(self, _map.get(key, key)): + response[key] = getattr(self, _map.get(key,key)) + return response + def path(self, name=''): + h = "%07d" % self.id + return os.path.join('texts', h[:2], h[2:4], h[4:6], h[6:], name) -class Attachment(models.Model): - file = models.FileField(upload_to='text/attachment') - caption = models.CharField(max_length=255, default="") + def update_icon(self): + frames = [] + if not self.poster_frames: + items = self.get_items(self.user).filter(rendered=True) + if items.count(): + poster_frames = [] + for i in range(0, items.count(), max(1, int(items.count()/4))): + poster_frames.append({ + 'item': items[int(i)].itemId, + 'position': items[int(i)].poster_frame + }) + self.poster_frames = tuple(poster_frames) + self.save() + for i in self.poster_frames: + from item.models import Item + qs = Item.objects.filter(itemId=i['item']) + if qs.count() > 0: + frame = qs[0].frame(i['position']) + if frame: + frames.append(frame) + self.icon.name = self.path('icon.jpg') + icon = self.icon.path + if frames: + while len(frames) < 4: + frames += frames + folder = os.path.dirname(icon) + ox.makedirs(folder) + for f in glob("%s/icon*.jpg" % folder): + os.unlink(f) + cmd = [ + settings.LIST_ICON, + '-f', ','.join(frames), + '-o', icon + ] + p = subprocess.Popen(cmd) + p.wait() + self.save() + + def get_icon(self, size=16): + path = self.path('icon%d.jpg' % size) + path = os.path.join(settings.MEDIA_ROOT, path) + if not os.path.exists(path): + folder = os.path.dirname(path) + ox.makedirs(folder) + if self.icon and os.path.exists(self.icon.path): + source = self.icon.path + max_size = min(self.icon.width, self.icon.height) + else: + source = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg') + max_size = 256 + if size < max_size: + extract.resize_image(source, path, size=size) + else: + path = source + return path + +class Position(models.Model): + + class Meta: + unique_together = ("user", "text", "section") + + text = models.ForeignKey(Text, related_name='position') + user = models.ForeignKey(User, related_name='text_position') + section = models.CharField(max_length='255') + position = models.IntegerField(default=0) + + def __unicode__(self): + return u'%s/%s/%s' % (self.section, self.position, self.text) - def get_absolute_url(self): - return self.file.url diff --git a/pandora/text/views.py b/pandora/text/views.py index 9001c1b0..0e2260bf 100644 --- a/pandora/text/views.py +++ b/pandora/text/views.py @@ -1,27 +1,96 @@ # -*- coding: utf-8 -*- # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division +import os +import re - +import ox from ox.utils import json -from ox.django.decorators import login_required_json -from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response - -import models from ox.django.api import actions +from ox.django.decorators import login_required_json +from ox.django.http import HttpFileResponse +from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response +from django.db.models import Count, Q, Sum, Max +from item import utils +import models + +def get_text_or_404_json(id): + id = id.split(':') + username = id[0] + textname = ":".join(id[1:]) + return get_object_or_404_json(models.Text, user__username=username, name=textname) + +@login_required_json +def addText(request): + ''' + param data { + name: value, + } + return { + status: {'code': int, 'text': string}, + data: { + id: + name: + ... + } + } + ''' + data = json.loads(request.POST['data']) + data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip() + name = data['name'] + if not name: + name = "Untitled" + num = 1 + created = False + while not created: + text, created = models.Text.objects.get_or_create(name=name, user=request.user) + num += 1 + name = data['name'] + ' [%d]' % num + text.save() + + if text.status == 'featured': + pos, created = models.Position.objects.get_or_create(text=text, + user=request.user, section='featured') + qs = models.Position.objects.filter(section='featured') + else: + pos, created = models.Position.objects.get_or_create(text=text, + user=request.user, section='personal') + qs = models.Position.objects.filter(user=request.user, section='personal') + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + response = json_response(status=200, text='created') + response['data'] = text.json() + return render_to_json_response(response) +actions.register(addText, cache=False) def getText(request): ''' - param data - string id - - return page + param data { + id: textid + } + return { + id: + text: + ... + } ''' - response = json_response({}) - itemId = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Text, pk=itemId) - response['data']['page'] = item.html() + response = json_response() + data = json.loads(request.POST['data']) + public_id = data['id'] + if public_id == '': + qs = models.Text.objects.filter(name='') + if qs.count() == 0: + text = models.Text() + text.name = '' + text.text = 'Please put something here' + text.user = request.user + text.save() + else: + text = qs[0] + else: + text = get_text_or_404_json(data['id']) + response['data'] = text.json() return render_to_json_response(response) actions.register(getText) @@ -29,22 +98,355 @@ actions.register(getText) @login_required_json def editText(request): ''' - param data - string id - - return page + param data { + id: + text: + public: boolean + } + return { + id: + text: + ... + } ''' - response = json_response({}) - itemId = json.loads(request.POST['data']) - item = get_object_or_404_json(models.Text, pk=itemId) - response['data']['page'] = item.html() + response = json_response() + data = json.loads(request.POST['data']) + if data['id']: + public_id = data['id'].split(':') + username = public_id[0] + name = ":".join(public_id[1:]) + text, created = models.Text.objects.get_or_create(name=name, user=models.User.objects.get(username=username)) + if created: + text.user = request.user + else: + qs = models.Text.objects.filter(name='') + if qs.count() == 0: + if request.user.get_profile().capability('canEditFeaturedTexts'): + text = models.Text(name='', user=request.user) + text.save() + else: + response = json_response(status=403, text='permission denied') + return render_to_json_response(response) + else: + text = qs[0] + if text.editable(request.user): + for key in data: + if key == 'status': + value = data[key] + if value not in text._status: + value = text._status[0] + if value == 'private': + for user in text.subscribed_users.all(): + text.subscribed_users.remove(user) + qs = models.Position.objects.filter(user=request.user, + section='section', text=text) + if qs.count() > 1: + pos = qs[0] + pos.section = 'personal' + pos.save() + elif value == 'featured': + if request.user.get_profile().capability('canEditFeaturedTexts'): + pos, created = models.Position.objects.get_or_create(text=text, user=request.user, + section='featured') + if created: + qs = models.Position.objects.filter(user=request.user, section='featured') + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + models.Position.objects.filter(text=text).exclude(id=pos.id).delete() + else: + value = text.status + elif text.status == 'featured' and value == 'public': + models.Position.objects.filter(text=text).delete() + pos, created = models.Position.objects.get_or_create(text=text, + user=text.user,section='personal') + qs = models.Position.objects.filter(user=text.user, + section='personal') + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + for u in text.subscribed_users.all(): + pos, created = models.Position.objects.get_or_create(text=text, user=u, + section='public') + qs = models.Position.objects.filter(user=u, section='public') + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + + text.status = value + elif key == 'name': + data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip() + name = data['name'] + if not name: + name = "Untitled" + num = 1 + while models.Text.objects.filter(name=name, user=text.user).exclude(id=text.id).count()>0: + num += 1 + name = data['name'] + ' [%d]' % num + text.name = name + elif key == 'description': + text.description = ox.sanitize_html(data['description']) + elif key == 'text': + text.text = ox.sanitize_html(data['text']) + + if 'position' in data: + pos, created = models.Position.objects.get_or_create(text=text, user=request.user) + pos.position = data['position'] + pos.section = 'featured' + if text.status == 'private': + pos.section = 'personal' + pos.save() + if 'type' in data: + if data['type'] == 'pdf': + text.type = 'pdf' + else: + text.type = 'html' + if 'posterFrames' in data: + text.poster_frames = tuple(data['posterFrames']) + text.update_icon() + text.save() + response['data'] = text.json(user=request.user) + else: + response = json_response(status=403, text='permission denied') return render_to_json_response(response) actions.register(editText, cache=False) -def findText(request): +def _order_query(qs, sort): + order_by = [] + for e in sort: + operator = e['operator'] + if operator != '-': + operator = '' + key = { + 'subscribed': 'subscribed_users', + 'items': 'numberofitems' + }.get(e['key'], e['key']) + order = '%s%s' % (operator, key) + order_by.append(order) + if key == 'subscribers': + qs = qs.annotate(subscribers=Sum('subscribed_users')) + if order_by: + qs = qs.order_by(*order_by) + qs = qs.distinct() + return qs + +def parse_query(data, user): + query = {} + query['range'] = [0, 100] + query['sort'] = [{'key':'user', 'operator':'+'}, {'key':'name', 'operator':'+'}] + for key in ('keys', 'group', 'text', 'range', 'position', 'positions', 'sort'): + if key in data: + query[key] = data[key] + print data + query['qs'] = models.Text.objects.find(data, user).exclude(name='') + print query + return query + + +def findTexts(request): ''' + param data { + query: { + conditions: [ + { + key: 'user', + value: 'something', + operator: '=' + } + ] + operator: "," + }, + sort: [{key: 'name', operator: '+'}], + range: [0, 100] + keys: [] + } + + possible query keys: + name, user, featured, subscribed + + possible keys: + name, user, featured, subscribed, query + + } + return {status: {code: int, text: string}, + data: { + items: [ + {name:, user:, featured:, public...} + ] + } + } ''' - response = json_response({}) + data = json.loads(request.POST['data']) + query = parse_query(data, request.user) + + #order + is_section_request = query['sort'] == [{u'operator': u'+', u'key': u'position'}] + def is_featured_condition(x): + return x['key'] == 'status' and \ + x['value'] == 'featured' and \ + x['operator'] in ('=', '==') + is_featured = len(filter(is_featured_condition, data['query'].get('conditions', []))) > 0 + + if is_section_request: + qs = query['qs'] + if not is_featured and not request.user.is_anonymous(): + qs = qs.filter(position__in=models.Position.objects.filter(user=request.user)) + qs = qs.order_by('position__position') + else: + qs = _order_query(query['qs'], query['sort']) + + response = json_response() + if 'keys' in data: + qs = qs[query['range'][0]:query['range'][1]] + + response['data']['items'] = [l.json(data['keys'], request.user) for l in qs] + elif 'position' in data: + #FIXME: actually implement position requests + response['data']['position'] = 0 + elif 'positions' in data: + ids = [i.get_id() for i in qs] + response['data']['positions'] = utils.get_positions(ids, query['positions']) + else: + response['data']['items'] = qs.count() return render_to_json_response(response) -actions.register(findText) +actions.register(findTexts) + + +@login_required_json +def removeText(request): + ''' + param data { + id: testId, + } + return { + status: {'code': int, 'text': string}, + data: { + } + } + ''' + data = json.loads(request.POST['data']) + text = get_text_or_404_json(data['id']) + response = json_response() + if text.editable(request.user): + text.delete() + else: + response = json_response(status=403, text='not allowed') + return render_to_json_response(response) +actions.register(removeText, cache=False) + + +@login_required_json +def subscribeToText(request): + ''' + param data { + id: testId, + } + return { + status: {'code': int, 'text': string}, + data: { + } + } + ''' + data = json.loads(request.POST['data']) + text = get_text_or_404_json(data['id']) + user = request.user + if text.status == 'public' and \ + text.subscribed_users.filter(username=user.username).count() == 0: + text.subscribed_users.add(user) + pos, created = models.Position.objects.get_or_create(text=text, user=user, section='public') + if created: + qs = models.Position.objects.filter(user=user, section='public') + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + response = json_response() + return render_to_json_response(response) +actions.register(subscribeToText, cache=False) + + +@login_required_json +def unsubscribeFromText(request): + ''' + param data { + id: testId, + user: username(only admins) + } + return { + status: {'code': int, 'text': string}, + data: { + } + } + ''' + data = json.loads(request.POST['data']) + text = get_text_or_404_json(data['id']) + user = request.user + text.subscribed_users.remove(user) + models.Position.objects.filter(text=text, user=user, section='public').delete() + response = json_response() + return render_to_json_response(response) +actions.register(unsubscribeFromText, cache=False) + + +@login_required_json +def sortTexts(request): + ''' + param data { + section: 'personal', + ids: [1,2,4,3] + } + known sections: 'personal', 'public', 'featured' + featured can only be edited by admins + return { + status: {'code': int, 'text': string}, + data: { + } + } + ''' + data = json.loads(request.POST['data']) + position = 0 + section = data['section'] + #ids = list(set(data['ids'])) + ids = data['ids'] + if section == 'featured' and not request.user.get_profile().capability('canEditFeaturedTexts'): + response = json_response(status=403, text='not allowed') + else: + user = request.user + if section == 'featured': + for i in ids: + l = get_text_or_404_json(i) + qs = models.Position.objects.filter(section=section, text=l) + if qs.count() > 0: + pos = qs[0] + else: + pos = models.Position(text=l, user=user, section=section) + if pos.position != position: + pos.position = position + pos.save() + position += 1 + models.Position.objects.filter(section=section, text=l).exclude(id=pos.id).delete() + else: + for i in ids: + l = get_text_or_404_json(i) + pos, created = models.Position.objects.get_or_create(text=l, + user=request.user, section=section) + if pos.position != position: + pos.position = position + pos.save() + position += 1 + + response = json_response() + return render_to_json_response(response) +actions.register(sortTexts, cache=False) + + +def icon(request, id, size=16): + if not size: + size = 16 + + id = id.split(':') + username = id[0] + textname = ":".join(id[1:]) + qs = models.Text.objects.filter(user__username=username, name=textname) + if qs.count() == 1 and qs[0].accessible(request.user): + text = qs[0] + icon = text.get_icon(int(size)) + else: + icon = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg') + return HttpFileResponse(icon, content_type='image/jpeg') diff --git a/pandora/urls.py b/pandora/urls.py index 7c08e735..c1abf5e3 100644 --- a/pandora/urls.py +++ b/pandora/urls.py @@ -28,6 +28,7 @@ urlpatterns = patterns('', (r'^api/?$', include(ox.django.api.urls)), (r'^resetUI$', 'user.views.reset_ui'), (r'^list/(?P.*?)/icon(?P\d*).jpg$', 'itemlist.views.icon'), + (r'^text/(?P.*?)/icon(?P\d*).jpg$', 'text.views.icon'), (r'^robots.txt$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'robots.txt'), 'content_type': 'text/plain'}), (r'^favicon.ico$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'png/icon.16.png'), 'content_type': 'image/x-icon'}), (r'^opensearch.xml$', 'app.views.opensearch_xml'), diff --git a/pandora/user/models.py b/pandora/user/models.py index 1c7f0df5..e3172bc5 100644 --- a/pandora/user/models.py +++ b/pandora/user/models.py @@ -14,6 +14,7 @@ from ox.django.fields import DictField from ox.utils import json from itemlist.models import List, Position +import text import managers import tasks @@ -261,6 +262,29 @@ def get_ui(user_ui, user=None): ''' ids.append(id) return ids + + def add_texts(texts, section): + P = text.models.Position + ids = [] + for t in texts: + qs = P.objects.filter(section=section) + if section == 'featured': + try: + pos = P.objects.get(text=t, section=section) + created = False + except P.DoesNotExist: + pos = P(text=t, section=section, user=l.user) + pos.save() + created = True + else: + pos, created = P.objects.get_or_create(text=t, user=user, section=section) + qs = qs.filter(user=user) + if created: + pos.position = qs.aggregate(Max('position'))['position__max'] + 1 + pos.save() + ids.append(t.get_id()) + return ids + ids = [''] if user: @@ -270,6 +294,11 @@ def get_ui(user_ui, user=None): for i in ui['lists'].keys(): if i not in ids: del ui['lists'][i] + tids = [''] + if user: + tids += add_texts(user.texts.exclude(status="featured"), 'personal') + tids += add_texts(user.subscribed_texts.filter(status='public'), 'public') + tids += add_texts(text.models.Text.objects.filter(status='featured'), 'featured') return ui def init_user(user, request=None): diff --git a/static/js/pandora/UI.js b/static/js/pandora/UI.js index b9e3b68f..7754b905 100644 --- a/static/js/pandora/UI.js +++ b/static/js/pandora/UI.js @@ -54,102 +54,106 @@ pandora.UI = (function() { self.previousUI = Ox.clone(pandora.user.ui, true); self.previousUI._list = pandora.getListState(self.previousUI.find); - - if ('find' in args) { - // the challenge here is that find may change list, - // and list may then change listSort and listView, - // which we don't want to trigger, since find triggers - // (values we put in add will be changed, but won't trigger) - list = pandora.getListState(args.find); - pandora.user.ui._list = list; - pandora.user.ui._filterState = pandora.getFilterState(args.find); - pandora.user.ui._findState = pandora.getFindState(args.find); - if (pandora.$ui.appPanel && !pandora.stayInItemView) { - // if we're not on page load, and if find isn't a context change - // caused by an edit, then switch from item view to list view - args['item'] = ''; - } - if (list != self.previousUI._list) { - Ox.Log('UI', 'FIND HAS CHANGED LIST') - // if find has changed list - Ox.forEach(listSettings, function(listSetting, setting) { - // then for each setting that corresponds to a list setting - if (!pandora.user.ui.lists[list]) { - // either add the default setting - add[setting] = pandora.site.user.ui[setting]; - } else { - // or the existing list setting - add[setting] = pandora.user.ui.lists[list][listSetting] - } - }); - } - add.itemFind = pandora.getItemFind(args.find); + if (args.section == 'texts') { + trigger['section'] = args['section']; + trigger['text'] = args['text']; } else { - list = self.previousUI._list; - } - // it is important to check for find first, so that - // if find changes list, list is correct here - item = args.item || pandora.user.ui.item; - listView = add.listView || args.listView; - - if (listView) { - if (pandora.isClipView(listView)) { - // when switching to a clip view, clear list selection - // (but don't trigger an additional event) - add.listSelection = []; - } else if (['text', 'position'].indexOf(pandora.user.ui.listSort[0].key) > -1) { - // when switching to a non-clip view, with a sort key - // that only exists in clip view, reset sort to default - args.listSort = pandora.site.user.ui.listSort; + if ('find' in args) { + // the challenge here is that find may change list, + // and list may then change listSort and listView, + // which we don't want to trigger, since find triggers + // (values we put in add will be changed, but won't trigger) + list = pandora.getListState(args.find); + pandora.user.ui._list = list; + pandora.user.ui._filterState = pandora.getFilterState(args.find); + pandora.user.ui._findState = pandora.getFindState(args.find); + if (pandora.$ui.appPanel && !pandora.stayInItemView) { + // if we're not on page load, and if find isn't a context change + // caused by an edit, then switch from item view to list view + args['item'] = ''; + } + if (list != self.previousUI._list) { + Ox.Log('UI', 'FIND HAS CHANGED LIST') + // if find has changed list + Ox.forEach(listSettings, function(listSetting, setting) { + // then for each setting that corresponds to a list setting + if (!pandora.user.ui.lists[list]) { + // either add the default setting + add[setting] = pandora.site.user.ui[setting]; + } else { + // or the existing list setting + add[setting] = pandora.user.ui.lists[list][listSetting] + } + }); + } + add.itemFind = pandora.getItemFind(args.find); + } else { + list = self.previousUI._list; } - } + // it is important to check for find first, so that + // if find changes list, list is correct here + item = args.item || pandora.user.ui.item; + listView = add.listView || args.listView; - if (!pandora.user.ui.lists[list]) { - add['lists.' + that.encode(list)] = {}; - } - Ox.forEach(listSettings, function(listSetting, setting) { - // for each setting that corresponds to a list setting - // set that list setting to - var key = 'lists.' + that.encode(list) + '.' + listSetting; - if (setting in args) { - // the setting passed to UI.set - add[key] = args[setting]; - } else if (setting in add) { - // or the setting changed via find - add[key] = add[setting]; - } else if (!pandora.user.ui.lists[list]) { - // or the default setting - add[key] = pandora.site.user.ui[setting]; + if (listView) { + if (pandora.isClipView(listView)) { + // when switching to a clip view, clear list selection + // (but don't trigger an additional event) + add.listSelection = []; + } else if (['text', 'position'].indexOf(pandora.user.ui.listSort[0].key) > -1) { + // when switching to a non-clip view, with a sort key + // that only exists in clip view, reset sort to default + args.listSort = pandora.site.user.ui.listSort; + } } - }); - if (args.item) { - // when switching to an item, update list selection - add['listSelection'] = [args.item]; - add['lists.' + that.encode(list) + '.selection'] = [args.item]; - if ( - !args.itemView - && ['timeline', 'player', 'editor'].indexOf(pandora.user.ui.itemView) > -1 - && !pandora.user.ui.videoPoints[item] - && !args['videoPoints.' + item] - ) { - // if the item view doesn't change, remains a video view, - // video points don't exist yet, and won't be set, - // add default video points - add['videoPoints.' + item] = {annotation: '', 'in': 0, out: 0, position: 0}; + if (!pandora.user.ui.lists[list]) { + add['lists.' + that.encode(list)] = {}; } - } + Ox.forEach(listSettings, function(listSetting, setting) { + // for each setting that corresponds to a list setting + // set that list setting to + var key = 'lists.' + that.encode(list) + '.' + listSetting; + if (setting in args) { + // the setting passed to UI.set + add[key] = args[setting]; + } else if (setting in add) { + // or the setting changed via find + add[key] = add[setting]; + } else if (!pandora.user.ui.lists[list]) { + // or the default setting + add[key] = pandora.site.user.ui[setting]; + } + }); - if (['timeline', 'player', 'editor'].indexOf(args.itemView) > -1) { - // when switching to a video view, add it as default video view - args.videoView = args.itemView; - if ( - !pandora.user.ui.videoPoints[item] - && !args['videoPoints.' + item] - ) { - // if video points don't exist yet, and won't be set, - // add default video points - add['videoPoints.' + item] = {annotation: '', 'in': 0, out: 0, position: 0}; + if (args.item) { + // when switching to an item, update list selection + add['listSelection'] = [args.item]; + add['lists.' + that.encode(list) + '.selection'] = [args.item]; + if ( + !args.itemView + && ['timeline', 'player', 'editor'].indexOf(pandora.user.ui.itemView) > -1 + && !pandora.user.ui.videoPoints[item] + && !args['videoPoints.' + item] + ) { + // if the item view doesn't change, remains a video view, + // video points don't exist yet, and won't be set, + // add default video points + add['videoPoints.' + item] = {annotation: '', 'in': 0, out: 0, position: 0}; + } + } + + if (['timeline', 'player', 'editor'].indexOf(args.itemView) > -1) { + // when switching to a video view, add it as default video view + args.videoView = args.itemView; + if ( + !pandora.user.ui.videoPoints[item] + && !args['videoPoints.' + item] + ) { + // if video points don't exist yet, and won't be set, + // add default video points + add['videoPoints.' + item] = {annotation: '', 'in': 0, out: 0, position: 0}; + } } } @@ -187,7 +191,7 @@ pandora.UI = (function() { }); }); }); - + pandora.URL.update(Object.keys( !pandora.$ui.appPanel ? args : trigger )); diff --git a/static/js/pandora/URL.js b/static/js/pandora/URL.js index cda04fa7..41502b5b 100644 --- a/static/js/pandora/URL.js +++ b/static/js/pandora/URL.js @@ -16,17 +16,18 @@ pandora.URL = (function() { var state = {}; - state.type = pandora.site.itemsSection; + state.type = pandora.user.ui.section == 'items' ? pandora.site.itemsSection : pandora.user.ui.section; + state.item = pandora.user.ui[pandora.user.ui.section.slice(0, -1)]; - state.item = pandora.user.ui.item; - - if (!pandora.user.ui.item) { - state.view = pandora.user.ui.listView; - state.sort = pandora.user.ui.listSort; - state.find = pandora.user.ui.find; - } else { - state.view = pandora.user.ui.itemView; - state.sort = pandora.user.ui.itemSort; + if(pandora.user.ui.section == 'items') { + if (!pandora.user.ui.item) { + state.view = pandora.user.ui.listView; + state.sort = pandora.user.ui.listSort; + state.find = pandora.user.ui.find; + } else { + state.view = pandora.user.ui.itemView; + state.sort = pandora.user.ui.itemSort; + } } if (state.view == 'map') { @@ -79,56 +80,58 @@ pandora.URL = (function() { var set = { section: state.type == pandora.site.itemsSection ? 'items' : state.type, - item: state.item, page: '' }; + set[set.section.slice(0, -1)] = state.item; - if (state.view) { - set[!state.item ? 'listView' : 'itemView'] = state.view; - } - - if (state.span) { - if (['timeline', 'player', 'editor'].indexOf(state.view) > -1) { - if (Ox.isArray(state.span)) { - set['videoPoints.' + state.item] = { - annotation: '', - 'in': state.span[state.span.length - 2] || 0, - out: state.span.length == 1 ? 0 : Math.max( - state.span[state.span.length - 2], - state.span[state.span.length - 1] - ), - position: state.span[0] - }; - } else { - set['videoPoints.' + state.item + '.annotation'] = state.span; - } - } else if (state.view == 'map') { - // fixme: this doesn't handle map coordinates - if (state.span[0] != '@') { - //pandora.user.ui.mapSelection = state.span; - set['mapSelection'] = state.span; - set['mapFind'] = ''; - } else { - //pandora.user.ui.mapFind = state.span.slice(1); - set['mapFind'] = state.span.slice(1); - set['mapSelection'] = ''; - } - } else if (state.view == 'calendar') { - // ... + if (set.section == 'items') { + if (state.view) { + set[!state.item ? 'listView' : 'itemView'] = state.view; } - } - if (state.sort) { - set[!state.item ? 'listSort' : 'itemSort'] = state.sort; - } + if (state.span) { + if (['timeline', 'player', 'editor'].indexOf(state.view) > -1) { + if (Ox.isArray(state.span)) { + set['videoPoints.' + state.item] = { + annotation: '', + 'in': state.span[state.span.length - 2] || 0, + out: state.span.length == 1 ? 0 : Math.max( + state.span[state.span.length - 2], + state.span[state.span.length - 1] + ), + position: state.span[0] + }; + } else { + set['videoPoints.' + state.item + '.annotation'] = state.span; + } + } else if (state.view == 'map') { + // fixme: this doesn't handle map coordinates + if (state.span[0] != '@') { + //pandora.user.ui.mapSelection = state.span; + set['mapSelection'] = state.span; + set['mapFind'] = ''; + } else { + //pandora.user.ui.mapFind = state.span.slice(1); + set['mapFind'] = state.span.slice(1); + set['mapSelection'] = ''; + } + } else if (state.view == 'calendar') { + // ... + } + } - if (!state.item) { - if (state.find) { - set.find = state.find; - } else if (!pandora.$ui.appPanel) { - // when loading results without find, clear find, so that - // removing a query and reloading works as expected - set.find = pandora.site.user.ui.find; + if (state.sort) { + set[!state.item ? 'listSort' : 'itemSort'] = state.sort; + } + + if (!state.item) { + if (state.find) { + set.find = state.find; + } else if (!pandora.$ui.appPanel) { + // when loading results without find, clear find, so that + // removing a query and reloading works as expected + set.find = pandora.site.user.ui.find; + } } } @@ -246,6 +249,19 @@ pandora.URL = (function() { calendar: 'date' } }; + //Text + views['texts'] = { + list: [], + item: ['text'] + } + spanType['texts'] = { + list: [], + item: {} + } + sortKeys['texts'] = { + list: {}, + item: {} + } findKeys = [{id: 'list', type: 'string'}].concat(pandora.site.itemKeys); diff --git a/static/js/pandora/allItems.js b/static/js/pandora/allItems.js index ebaf98d9..7165c37b 100644 --- a/static/js/pandora/allItems.js +++ b/static/js/pandora/allItems.js @@ -12,7 +12,11 @@ pandora.ui.allItems = function() { .on({ click: function() { that.gainFocus(); - pandora.user.ui._list && pandora.UI.set('find', {conditions: [], operator: '&'}); + if (pandora.user.ui.section == 'items') { + pandora.user.ui._list && pandora.UI.set('find', {conditions: [], operator: '&'}); + } else { + pandora.UI.set(pandora.user.ui.section.slice(0, -1), ''); + } } }) .bindEvent({ @@ -33,35 +37,38 @@ pandora.ui.allItems = function() { overflow: 'hidden', whiteSpace: 'nowrap' }) - .html('All ' + pandora.site.itemName.plural) - .appendTo(that), - $items = $('
') - .css({ - float: 'left', - width: '42px', - margin: '1px 4px 1px 3px', - textAlign: 'right' - }) - .appendTo(that), - $clickButton = Ox.Button({ - style: 'symbol', - title: 'click', - type: 'image' - }) - .css({opacity: 0.25}) - .appendTo(that), - $uploadButton = Ox.Button({ - style: 'symbol', - title: 'upload', - type: 'image' - }) + .html(pandora.user.ui.section == 'items' ? 'All ' + pandora.site.itemName.plural + : pandora.site.site.name + ' ' + Ox.toTitleCase(pandora.user.ui.section)) .appendTo(that); - pandora.api.find({ - query: {conditions: [], operator: '&'} - }, function(result) { - that.update(result.data.items); - }); + if (pandora.user.ui.section == 'items') { + var $items = $('
') + .css({ + float: 'left', + width: '42px', + margin: '1px 4px 1px 3px', + textAlign: 'right' + }) + .appendTo(that), + $clickButton = Ox.Button({ + style: 'symbol', + title: 'click', + type: 'image' + }) + .css({opacity: 0.25}) + .appendTo(that), + $uploadButton = Ox.Button({ + style: 'symbol', + title: 'upload', + type: 'image' + }) + .appendTo(that); + pandora.api.find({ + query: {conditions: [], operator: '&'} + }, function(result) { + that.update(result.data.items); + }); + } that.update = function(items) { $items.html(Ox.formatNumber(items)); diff --git a/static/js/pandora/deleteListDialog.js b/static/js/pandora/deleteListDialog.js index 12e37a72..22b3e396 100644 --- a/static/js/pandora/deleteListDialog.js +++ b/static/js/pandora/deleteListDialog.js @@ -3,6 +3,9 @@ 'use strict'; pandora.ui.deleteListDialog = function(list) { + var ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1); var listData = pandora.getListData(list), $folderList = pandora.$ui.folderList[listData.folder], @@ -10,7 +13,7 @@ pandora.ui.deleteListDialog = function(list) { buttons: [ Ox.Button({ id: 'keep', - title: 'Keep List' + title: 'Keep ' + folderItem }).bindEvent({ click: function() { that.close(); @@ -18,23 +21,27 @@ pandora.ui.deleteListDialog = function(list) { }), Ox.Button({ id: 'delete', - title: 'Delete List' + title: 'Delete ' + folderItem }).bindEvent({ click: function() { that.close(); - pandora.api.removeList({ + pandora.api['remove' + folderItem]({ id: listData.id }, function(result) { - Ox.Request.clearCache('findLists'); + Ox.Request.clearCache('find' + folderItems); Ox.Request.clearCache(listData.id); $folderList .options({selected: []}) .bindEventOnce({ load: function() { - pandora.UI.set('lists.' + listData.id, null); - pandora.UI.set({ - find: pandora.site.user.ui.find - }); + if (ui.section == 'items') { + pandora.UI.set('lists.' + listData.id, null); + pandora.UI.set({ + find: pandora.site.user.ui.find + }); + } else { + pandora.UI.set(folderItem.toLowerCase(), ''); + } } }) .reloadList(); @@ -51,7 +58,7 @@ pandora.ui.deleteListDialog = function(list) { .append( $('
') .css({position: 'absolute', left: '96px', top: '16px', width: '192px'}) - .html('Are you sure you want to delete the list "' + listData.name + '"?') + .html('Are you sure you want to delete the ' + folderItem.toLowerCase() + ' "' + listData.name + '"?') ), height: 128, keys: {enter: 'delete', escape: 'keep'}, @@ -61,4 +68,4 @@ pandora.ui.deleteListDialog = function(list) { return that; -} \ No newline at end of file +} diff --git a/static/js/pandora/folderBrowserBar.js b/static/js/pandora/folderBrowserBar.js index 7a8c851f..aa504586 100644 --- a/static/js/pandora/folderBrowserBar.js +++ b/static/js/pandora/folderBrowserBar.js @@ -1,7 +1,10 @@ // vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; pandora.ui.folderBrowserBar = function(id) { - var that = Ox.Bar({ + var ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1), + that = Ox.Bar({ size: 24 }); pandora.$ui.findListElement[id] = Ox.FormElementGroup({ @@ -9,7 +12,7 @@ pandora.ui.folderBrowserBar = function(id) { pandora.$ui.findListSelect[id] = Ox.Select({ items: [ {id: 'user', title: 'Find: User'}, - {id: 'name', title: 'Find: List'} + {id: 'name', title: 'Find: ' + folderItem} ], overlap: 'right', type: 'image' @@ -56,7 +59,7 @@ pandora.ui.folderBrowserBar = function(id) { {key: 'status', value: 'private', operator: '!='}, {key: key, value: value, operator: '='} ], operator: '&'}; - return pandora.api.findLists(Ox.extend(data, { + return pandora.api['find' + folderItems](Ox.extend(data, { query: query }), callback); } diff --git a/static/js/pandora/folderBrowserList.js b/static/js/pandora/folderBrowserList.js index 2d1e1029..3580f758 100644 --- a/static/js/pandora/folderBrowserList.js +++ b/static/js/pandora/folderBrowserList.js @@ -3,8 +3,11 @@ pandora.ui.folderBrowserList = function(id) { // fixme: user and name are set to the same width here, // but resizeFolders will set them to different widths - var columnWidth = (pandora.user.ui.sidebarSize - Ox.UI.SCROLLBAR_SIZE - 96) / 2, - i = Ox.getIndexById(pandora.site.sectionFolders[pandora.user.ui.section], id), + var ui = pandora.user.ui, + columnWidth = (ui.sidebarSize - Ox.UI.SCROLLBAR_SIZE - (ui.section == 'items' ? 96 : 32)) / 2, + i = Ox.getIndexById(pandora.site.sectionFolders[ui.section], id), + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1), that = Ox.TableList({ columns: [ { @@ -52,7 +55,7 @@ pandora.ui.folderBrowserList = function(id) { }, id: 'name', operator: '+', - title: 'List', + title: folderItem, visible: true, width: Math.ceil(columnWidth) }, @@ -62,7 +65,7 @@ pandora.ui.folderBrowserList = function(id) { format: {type: 'number'}, operator: '-', title: 'Items', - visible: true, + visible: ui.section == 'items', width: 48 }, { @@ -72,7 +75,7 @@ pandora.ui.folderBrowserList = function(id) { format: function(value, data) { return $('') .attr({ - src: Ox.UI.getImageURL(value == 'static' ? 'symbolClick' : 'symbolFind') + src: Ox.UI.getImageURL(value == 'smart' ? 'symbolFind' : value == 'pdf' ? 'symbolFiles' : value == 'html' ? 'symbolFile' : 'symbolClick') }) .css({ width: '10px', @@ -89,7 +92,7 @@ pandora.ui.folderBrowserList = function(id) { ? (data.user == pandora.user.username ? 'Edit Query' : 'Show Query') : (data.user == pandora.user.username ? 'Edit Default View' : 'Default View: ...'); }, - visible: true, + visible: ui.section == 'items', width: 16 }, { @@ -116,7 +119,7 @@ pandora.ui.folderBrowserList = function(id) { tooltip: function(data) { var checked = id == 'favorite' ? data.subscribed : data.status == 'featured'; return (checked ? 'Remove from' : 'Add to') - + ' ' + Ox.toTitleCase(id) + ' Lists'; + + ' ' + Ox.toTitleCase(id) + ' ' + folderItems; }, visible: true, width: 16 @@ -130,7 +133,7 @@ pandora.ui.folderBrowserList = function(id) { ], operator: '&'} : {conditions: [ {key: 'status', value: 'private', operator: '!='} ], operator: ''}; - return pandora.api.findLists(Ox.extend(data, { + return pandora.api['find' + folderItems](Ox.extend(data, { query: query }), callback); }, @@ -139,7 +142,7 @@ pandora.ui.folderBrowserList = function(id) { // not-featured list may be in the user's favorites folder keys: id == 'featured' ? ['subscribed'] : [], pageLength: 1000, - selected: pandora.getListData().folder == id ? [pandora.user.ui._list] : [], + selected: pandora.getListData().folder == id ? [ui.section == 'items' ? ui._list : ui[ui.section.slice(0, -1)]] : [], sort: [{key: 'name', operator: '+'}], unique: 'id' }) @@ -153,19 +156,19 @@ pandora.ui.folderBrowserList = function(id) { */ } else if (data.key == 'subscribed') { var subscribed = that.value(data.id, 'subscribed'); - pandora.api[subscribed ? 'unsubscribeFromList' : 'subscribeToList']({ + pandora.api[subscribed ? 'unsubscribeFrom' + folderItem : 'subscribeTo' + folderItem]({ id: data.id }, function(result) { that.value(data.id, 'subscribed', !subscribed); }); } else if (data.key == 'status') { - pandora.api.editList({ + pandora.api['edit' + folderItem]({ id: data.id, status: that.value(data.id, 'status') == 'featured' ? 'public' : 'featured' }, function(result) { Ox.Log('', 'result', result) if (result.data.user == pandora.user.username || result.data.subscribed) { - Ox.Request.clearCache(); // fixme: remove + Ox.Request.clearCache(); // fixme: removen pandora.$ui.folderList[ result.data.user == pandora.user.username ? 'personal' : 'favorite' ].reloadList(); @@ -175,7 +178,7 @@ pandora.ui.folderBrowserList = function(id) { } }, init: function(data) { - pandora.site.sectionFolders[pandora.user.ui.section][i].items = data.items; + pandora.site.sectionFolders[ui.section][i].items = data.items; pandora.$ui.folder[i].$content.css({ height: 40 + data.items * 16 + 'px' }); @@ -185,7 +188,9 @@ pandora.ui.folderBrowserList = function(id) { pandora.resizeFolders(); }, paste: function(data) { - pandora.$ui.list.triggerEvent('paste', data); + if (ui.section == 'items') { + pandora.$ui.list.triggerEvent('paste', data); + } }, select: function(data) { // fixme: duplicated @@ -195,14 +200,18 @@ pandora.ui.folderBrowserList = function(id) { id != id_ && $list.options('selected', []); }); } - pandora.UI.set({ - find: { - conditions: list ? [ - {key: 'list', value: data.ids[0], operator: '=='} - ] : [], - operator: '&' - } - }); + if (ui.section == 'items') { + pandora.UI.set({ + find: { + conditions: list ? [ + {key: 'list', value: data.ids[0], operator: '=='} + ] : [], + operator: '&' + } + }); + } else { + pandora.UI.set(ui.section.slice(0, -1), list); + } } }); return that; diff --git a/static/js/pandora/folderList.js b/static/js/pandora/folderList.js index c7798f0d..73ee510d 100644 --- a/static/js/pandora/folderList.js +++ b/static/js/pandora/folderList.js @@ -1,382 +1,386 @@ // vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; + pandora.ui.folderList = function(id) { - var i = Ox.getIndexById(pandora.site.sectionFolders[pandora.user.ui.section], id), - canEditFeaturedLists = pandora.site.capabilities.canEditFeaturedLists[pandora.user.level], + var ui = pandora.user.ui, + i = Ox.getIndexById(pandora.site.sectionFolders[ui.section], id), + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1), + canEditFeatured = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level], that; - if (pandora.user.ui.section == 'items') { - var columns, items; - if (id != 'volumes') { - columns = [ - { - clickable: function(data) { - return data.user == pandora.user.username || (id == 'featured' && canEditFeaturedLists); - }, - format: function(value, data) { - return $('').attr({ - src: '/list/' + data.id + '/icon.jpg' - }).css({ - width: '14px', - height: '14px', - borderRadius: '4px', - margin: '0 0 0 -3px' - }); - }, - id: 'user', - operator: '+', - tooltip: function(data) { - return data.user == pandora.user.username - || (id == 'featured' && canEditFeaturedLists) - ? 'Edit Icon' - : ''; - }, - visible: true, - width: 16 + var columns, items; + if (id != 'volumes') { + columns = [ + { + clickable: function(data) { + return data.user == pandora.user.username || (id == 'featured' && canEditFeatured); }, - { - format: function(value) { - return Ox.encodeHTMLEntities(value.split(':').join(': ')); - }, - id: 'id', - operator: '+', - visible: id == 'favorite', - // fixme: user and name are set to the same width here, - // but resizeFolders will set them to different widths - width: pandora.user.ui.sidebarWidth - 96 - }, - { - editable: function(data) { - return data.user == pandora.user.username; - }, - format: function(value) { - return Ox.encodeHTMLEntities(value); - }, - id: 'name', - input: { - autovalidate: pandora.ui.autovalidateListname - }, - operator: '+', - tooltip: id == 'personal' ? 'Edit Title' : '', - unformat: function(value) { - return Ox.decodeHTMLEntities(value); - }, - visible: id != 'favorite', - width: pandora.user.ui.sidebarWidth - 96 - }, - { - align: 'right', - id: 'items', - format: {type: 'number'}, - operator: '-', - visible: true, - width: 48 - }, - { - clickable: function(data) { - return data.type == 'smart' || data.user == pandora.user.username; - }, - format: function(value, data) { - return $('') - .attr({ - src: Ox.UI.getImageURL(value == 'static' ? 'symbolClick' : 'symbolFind') - }) - .css({ - width: '10px', - height: '10px', - padding: '3px', - opacity: data.user == pandora.user.username ? 1 : 0.25 - }); - }, - id: 'type', - operator: '+', - tooltip: function(data) { - return data.type == 'smart' - ? (data.user == pandora.user.username ? 'Edit Query' : 'Show Query') - : (data.user == pandora.user.username ? 'Edit Default View' : 'Default View: ...'); - }, - visible: true, - width: 16 - }, - { - clickable: id == 'personal', - format: function(value) { - var symbols = {personal: 'Publish', favorite: 'Like', featured: 'Star'}; - return $('') - .attr({ - src: Ox.UI.getImageURL( - 'symbol' + symbols[id] - ) - }) - .css({ - width: '10px', - height: '10px', - padding: '3px', - opacity: value == 'private' ? 0.25 : 1 - }); - }, - id: 'status', - operator: '+', - tooltip: id == 'personal' ? function(data) { - return data.status == 'private' ? 'Make Public' : 'Make Private'; - } : null, - visible: true, - width: 16 - } - ]; - items = function(data, callback) { - var query; - if (id == 'personal') { - query = {conditions: [ - {key: 'user', value: pandora.user.username, operator: '=='}, - {key: 'status', value: 'featured', operator: '!='} - ], operator: '&'}; - } else if (id == 'favorite') { - query = {conditions: [ - {key: 'subscribed', value: true, operator: '='}, - {key: 'status', value: 'featured', operator: '!='} - ], operator: '&'}; - } else if (id == 'featured') { - query = {conditions: [ - {key: 'status', value: 'featured', operator: '='} // fixme: '==' performs better - ], operator: '&'}; - } - return pandora.api.findLists(Ox.extend(data, { - query: query - }), callback); - }; - } else { - columns = [ - { - format: function() { - return $('').attr({ - src: Ox.UI.getImageURL('symbolVolume') - }).css({ - width: '10px', - height: '10px', - padding: '3px' - }); - }, - id: 'user', - operator: '+', - visible: true, - width: 16 - }, - { - editable: true, - id: 'name', - operator: '+', - tooltip: 'Edit Title', - visible: true, - width: pandora.user.ui.sidebarWidth - 96 - }, - { - align: 'right', - id: 'items', - format: {type: 'number'}, - operator: '-', - visible: true, - width: 48 - }, - { - clickable: function(data) { - return data.mounted; - }, - format: function(value, data) { - return $('') - .attr({ - src: Ox.UI.getImageURL(data.mounted ? 'symbolSync' : 'symbolEdit') - }) - .css({ - width: '10px', - height: '10px', - padding: '3px' - }); - }, - id: 'path', - operator: '+', - tooltip: function(data) { - return data.mounted ? 'Scan Volume' : 'Edit Path'; - }, - visible: true, - width: 16 - }, - { - clickable: true, - format: function(value, data) { - return $('') - .attr({ - src: Ox.UI.getImageURL('symbolMount') - }) - .css({ - width: '10px', - height: '10px', - padding: '3px 2px 1px 2px', - opacity: data.mounted ? 1 : 0.25 - }); - }, - id: 'mounted', - operator: '+', - tooltip: function(data) { - return data.mounted ? 'Unmount Volume' : 'Mount Volume'; - }, - visible: true, - width: 16 - } - ]; - items = function(data, callback) { - var volumes = pandora.user.volumes || []; - if (!data.keys) { - data = {items: volumes.length}; - } else { - data = {items: volumes.map(function(volume) { - return Ox.extend({id: volume.name, user: pandora.user.username}, volume); - })}; - } - // fixme: ridiculous (we're binding to init too late) - setTimeout(function() { - callback({data: data}); - }, 1000); - }; - } - that = Ox.TableList({ - columns: columns, - items: items, - keys: ['query'], - max: 1, - min: 0, - pageLength: 1000, - //selected: pandora.getListData().folder == id ? [pandora.user.ui._list] : [], - sort: [{key: 'position', operator: '+'}], - sortable: id != 'featured' || canEditFeaturedLists, - unique: id != 'volumes' ? 'id' : 'name' - }) - .css({ - left: 0, - top: 0, - width: pandora.user.ui.sidebarWidth + 'px' - }) - .bindEvent({ - add: function(event) { - // fixme: this is duplicated, - // see folder collapse panel menu handler - var i = ['personal', 'favorite', 'featured'].indexOf(id); - if (id == 'personal') { - if (event.keys == '' || event.keys == 'alt') { - pandora.api.addList({ - name: 'Untitled', - status: 'private', - type: event.keys == '' ? 'static' : 'smart' - }, function(result) { - var id = result.data.id; - pandora.UI.set({ - find: { - conditions: [{key: 'list', value: id, operator: '=='}], - operator: '&' - } - }); - Ox.Request.clearCache(); // fixme: remove - that.reloadList().bindEventOnce({ - load: function(data) { - that.gainFocus() - .options({selected: [id]}) - .editCell(id, 'name'); - } - }); + format: function(value, data) { + return $('').attr({ + src: '/' + folderItem.toLowerCase() + '/' + data.id + '/icon.jpg' + }).css({ + width: '14px', + height: '14px', + borderRadius: '4px', + margin: '0 0 0 -3px' }); - } - } else if (id == 'favorite' || (id == 'featured' && canEditFeaturedLists)) { - // this makes the button trigger a change event, - // which is already being handled in folders.js - pandora.$ui.manageListsButton[id].options({value: true}); - /* - if (!pandora.site.sectionFolders.items[i].showBrowser) { - pandora.site.sectionFolders.items[i].showBrowser = true; - pandora.$ui.manageListsButton[id].options({selected: true}); - pandora.$ui.folderList[id].replaceWith( - pandora.$ui.folderBrowser[id] = pandora.ui.folderBrowser(id) - ); - } - */ - } + }, + id: 'user', + operator: '+', + tooltip: function(data) { + return data.user == pandora.user.username + || (id == 'featured' && canEditFeatured) + ? 'Edit Icon' + : ''; + }, + visible: true, + width: 16 }, - click: function(data) { - //var $list = pandora.$ui.folderList[id]; - if (data.key == 'user') { - pandora.$ui.listDialog = pandora.ui.listDialog('icon').open(); - } else if (data.key == 'type') { - if (that.value(data.id, 'type') == 'smart') { - pandora.$ui.listDialog = pandora.ui.listDialog('query').open(); - } - } else if (data.key == 'status') { - var status = that.value(data.id, data.key) == 'private' ? 'public' : 'private'; - pandora.changeListStatus(data.id, status, function(result) { - that.value(result.data.id, 'status', result.data.status); - }); - } else if (data.key == 'path') { - - } else if (data.key == 'mounted') { - alert(JSON.stringify(data)); - } + { + format: function(value) { + return Ox.encodeHTMLEntities(value.split(':').join(': ')); + }, + id: 'id', + operator: '+', + visible: id == 'favorite', + // fixme: user and name are set to the same width here, + // but resizeFolders will set them to different widths + width: ui.sidebarWidth - 96 }, - 'delete': function(data) { - if (id == 'personal') { - pandora.ui.deleteListDialog(data.ids[0]).open(); - } else if (id == 'favorite') { - that.options({selected: []}); - pandora.api.unsubscribeFromList({ - id: data.ids[0] + { + editable: function(data) { + return data.user == pandora.user.username; + }, + format: function(value) { + return Ox.encodeHTMLEntities(value); + }, + id: 'name', + input: { + autovalidate: pandora.ui.autovalidateListname + }, + operator: '+', + tooltip: id == 'personal' ? 'Edit Title' : '', + unformat: function(value) { + return Ox.decodeHTMLEntities(value); + }, + visible: id != 'favorite', + width: ui.sidebarWidth - 96 + }, + { + align: 'right', + id: 'items', + format: {type: 'number'}, + operator: '-', + visible: true, + width: 48 + }, + { + clickable: function(data) { + return data.type == 'smart' || data.user == pandora.user.username; + }, + format: function(value, data) { + return $('') + .attr({ + src: Ox.UI.getImageURL(value == 'smart' ? 'symbolFind' : value == 'pdf' ? 'symbolFiles' : value == 'html' ? 'symbolFile' : 'symbolClick') + }) + .css({ + width: '10px', + height: '10px', + padding: '3px', + opacity: data.user == pandora.user.username ? 1 : 0.25 + }); + }, + id: 'type', + operator: '+', + tooltip: function(data) { + return data.type == 'smart' + ? (data.user == pandora.user.username ? 'Edit Query' : 'Show Query') + : (data.user == pandora.user.username ? 'Edit Default View' : 'Default View: ...'); + }, + visible: true, + width: 16 + }, + { + clickable: id == 'personal', + format: function(value) { + var symbols = {personal: 'Publish', favorite: 'Like', featured: 'Star'}; + return $('') + .attr({ + src: Ox.UI.getImageURL( + 'symbol' + symbols[id] + ) + }) + .css({ + width: '10px', + height: '10px', + padding: '3px', + opacity: value == 'private' ? 0.25 : 1 + }); + }, + id: 'status', + operator: '+', + tooltip: id == 'personal' ? function(data) { + return data.status == 'private' ? 'Make Public' : 'Make Private'; + } : null, + visible: true, + width: 16 + } + ]; + items = function(data, callback) { + var query; + if (id == 'personal') { + query = {conditions: [ + {key: 'user', value: pandora.user.username, operator: '=='}, + {key: 'status', value: 'featured', operator: '!='} + ], operator: '&'}; + } else if (id == 'favorite') { + query = {conditions: [ + {key: 'subscribed', value: true, operator: '='}, + {key: 'status', value: 'featured', operator: '!='} + ], operator: '&'}; + } else if (id == 'featured') { + query = {conditions: [ + {key: 'status', value: 'featured', operator: '='} // fixme: '==' performs better + ], operator: '&'}; + } + return pandora.api['find' + folderItems](Ox.extend(data, { + query: query + }), callback); + }; + } else { + columns = [ + { + format: function() { + return $('').attr({ + src: Ox.UI.getImageURL('symbolVolume') + }).css({ + width: '10px', + height: '10px', + padding: '3px' + }); + }, + id: 'user', + operator: '+', + visible: true, + width: 16 + }, + { + editable: true, + id: 'name', + operator: '+', + tooltip: 'Edit Title', + visible: true, + width: ui.sidebarWidth - 96 + }, + { + align: 'right', + id: 'items', + format: {type: 'number'}, + operator: '-', + visible: true, + width: 48 + }, + { + clickable: function(data) { + return data.mounted; + }, + format: function(value, data) { + return $('') + .attr({ + src: Ox.UI.getImageURL(data.mounted ? 'symbolSync' : 'symbolEdit') + }) + .css({ + width: '10px', + height: '10px', + padding: '3px' + }); + }, + id: 'path', + operator: '+', + tooltip: function(data) { + return data.mounted ? 'Scan Volume' : 'Edit Path'; + }, + visible: true, + width: 16 + }, + { + clickable: true, + format: function(value, data) { + return $('') + .attr({ + src: Ox.UI.getImageURL('symbolMount') + }) + .css({ + width: '10px', + height: '10px', + padding: '3px 2px 1px 2px', + opacity: data.mounted ? 1 : 0.25 + }); + }, + id: 'mounted', + operator: '+', + tooltip: function(data) { + return data.mounted ? 'Unmount Volume' : 'Mount Volume'; + }, + visible: true, + width: 16 + } + ]; + items = function(data, callback) { + var volumes = pandora.user.volumes || []; + if (!data.keys) { + data = {items: volumes.length}; + } else { + data = {items: volumes.map(function(volume) { + return Ox.extend({id: volume.name, user: pandora.user.username}, volume); + })}; + } + // fixme: ridiculous (we're binding to init too late) + setTimeout(function() { + callback({data: data}); + }, 1000); + }; + } + that = Ox.TableList({ + columns: columns, + items: items, + keys: ui.section == 'items' ? ['query'] : [], + max: 1, + min: 0, + pageLength: 1000, + //selected: pandora.getListData().folder == id ? [ui._list] : [], + sort: [{key: 'position', operator: '+'}], + sortable: id != 'featured' || canEditFeatured, + unique: id != 'volumes' ? 'id' : 'name' + }) + .css({ + left: 0, + top: 0, + width: ui.sidebarWidth + 'px' + }) + .bindEvent({ + add: function(event) { + // fixme: this is duplicated, + // see folder collapse panel menu handler + var i = ['personal', 'favorite', 'featured'].indexOf(id); + if (id == 'personal') { + if (event.keys == '' || event.keys == 'alt') { + pandora.api.addList({ + name: 'Untitled', + status: 'private', + type: event.keys == '' ? 'static' : 'smart' }, function(result) { + var id = result.data.id; + pandora.UI.set({ + find: { + conditions: [{key: 'list', value: id, operator: '=='}], + operator: '&' + } + }); Ox.Request.clearCache(); // fixme: remove - that.reloadList(); - }); - } else if (id == 'featured' && canEditFeaturedLists) { - that.options({selected: []}); - pandora.api.editList({ - id: data.ids[0], - status: 'public' - }, function(result) { - // fixme: duplicated - if (result.data.user == pandora.user.username || result.data.subscribed) { - Ox.Request.clearCache(); // fixme: remove - pandora.$ui.folderList[ - result.data.user == pandora.user.username ? 'personal' : 'favorite' - ].reloadList(); - } - that.reloadList(); + that.reloadList().bindEventOnce({ + load: function(data) { + that.gainFocus() + .options({selected: [id]}) + .editCell(id, 'name'); + } + }); }); } - }, - /* - edit: function() { - pandora.ui.listDialog().open(); - }, - */ - init: function(data) { - pandora.site.sectionFolders[pandora.user.ui.section][i].items = data.items; - pandora.$ui.folder[i].$content.css({ - height: data.items * 16 + 'px' - }); - pandora.$ui.folderList[id].css({ - height: data.items * 16 + 'px' - }); - pandora.resizeFolders(); - }, - move: function(data) { - pandora.api.sortLists({ - section: id, - ids: data.ids - }); - }, - paste: function(data) { - pandora.$ui.list.triggerEvent('paste', data); - }, - select: function(data) { - var list = data.ids.length ? data.ids[0] : ''; - if (list) { - Ox.forEach(pandora.$ui.folderList, function($list, id_) { - id != id_ && $list.options('selected', []); - }); + } else if (id == 'favorite' || (id == 'featured' && canEditFeatured)) { + // this makes the button trigger a change event, + // which is already being handled in folders.js + pandora.$ui.manageListsButton[id].options({value: true}); + /* + if (!pandora.site.sectionFolders.items[i].showBrowser) { + pandora.site.sectionFolders.items[i].showBrowser = true; + pandora.$ui.manageListsButton[id].options({selected: true}); + pandora.$ui.folderList[id].replaceWith( + pandora.$ui.folderBrowser[id] = pandora.ui.folderBrowser(id) + ); } + */ + } + }, + click: function(data) { + //var $list = pandora.$ui.folderList[id]; + if (data.key == 'user') { + pandora.$ui.listDialog = pandora.ui.listDialog('icon').open(); + } else if (data.key == 'type') { + if (that.value(data.id, 'type') == 'smart') { + pandora.$ui.listDialog = pandora.ui.listDialog('query').open(); + } + } else if (data.key == 'status') { + var status = that.value(data.id, data.key) == 'private' ? 'public' : 'private'; + pandora.changeFolderItemStatus(data.id, status, function(result) { + that.value(result.data.id, 'status', result.data.status); + }); + } else if (data.key == 'path') { + + } else if (data.key == 'mounted') { + alert(JSON.stringify(data)); + } + }, + 'delete': function(data) { + if (id == 'personal') { + pandora.ui.deleteListDialog(data.ids[0]).open(); + } else if (id == 'favorite') { + that.options({selected: []}); + pandora.api['unsubscribeFrom' + folderItem]({ + id: data.ids[0] + }, function(result) { + Ox.Request.clearCache(); // fixme: remove + that.reloadList(); + }); + } else if (id == 'featured' && canEditFeatured) { + that.options({selected: []}); + pandora.api['edit' + folderItem]({ + id: data.ids[0], + status: 'public' + }, function(result) { + // fixme: duplicated + if (result.data.user == pandora.user.username || result.data.subscribed) { + Ox.Request.clearCache(); // fixme: remove + pandora.$ui.folderList[ + result.data.user == pandora.user.username ? 'personal' : 'favorite' + ].reloadList(); + } + that.reloadList(); + }); + } + }, + /* + edit: function() { + pandora.ui.listDialog().open(); + }, + */ + init: function(data) { + pandora.site.sectionFolders[ui.section][i].items = data.items; + pandora.$ui.folder[i].$content.css({ + height: data.items * 16 + 'px' + }); + pandora.$ui.folderList[id].css({ + height: data.items * 16 + 'px' + }); + pandora.resizeFolders(); + }, + move: function(data) { + pandora.api['sort' + foldeItem]({ + section: id, + ids: data.ids + }); + }, + paste: function(data) { + pandora.$ui.list.triggerEvent('paste', data); + }, + select: function(data) { + var list = data.ids.length ? data.ids[0] : ''; + if (list) { + Ox.forEach(pandora.$ui.folderList, function($list, id_) { + id != id_ && $list.options('selected', []); + }); + } + if (ui.section == 'items') { pandora.UI.set({ find: { conditions: list ? [ @@ -385,18 +389,20 @@ pandora.ui.folderList = function(id) { operator: '&' } }); - }, - submit: function(data) { - var data_ = {id: data.id}; - data_[data.key] = data.value; - pandora.api.editList(data_, function(result) { - if (result.data.id != data.id) { - pandora.renameList(data.id, result.data.id, result.data.name, id); - pandora.$ui.info.updateListInfo(); - } - }); + } else { + pandora.UI.set(ui.section.slice(0, -1), list); } - }); - } + }, + submit: function(data) { + var data_ = {id: data.id}; + data_[data.key] = data.value; + pandora.api['edit' + folderItem](data_, function(result) { + if (result.data.id != data.id) { + pandora.renameList(data.id, result.data.id, result.data.name, id); + pandora.$ui.info.updateListInfo(); + } + }); + } + }); return that; }; diff --git a/static/js/pandora/folders.js b/static/js/pandora/folders.js index 6454c4c8..284df4d3 100644 --- a/static/js/pandora/folders.js +++ b/static/js/pandora/folders.js @@ -7,7 +7,9 @@ pandora.ui.folders = function() { .css({overflowX: 'hidden', overflowY: 'auto'}) .bindEvent({ resize: pandora.resizeFolders - }); + }), + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1); //var $sections = []; pandora.$ui.allItems = pandora.ui.allItems().appendTo(that); @@ -18,15 +20,17 @@ pandora.ui.folders = function() { pandora.$ui.findListSelect = {}; pandora.$ui.findListInput = {}; pandora.$ui.manageListsButton = {}; - if (ui.section == 'items') { - pandora.site.sectionFolders.items.forEach(function(folder, i) { - var extras, $select; - if (folder.id == 'personal') { - if (pandora.user.level == 'guest') { - extras = [ - infoButton('Personal Lists', 'To create and share your own lists of movies, please sign up or sign in.') - ]; - } else { + pandora.site.sectionFolders[ui.section].forEach(function(folder, i) { + var extras, $select; + if (folder.id == 'personal') { + if (pandora.user.level == 'guest') { + extras = [ + infoButton('Personal ' + folderItems, 'To create and share your own ' + (ui.section == 'items' + ? 'lists of ' + pandora.site.itemName.plural.toLowerCase() + : ui.section) + ', please sign up or sign in.') + ]; + } else { + if (ui.section == 'items') { extras = [ pandora.$ui.personalListsMenu = Ox.MenuButton({ items: [ @@ -78,195 +82,225 @@ pandora.ui.folders = function() { } }) ]; - } - } else if (folder.id == 'favorite') { - if (pandora.user.level == 'guest') { - extras = [infoButton('Favorite Lists', 'To browse and subscribe to shared lists from other users, please sign up or sign in.')]; } else { - extras = [pandora.$ui.manageListsButton['favorite'] = Ox.Button({ - selectable: true, - style: 'symbol', - title: 'Edit', - tooltip: 'Manage Favorite Lists', - type: 'image' - }) - .bindEvent({ - change: function(data) { - var listData; - Ox.Request.clearCache(); // fixme: remove - pandora.site.sectionFolders.items[i].showBrowser = !pandora.site.sectionFolders.items[i].showBrowser; - if (pandora.site.sectionFolders.items[i].showBrowser) { - pandora.$ui.folderList.favorite.replaceWith( - pandora.$ui.folderBrowser.favorite = pandora.ui.folderBrowser('favorite') - ); - } else { - listData = pandora.getListData(); - if ( - pandora.$ui.folderList.favorite.options('selected').length - && !listData.subscribed - ) { - // the selected list in the favorites browser is not in the favorites folder - pandora.$ui.folderList.favorite.options({selected: []}); - if (Ox.getObjectById(pandora.site.sectionFolders.items, 'featured').showBrowser) { - // but in the featured browser - pandora.$ui.folderList.featured.options({selected: [listData.id]}); - } else { - // and nowhere else - pandora.UI.set({ - find: pandora.site.user.ui.find - }); - } + extras = [ + pandora.$ui.personalListsMenu = Ox.MenuButton({ + items: [ + { id: 'new' + folderItem.toLowerCase(), title: 'New ' + folderItem }, + {}, + { id: 'delete' + folderItem.toLowerCase(), title: 'Delete Selected ' + folderItem + '...', disabled: !ui[ui.section.slice(0,-1)] } + ], + title: 'edit', + tooltip: 'Manage Personal ' + folderItems, + type: 'image' + }) + .bindEvent({ + click: function(data) { + var $list = pandora.$ui.folderList[folder.id]; + // fixme: duplicated + if (data.id == 'new' + folderItem.toLowerCase()) { + pandora['add' + folderItem](); + } else if (data.id == 'delete' + folderItem.toLowerCase()) { + pandora.ui.deleteListDialog().open(); } - pandora.$ui.folderBrowser.favorite.replaceWith( - pandora.$ui.folderList.favorite = pandora.ui.folderList('favorite') - ); } - pandora.resizeFolders(); - } - })]; - } - } else if (folder.id == 'featured') { - if (pandora.user.level != 'admin') { - extras = [infoButton('Featured Lists', 'Featured lists are selected public lists, picked by the ' + pandora.site.site.name + ' staff.')]; - } else { - extras = [pandora.$ui.manageListsButton['featured'] = Ox.Button({ - selectable: true, - style: 'symbol', - title: 'Edit', - tooltip: 'Manage Featured Lists', - type: 'image' - }) - .bindEvent({ - change: function(data) { - var listData; - Ox.Request.clearCache(); // fixme: remove - pandora.site.sectionFolders.items[i].showBrowser = !pandora.site.sectionFolders.items[i].showBrowser; - if (pandora.site.sectionFolders.items[i].showBrowser) { - pandora.$ui.folderList.featured.replaceWith( - pandora.$ui.folderBrowser.featured = pandora.ui.folderBrowser('featured') - ); - } else { - listData = pandora.getListData(); - Ox.Log('', 'FEATURED', listData) - if ( - pandora.$ui.folderList.featured.options('selected').length - && listData.status != 'featured' - ) { - // the selected list in the featured browser is not in the featured folder - pandora.$ui.folderList.featured.options({selected: []}); - if (listData.user == pandora.user.username) { - // but in the personal folder - pandora.$ui.folderList.personal.options({selected: [listData.id]}); - } else if ( - listData.subscribed - || Ox.getObjectById(pandora.site.sectionFolders.items, 'favorite').showBrowser - ) { - // but in the favorites folder or browser - pandora.$ui.folderList.favorite.options({selected: [listData.id]}); - } else { - // and nowhere else - pandora.UI.set({ - find: pandora.site.user.ui.find - }); - } - } - pandora.$ui.folderBrowser.featured.replaceWith( - pandora.$ui.folderList.featured = pandora.ui.folderList('featured') - ); - } - pandora.resizeFolders(); - } - })]; - } - } else if (folder.id == 'volumes') { - if (pandora.user.level == 'guest') { - extras = [infoButton('Local Volumes', 'To import movies from a local disk, please sign up or sign in.')]; - } else { - extras = [Ox.MenuButton({ - items: [ - { id: 'add', title: 'Add Volume...', disabled: true }, - { id: 'scan', title: 'Scan Selected Volume...', disabled: true }, - { id: 'remove', title: 'Remove Selected Volume...', disabled: true }, - {}, - { id: 'import', title: 'Import Movies...', disabled: true } - ], - title: 'edit', - tooltip: 'Manage Volumes', - type: 'image' - }) - .bindEvent({ - click: function(data) { - } - })]; + }) + .bindEvent('pandora_' + ui.section.slice(0,-1), function(data) { + pandora.$ui.personalListsMenu[ + data.value && data.value.length ? 'enableItem' : 'disableItem' + ]('delete' + folderItem.toLowerCase()); + }) + ]; } } - pandora.$ui.folder[i] = Ox.CollapsePanel({ - id: folder.id, - collapsed: !ui.showFolder.items[folder.id], - extras: extras, - size: 16, - title: folder.title + } else if (folder.id == 'favorite') { + if (pandora.user.level == 'guest') { + extras = [infoButton('Favorite ' + folderItems, + 'To browse and subscribe to shared ' + folderItems.toLowerCase() + ' from other users, please sign up or sign in.')]; + } else { + extras = [pandora.$ui.manageListsButton['favorite'] = Ox.Button({ + selectable: true, + style: 'symbol', + title: 'Edit', + tooltip: 'Manage Favorite ' + folderItems, + type: 'image' }) .bindEvent({ - // fixme: duplicated - click: function(data) { - var $list = pandora.$ui.folderList[i], - hasFocus, id; - if (data.id == 'new' || data.id == 'newsmart') { - pandora.api.addList({ - name: 'Untitled', - status: 'private', - type: data.id == 'new' ? 'static' : 'smart' - }, function(result) { - id = result.data.id; - pandora.URL.set('?find=list:' + id) - Ox.Request.clearCache(); // fixme: remove - $list.reloadList().bindEventOnce({ - load: function(data) { - $list.gainFocus() - .options({selected: [id]}) - .editCell(id, 'name'); - } - }); - }); - } else if (data.id == 'browse') { - // alert('??') - /* - pandora.$ui.sectionList[1].replaceWith(pandora.$ui.publicLists = pandora.ui.publicLists()); - pandora.site.showAllPublicLists = true; - */ + change: function(data) { + var listData; + Ox.Request.clearCache(); // fixme: remove + pandora.site.sectionFolders[ui.section][i].showBrowser = !pandora.site.sectionFolders[ui.section][i].showBrowser; + if (pandora.site.sectionFolders[ui.section][i].showBrowser) { + pandora.$ui.folderList.favorite.replaceWith( + pandora.$ui.folderBrowser.favorite = pandora.ui.folderBrowser('favorite') + ); + } else { + listData = pandora.getListData(); + if ( + pandora.$ui.folderList.favorite.options('selected').length + && !listData.subscribed + ) { + // the selected list in the favorites browser is not in the favorites folder + pandora.$ui.folderList.favorite.options({selected: []}); + if (Ox.getObjectById(pandora.site.sectionFolders[ui.section], 'featured').showBrowser) { + // but in the featured browser + pandora.$ui.folderList.featured.options({selected: [listData.id]}); + } else { + // and nowhere else + pandora.UI.set({ + find: pandora.site.user.ui.find + }); + } + } + pandora.$ui.folderBrowser.favorite.replaceWith( + pandora.$ui.folderList.favorite = pandora.ui.folderList('favorite') + ); } - }, - toggle: function(data) { - data.collapsed && pandora.$ui.folderList[folder.id].loseFocus(); - pandora.UI.set('showFolder.items.' + folder.id, !data.collapsed); pandora.resizeFolders(); } - }); - //$sections.push(pandora.$ui.section[i]); - pandora.$ui.folderList[folder.id] = pandora.ui.folderList(folder.id) + })]; + } + } else if (folder.id == 'featured') { + if (pandora.user.level != 'admin') { + extras = [infoButton('Featured ' + folderItems, 'Featured ' + folderItems.toLowerCase() + ' are selected public ' + folderItems.toLowerCase() + ', picked by the ' + pandora.site.site.name + ' staff.')]; + } else { + extras = [pandora.$ui.manageListsButton['featured'] = Ox.Button({ + selectable: true, + style: 'symbol', + title: 'Edit', + tooltip: 'Manage Featured ' + folderItems, + type: 'image' + }) .bindEvent({ - selectafter: function() { - // ... - }, - selectbefore: function() { - // ... - } - }) - .bindEventOnce({ - init: function(data) { - if (++counter == 4) { - pandora.$ui.folder.forEach(function($folder) { - that.append($folder); - }); - pandora.resizeFolders(); - pandora.selectList(); + change: function(data) { + var listData; + Ox.Request.clearCache(); // fixme: remove + pandora.site.sectionFolders[ui.section][i].showBrowser = !pandora.site.sectionFolders[ui.section][i].showBrowser; + if (pandora.site.sectionFolders[ui.section][i].showBrowser) { + pandora.$ui.folderList.featured.replaceWith( + pandora.$ui.folderBrowser.featured = pandora.ui.folderBrowser('featured') + ); + } else { + listData = pandora.getListData(); + Ox.Log('', 'FEATURED', listData) + if ( + pandora.$ui.folderList.featured.options('selected').length + && listData.status != 'featured' + ) { + // the selected list in the featured browser is not in the featured folder + pandora.$ui.folderList.featured.options({selected: []}); + if (listData.user == pandora.user.username) { + // but in the personal folder + pandora.$ui.folderList.personal.options({selected: [listData.id]}); + } else if ( + listData.subscribed + || Ox.getObjectById(pandora.site.sectionFolders[ui.section], 'favorite').showBrowser + ) { + // but in the favorites folder or browser + pandora.$ui.folderList.favorite.options({selected: [listData.id]}); + } else { + // and nowhere else + pandora.UI.set({ + find: pandora.site.user.ui.find + }); + } + } + pandora.$ui.folderBrowser.featured.replaceWith( + pandora.$ui.folderList.featured = pandora.ui.folderList('featured') + ); } + pandora.resizeFolders(); } + })]; + } + } else if (folder.id == 'volumes') { + if (pandora.user.level == 'guest') { + extras = [infoButton('Local Volumes', 'To import movies from a local disk, please sign up or sign in.')]; + } else { + extras = [Ox.MenuButton({ + items: [ + { id: 'add', title: 'Add Volume...', disabled: true }, + { id: 'scan', title: 'Scan Selected Volume...', disabled: true }, + { id: 'remove', title: 'Remove Selected Volume...', disabled: true }, + {}, + { id: 'import', title: 'Import Movies...', disabled: true } + ], + title: 'edit', + tooltip: 'Manage Volumes', + type: 'image' }) - .appendTo(pandora.$ui.folder[i].$content); - }); - } + .bindEvent({ + click: function(data) { + } + })]; + } + } + pandora.$ui.folder[i] = Ox.CollapsePanel({ + id: folder.id, + collapsed: !ui.showFolder.items[folder.id], + extras: extras, + size: 16, + title: folder.title + }) + .bindEvent({ + // fixme: duplicated + click: function(data) { + var $list = pandora.$ui.folderList[i], + hasFocus, id; + if (data.id == 'new' || data.id == 'newsmart') { + pandora.api.addList({ + name: 'Untitled', + status: 'private', + type: data.id == 'new' ? 'static' : 'smart' + }, function(result) { + id = result.data.id; + pandora.URL.set('?find=list:' + id) + Ox.Request.clearCache(); // fixme: remove + $list.reloadList().bindEventOnce({ + load: function(data) { + $list.gainFocus() + .options({selected: [id]}) + .editCell(id, 'name'); + } + }); + }); + } else if (data.id == 'browse') { + // alert('??') + /* + pandora.$ui.sectionList[1].replaceWith(pandora.$ui.publicLists = pandora.ui.publicLists()); + pandora.site.showAllPublicLists = true; + */ + } + }, + toggle: function(data) { + data.collapsed && pandora.$ui.folderList[folder.id].loseFocus(); + pandora.UI.set('showFolder.items.' + folder.id, !data.collapsed); + pandora.resizeFolders(); + } + }); + //$sections.push(pandora.$ui.section[i]); + pandora.$ui.folderList[folder.id] = pandora.ui.folderList(folder.id) + .bindEvent({ + selectafter: function() { + // ... + }, + selectbefore: function() { + // ... + } + }) + .bindEventOnce({ + init: function(data) { + if (++counter == pandora.site.sectionFolders[ui.section].length) { + pandora.$ui.folder.forEach(function($folder) { + that.append($folder); + }); + pandora.resizeFolders(); + pandora.selectList(); + } + } + }) + .appendTo(pandora.$ui.folder[i].$content); + }); function infoButton(title, text) { return Ox.Button({ style: 'symbol', @@ -340,6 +374,13 @@ pandora.ui.folders = function() { }); } */ + }, + pandora_text: function() { + if (!pandora.user.ui.text) { + Ox.forEach(pandora.$ui.folderList, function($list, id) { + $list.options('selected', []); + }); + } } }) return that; diff --git a/static/js/pandora/listDialog.js b/static/js/pandora/listDialog.js index e1a00eef..620b1aec 100644 --- a/static/js/pandora/listDialog.js +++ b/static/js/pandora/listDialog.js @@ -13,7 +13,10 @@ pandora.ui.listDialog = function(section) { ], listData.type == 'smart' ? [{id: 'query', title: 'Query'}] : [] - ); + ), + ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1); Ox.getObjectById(tabs, section).selected = true; pandora.$ui.listDialogTabPanel = Ox.TabPanel({ @@ -103,7 +106,7 @@ pandora.ui.listDialog = function(section) { height: 312, // keys: {enter: 'save', escape: 'cancel'}, removeOnClose: true, - title: 'List — ' + Ox.encodeHTMLEntities(listData.name), + title: folderItem + ' — ' + Ox.encodeHTMLEntities(listData.name), width: width }); @@ -117,8 +120,11 @@ pandora.ui.listDialog = function(section) { }; pandora.ui.listGeneralPanel = function(listData) { - var that = Ox.Element(); - pandora.api.findLists({ + var that = Ox.Element(), + ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1); + pandora.api['find' + folderItems]({ query: {conditions: [{key: 'id', value: listData.id, operator: '=='}]}, keys: ['description', 'subscribers'] }, function(result) { @@ -129,7 +135,7 @@ pandora.ui.listGeneralPanel = function(listData) { tooltip: 'Doubleclick to edit icon' }) .attr({ - src: '/list/' + listData.id + '/icon256.jpg?' + Ox.uid() + src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon256.jpg?' + Ox.uid() }) .css({ position: 'absolute', @@ -157,7 +163,8 @@ pandora.ui.listGeneralPanel = function(listData) { submit: editName }) .appendTo(that), - $itemsInput = Ox.Input({ + $itemsInput = ui.section == 'items' + ? Ox.Input({ disabled: true, label: 'Items', labelWidth: 80, @@ -165,6 +172,21 @@ pandora.ui.listGeneralPanel = function(listData) { width: 320 }) .css({position: 'absolute', left: '160px', top: '40px'}) + .appendTo(that) + : Ox.Select({ + items: [ + {id: 'html', title: 'HMTL'}, + {id: 'pdf', title: 'PDF'} + ], + label: 'Type', + labelWidth: 80, + value: listData.type, + width: 320 + }) + .css({position: 'absolute', left: '160px', top: '40px'}) + .bindEvent({ + change: editType + }) .appendTo(that), $statusSelect = listData.status == 'featured' ? Ox.Input({ @@ -216,19 +238,19 @@ pandora.ui.listGeneralPanel = function(listData) { .appendTo(that); function editDescription(data) { if (data.value != description) { - pandora.api.editList({ + pandora.api['edit' + folderItem]({ id: listData.id, description: data.value }, function(result) { description = result.data.description; - Ox.Request.clearCache('findLists'); + Ox.Request.clearCache('find' + folderItems); pandora.$ui.info.updateListInfo(); }); } } function editName(data) { if (data.value != listData.name) { - pandora.api.editList({ + pandora.api['edit' + folderItem]({ id: listData.id, name: data.value }, function(result) { @@ -236,10 +258,10 @@ pandora.ui.listGeneralPanel = function(listData) { pandora.renameList(listData.id, result.data.id, result.data.name); listData.id = result.data.id; listData.name = result.data.name; - Ox.Request.clearCache('findLists'); + Ox.Request.clearCache('find' + folderItems); pandora.$ui.info.updateListInfo(); pandora.$ui.listDialog.options({ - title: 'List — ' + Ox.encodeHTMLEntities(listData.name) + title: folderItme + ' — ' + Ox.encodeHTMLEntities(listData.name) }); } }); @@ -248,7 +270,7 @@ pandora.ui.listGeneralPanel = function(listData) { function editStatus(data) { var status = data.value; $statusSelect.value(status == 'private' ? 'public' : 'private'); - pandora.changeListStatus(listData.id, status, function(result) { + pandora.changeFolderItemStatus(listData.id, status, function(result) { listData.status = result.data.status; if (result.data.status == 'private') { subscribers = 0; @@ -264,6 +286,18 @@ pandora.ui.listGeneralPanel = function(listData) { ); }); } + function editType(data) { + var type = data.value; + $itemsInput.value(type == 'html' ? 'html' : 'pdf'); + pandora.api.editText({ + id: listData.id, + type: type + }, function(result) { + Ox.Request.clearCache('getText'); + //fixme: reload text and folder list + $itemsInput.value(result.data.type); + }); + } function getDescriptionHeight() { return listData.status == 'private' ? 184 : 160; } @@ -282,10 +316,14 @@ pandora.ui.listIconPanel = function(listData) { var quarter = 0, quarters = ['top-left', 'top-right', 'bottom-left', 'bottom-right'], + ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1), + $iconPanel = Ox.Element(), $icon = $('') - .attr({src: '/list/' + listData.id + '/icon256.jpg?' + Ox.uid()}) + .attr({src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon256.jpg?' + Ox.uid()}) .css({position: 'absolute', borderRadius: '64px', margin: '16px'}) .appendTo($iconPanel), @@ -295,6 +333,10 @@ pandora.ui.listIconPanel = function(listData) { $list = Ox.Element(), + ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1), + that = Ox.SplitPanel({ elements: [ { @@ -312,7 +354,7 @@ pandora.ui.listIconPanel = function(listData) { orientation: 'horizontal' }); - pandora.api.findLists({ + pandora.api['find' + folderItems]({ query: { conditions: [{key: 'id', value: listData.id, operator: '=='}], operator: '&' @@ -364,7 +406,7 @@ pandora.ui.listIconPanel = function(listData) { items: function(data, callback) { pandora.api.find(Ox.extend(data, { query: { - conditions: [{key: 'list', value: listData.id, operator: '=='}], + conditions: ui.section == 'items' ? [{key: 'list', value: listData.id, operator: '=='}] : [], operator: '&' } }), callback); @@ -375,7 +417,7 @@ pandora.ui.listIconPanel = function(listData) { //orientation: 'vertical', selected: posterFrame ? [posterFrame.item] : [], size: 128, - sort: pandora.user.ui.listSort, + sort: ui.section == 'items' ? pandora.user.ui.listSort : pandora.site.user.ui.listSort, unique: 'id' }) //.css({width: '144px'}) @@ -461,17 +503,17 @@ pandora.ui.listIconPanel = function(listData) { } else { posterFrames = Ox.repeat([posterFrame], 4); } - pandora.api.editList({ + pandora.api['edit' + folderItem]({ id: listData.id, posterFrames: posterFrames }, function() { $icon.attr({ - src: '/list/' + listData.id + '/icon256.jpg?' + Ox.uid() + src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon256.jpg?' + Ox.uid() }); pandora.$ui.folderList[listData.folder].$element .find('img[src*="/' + listData.id + '/"]') .attr({ - src: '/list/' + listData.id + '/icon.jpg?' + Ox.uid() + src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon.jpg?' + Ox.uid() }); pandora.$ui.info.updateListInfo(); pandora.clearListIconCache(listData.id); diff --git a/static/js/pandora/mainPanel.js b/static/js/pandora/mainPanel.js index 1f9e5f90..5fbb585f 100644 --- a/static/js/pandora/mainPanel.js +++ b/static/js/pandora/mainPanel.js @@ -65,6 +65,12 @@ pandora.ui.mainPanel = function() { } } }, + pandora_section: function(data) { + if (data.value != data.previousValue) { + that.replaceElement(0, pandora.$ui.leftPanel = pandora.ui.leftPanel()); + that.replaceElement(1, pandora.$ui.rightPanel = pandora.ui.rightPanel()); + } + }, pandora_item: function(data) { if (!data.value || !data.previousValue) { that.replaceElement(1, pandora.$ui.rightPanel = pandora.ui.rightPanel()); diff --git a/static/js/pandora/rightPanel.js b/static/js/pandora/rightPanel.js index 7c4740b6..2b11ff9d 100644 --- a/static/js/pandora/rightPanel.js +++ b/static/js/pandora/rightPanel.js @@ -57,6 +57,8 @@ pandora.ui.rightPanel = function() { } } }); + } else if (pandora.user.ui.section == 'texts') { + that = pandora.$ui.text = pandora.ui.text(); } return that; }; diff --git a/static/js/pandora/sectionButtons.js b/static/js/pandora/sectionButtons.js index 8e4bb831..826a1cc1 100644 --- a/static/js/pandora/sectionButtons.js +++ b/static/js/pandora/sectionButtons.js @@ -5,7 +5,7 @@ pandora.ui.sectionButtons = function() { buttons: [ {id: 'items', title: pandora.site.itemName.plural}, {id: 'edits', title: 'Edits', disabled: true}, - {id: 'texts', title: 'Texts', disabled: true} + {id: 'texts', title: 'Texts', disabled: pandora.user.level != 'admin'} ], id: 'sectionButtons', selectable: true, @@ -17,13 +17,7 @@ pandora.ui.sectionButtons = function() { .bindEvent({ change: function(data) { var section = data.value; - if (section == 'items') { - pandora.URL.set(pandora.Query.toString()); - } else if (section == 'clips') { - pandora.URL.set('clips'); - } else if (section == 'texts') { - pandora.URL.set('texts'); - } + pandora.UI.set({section: section}); } }); return that; diff --git a/static/js/pandora/text.js b/static/js/pandora/text.js new file mode 100644 index 00000000..05df5c84 --- /dev/null +++ b/static/js/pandora/text.js @@ -0,0 +1,58 @@ +'use strict'; + +pandora.ui.text = function() { + var ui = pandora.user.ui, + canEdit = pandora.site.capabilities.canEditSitePages[pandora.user.level], + that, + text, + $text; + + getText(ui.text); + + that = Ox.Element() + .bindEvent({ + pandora_text: function(data) { + if (ui.text != text) { + text = ui.text; + getText(ui.text); + } + } + }); + + function getText(id) { + pandora.api.getText({id: id}, function(result) { + if (result.data.type == 'pdf') { + $text && $text.remove(); + $text = Ox.Editable({ + clickLink: pandora.clickLink, + editable: false, + type: 'textarea', + value: 'REPLACE ME WITH PDF VIEWER' + }) + .appendTo(that); + } else { + var text = result.data ? result.data.text : ''; + $text && $text.remove(); + $text = Ox.Editable({ + clickLink: pandora.clickLink, + editable: canEdit, + tooltip: canEdit ? 'Doubleclick to edit' : '', + type: 'textarea', + placeholder: 'No text', + value: text + }) + .bindEvent({ + submit: function(data) { + Ox.Request.clearCache('getText'); + pandora.api.editText({ + id: ui.text, + text: data.value + }); + } + }) + .appendTo(that); + } + }); + } + return that; +} diff --git a/static/js/pandora/utils.js b/static/js/pandora/utils.js index 64fb73be..13bb9e71 100644 --- a/static/js/pandora/utils.js +++ b/static/js/pandora/utils.js @@ -147,10 +147,31 @@ pandora.addList = function() { }).reloadList(); } }; +pandora.addText = function() { + var $folderList = pandora.$ui.folderList.personal; + pandora.api.addText({name: 'Untitled'}, function(result) { + reloadFolder(result.data.id); + }); + function reloadFolder(newId) { + pandora.$ui.folder[0].options({collapsed: false}); + Ox.Request.clearCache('findTexts'); + $folderList.bindEventOnce({ + load: function(data) { + $folderList.gainFocus() + .options({selected: [newId]}) + .editCell(newId, 'name', true); + pandora.UI.set(pandora.user.ui.section.slice(0, -1), newId); + } + }).reloadList(); + } +} -pandora.changeListStatus = function(id, status, callback) { +pandora.changeFolderItemStatus = function(id, status, callback) { + var ui = pandora.user.ui, + folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section), + folderItem = folderItems.slice(0, -1); if (status == 'private') { - pandora.api.findLists({ + pandora.api['find' + folderItems]({ query: {conditions: [{key: 'id', value: id, operator: '=='}]}, keys: ['name', 'subscribers'] }, function(result) { @@ -168,14 +189,14 @@ pandora.changeListStatus = function(id, status, callback) { } }).open(); } else { - changeListStatus(); + changeFolderItemStatus(); } }); } else { - changeListStatus(); + changeFolderItemStatus(); } - function changeListStatus() { - pandora.api.editList({ + function changeFolderItemStatus() { + pandora.api['edit' + folderItem]({ id: id, status: status }, callback); @@ -606,42 +627,46 @@ pandora.getInfoHeight = function(includeHidden) { return height; } -pandora.getItemByIdOrTitle = function(str, callback) { - var sortKey = Ox.getObjectById(pandora.site.itemKeys, 'votes') - ? 'votes' - : 'timesaccessed'; - pandora.api.get({id: str, keys: ['id']}, function(result) { - if (result.status.code == 200) { - callback(result.data.id); - } else { - pandora.api.find({ - query: { - conditions: [{key: 'title', value: str, operator: '='}], - operator: '&' - }, - sort: [{key: sortKey, operator: ''}], - range: [0, 100], - keys: ['id', 'title', sortKey] - }, function(result) { - var id = ''; - if (result.data.items.length) { - var items = Ox.filter(Ox.map(result.data.items, function(item) { - // test if exact match or word match - var sort = new RegExp('^' + str + '$', 'i').test(item.title) ? 2000000 - : new RegExp('\\b' + str + '\\b', 'i').test(item.title) ? 1000000 : 0; - return sort ? {id: item.id, sort: sort + (parseInt(item[sortKey]) || 0)} : null; - // fixme: remove the (...|| 0) check once the backend sends correct data - })); - if (items.length) { - id = items.sort(function(a, b) { - return b.sort - a.sort; - })[0].id; +pandora.getItemByIdOrTitle = function(type, str, callback) { + if (type == pandora.site.itemName.plural.toLowerCase()) { + var sortKey = Ox.getObjectById(pandora.site.itemKeys, 'votes') + ? 'votes' + : 'timesaccessed'; + pandora.api.get({id: str, keys: ['id']}, function(result) { + if (result.status.code == 200) { + callback(result.data.id); + } else { + pandora.api.find({ + query: { + conditions: [{key: 'title', value: str, operator: '='}], + operator: '&' + }, + sort: [{key: sortKey, operator: ''}], + range: [0, 100], + keys: ['id', 'title', sortKey] + }, function(result) { + var id = ''; + if (result.data.items.length) { + var items = Ox.filter(Ox.map(result.data.items, function(item) { + // test if exact match or word match + var sort = new RegExp('^' + str + '$', 'i').test(item.title) ? 2000000 + : new RegExp('\\b' + str + '\\b', 'i').test(item.title) ? 1000000 : 0; + return sort ? {id: item.id, sort: sort + (parseInt(item[sortKey]) || 0)} : null; + // fixme: remove the (...|| 0) check once the backend sends correct data + })); + if (items.length) { + id = items.sort(function(a, b) { + return b.sort - a.sort; + })[0].id; + } } - } - callback(id); - }); - } - }); + callback(id); + }); + } + }); + } else { + callback(str); + } } pandora.getItemFind = function(find) { @@ -706,7 +731,11 @@ pandora.getItemIdAndPosition = function() { pandora.getListData = function(list) { var data = {}, folder; - list = Ox.isUndefined(list) ? pandora.user.ui._list : list; + if (pandora.user.ui.section == 'items') { + list = Ox.isUndefined(list) ? pandora.user.ui._list : list; + } else { + list = Ox.isUndefined(list) ? pandora.user.ui[pandora.user.ui.section.slice(0, -1)] : list; + } if (list) { Ox.forEach(pandora.$ui.folderList, function($list, id) { var ret = true; @@ -726,8 +755,12 @@ pandora.getListData = function(list) { // FIXME: Is there a `return ret` statement missing here? }); if (folder) { - data = pandora.$ui.folderList[folder].value(pandora.user.ui._list); - data.editable = data.user == pandora.user.username && data.type == 'static'; + data = pandora.$ui.folderList[folder].value(list); + if (pandora.user.ui.section == 'item') { + data.editable = data.user == pandora.user.username && data.type == 'static'; + } else { + data.editable = data.user == pandora.user.username; + } data.folder = folder; } } @@ -1076,24 +1109,28 @@ pandora.renameList = function(oldId, newId, newName, folder) { folder = folder || pandora.getListData(oldId).folder; pandora.$ui.folderList[folder].value(oldId, 'name', newName); pandora.$ui.folderList[folder].value(oldId, 'id', newId); - pandora.$ui.toolbar.updateListName(newId); - pandora.UI.set({ - find: { - conditions: [{key: 'list', value: newId, operator: '=='}], - operator: '&' - } - }, false); + if (pandora.user.ui.section == 'items') { + pandora.$ui.toolbar.updateListName(newId); + pandora.UI.set({ + find: { + conditions: [{key: 'list', value: newId, operator: '=='}], + operator: '&' + } + }, false); + } else { + pandora.UI.set(pandora.user.ui.section.slice(0, -1), newId); + } }; pandora.resizeFilters = function(width) { pandora.user.ui.filterSizes = pandora.getFilterSizes(); - pandora.$ui.browser + pandora.$ui.browser && pandora.$ui.browser .size(0, pandora.user.ui.filterSizes[0]) .size(2, pandora.user.ui.filterSizes[4]); - pandora.$ui.filtersInnerPanel + pandora.$ui.filtersInnerPanel && pandora.$ui.filtersInnerPanel .size(0, pandora.user.ui.filterSizes[1]) .size(2, pandora.user.ui.filterSizes[3]); - pandora.$ui.filters.forEach(function($list, i) { + pandora.$ui.filters && pandora.$ui.filters.forEach(function($list, i) { $list.resizeColumn('name', pandora.user.ui.filterSizes[i] - 44 - Ox.UI.SCROLLBAR_SIZE); if (pandora.user.ui.showFlags) { $list.find('.flagname').css({width: pandora.user.ui.filterSizes[i] - 68 - Ox.UI.SCROLLBAR_SIZE}) @@ -1103,27 +1140,24 @@ pandora.resizeFilters = function(width) { pandora.resizeFolders = function() { var width = pandora.getFoldersWidth(), - columnWidth = {}; - if (pandora.user.ui.section == 'items') { - columnWidth = {user: parseInt((width - 96) * 0.4)}; - columnWidth.name = (width - 96) - columnWidth.user; - } + columnWidth = {}, + sectionWidth = pandora.user.ui.section == 'items'? 96 : 32; + columnWidth = {user: parseInt((width - (sectionWidth)) * 0.4)}; + columnWidth.name = (width - sectionWidth) - columnWidth.user; Ox.Log('', 'RESIZE FOLDERS', width); pandora.$ui.allItems.resizeElement(width - 104); Ox.forEach(pandora.$ui.folderList, function($list, id) { var pos = Ox.getIndexById(pandora.site.sectionFolders[pandora.user.ui.section], id); pandora.$ui.folder[pos].css({width: width + 'px'}); $list.css({width: width + 'px'}); - if (pandora.user.ui.section == 'items') { - if (pandora.site.sectionFolders[pandora.user.ui.section][pos].showBrowser) { - pandora.$ui.findListInput[id].options({ - width: width - 24 - }); - $list.resizeColumn('user', columnWidth.user) - .resizeColumn('name', columnWidth.name); - } else { - $list.resizeColumn(id == 'favorite' ? 'id' : 'name', width - 96); - } + if (pandora.site.sectionFolders[pandora.user.ui.section][pos].showBrowser) { + pandora.$ui.findListInput[id].options({ + width: width - 24 + }); + $list.resizeColumn('user', columnWidth.user) + .resizeColumn('name', columnWidth.name); + } else { + $list.resizeColumn(id == 'favorite' ? 'id' : 'name', width - 96); } if (!pandora.user.ui.showFolder[pandora.user.ui.section][id]) { pandora.$ui.folder[pos].updatePanel(); @@ -1135,88 +1169,105 @@ pandora.resizeWindow = function() { // FIXME: a lot of this throws errors on load pandora.$ui.leftPanel && pandora.$ui.leftPanel.size(2, pandora.getInfoHeight(true)); pandora.resizeFolders(); - if (!pandora.user.ui.item) { - pandora.resizeFilters(pandora.$ui.rightPanel.width()); - if (pandora.user.ui.listView == 'clips') { - var clipsItems = pandora.getClipsItems(), - previousClipsItems = pandora.getClipsItems(pandora.$ui.list.options('width')); - pandora.$ui.list.options({ - width: window.innerWidth - - pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize - 1 - - Ox.UI.SCROLLBAR_SIZE - }); - if (clipsItems != previousClipsItems) { - Ox.Request.clearCache(); // fixme - pandora.$ui.list.reloadList(true); + if (pandora.user.ui.section == 'item') { + if (!pandora.user.ui.item) { + pandora.resizeFilters(pandora.$ui.rightPanel.width()); + if (pandora.user.ui.listView == 'clips') { + var clipsItems = pandora.getClipsItems(), + previousClipsItems = pandora.getClipsItems(pandora.$ui.list.options('width')); + pandora.$ui.list.options({ + width: window.innerWidth + - pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize - 1 + - Ox.UI.SCROLLBAR_SIZE + }); + if (clipsItems != previousClipsItems) { + Ox.Request.clearCache(); // fixme + pandora.$ui.list.reloadList(true); + } + } else if (pandora.user.ui.listView == 'timelines') { + pandora.$ui.list.options({ + width: window.innerWidth + - pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize - 1 + - Ox.UI.SCROLLBAR_SIZE + }); + } else if (pandora.user.ui.listView == 'map') { + pandora.$ui.map && pandora.$ui.map.resizeMap(); + } else if (pandora.user.ui.listView == 'calendar') { + pandora.$ui.calendar && pandora.$ui.calendar.resizeCalendar(); + } else { + pandora.$ui.list && pandora.$ui.list.size(); } - } else if (pandora.user.ui.listView == 'timelines') { - pandora.$ui.list.options({ - width: window.innerWidth - - pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize - 1 - - Ox.UI.SCROLLBAR_SIZE - }); - } else if (pandora.user.ui.listView == 'map') { - pandora.$ui.map && pandora.$ui.map.resizeMap(); - } else if (pandora.user.ui.listView == 'calendar') { - pandora.$ui.calendar && pandora.$ui.calendar.resizeCalendar(); } else { - pandora.$ui.list && pandora.$ui.list.size(); - } - } else { - pandora.$ui.browser.scrollToSelection(); - if (pandora.user.ui.itemView == 'info') { - pandora.$ui.item.resize(); - } else if (pandora.user.ui.itemView == 'clips') { - pandora.$ui.clipList.size(); - } else if (pandora.user.ui.itemView == 'timeline') { - pandora.$ui.timeline && pandora.$ui.timeline.options({ - // fixme: duplicated - height: pandora.$ui.contentPanel.size(1), - width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1 - }); - } else if (pandora.user.ui.itemView == 'player') { - pandora.$ui.player && pandora.$ui.player.options({ - // fixme: duplicated - height: pandora.$ui.contentPanel.size(1), - width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1 - }); - } else if (pandora.user.ui.itemView == 'editor') { - pandora.$ui.editor && pandora.$ui.editor.options({ - // fixme: duplicated - height: pandora.$ui.contentPanel.size(1), - width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1 - }); - } else if (pandora.user.ui.itemView == 'map') { - pandora.$ui.map.resizeMap(); - } else if (pandora.user.ui.itemView == 'calendar') { - pandora.$ui.calendar.resizeCalendar(); + pandora.$ui.browser.scrollToSelection(); + if (pandora.user.ui.itemView == 'info') { + pandora.$ui.item.resize(); + } else if (pandora.user.ui.itemView == 'clips') { + pandora.$ui.clipList.size(); + } else if (pandora.user.ui.itemView == 'timeline') { + pandora.$ui.timeline && pandora.$ui.timeline.options({ + // fixme: duplicated + height: pandora.$ui.contentPanel.size(1), + width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1 + }); + } else if (pandora.user.ui.itemView == 'player') { + pandora.$ui.player && pandora.$ui.player.options({ + // fixme: duplicated + height: pandora.$ui.contentPanel.size(1), + width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1 + }); + } else if (pandora.user.ui.itemView == 'editor') { + pandora.$ui.editor && pandora.$ui.editor.options({ + // fixme: duplicated + height: pandora.$ui.contentPanel.size(1), + width: pandora.$ui.document.width() - pandora.$ui.mainPanel.size(0) - 1 + }); + } else if (pandora.user.ui.itemView == 'map') { + pandora.$ui.map.resizeMap(); + } else if (pandora.user.ui.itemView == 'calendar') { + pandora.$ui.calendar.resizeCalendar(); + } } } }; pandora.selectList = function() { - if (pandora.user.ui._list) { - pandora.api.findLists({ - keys: ['status', 'user'], - query: { - conditions: [{key: 'id', value: pandora.user.ui._list, operator: '=='}], - operator: '' - }, - range: [0, 1] - }, function(result) { - var folder, list; - if (result.data.items.length) { - list = result.data.items[0]; - folder = list.status == 'featured' ? 'featured' : ( - list.user == pandora.user.username ? 'personal' : 'favorite' - ); - pandora.$ui.folderList[folder] - .options({selected: [pandora.user.ui._list]}); - if (!pandora.hasDialogOrScreen() && !pandora.hasFocusedInput()) { - pandora.$ui.folderList[folder].gainFocus(); + if (pandora.user.ui.section == 'items') { + if (pandora.user.ui._list) { + pandora.api.findLists({ + keys: ['status', 'user'], + query: { + conditions: [{key: 'id', value: pandora.user.ui._list, operator: '=='}], + operator: '' + }, + range: [0, 1] + }, function(result) { + var folder, list; + if (result.data.items.length) { + list = result.data.items[0]; + folder = list.status == 'featured' ? 'featured' : ( + list.user == pandora.user.username ? 'personal' : 'favorite' + ); + pandora.$ui.folderList[folder] + .options({selected: [pandora.user.ui._list]}); + if (!pandora.hasDialogOrScreen() && !pandora.hasFocusedInput()) { + pandora.$ui.folderList[folder].gainFocus(); + } } - } - }); + }); + } + } else { + var id = pandora.user.ui[pandora.user.ui.section.slice(0,-1)]; + if (id) { + pandora.api.getText({id: id}, function(result) { + var folder; + if (result.data.id) { + folder = result.data.status == 'featured' ? 'featured' : ( + result.data.user == pandora.user.username ? 'personal' : 'favorite' + ); + pandora.$ui.folderList[folder].options({selected: [id]}); + } + }); + } } };