From 9a0f2b82db4e84e0651d6e7f52135fe5a31943f2 Mon Sep 17 00:00:00 2001 From: "fergal.moran" Date: Mon, 20 Aug 2012 11:07:23 +0100 Subject: [PATCH] Added avatar handling to user page --- core/utils/file.py | 6 +- dss/settings.py | 1 + requirements.txt | 2 + spa/ajax.py | 33 +- spa/api/v1/UserResource.py | 17 + spa/models/UserProfile.py | 33 +- static/css/style.css | 222 ++++- static/css/uploadifive.css | 72 ++ static/img/loader.gif | Bin 0 -> 10819 bytes static/img/switch_handle-OLD.png | Bin 0 -> 320 bytes static/img/switch_handle-gray.png | Bin 0 -> 310 bytes static/img/switch_handle-light.png | Bin 0 -> 409 bytes static/img/switch_handle.png | Bin 0 -> 344 bytes static/img/switch_handle_pressed.png | Bin 0 -> 348 bytes static/img/switch_track-dark.png | Bin 0 -> 602 bytes static/img/switch_track-gray.png | Bin 0 -> 679 bytes static/img/switch_track-light.png | Bin 0 -> 571 bytes static/img/switch_track.png | Bin 0 -> 541 bytes static/img/uploadifive-cancel.png | Bin 0 -> 1065 bytes static/js/app/social.js | 23 + static/js/app/views/mix.js | 13 +- static/js/app/views/user.js | 74 +- static/js/libs/jquery.iphone-switch.js | 239 +++++ .../js/libs/uploadify/jquery.uploadifive.js | 887 ++++++++++++++++++ .../libs/uploadify/jquery.uploadifive.min.js | 6 + templates/base.html | 6 +- templates/inc/_MixItemInsert.html | 2 - templates/inc/facebook_init.html | 3 +- templates/views/UserView.html | 162 ++-- 29 files changed, 1698 insertions(+), 103 deletions(-) create mode 100644 static/css/uploadifive.css create mode 100644 static/img/loader.gif create mode 100644 static/img/switch_handle-OLD.png create mode 100644 static/img/switch_handle-gray.png create mode 100644 static/img/switch_handle-light.png create mode 100644 static/img/switch_handle.png create mode 100644 static/img/switch_handle_pressed.png create mode 100644 static/img/switch_track-dark.png create mode 100644 static/img/switch_track-gray.png create mode 100644 static/img/switch_track-light.png create mode 100644 static/img/switch_track.png create mode 100644 static/img/uploadifive-cancel.png create mode 100644 static/js/app/social.js create mode 100644 static/js/libs/jquery.iphone-switch.js create mode 100644 static/js/libs/uploadify/jquery.uploadifive.js create mode 100644 static/js/libs/uploadify/jquery.uploadifive.min.js diff --git a/core/utils/file.py b/core/utils/file.py index f697da8..930b2d4 100644 --- a/core/utils/file.py +++ b/core/utils/file.py @@ -1,5 +1,7 @@ import uuid +import os -__author__ = 'fergalm' def generate_save_file_name(prefix, filename): - return '/'.join([prefix, str(uuid.uuid1()), filename]) + filename, extension = os.path.splitext(filename) + ret = "%s%s" % ('/'.join([prefix, str(uuid.uuid1())]), extension) + return ret diff --git a/dss/settings.py b/dss/settings.py index 1c74209..636784d 100644 --- a/dss/settings.py +++ b/dss/settings.py @@ -128,6 +128,7 @@ INSTALLED_APPS = ( 'django_facebook', 'djcelery', 'crispy_forms', + 'sorl.thumbnail', #'pipeline', 'avatar', 'notification', diff --git a/requirements.txt b/requirements.txt index e57acdf..9d6b16a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +PIL Django>=1.4 MySQL-python>=1.2.3 pillow>=1.7.7 @@ -17,3 +18,4 @@ git+git://github.com/maraujop/django-crispy-forms.git#django-crispy-forms git+git://github.com/tschellenbach/Django-facebook.git#django-facebook django-grappelli humanize +sorl-thumbnail>=11.12 diff --git a/spa/ajax.py b/spa/ajax.py index 050171f..77279ba 100644 --- a/spa/ajax.py +++ b/spa/ajax.py @@ -5,14 +5,19 @@ from annoying.decorators import render_to from django.shortcuts import render_to_response import json from django.utils import simplejson +from django.views.decorators.csrf import csrf_exempt +import os from core.utils import live +from spa.models import UserProfile from spa.models.Mix import Mix from spa.models.Comment import Comment from spa.models.MixLike import MixLike +from PIL import Image +import logging +logger = logging.getLogger(__name__) class AjaxHandler(object): - import logging # Get an instance of a logger logger = logging.getLogger(__name__) @@ -31,6 +36,8 @@ class AjaxHandler(object): url(r'^release_player/(?P\d+)/$', 'spa.ajax.release_player'), url(r'^live_now_playing/$', 'spa.ajax.live_now_playing'), url(r'^like/$', 'spa.ajax.like', name='ajax_mix-description'), + url(r'^facebook_post_likes_allowed/$', 'spa.ajax.facebook_post_likes_allowed', name='ajax_facebook_post_likes_allowed'), + url(r'^upload_avatar_image/$', 'spa.ajax.upload_avatar_image', name='ajax_upload_avatar_image'), ] return pattern_list @@ -113,3 +120,27 @@ def like(request): mix.likes.add(MixLike(mix = mix, user = request.user)) mix.save() return HttpResponse(simplejson.dumps(request.raw_post_data)) + +@login_required() +def facebook_post_likes_allowed(request): + profile = request.user.get_profile(); + if profile is not None: + likes_allowed = profile.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES + facebook_allowed = profile.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK + + return HttpResponse(json.dumps(_get_json(bool(likes_allowed & facebook_allowed))), mimetype="application/json") + + return HttpResponse(json.dumps(_get_json(False)), mimetype="application/json") + +@csrf_exempt +def upload_avatar_image(request): + try: + if 'Filedata' in request.FILES: + profile = request.user.get_profile() + if profile: + profile.avatar_image = request.FILES['Filedata'] + profile.save() + return HttpResponse(json.dumps(_get_json("Success"))) + except Exception, ex: + logger.exception("Error uploading avatar") + return HttpResponse(json.dumps(_get_json("Failed"))) \ No newline at end of file diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py index 71a4468..250dfec 100644 --- a/spa/api/v1/UserResource.py +++ b/spa/api/v1/UserResource.py @@ -8,6 +8,7 @@ from spa.models import UserProfile class UserResource(BackboneCompatibleResource): class Meta: queryset = UserProfile.objects.all() + excludes = [] authorization = Authorization() authentication = Authentication() always_return_data = True @@ -20,6 +21,22 @@ class UserResource(BackboneCompatibleResource): bundle.data['first_name'] = bundle.obj.first_name bundle.data['last_name'] = bundle.obj.last_name bundle.data['email'] = bundle.obj.email + + if bundle.obj.activity_sharing is not None: + bundle.data['activity_share_likes'] = (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES) != 0; + bundle.data['activity_share_favourites'] = (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_FAVOURITES) != 0; + bundle.data['activity_share_comments'] = (bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_COMMENTS) != 0; + else: + bundle.data['activity_share_likes'] =0 + bundle.data['activity_share_favourites'] =0 + bundle.data['activity_share_comments'] =0 + + if bundle.obj.activity_sharing_networks is not None: + bundle.data['activity_share_networks_facebook'] = (bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK) != 0; + bundle.data['activity_share_networks_twitter'] = (bundle.obj.activity_sharing_networks& UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER) != 0; + else: + bundle.data['activity_share_networks_facebook'] = 0 + bundle.data['activity_share_networks_facebook'] = 0 return bundle def apply_authorization_limits(self, request, object_list): diff --git a/spa/models/UserProfile.py b/spa/models/UserProfile.py index d1f9662..a35665f 100644 --- a/spa/models/UserProfile.py +++ b/spa/models/UserProfile.py @@ -5,18 +5,46 @@ from django.core.urlresolvers import reverse from django.db import models from django.db.models.signals import post_save from django_gravatar.helpers import has_gravatar, get_gravatar_url +import os +from core.utils.file import generate_save_file_name from dss import settings from spa.models._BaseModel import _BaseModel +def avatar_name(instance, filename): + return generate_save_file_name('avatars', filename) class UserProfile(_BaseModel): class Meta: app_label = 'spa' + ACTIVITY_SHARE_LIKES = 1 + ACTIVITY_SHARE_FAVOURITES = 2 + ACTIVITY_SHARE_COMMENTS = 4 + + ACTIVITY_SHARE_NETWORK_FACEBOOK = 1 + ACTIVITY_SHARE_NETWORK_TWITTER = 2 + # This field is required. user = models.ForeignKey(User, unique=True) avatar_type = models.CharField(max_length=15) - avatar_image = models.ImageField(blank=True, upload_to='/avatars/') + avatar_image = models.ImageField(blank=True, upload_to=avatar_name) display_name = models.CharField(blank=True, max_length=35) + activity_sharing = models.IntegerField(default=0) + activity_sharing_networks = models.IntegerField(default=0) + def save(self, size=(260, 180)): + """ + Save Photo after ensuring it is not blank. Resize as needed. + """ + from PIL import Image + if not self.id and not self.source: + return + + super(UserProfile, self).save() + + filename = self.get_source_filename() + image = Image.open(filename) + + image.thumbnail(size, Image.ANTIALIAS) + image.save(filename) def create_user_profile(sender, instance, created, **kwargs): if created: @@ -65,4 +93,5 @@ class UserProfile(_BaseModel): return urlparse.urljoin(settings.STATIC_URL, "img/default-avatar-32.png") def save(self, force_insert=False, force_update=False, using=None): - return super(UserProfile, self).save(force_insert, force_update, using) \ No newline at end of file + return super(UserProfile, self).save(force_insert, force_update, using) + diff --git a/static/css/style.css b/static/css/style.css index 238064e..a6dfbcc 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -8,8 +8,12 @@ body { padding-bottom: 40px; padding-right: 32px; } + /* IE/Chrome image fix */ -img.event-content {width: auto; height: auto;} +img.event-content { + width: auto; + height: auto; +} #header { margin-top: 20px; @@ -23,7 +27,7 @@ img.event-content {width: auto; height: auto;} border-bottom: 3px solid #CEC3B3; } -.page-header{ +.page-header { -moz-border-bottom-colors: none; -moz-border-image: none; -moz-border-left-colors: none; @@ -154,7 +158,9 @@ ul.comment-listing .comment-entry { .list-horiz li { float: left; + list-style-type: none; position: relative; + padding-right: 20px; } .bordered-right { @@ -166,7 +172,8 @@ ul.comment-listing .comment-entry { margin-right: 7px; padding-right: 7px; } -.stats{ + +.stats { border: 0; font: 0/0 a; text-shadow: none; @@ -180,21 +187,26 @@ table.tablesorter { width: 1024px; border: 1px solid #000; } + table.tablesorter th { text-align: left; padding: 5px; background-color: #6E6E6E; } + table.tablesorter td { color: #FFF; padding: 5px; } + table.tablesorter .even { background-color: #3D3D3D; } + table.tablesorter .odd { background-color: #6E6E6E; } + table.tablesorter .header { background-image: url(../img/bg.png); background-repeat: no-repeat; @@ -205,10 +217,12 @@ table.tablesorter .header { padding-top: 8px; height: auto; } + table.tablesorter .headerSortUp { background-image: url(../img/asc.png); background-repeat: no-repeat; } + table.tablesorter .headerSortDown { background-image: url(../img/desc.png); background-repeat: no-repeat; @@ -217,6 +231,7 @@ table.tablesorter .headerSortDown { table .header { cursor: pointer; } + table .header:after { content: ""; float: right; @@ -226,19 +241,23 @@ table .header:after { border-color: #000000 transparent; visibility: hidden; } + table .headerSortUp, table .headerSortDown { background-color: #f7f7f9; text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); } + table .header:hover:after { visibility: visible; } + table .headerSortDown:after, table .headerSortDown:hover:after { visibility: visible; - filter: alpha(opacity=60); + filter: alpha(opacity = 60); -moz-opacity: 0.6; opacity: 0.6; } + table .headerSortUp:after { border-bottom: none; border-left: 4px solid transparent; @@ -248,7 +267,200 @@ table .headerSortUp:after { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; - filter: alpha(opacity=60); + filter: alpha(opacity = 60); -moz-opacity: 0.6; opacity: 0.6; +} + +input[type="checkbox"] { + visibility: hidden; +} + +.slideThree { + width: 80px; + height: 26px; + background: #333; + margin: 20px auto; + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 50px; + position: relative; + -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); +} + +.slideThree { + width: 80px; + height: 26px; + background: #333; + margin: 20px auto; + + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 50px; + position: relative; + + -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.5), 0px 1px 0px rgba(255, 255, 255, 0.2); +} + +.slideThree:after { + content: 'OFF'; + font: 12px/26px Arial, sans-serif; + color: #000; + position: absolute; + right: 10px; + z-index: 0; + font-weight: bold; + text-shadow: 1px 1px 0px rgba(255, 255, 255, .15); +} + +.slideThree:before { + content: 'ON'; + font: 12px/26px Arial, sans-serif; + color: #00bf00; + position: absolute; + left: 10px; + z-index: 0; + font-weight: bold; +} + +.slideThree label { + display: block; + width: 34px; + height: 20px; + + -webkit-border-radius: 50px; + -moz-border-radius: 50px; + border-radius: 50px; + + -webkit-transition: all .4s ease; + -moz-transition: all .4s ease; + -o-transition: all .4s ease; + -ms-transition: all .4s ease; + transition: all .4s ease; + cursor: pointer; + position: absolute; + top: 3px; + left: 3px; + z-index: 1; + + -webkit-box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3); + background: #fcfff4; + + background: -webkit-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -moz-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -o-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -ms-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#fcfff4', endColorstr = '#b3bead', GradientType = 0); +} + +.slideThree input[type=checkbox]:checked + label { + left: 43px; +} + +.droparea { + position: relative; + text-align: center; +} + +.multiple { + position: relative; + height: 20px; +} + +.droparea div, .droparea input, .multiple div, .multiple input { + position: absolute; + top: 0; + width: 100%; + height: 100%; +} + +.droparea input, .multiple input { + cursor: pointer; + opacity: 0; +} + +.droparea .instructions, .multiple .instructions { + border: 2px dashed #ddd; + opacity: .8; +} + +.droparea .instructions.over, .multiple .instructions.over { + border: 2px dashed #000; + background: #ffa; +} + +.droparea .progress, .multiple .progress { + position: absolute; + bottom: 0; + width: 100%; + height: 0; + color: #fff; + background: #6b0; +} + +.multiple .progress { + width: 0; + height: 100%; +} + +#areas { + float: left; + width: 480px; +} + +div.spot { + float: left; + margin: 0 20px 0 0; + width: 120px; + height: 120px; +} + +.thumb { + float: left; + margin: 0 20px 20px 0; + width: 140px; + min-height: 105px; +} + +.desc { + float: right; + width: 460px; +} + +.signature a { + color: #555; + text-decoration: none; +} + +.signature img { + margin-right: 5px; + vertical-align: middle; +} + +input[type=text], textarea { + margin-bottom: 10px; + padding: 5px; + width: 288px; + height: 18px; + border: 1px solid #bbb; +} + +textarea { + height: 58px; +} + +input[type=submit] { + float: right; + margin-right: 20px; + padding: 5px 25px; + color: #111; + border: 1px solid #666; + background: #888; } \ No newline at end of file diff --git a/static/css/uploadifive.css b/static/css/uploadifive.css new file mode 100644 index 0000000..36b92fd --- /dev/null +++ b/static/css/uploadifive.css @@ -0,0 +1,72 @@ +.uploadifive-button { + background-color: #505050; + background-image: linear-gradient(bottom, #505050 0%, #707070 100%); + background-image: -o-linear-gradient(bottom, #505050 0%, #707070 100%); + background-image: -moz-linear-gradient(bottom, #505050 0%, #707070 100%); + background-image: -webkit-linear-gradient(bottom, #505050 0%, #707070 100%); + background-image: -ms-linear-gradient(bottom, #505050 0%, #707070 100%); + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0, #505050), + color-stop(1, #707070) + ); + background-position: center top; + background-repeat: no-repeat; + -webkit-border-radius: 30px; + -moz-border-radius: 30px; + border-radius: 30px; + border: 2px solid #808080; + color: #FFF; + font: bold 12px Arial, Helvetica, sans-serif; + text-align: center; + text-shadow: 0 -1px 0 rgba(0,0,0,0.25); + text-transform: uppercase; + width: 100%; +} +.uploadifive-button:hover { + background-color: #606060; + background-image: linear-gradient(top, #606060 0%, #808080 100%); + background-image: -o-linear-gradient(top, #606060 0%, #808080 100%); + background-image: -moz-linear-gradient(top, #606060 0%, #808080 100%); + background-image: -webkit-linear-gradient(top, #606060 0%, #808080 100%); + background-image: -ms-linear-gradient(top, #606060 0%, #808080 100%); + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0, #606060), + color-stop(1, #808080) + ); + background-position: center bottom; +} +.uploadifive-queue-item { + background-color: #F5F5F5; + border-bottom: 1px dotted #D5D5D5; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + font: 12px Arial, Helvetica, Sans-serif; + margin-top: 3px; + padding: 15px; +} +.uploadifive-queue-item .close { + background: url('../img/uploadifive-cancel.png') 0 0 no-repeat; + display: block; + float: right; + height: 16px; + text-indent: -9999px; + width: 16px; +} +.uploadifive-queue-item .progress { + border: 1px solid #D0D0D0; + height: 3px; + margin-top: 5px; + width: 100%; +} +.uploadifive-queue-item .progress-bar { + background-color: #0072BC; + height: 3px; + width: 0; +} \ No newline at end of file diff --git a/static/img/loader.gif b/static/img/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..ab023038e780a94be9c909724b290f3444e5e14c GIT binary patch literal 10819 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 h?O$6>wAFdl2YX1^@s6wkGG*0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy;z>k7RCwC#*Fg${Fc1aMWE`Q0OV6R_ z@CZe^>Z0I*3N3Wu8Qgddy-F!XqUn5TdxT~RnN1{{Jd&A_v3=j0BuUQex}KQ%X#$0F zZbw9yx~^Ms@UWM_DM8T#6aT@h}VnlL(JhRpkOQAp_^b0S~8X`jEZcdv7!Z zJS?l%0(ysMfHa|CA)_!<-qeOdIjTdcys3!#Th!+xwuG2dv3dUbhpesg^tJ@aC#wks z56|$@-;IUoTedj(|gUx-*vTSo-(=_c%fB^t2kIs}E SSl#mg0000X1^@s6L)dI(0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy*hxe|RCwC#*RcwNFc1dd7%zr)Yga)i z2>LWmeMfg6qIC2Dgn;0rbaIlA^sc2#hb|p*wD}=$<45w7kfiInJph=ZD2`=W;s_#S zthHho20M=94d)XmrP@5t4^>q?WZwIrz%)$-S)6l+X_{Ktwymk_Iyme-Xi~8{i!5X!+g*SG00byBFJ~lm>Hq)$07*qo IM6N<$g6g$?KL7v# literal 0 HcmV?d00001 diff --git a/static/img/switch_handle-light.png b/static/img/switch_handle-light.png new file mode 100644 index 0000000000000000000000000000000000000000..31e10a6b681dfe50df201cc3a580d86055bd25a0 GIT binary patch literal 409 zcmV;K0cQS*P)X1^@s6L)dI(00009a7bBm000XU z000XU0RWnu7ytkOAY({UO#lFHCjbC*uK)n|Yybd;O#lFG^8f(q!T2-!4E+jX5eM4P5*g%C2& z^C@t)w=o=#$G1wU&7ojFtCZSICX+j0*aNPF5RaqL=n{)kRn<$HrZ)j_k|fE6F~%kz zNs^oc#{m!x27}NxU=&3G5P3ikd(#U5Y+LVp9MAzB@LvFTyPaFm0Ugi*e-UUb1{x30 z>-BnTn@nrn0onsri^by27=ul%>-rT~`9P<&-ey^L9fo0G!+LGo>e+1eAfnMFU#@?`1l$CFar5E`YM8F00000NkvXXu0mjf D8V9FE literal 0 HcmV?d00001 diff --git a/static/img/switch_handle.png b/static/img/switch_handle.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7d5c42968edcfe6a6182e779a8262c64b93d0a GIT binary patch literal 344 zcmV-e0jK_nP)X1^@s6L)dI(0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy`bk7VRCwCd*3AjRKoAAsnPiVS8Sv+% zf@lmRdea8%z*HiUl#mJp!5(bFn_|#o%-ss~pt$Q!z#g6t_Wm(%CmUWSNt2oHS(Z7I zI3|Q#u2;(QTpMFL?rc6ktkbj;MG>K--D=S|2)HPUg(BiX7=~oEgcLD}O?jU8LBstb zHbqQ?3KW||;E_9sJ3(txmIn(Io3aOYf|jKWaK&PS-(pouVVC4DfYzq9#$6Kl@1Oy$ z*whuX0p4Qs2NlD=Bn*59#RmV>;r|alo(~swf?|^iR^_d#YeqU^5}P|5h5O^_yz2M5 q?E4Lf$(7}eHrt(kz24KW00RIJzvSH%+D*U!0000X1^@s6wkGG*0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy{z*hZRCwC#mpzJvKoEsrYbhd%nTg;r zCif<$1G@}Nyy=FU4eV_ck6~mqi69uL`0v`n9AKlaWqr_e`cbc{cqNh~xd6C2juV@v z2~}#_7Q-++q9}SN2^0iDY}+>Gc~%DH`##FDd`r`GA=h(|4|f6YBr;)QgUbmSq9$nHXUhvH%;$5nb0o+fSQEQ4~iEm0+sgG)>6!TylH} uS*|S0zKG~9L%YJG;kkd```!2XBftQ$J5$5BWyCE20000`U&$rm#}n~#BItYL#cmrRxA7uObK?b%|879YsWfzuJ%fI8Jk41j`V-*)=K)DqtJTa!_9Q^Z z_2}tcMqx_4SGwJ9>)~*?lR3VL6m=$(i7#)hk?U@t3s0#= zqcN>iDleLT?#kuzHC1yw9*4a3Xzvw3$3thH%(B&LG4S`nVDP|B|DtQI$okzlj`3NY o`-RjarZXCQ{eItkR`)Bw0IlyTv}2+~GXMYp07*qoM6N<$f{&sdssI20 literal 0 HcmV?d00001 diff --git a/static/img/switch_track-gray.png b/static/img/switch_track-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1d90d235dbc8d29c9d23aa55c9e73a88cb8f31 GIT binary patch literal 679 zcmV;Y0$BZtP)sQ5Rwxu=G5%HS$iJDYpp z+SS#%`TEr9+m>oJnFhl4@t0`inuDm`jp9J?i!88i)^)8AIIbIui<7ue};oNhp4yxDC2LC2jiWp^*E)Ag3GKF0-s zwu@cv7CPK+*y>C-7wi_1p+^J%;6Z90oF-Zry4zg0x$fh|oY=*3@AAFV@qxQw7ejaJ zbert~bQ3WQJsRltyN^1y1Q>dB4^fVn{~a$3{mjt0dpUFWV)yCn^k|*F@6(UDoS{eO z^n;h9W6OZ$4!V6*f5-ga%Ng573Ft}A^z&spDW!bh{~3ni=VQx(Q%^vA&HUs@-tyXJDnz)=!r#GEW=V3CLT+=cL+3jv!PDv8W z=kvpAwR)e+<-P+ta=l1(O?5RIjjjkd@7K8l9~O(n-!w!GsGeU17yu_;>4y&RW!wM& N002ovPDHLkV1g(jMkN3M literal 0 HcmV?d00001 diff --git a/static/img/switch_track-light.png b/static/img/switch_track-light.png new file mode 100644 index 0000000000000000000000000000000000000000..024752770cc85ced122ba6faba2ef6f60f6aaa73 GIT binary patch literal 571 zcmV-B0>u4^P){} z-fuRWN3~wBUn-@(MNt$FheHsNC~BDN^@{WPjLYS6V&*$L7z`f5FpQ_uDToNh7$~L0 zR6{T`oO75=CO8}paaooR)_Z?HpU*KGjSvI@j4@)WVVrZQstU#!>~_01);ZT3kH_fu z`v`&n0HpMWO9z0es-TpD_nxfQ8bkyl64@Kj}&-sl21= zMf$Cz*H=W5-i>r)%%9Th5!$8pET!L~bYqN^-eKua>qSbx!9gmeOX*U&&_e0;^IuBu z2DzupJ^ep>k&5(=4^okSBk8i!JKpKNUhfw(TPeMLm>ERmY?36$JkMuQ6p59V$@3hG z#p0;bH2qQ(MU`b4oO4h6U8cEl&LPV(6h%>`Y5G|)^NZ8z^l7`@zTWTm{?tH3%M0m4$dRB1IM$|T} zeCXOCqI+@vIG;A;ov(_b08a#7(Ib3Px2XAFq;!+c0^VrXHK;eCLr`=gh4<~W8+Oik z2@$-3T3pqebehQ*5#D<;^pVgV);j(%JxJe>twpfCj(&V?=y}l*ns|3U#AkYtrW_Y= zRB)U0>?f&eqTXGjt6`dSNRKbwZaG$23U3+`x*`Y7p0wYXQ|9VyJ6h^@$-b?|*wtZx z&W1iIx-@Y+{%r9wYP@v5e2EvZNuOlWVUX#8Q)4^+R=Fp=?}*NJkWQNE+#RIdN`t08 z>dF{e_4MV4uXEjMgh_`!x@}Lt%bsrN)1&L=$b8*axzPu{a`k)qpsg~a-`LvE0C)Hq zz!}N(qPQo{mdE fJc#%HPk;dccyMXs29BB700000NkvXXu0mjfJtqed literal 0 HcmV?d00001 diff --git a/static/img/uploadifive-cancel.png b/static/img/uploadifive-cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..1d97535edaabd9d2b15c6202b55f9aabc03d5573 GIT binary patch literal 1065 zcmaJ=Pe>F|7$4U^+2V#Ek$R}d7}3Gq`LqArVb^~9qyIXES;OxxP9dzbRGf#K* z(kc=r1a^qJNEvnYV05Y|Nu(l@hzNo%B3;E>$KI%`9optF?|YBm@Av(_?|tvi(BLJH zyVXrmlqcCQrpW5V?)p0NzsqmECQA!WWN{je;(}~Jsz*gR2$H%y22)U0r*D3MQHrVt zS|*FL(tw~K-6uO3Ur{#+o1&s!MN?MBAqF`(rWrAM_vITJXljfe4oa+K#$jITpRr(i zW-z17j4Kh9?&<{5qCf<6h-FaJCk$IC#^`-rfyB-+LxX(?9*@xnrn1ryh$9PvppW$` zY={LR&d2!!;c&Pe@GQqNEYEPfmkSGiUSL^p_|YVrrH%?IvF|V!*~REQ#-_k9g+jqs z@cWQ8#&D5H#L?h+FF|keuVJ^96+~z&mS$oN8NgALb7&IBXyWvs1l>H6HSEJS zk%lov*y%UmO49$Kx_*SVaS9&${im>LxXe|Lxnf1GGYGm#GKtEMgJ|eT@ghxY5Hb-L*4KnK%8S2LBm7nB6JW7f^nc|OJgU16) zt|rj=uHPOZLd4JT-EfccDr5;&%Hg') + .addClass('switch-container') + .css({ + 'height':settings.switch_height, + 'width':settings.switch_width, + 'position':'relative', + 'overflow':'hidden', + 'font':"normal normal normal 12px/18px 'Lucida Grande', Verdana, sans-serif" + }); + /**** make the track ****/ + track = jQuery('
') + .addClass(settings.track_class) + .css({ + 'height':settings.track_height, + 'width':settings.track_width, + 'position':'absolute', + 'background-image':track_bg, + 'background-repeat':'no-repeat' + }); + + /**** Make the handle ****/ + handle = jQuery('
') + .addClass(settings.handle_class) + .css({ + 'height':settings.handle_height, + 'width':settings.handle_width, + 'left':left, + 'right':right, + 'top':1, + 'bottom':1, + 'position':'absolute', + 'background-image':handle_bg, + 'background-repeat':'no-repeat', + 'cursor':'pointer', + '-webkit-user-select':'none', + '-moz-user-select':'none', + '-o-user-select':'none', + 'user-select':'none' + }); + + /**** Make the labels ****/ + label_on = jQuery('') + .addClass(settings.label_class) + .addClass('left') + .text(settings.on_label) + .css({ + 'height':settings.handle_height, + 'width':settings.handle_width, + 'line-height':settings.track_height + 'px', + 'color':settings.label_color, + 'font-size':settings.label_font_size, + 'text-align':'center', + 'text-shadow':'#333 0px 1px 0px', + '-webkit-user-select':'none', + '-moz-user-select':'none', + '-o-user-select':'none', + 'user-select':'none', + 'cursor':'default', + 'float':'left' + }); + label_off = jQuery('') + .addClass(settings.label_class) + .addClass('right') + .text(settings.off_label) + .css({ + 'height':settings.handle_height, + 'width':settings.handle_width, + 'line-height':settings.track_height + 'px', + 'color':settings.label_color, + 'font-size':settings.label_font_size, + 'text-align':'center', + 'text-shadow':'#333 0px 1px 0px', + '-webkit-user-select':'none', + '-moz-user-select':'none', + '-o-user-select':'none', + 'user-select':'none', + 'cursor':'default', + 'position':'absolute', + 'top':1, + 'right':1, + 'bottom':1 + }); + // CSS3 - imagless + if (!settings.use_images) { + track.css({ + 'background-color':settings.track_bg_color, + '-webkit-border-radius':settings.switch_radius, + '-moz-border-radius':settings.switch_radius, + '-o-border-radius':settings.switch_radius, + 'border-radius':settings.switch_radius, + '-webkit-box-shadow': settings.track_dropshadow_color + ' 0px 1px 1px, rgba(1, 1, 1, 0.65) 0px 3px 6px inset', + '-moz-box-shadow': settings.track_dropshadow_color + ' 0px 1px 1px, rgba(1, 1, 1, 0.65) 0px 3px 6px inset', + '-o-box-shadow': settings.track_dropshadow_color + ' 0px 1px 1px, rgba(1, 1, 1, 0.65) 0px 3px 6px inset', + 'box-shadow': settings.track_dropshadow_color + ' 0px 1px 1px, rgba(1, 1, 1, 0.65) 0px 3px 6px inset', + '-webkit-background-clip':'padding-box', + 'background-clip':'padding-box' + }); + handle.css({ + 'background':'-webkit-gradient(linear, 0% 0%, 0% 100%, from(#fcfcfc), to(#e6e6e6))', + 'background-image':'-moz-linear-gradient(-90deg, #fcfcfc, #e6e6e6)', + 'background-color':settings.handle_bg_color, + '-webkit-border-radius':settings.switch_radius -1, + '-moz-border-radius':settings.switch_radius -1, + '-o-border-radius':settings.switch_radius -1, + 'border-radius':settings.switch_radius -1, + '-webkit-box-shadow':'rgba(255,255,255,1) 0px 0px 3px inset, rgba(0, 0, 0, 0.99) 0px 0px 3px', + '-moz-box-shadow':'rgba(255,255,255,1) 0px 0px 3px inset, rgba(0, 0, 0, 0.99) 0px 0px 3px', + '-o-box-shadow':'rgba(255,255,255,1) 0px 0px 3px inset, rgba(0, 0, 0, 0.99) 0px 0px 3px', + 'box-shadow':'rgba(255,255,255,1) 0px 0px 3px inset, rgba(0, 0, 0, 0.99) 0px 0px 3px', + '-webkit-background-clip':'padding-box', + 'background-clip':'padding-box' + }); + } + + /* insert into placeholder */ + checkbox.wrap(container); + track.append(label_on) + .append(label_off) + .append(handle); + checkbox.after(track); + + var mySwitch = checkbox.parent(); + + // click handling + jQuery(mySwitch).find('.' + settings.handle_class).click(function() { + var myHandle = jQuery(this); + var cb = myHandle.parent().siblings('input:checkbox'); + var checkd = cb.is('input:checked'); + var l = (checkd ? track_padding : offset); + var r = (checkd ? offset : track_padding); + var switched_callback = (checkd ? switched_off_callback : switched_on_callback); + // slide the handle + slide_handle(myHandle, l, r, settings.speed, switched_callback); + cb.attr('checked', (checkd ? false : true)).trigger('change'); + });//- END .click() + + });//- END .each() + }//- END $.fn.iphoneSwitch() + + /*** Private functions ***/ + function slide_handle(handle, left_pos, right_pos, speed, switch_callback) + { + jQuery(handle).animate({left: left_pos,right: right_pos}, speed, function() { + if (typeof switch_callback == 'function') + { + switch_callback.call(this); + }//- END if + });//- END animate + }//- END slide_handle() + +})(jQuery); \ No newline at end of file diff --git a/static/js/libs/uploadify/jquery.uploadifive.js b/static/js/libs/uploadify/jquery.uploadifive.js new file mode 100644 index 0000000..9928813 --- /dev/null +++ b/static/js/libs/uploadify/jquery.uploadifive.js @@ -0,0 +1,887 @@ +/* +UploadiFive 1.0.5 +Copyright (c) 2012 Reactive Apps, Ronnie Garcia +Released under the UploadiFive Standard License +*/ +;(function($) { + + var methods = { + + init : function(options) { + + return this.each(function() { + + // Create a reference to the jQuery DOM object + var $this = $(this); + $this.data('uploadifive', { + inputs : {}, // The object that contains all the file inputs + inputCount : 0, // The total number of file inputs created + fileID : 0, + queue : { + count : 0, // Total number of files in the queue + selected : 0, // Number of files selected in the last select operation + replaced : 0, // Number of files replaced in the last select operation + errors : 0, // Number of files that returned an error in the last select operation + queued : 0, // Number of files added to the queue in the last select operation + cancelled : 0 // Total number of files that have been cancelled or removed from the queue + }, + uploads : { + current : 0, // Number of files currently being uploaded + attempts : 0, // Number of file uploads attempted in the last upload operation + successful : 0, // Number of files successfully uploaded in the last upload operation + errors : 0, // Number of files returning errors in the last upload operation + count : 0 // Total number of files uploaded successfully + } + }); + var $data = $this.data('uploadifive'); + + // Set the default options + var settings = $data.settings = $.extend({ + 'auto' : true, // Automatically upload a file when it's added to the queue + 'buttonClass' : false, // A class to add to the UploadiFive button + 'buttonText' : 'Select Files', // The text that appears on the UploadiFive button + 'checkScript' : false, // Path to the script that checks for existing file names + 'dnd' : true, // Allow drag and drop into the queue + 'dropTarget' : false, // Selector for the drop target + 'fileSizeLimit' : 0, // Maximum allowed size of files to upload + 'fileType' : false, // Type of files allowed (image, etc) + 'formData' : {}, // Additional data to send to the upload script + 'height' : 30, // The height of the button + 'method' : 'post', // The method to use when submitting the upload + 'multi' : true, // Set to true to allow multiple file selections + 'overrideEvents' : [], // An array of events to override + 'queueID' : false, // The ID of the file queue + 'queueSizeLimit' : 0, // The maximum number of files that can be in the queue + 'removeCompleted' : false, // Set to true to remove files that have completed uploading + 'simUploadLimit' : 0, // The maximum number of files to upload at once + 'truncateLength' : 0, // The length to truncate the file names to + 'uploadLimit' : 0, // The maximum number of files you can upload + 'uploadScript' : 'uploadifive.php', // The path to the upload script + 'width' : 100 // The width of the button + + /* + // Events + 'onAddQueueItem' : function(file) {}, // Triggered for each file that is added to the queue + 'onCancel' : function(file) {}, // Triggered when a file is cancelled or removed from the queue + 'onCheck' : function(file, exists) {}, // Triggered when the server is checked for an existing file + 'onClearQueue' : function(queue) {}, // Triggered during the clearQueue function + 'onDestroy' : function() {} // Triggered during the destroy function + 'onDrop' : function(files, numberOfFilesDropped) {}, // Triggered when files are dropped into the file queue + 'onError' : function(file, fileType, data) {}, // Triggered when an error occurs + 'onFallback' : function() {}, // Triggered if the HTML5 File API is not supported by the browser + 'onInit' : function() {}, // Triggered when UploadiFive if initialized + 'onQueueComplete' : function() {}, // Triggered once when an upload queue is done + 'onProgress' : function(file, event) {}, // Triggered during each progress update of an upload + 'onSelect' : function() {}, // Triggered once when files are selected from a dialog box + 'onUpload' : function(file) {}, // Triggered when an upload queue is started + 'onUploadComplete' : function(file, data) {}, // Triggered when a file is successfully uploaded + 'onUploadFile' : function(file) {}, // Triggered for each file being uploaded + */ + }, options); + + // Calculate the file size limit + if (isNaN(settings.fileSizeLimit)) { + var fileSizeLimitBytes = parseInt(settings.fileSizeLimit) * 1.024 + if (settings.fileSizeLimit.indexOf('KB') > -1) { + settings.fileSizeLimit = fileSizeLimitBytes * 1000; + } else if (settings.fileSizeLimit.indexOf('MB') > -1) { + settings.fileSizeLimit = fileSizeLimitBytes * 1000000; + } else if (settings.fileSizeLimit.indexOf('GB') > -1) { + settings.fileSizeLimit = fileSizeLimitBytes * 1000000000; + } + } else { + settings.fileSizeLimit = settings.fileSizeLimit * 1024; + } + + // Create a template for a file input + $data.inputTemplate = $('') + .css({ + 'opacity' : 0, + 'position' : 'absolute', + 'z-index' : 999 + }); + + // Create a new input + $data.createInput = function() { + + // Create a clone of the file input + var input = $data.inputTemplate.clone(); + // Create a unique name for the input item + var inputName = input.name = 'input' + $data.inputCount++; + // Set the multiple attribute + if (settings.multi) { + input.attr('multiple', true); + } + // Set the onchange event for the input + input.bind('change', function() { + $data.queue.selected = 0; + $data.queue.replaced = 0; + $data.queue.errors = 0; + $data.queue.queued = 0; + // Add a queue item to the queue for each file + var limit = this.files.length; + $data.queue.selected = limit; + if (($data.queue.count + limit) > settings.queueSizeLimit && settings.queueSizeLimit !== 0) { + if ($.inArray('onError', settings.overrideEvents) < 0) { + alert('The maximum number of queue items has been reached (' + settings.queueSizeLimit + '). Please select fewer files.'); + } + // Trigger the error event + if (typeof settings.onError === 'function') { + settings.onError.call($this, 'QUEUE_LIMIT_EXCEEDED'); + } + } else { + for (var n = 0; n < limit; n++) { + file = this.files[n]; + $data.addQueueItem(file); + } + $data.inputs[inputName] = this; + $data.createInput(); + } + // Upload the file if auto-uploads are enabled + if (settings.auto) { + methods.upload.call($this); + } + // Trigger the select event + if (typeof settings.onSelect === 'function') { + settings.onSelect.call($this, $data.queue); + } + }); + // Hide the existing current item and add the new one + if ($data.currentInput) { + $data.currentInput.hide(); + } + $data.button.append(input); + $data.currentInput = input; + } + + // Remove an input + $data.destroyInput = function(key) { + $($data.inputs[key]).remove(); + delete $data.inputs[key]; + $data.inputCount--; + } + + // Drop a file into the queue + $data.drop = function(e) { + $data.queue.selected = 0; + $data.queue.replaced = 0; + $data.queue.errors = 0; + $data.queue.queued = 0; + + var fileData = e.dataTransfer; + + var inputName = fileData.name = 'input' + $data.inputCount++; + // Add a queue item to the queue for each file + var limit = fileData.files.length; + $data.queue.selected = limit; + if (($data.queue.count + limit) > settings.queueSizeLimit && settings.queueSizeLimit !== 0) { + // Check if the queueSizeLimit was reached + if ($.inArray('onError', settings.overrideEvents) < 0) { + alert('The maximum number of queue items has been reached (' + settings.queueSizeLimit + '). Please select fewer files.'); + } + // Trigger the onError event + if (typeof settings.onError === 'function') { + settings.onError.call($this, 'QUEUE_LIMIT_EXCEEDED'); + } + } else { + // Add a queue item for each file + for (var n = 0; n < limit; n++) { + file = fileData.files[n]; + $data.addQueueItem(file); + } + // Save the data to the inputs object + $data.inputs[inputName] = fileData; + } + + // Upload the file if auto-uploads are enabled + if (settings.auto) { + methods.upload.call($this); + } + + // Trigger the onDrop event + if (typeof settings.onDrop === 'function') { + settings.onDrop.call($this, fileData.files, fileData.files.length); + } + + // Stop FireFox from opening the dropped file(s) + e.preventDefault(); + e.stopPropagation(); + } + + // Check if a filename exists in the queue + $data.fileExistsInQueue = function(file) { + for (var key in $data.inputs) { + input = $data.inputs[key]; + limit = input.files.length; + for (var n = 0; n < limit; n++) { + existingFile = input.files[n]; + // Check if the filename matches + if (existingFile.name == file.name && !existingFile.complete) { + return true; + } + } + } + return false; + } + + // Remove an existing file in the queue + $data.removeExistingFile = function(file) { + for (var key in $data.inputs) { + input = $data.inputs[key]; + limit = input.files.length; + for (var n = 0; n < limit; n++) { + existingFile = input.files[n]; + // Check if the filename matches + if (existingFile.name == file.name && !existingFile.complete) { + $data.queue.replaced++; + methods.cancel.call($this, existingFile, true); + } + } + } + } + + // Queue item template + $data.queueItem = $('
\ + X\ +
\ +
\ +
\ +
\ +
'); + + // Add an item to the queue + $data.addQueueItem = function(file) { + if ($.inArray('onAddQueueItem', settings.overrideEvents) < 0) { + // Check if the filename already exists in the queue + $data.removeExistingFile(file); + // Create a clone of the queue item template + file.queueItem = $data.queueItem.clone(); + // Add an ID to the queue item + file.queueItem.attr('id', settings.id + '-file-' + $data.fileID++); + // Bind the close event to the close button + file.queueItem.find('.close').bind('click', function() { + methods.cancel.call($this, file); + return false; + }); + var fileName = file.name; + if (fileName.length > settings.truncateLength && settings.truncateLength != 0) { + fileName = fileName.substring(0, settings.truncateLength) + '...'; + } + file.queueItem.find('.filename').html(fileName); + // Add a reference to the file + file.queueItem.data('file', file); + $data.queueEl.append(file.queueItem); + } + // Trigger the addQueueItem event + if (typeof settings.onAddQueueItem === 'function') { + settings.onAddQueueItem.call($this, file); + } + // Check the filetype + if (settings.fileType) { + if ($.isArray(settings.fileType)) { + var isValidFileType = false; + for (var n = 0; n < settings.fileType.length; n++) { + if (file.type.indexOf(settings.fileType[n]) > -1) { + isValidFileType = true; + } + } + if (!isValidFileType) { + $data.error('FORBIDDEN_FILE_TYPE', file); + } + } else { + if (file.type.indexOf(settings.fileType) < 0) { + $data.error('FORBIDDEN_FILE_TYPE', file); + } + } + } + // Check the filesize + if (file.size > settings.fileSizeLimit && settings.fileSizeLimit != 0) { + $data.error('FILE_SIZE_LIMIT_EXCEEDED', file); + } else { + $data.queue.queued++; + $data.queue.count++; + } + } + + // Remove an item from the queue + $data.removeQueueItem = function(file, instant, delay) { + // Set the default delay + if (!delay) delay = 0; + var fadeTime = instant ? 0 : 500; + if (file.queueItem) { + if (file.queueItem.find('.fileinfo').html() != ' - Completed') { + file.queueItem.find('.fileinfo').html(' - Cancelled'); + } + file.queueItem.find('.progress-bar').width(0); + file.queueItem.delay(delay).fadeOut(fadeTime, function() { + $(this).remove(); + }); + delete file.queueItem; + $data.queue.count--; + } + } + + // Count the number of files that need to be uploaded + $data.filesToUpload = function() { + var filesToUpload = 0; + for (var key in $data.inputs) { + input = $data.inputs[key]; + limit = input.files.length; + for (var n = 0; n < limit; n++) { + file = input.files[n]; + if (!file.skip && !file.complete) { + filesToUpload++; + } + } + } + return filesToUpload; + } + + // Check if a file exists + $data.checkExists = function(file) { + if ($.inArray('onCheck', settings.overrideEvents) < 0) { + // This request needs to be synchronous + $.ajaxSetup({ + 'async' : false + }); + // Send the filename to the check script + var checkData = $.extend(settings.formData, {filename: file.name}); + $.post(settings.checkScript, checkData, function(fileExists) { + file.exists = parseInt(fileExists); + }); + if (file.exists) { + if (!confirm('A file named ' + file.name + ' already exists in the upload folder.\nWould you like to replace it?')) { + // If not replacing the file, cancel the upload + methods.cancel.call($this, file); + return true; + } + } + } + // Trigger the check event + if (typeof settings.onCheck === 'function') { + settings.onCheck.call($this, file, file.exists); + } + return false; + } + + // Upload a single file + $data.uploadFile = function(file, uploadAll) { + if (!file.skip && !file.complete && !file.uploading) { + file.uploading = true; + $data.uploads.current++; + $data.uploads.attempted++; + + // Create a new AJAX request + xhr = file.xhr = new XMLHttpRequest(); + + // Start the upload + // Use the faster FormData if it exists + if (typeof FormData === 'function' || typeof FormData === 'object') { + + // Create a new FormData object + var formData = new FormData(); + + // Add the form data + formData.append('Filedata', file); + + // Add the rest of the formData + for (i in settings.formData) { + formData.append(i, settings.formData[i]); + } + + // Open the AJAX call + xhr.open(settings.method, settings.uploadScript, true); + + // On progress function + xhr.upload.addEventListener('progress', function(e) { + if (e.lengthComputable) { + $data.progress(e, file); + } + }, false); + + // On complete function + xhr.addEventListener('load', function(e) { + if (this.readyState == 4) { + file.uploading = false; + if (this.status == 200) { + if (file.xhr.responseText !== 'Invalid file type.') { + $data.uploadComplete(e, file, uploadAll); + } else { + $data.error(file.xhr.responseText, file, uploadAll); + } + } else if (this.status == 404) { + $data.error('404_FILE_NOT_FOUND', file, uploadAll); + } else if (this.status == 403) { + $data.error('403_FORBIDDEN', file, uplaodAll); + } else { + $data.error('Unknown Error', file, uploadAll); + } + } + }); + + // Send the form data (multipart/form-data) + xhr.send(formData); + + } else { + + // Send as binary + var reader = new FileReader(); + reader.onload = function(e) { + + // Set some file builder variables + var boundary = '-------------------------' + (new Date).getTime(), + dashes = '--', + eol = '\r\n', + binFile = ''; + + // Build an RFC2388 String + binFile += dashes + boundary + eol; + // Generate the headers + binFile += 'Content-Disposition: form-data; name="Filedata"'; + if (file.name) { + binFile += '; filename="' + file.name + '"'; + } + binFile += eol; + binFile += 'Content-Type: application/octet-stream' + eol + eol; + binFile += e.target.result + eol; + + for (key in settings.formData) { + binFile += dashes + boundary + eol; + binFile += 'Content-Disposition: form-data; name="' + key + '"' + eol + eol; + binFile += settings.formData[key] + eol; + } + + binFile += dashes + boundary + dashes + eol; + + // On progress function + xhr.upload.addEventListener('progress', function(e) { + $data.progress(e, file); + }, false); + + // On complete function + xhr.addEventListener('load', function(e) { + file.uploading = false; + var status = this.status; + if (status == 404) { + $data.error('404_FILE_NOT_FOUND', file, uploadAll); + } else { + if (file.xhr.responseText != 'Invalid file type.') { + $data.uploadComplete(e, file, uploadAll); + } else { + $data.error(file.xhr.responseText, file, uploadAll); + } + } + }, false); + + // Open the ajax request + var url = settings.uploadScript; + if (settings.method == 'get') { + var params = $(settings.formData).param(); + url += params; + } + xhr.open(settings.method, settings.uploadScript, true); + xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary); + + // Trigger the uploadFile event + if (typeof settings.onUploadFile === 'function') { + settings.onUploadFile.call($this, file); + } + + // Send the file for upload + xhr.sendAsBinary(binFile); + } + reader.readAsBinaryString(file); + + } + } + } + + // Update a file upload's progress + $data.progress = function(e, file) { + if ($.inArray('onProgress', settings.overrideEvents) < 0) { + if (e.lengthComputable) { + var percent = Math.round((e.loaded / e.total) * 100); + } + file.queueItem.find('.fileinfo').html(' - ' + percent + '%'); + file.queueItem.find('.progress-bar').css('width', percent + '%'); + } + // Trigger the progress event + if (typeof settings.onProgress === 'function') { + settings.onProgress.call($this, file, e); + } + } + + // Trigger an error + $data.error = function(errorType, file, uploadAll) { + if ($.inArray('onError', settings.overrideEvents) < 0) { + // Get the error message + switch(errorType) { + case '404_FILE_NOT_FOUND': + errorMsg = '404 Error'; + break; + case '403_FORBIDDEN': + errorMsg = '403 Forbidden'; + break; + case 'FORBIDDEN_FILE_TYPE': + errorMsg = 'Forbidden File Type'; + break; + case 'FILE_SIZE_LIMIT_EXCEEDED': + errorMsg = 'File Too Large'; + break; + default: + errorMsg = 'Unknown Error'; + break; + } + + // Add the error class to the queue item + file.queueItem.addClass('error') + // Output the error in the queue item + .find('.fileinfo').html(' - ' + errorMsg); + // Hide the + file.queueItem.find('.progress').remove(); + } + // Trigger the error event + if (typeof settings.onError === 'function') { + settings.onError.call($this, errorType, file); + } + file.skip = true; + if (errorType == '404_FILE_NOT_FOUND') { + $data.uploads.errors++; + } else { + $data.queue.errors++; + } + if (uploadAll) { + methods.upload.call($this, null, true); + } + } + + // Trigger when a single file upload is complete + $data.uploadComplete = function(e, file, uploadAll) { + if ($.inArray('onUploadComplete', settings.overrideEvents) < 0) { + file.queueItem.find('.progress-bar').css('width', '100%'); + file.queueItem.find('.fileinfo').html(' - Completed'); + file.queueItem.find('.progress').slideUp(250); + file.queueItem.addClass('complete'); + } + // Trigger the complete event + if (typeof settings.onUploadComplete === 'function') { + settings.onUploadComplete.call($this, file, file.xhr.responseText); + } + if (settings.removeCompleted) { + setTimeout(function() { methods.cancel.call($this, file); }, 3000); + } + file.complete = true; + $data.uploads.successful++; + $data.uploads.count++; + $data.uploads.current--; + delete file.xhr; + if (uploadAll) { + methods.upload.call($this, null, true); + } + } + + // Trigger when all the files are done uploading + $data.queueComplete = function() { + // Trigger the queueComplete event + if (typeof settings.onQueueComplete === 'function') { + settings.onQueueComplete.call($this, $data.uploads); + } + } + + // ---------------------- + // Initialize UploadiFive + // ---------------------- + + // Check if HTML5 is available + if (window.File && window.FileList && window.Blob && (window.FileReader || window.FormData)) { + // Assign an ID to the object + settings.id = 'uploadifive-' + $this.attr('id'); + + // Wrap the file input in a div with overflow set to hidden + $data.button = $('
' + settings.buttonText + '
'); + if (settings.buttonClass) $data.button.addClass(settings.buttonClass); + + // Style the button wrapper + $data.button.css({ + 'height' : settings.height, + 'line-height' : settings.height + 'px', + 'overflow' : 'hidden', + 'position' : 'relative', + 'text-align' : 'center', + 'width' : settings.width + }); + + // Insert the button above the file input + $this.before($data.button) + // Add the file input to the button + .appendTo($data.button) + // Modify the styles of the file input + .hide(); + + // Create a new input + $data.createInput.call($this); + + // Position the browse files button under the cursor + $data.button.mousemove(function(e) { + var offset = $data.button.offset(); + $data.currentInput.css({ + 'left' : e.pageX - offset.left - $this.width() + 10, + 'top' : e.pageY - offset.top - $this.height() + 10 + }); + }); + + // Create the queue container + if (!settings.queueID) { + settings.queueID = settings.id + '-queue'; + $data.queueEl = $('
'); + $data.button.after($data.queueEl); + } else { + $data.queueEl = $('#' + settings.queueID); + } + + // Add drag and drop functionality + if (settings.dnd) { + var $dropTarget = settings.dropTarget ? $(settings.dropTarget) : $data.queueEl.get(0); + $dropTarget.addEventListener('dragleave', function(e) { + // Stop FireFox from opening the dropped file(s) + e.preventDefault(); + e.stopPropagation(); + }, false); + $dropTarget.addEventListener('dragenter', function(e) { + // Stop FireFox from opening the dropped file(s) + e.preventDefault(); + e.stopPropagation(); + }, false); + $dropTarget.addEventListener('dragover', function(e) { + // Stop FireFox from opening the dropped file(s) + e.preventDefault(); + e.stopPropagation(); + }, false); + $dropTarget.addEventListener('drop', $data.drop, false); + } + + // Send as binary workaround for Chrome + if (!XMLHttpRequest.prototype.sendAsBinary) { + XMLHttpRequest.prototype.sendAsBinary = function(datastr) { + function byteValue(x) { + return x.charCodeAt(0) & 0xff; + } + var ords = Array.prototype.map.call(datastr, byteValue); + var ui8a = new Uint8Array(ords); + this.send(ui8a.buffer); + } + } + + // Trigger the oninit event + if (typeof settings.onInit === 'function') { + settings.onInit.call($this); + } + + } else { + + // Trigger the fallback event + if (typeof settings.onFallback === 'function') { + settings.onFallback.call($this); + } + return false; + + } + + }); + + }, + + + // Write some data to the console + debug : function() { + + return this.each(function() { + + console.log($(this).data('uploadifive')); + + }); + + }, + + // Clear all the items from the queue + clearQueue : function() { + + this.each(function() { + + var $this = $(this), + $data = $this.data('uploadifive'), + settings = $data.settings; + + for (var key in $data.inputs) { + input = $data.inputs[key]; + limit = input.files.length; + for (i = 0; i < limit; i++) { + file = input.files[i]; + methods.cancel.call($this, file); + } + } + // Trigger the onClearQueue event + if (typeof settings.onClearQueue === 'function') { + settings.onClearQueue.call($this, $('#' + $data.options.queueID)); + } + + }); + + }, + + // Cancel a file upload in progress or remove a file from the queue + cancel : function(file, fast) { + + this.each(function() { + + var $this = $(this), + $data = $this.data('uploadifive'), + settings = $data.settings; + + // If user passed a queue item ID instead of file... + if (typeof file === 'string') { + if (!isNaN(file)) { + fileID = 'uploadifive-' + $(this).attr('id') + '-file-' + file; + } + file = $('#' + fileID).data('file'); + } + + file.skip = true; + $data.filesCancelled++; + if (file.uploading) { + $data.uploads.current--; + file.uploading = false; + file.xhr.abort(); + delete file.xhr; + methods.upload.call($this); + } + if ($.inArray('onCancel', settings.overrideEvents) < 0) { + $data.removeQueueItem(file, fast); + } + + // Trigger the cancel event + if (typeof settings.onCancel === 'function') { + settings.onCancel.call($this, file); + } + + }); + + }, + + // Upload the files in the queue + upload : function(file, keepVars) { + + this.each(function() { + + var $this = $(this), + $data = $this.data('uploadifive'), + settings = $data.settings; + + if (file) { + + $data.uploadFile.call($this, file); + + } else { + + // Check if the upload limit was reached + if (($data.uploads.count + $data.uploads.current) < settings.uploadLimit || settings.uploadLimit == 0) { + if (!keepVars) { + $data.uploads.attempted = 0; + $data.uploads.successsful = 0; + $data.uploads.errors = 0; + var filesToUpload = $data.filesToUpload(); + // Trigger the onUpload event + if (typeof settings.onUpload === 'function') { + settings.onUpload.call($this, filesToUpload); + } + } + + // Loop through the files + $('#' + settings.queueID).find('.uploadifive-queue-item').not('.error, .complete').each(function() { + _file = $(this).data('file'); + // Check if the simUpload limit was reached + if (($data.uploads.current >= settings.simUploadLimit && settings.simUploadLimit !== 0) || ($data.uploads.current >= settings.uploadLimit && settings.uploadLimit !== 0) || ($data.uploads.count >= settings.uploadLimit && settings.uploadLimit !== 0)) { + return false; + } + if (settings.checkScript) { + // Let the loop know that we're already processing this file + _file.checking = true; + skipFile = $data.checkExists(_file); + _file.checking = false; + if (!skipFile) { + $data.uploadFile(_file, true); + } + } else { + $data.uploadFile(_file, true); + } + }); + if ($('#' + settings.queueID).find('.uploadifive-queue-item').not('.error, .complete').size() == 0) { + $data.queueComplete(); + } + } else { + if ($data.uploads.current == 0) { + if ($.inArray('onError', settings.overrideEvents) < 0) { + if ($data.filesToUpload() > 0 && settings.uploadLimit != 0) { + alert('The maximum upload limit has been reached.'); + } + } + // Trigger the onError event + if (typeof settings.onError === 'function') { + settings.onError.call($this, 'UPLOAD_LIMIT_EXCEEDED', $data.filesToUpload()); + } + } + } + + } + + }); + + }, + + // Destroy an instance of UploadiFive + destroy : function() { + + this.each(function() { + + var $this = $(this), + $data = $this.data('uploadifive'), + settings = $data.settings; + + // Clear the queue + methods.clearQueue.call($this); + // Destroy the queue if it was created + if (!settings.queueID) $('#' + settings.queueID).remove(); + // Remove extra inputs + $this.siblings('input').remove(); + // Show the original file input + $this.show() + // Move the file input out of the button + .insertBefore($data.button); + // Delete the button + $data.button.remove(); + // Trigger the destroy event + if (typeof settings.onDestroy === 'function') { + settings.onDestroy.call($this); + } + + }); + + } + + } + + $.fn.uploadifive = function(method) { + + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return methods.init.apply(this, arguments); + } else { + $.error('The method ' + method + ' does not exist in $.uploadify'); + } + + } + +})(jQuery); + +/* I gave the queueItems IDs and they each have a reference to the file held in the 'data' obj. */ \ No newline at end of file diff --git a/static/js/libs/uploadify/jquery.uploadifive.min.js b/static/js/libs/uploadify/jquery.uploadifive.min.js new file mode 100644 index 0000000..2155fb9 --- /dev/null +++ b/static/js/libs/uploadify/jquery.uploadifive.min.js @@ -0,0 +1,6 @@ +/* +UploadiFive 1.0.5 +Copyright (c) 2012 Reactive Apps, Ronnie Garcia +Released under the UploadiFive Standard License +*/ +;(function(b){var a={init:function(c){return this.each(function(){var g=b(this);g.data("uploadifive",{inputs:{},inputCount:0,fileID:0,queue:{count:0,selected:0,replaced:0,errors:0,queued:0,cancelled:0},uploads:{current:0,attempts:0,successful:0,errors:0,count:0}});var d=g.data("uploadifive");var f=d.settings=b.extend({auto:true,buttonClass:false,buttonText:"Select Files",checkScript:false,dnd:true,dropTarget:false,fileSizeLimit:0,fileType:false,formData:{},height:30,method:"post",multi:true,overrideEvents:[],queueID:false,queueSizeLimit:0,removeCompleted:false,simUploadLimit:0,truncateLength:0,uploadLimit:0,uploadScript:"uploadifive.php",width:100},c);if(isNaN(f.fileSizeLimit)){var e=parseInt(f.fileSizeLimit)*1.024;if(f.fileSizeLimit.indexOf("KB")>-1){f.fileSizeLimit=e*1000;}else{if(f.fileSizeLimit.indexOf("MB")>-1){f.fileSizeLimit=e*1000000;}else{if(f.fileSizeLimit.indexOf("GB")>-1){f.fileSizeLimit=e*1000000000;}}}}else{f.fileSizeLimit=f.fileSizeLimit*1024;}d.inputTemplate=b('').css({opacity:0,position:"absolute","z-index":999});d.createInput=function(){var j=d.inputTemplate.clone();var k=j.name="input"+d.inputCount++;if(f.multi){j.attr("multiple",true);}j.bind("change",function(){d.queue.selected=0;d.queue.replaced=0;d.queue.errors=0;d.queue.queued=0;var l=this.files.length;d.queue.selected=l;if((d.queue.count+l)>f.queueSizeLimit&&f.queueSizeLimit!==0){if(b.inArray("onError",f.overrideEvents)<0){alert("The maximum number of queue items has been reached ("+f.queueSizeLimit+"). Please select fewer files.");}if(typeof f.onError==="function"){f.onError.call(g,"QUEUE_LIMIT_EXCEEDED");}}else{for(var m=0;mf.queueSizeLimit&&f.queueSizeLimit!==0){if(b.inArray("onError",f.overrideEvents)<0){alert("The maximum number of queue items has been reached ("+f.queueSizeLimit+"). Please select fewer files.");}if(typeof f.onError==="function"){f.onError.call(g,"QUEUE_LIMIT_EXCEEDED");}}else{for(var o=0;o X
');d.addQueueItem=function(k){if(b.inArray("onAddQueueItem",f.overrideEvents)<0){d.removeExistingFile(k);k.queueItem=d.queueItem.clone();k.queueItem.attr("id",f.id+"-file-"+d.fileID++);k.queueItem.find(".close").bind("click",function(){a.cancel.call(g,k);return false;});var m=k.name;if(m.length>f.truncateLength&&f.truncateLength!=0){m=m.substring(0,f.truncateLength)+"...";}k.queueItem.find(".filename").html(m);k.queueItem.data("file",k);d.queueEl.append(k.queueItem);}if(typeof f.onAddQueueItem==="function"){f.onAddQueueItem.call(g,k);}if(f.fileType){if(b.isArray(f.fileType)){var j=false;for(var l=0;l-1){j=true;}}if(!j){d.error("FORBIDDEN_FILE_TYPE",k);}}else{if(k.type.indexOf(f.fileType)<0){d.error("FORBIDDEN_FILE_TYPE",k);}}}if(k.size>f.fileSizeLimit&&f.fileSizeLimit!=0){d.error("FILE_SIZE_LIMIT_EXCEEDED",k);}else{d.queue.queued++;d.queue.count++;}};d.removeQueueItem=function(m,l,k){if(!k){k=0;}var j=l?0:500;if(m.queueItem){if(m.queueItem.find(".fileinfo").html()!=" - Completed"){m.queueItem.find(".fileinfo").html(" - Cancelled");}m.queueItem.find(".progress-bar").width(0);m.queueItem.delay(k).fadeOut(j,function(){b(this).remove();});delete m.queueItem;d.queue.count--;}};d.filesToUpload=function(){var k=0;for(var j in d.inputs){input=d.inputs[j];limit=input.files.length;for(var l=0;l'+f.buttonText+"
");if(f.buttonClass){d.button.addClass(f.buttonClass);}d.button.css({height:f.height,"line-height":f.height+"px",overflow:"hidden",position:"relative","text-align":"center",width:f.width});g.before(d.button).appendTo(d.button).hide();d.createInput.call(g);d.button.mousemove(function(j){var k=d.button.offset();d.currentInput.css({left:j.pageX-k.left-g.width()+10,top:j.pageY-k.top-g.height()+10});});if(!f.queueID){f.queueID=f.id+"-queue";d.queueEl=b('
');d.button.after(d.queueEl);}else{d.queueEl=b("#"+f.queueID);}if(f.dnd){var h=f.dropTarget?b(f.dropTarget):d.queueEl.get(0);h.addEventListener("dragleave",function(j){j.preventDefault();j.stopPropagation();},false);h.addEventListener("dragenter",function(j){j.preventDefault();j.stopPropagation();},false);h.addEventListener("dragover",function(j){j.preventDefault();j.stopPropagation();},false);h.addEventListener("drop",d.drop,false);}if(!XMLHttpRequest.prototype.sendAsBinary){XMLHttpRequest.prototype.sendAsBinary=function(k){function l(n){return n.charCodeAt(0)&255;}var m=Array.prototype.map.call(k,l);var j=new Uint8Array(m);this.send(j.buffer);};}if(typeof f.onInit==="function"){f.onInit.call(g);}}else{if(typeof f.onFallback==="function"){f.onFallback.call(g);}return false;}});},debug:function(){return this.each(function(){console.log(b(this).data("uploadifive"));});},clearQueue:function(){this.each(function(){var f=b(this),c=f.data("uploadifive"),e=c.settings;for(var d in c.inputs){input=c.inputs[d];limit=input.files.length;for(i=0;i=f.simUploadLimit&&f.simUploadLimit!==0)||(e.uploads.current>=f.uploadLimit&&f.uploadLimit!==0)||(e.uploads.count>=f.uploadLimit&&f.uploadLimit!==0)){return false;}if(f.checkScript){_file.checking=true;skipFile=e.checkExists(_file);_file.checking=false;if(!skipFile){e.uploadFile(_file,true);}}else{e.uploadFile(_file,true);}});if(b("#"+f.queueID).find(".uploadifive-queue-item").not(".error, .complete").size()==0){e.queueComplete();}}else{if(e.uploads.current==0){if(b.inArray("onError",f.overrideEvents)<0){if(e.filesToUpload()>0&&f.uploadLimit!=0){alert("The maximum upload limit has been reached.");}}if(typeof f.onError==="function"){f.onError.call(h,"UPLOAD_LIMIT_EXCEEDED",e.filesToUpload());}}}}});},destroy:function(){this.each(function(){var e=b(this),c=e.data("uploadifive"),d=c.settings;a.clearQueue.call(e);if(!d.queueID){b("#"+d.queueID).remove();}e.siblings("input").remove();e.show().insertBefore(c.button);c.button.remove();if(typeof d.onDestroy==="function"){d.onDestroy.call(e);}});}};b.fn.uploadifive=function(c){if(a[c]){return a[c].apply(this,Array.prototype.slice.call(arguments,1));}else{if(typeof c==="object"||!c){return a.init.apply(this,arguments);}else{b.error("The method "+c+" does not exist in $.uploadify");}}};})(jQuery); \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index ee21c1b..cdd5f27 100644 --- a/templates/base.html +++ b/templates/base.html @@ -22,10 +22,13 @@ + + + @@ -49,7 +52,6 @@ {% include 'inc/facebook_init.html' %} {% include 'inc/ancient_browser.html' %} -
@@ -87,6 +89,7 @@ + @@ -104,6 +107,7 @@ + {% block footerscripts %} {% endblock %} diff --git a/templates/inc/_MixItemInsert.html b/templates/inc/_MixItemInsert.html index 8333fa4..a8eb7e5 100644 --- a/templates/inc/_MixItemInsert.html +++ b/templates/inc/_MixItemInsert.html @@ -1,11 +1,9 @@
- {% if model.has_image %}
mix-logo
- {% endif %}

<%= item.title %>

diff --git a/templates/inc/facebook_init.html b/templates/inc/facebook_init.html index 24c603e..ce23250 100644 --- a/templates/inc/facebook_init.html +++ b/templates/inc/facebook_init.html @@ -19,4 +19,5 @@ js.src = "//connect.facebook.net/en_US/all.js"; ref.parentNode.insertBefore(js, ref); }(document)); - \ No newline at end of file + +
\ No newline at end of file diff --git a/templates/views/UserView.html b/templates/views/UserView.html index 2c457d1..bcba7ac 100644 --- a/templates/views/UserView.html +++ b/templates/views/UserView.html @@ -1,66 +1,112 @@ -
- {% csrf_token %} -
- User details -
- +
+ +
+
+ + {% csrf_token %} +
+
+
+ -
- -
-
-
- +
+ +
+
+
+ -
- -
-
-
- +
+ +
+
+
+ -
- -
-
-
- +
+ +
+
+
+ -
- -
-
-
- +
+ +
+
+
+ -
- - - +
+ + + +
+
+
+
+ + {% csrf_token %} + + +
+
-
-
-
- + +
+
+

Share this activity

+
    +
  • +
    Likes
    + +
  • +
  • +
    Favourites
    + +
  • +
  • +
    Comments
    + +
  • +
+
+
+

On these networks

+
    +
  • +
    Facebook
    + +
  • +
  • +
    Twitter
    + +
  • +
+
+
+
+
-
-
- ‌ -
- + +
\ No newline at end of file